Below is the list of changes that have just been committed into a local
5.1 repository of rafal. When rafal does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-06-23 23:38:51+02:00, rafal@quant.(none) +27 -0
WL#3327/WL#3904 (Online Backup: Error Handling):
This patch introduces basic error handling to backup kernel code.
Function and method signatures are updated so that they return:
a) integer error code in case of functions which report errors themselves,
b) result_t value for functions which (will in the future) return
error description to the caller.
Logic for detecting errors and changing control flow accordingly
is added in many places. In execute_backup_command() (called from
the parser) errors are also sent to the client who issued a command.
Limitations:
- In case of functions returning result_t value, no error details
are provided - only the fact that an error has happened is signalled
(sometimes details are written to the debug trace).
- More extensive testing of error handling logic is needed (WL#3324).
mysql-test/r/backup.result@stripped, 2007-06-23 23:38:45+02:00, rafal@quant.(none) +0 -1
Result update.
mysql-test/r/backup_errors.result@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +24 -0
Results for backup_errors test
mysql-test/r/backup_errors.result@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +0 -0
mysql-test/r/backup_no_data.result@stripped, 2007-06-23 23:38:45+02:00, rafal@quant.(none) +0 -1
Result update.
mysql-test/t/backup.test@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +1 -1
Disable SHOW BACKUP which is not yet implemented.
mysql-test/t/backup_errors.opt@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +1 -0
Error injection option for backup_errors test.
mysql-test/t/backup_errors.opt@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +0 -0
mysql-test/t/backup_errors.test@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +29 -0
Basic test for error handling. This needs to be extended.
mysql-test/t/backup_errors.test@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +0 -0
mysql-test/t/backup_no_data.test@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +1 -3
Disable SHOW BACKUP which is not yet implemented.
sql/backup/api_types.h@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +8 -2
Added method for describing a table referenced by Table_ref object.
sql/backup/archive.cc@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +356 -196
- change methods so that they return result_t value
- add error checking to various methods
- refactoring of Archive_info::read() method - added comments
- added clear() method to Image_info::Tables class
- change read/write logic for table list so that empty lists are correctly
saved and read.
sql/backup/archive.h@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +79 -56
- change methods so that they return result_t values
- update comments and fix typos.
- change the names used to identify backup/restore drivers.
sql/backup/backup_kernel.h@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +51 -30
- add describe() method to Location class
- change methods to return int error code
- new ERROR state for Backup_info structure
- check errors when creating Restore_info instance and report
through is_valid() method
sql/backup/be_default.h@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +16 -11
Adapt Default_image implementation to new signature of Image_info.
Change name to fit to driver naming convention.
sql/backup/data_backup.cc@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +528 -253
- add new CANCELLED backup_state
- change methods so that they return int error code
- add logger object to Backup_pump and Scheduler classes so that they
can report errors
- add cancel_backup() method to Scheduler
- add error checks to functions and methods
- update comments and fix typos
sql/backup/debug.h@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +55 -2
Add error injection macros for testing error code. Document debug macros.
sql/backup/error.h@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +34 -0
Defines helper function report_mysql_error(),
sql/backup/error.h@stripped, 2007-06-23 23:38:48+02:00, rafal@quant.(none) +0 -0
sql/backup/logger.cc@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +40 -29
Add new methods for error reporting.
sql/backup/logger.h@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +81 -4
- add new methods for error reporting
- add methods for saving messages reported with the Logger class
sql/backup/map.h@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +25 -17
Code cleanup.
sql/backup/meta_backup.cc@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +140 -67
- change methods so that they return result_t values or int error code
- add error checks to methods
sql/backup/meta_backup.h@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +24 -9
Change methods so that they return result_t values or int error codes.
sql/backup/sql_backup.cc@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +491 -163
- add helper functions check_info() and report_errors()
- report errors to client in exec_backup_command()
- add more error checks to functions and methods.
- add some error injectors for testing error handling
sql/backup/stream.cc@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +14 -7
stream_result::value needs explicit cast to int type.
sql/backup/stream.h@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +88 -77
Type stream_result::value changed to define cast to type result_t.
Fixed some typos in comments.
sql/backup/string_pool.cc@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +186 -39
- describe a bit the format in which StringPool is saved
- refactor code for reading/writing string pools
- add error detection there
sql/backup/string_pool.h@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +20 -9
Change methods to return result_t value.
sql/share/errmsg.txt@stripped, 2007-06-23 23:38:47+02:00, rafal@quant.(none) +88 -0
Added messages for backup error reporting.
sql/sql_parse.cc@stripped, 2007-06-23 23:38:46+02:00, rafal@quant.(none) +3 -6
Change of comment.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: rafal
# Host: quant.(none)
# Root: /ext/mysql/bk/backup/prototype-errors
--- 1.636/sql/sql_parse.cc 2007-06-23 23:38:58 +02:00
+++ 1.637/sql/sql_parse.cc 2007-06-23 23:38:58 +02:00
@@ -1862,13 +1862,10 @@
{
#ifndef EMBEDDED_LIBRARY
/*
- Sure, it adds one extra level of indirection (one more switch
- inside execute_backup_command) but fortunatelly backup/restore
- statements are not time critical and we can isolate all related
- code into the backup/ source dir.
- */
+ Note: execute_backup_command() sends a correct response to the client
+ (either ok, result set or error message).
+ */
execute_backup_command(thd,lex);
- // TODO: error detection and reporting
#endif
break;
}
--- 1.141/sql/share/errmsg.txt 2007-06-23 23:38:58 +02:00
+++ 1.142/sql/share/errmsg.txt 2007-06-23 23:38:58 +02:00
@@ -3187,6 +3187,8 @@
+ER_CANT_OPEN_TABLE
+ eng "Can't open table: '%-.64s'"
ER_CANT_REOPEN_TABLE
@@ -6053,3 +6055,89 @@
ER_BINLOG_PURGE_EMFILE
eng "Too many files opened, please execute the command again"
+
+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_BACKUP
+ eng "Error during backup operation - server's error log contains more information about the error"
+ER_BACKUP_RESTORE
+ eng "Error during restore operation - server's error log contains more information about the error"
+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_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 "Found %d images in backup archive but maximum %d are supported"
+ER_BACKUP_WRITE_META
+ eng "Error when saving meta-data of %-.64s"
+ER_BACKUP_READ_META
+ eng "Error when reading meta-data list"
+ER_BACKUP_CREATE_META
+ eng "Can't create %-.64s"
+ER_BACKUP_GET_BUF
+ eng "Can't allocate buffer for image data transfer"
+ER_BACKUP_WRITE_DATA
+ eng "Error when writing %-.64s backup image data (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 backup 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 image 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 image data"
+ER_BACKUP_SEND_DATA
+ eng "Error when sending image 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"
--- 1.3/mysql-test/r/backup_no_data.result 2007-06-23 23:38:58 +02:00
+++ 1.4/mysql-test/r/backup_no_data.result 2007-06-23 23:38:58 +02:00
@@ -75,7 +75,6 @@
Database
information_schema
mysql
-SHOW BACKUP 'empty_db.bak';
SHOW DATABASES;
Database
information_schema
--- 1.2/mysql-test/t/backup_no_data.test 2007-06-23 23:38:58 +02:00
+++ 1.3/mysql-test/t/backup_no_data.test 2007-06-23 23:38:58 +02:00
@@ -54,7 +54,7 @@
SHOW DATABASES;
-SHOW BACKUP 'empty_db.bak';
+# SHOW BACKUP 'empty_db.bak';
disconnect backup;
@@ -75,5 +75,3 @@
SHOW TABLES IN empty_db;
DROP DATABASE IF EXISTS empty_db;
-
-
--- New file ---
+++ mysql-test/r/backup_errors.result 07/06/23 23:38:48
DROP DATABASE IF EXISTS adb;
Warnings:
Note 1008 Can't drop database 'adb'; database doesn't exist
DROP DATABASE IF EXISTS bdb;
Warnings:
Note 1008 Can't drop database 'bdb'; database doesn't exist
RESTORE DATABASE FROM 'test.ba';
ERROR HY000: Can't read backup location 'test.ba'
CREATE DATABASE adb;
CREATE DATABASE bdb;
BACKUP DATABASE adb TO '';
ERROR HY000: Can't write to backup location ''
DROP DATABASE IF EXISTS foo;
Warnings:
Note 1008 Can't drop database 'foo'; database doesn't exist
DROP DATABASE IF EXISTS bar;
Warnings:
Note 1008 Can't drop database 'bar'; database doesn't exist
BACKUP DATABASE foo TO 'test.ba';
ERROR HY000: Unknown database 'foo'
BACKUP DATABASE test,foo,bdb,bar TO 'test.ba';
ERROR HY000: Unknown database 'foo,bar'
DROP DATABASE IF EXISTS adb;
DROP DATABASE IF EXISTS bdb;
--- New file ---
+++ mysql-test/t/backup_errors.opt 07/06/23 23:38:48
--loose-debug=d,backup_error_test
--- New file ---
+++ mysql-test/t/backup_errors.test 07/06/23 23:38:48
DROP DATABASE IF EXISTS adb;
DROP DATABASE IF EXISTS bdb;
# non-existent backup archive
-- exec rm -f $MYSQLTEST_VARDIR/master-data/test.ba
-- error 1595
RESTORE DATABASE FROM 'test.ba';
CREATE DATABASE adb;
CREATE DATABASE bdb;
# bad archive name
-- error 1596
BACKUP DATABASE adb TO '';
# non-existent database
DROP DATABASE IF EXISTS foo;
DROP DATABASE IF EXISTS bar;
-- error 1592
BACKUP DATABASE foo TO 'test.ba';
-- error 1592
BACKUP DATABASE test,foo,bdb,bar TO 'test.ba';
# TODO: fix error injection and test more errors
DROP DATABASE IF EXISTS adb;
DROP DATABASE IF EXISTS bdb;
--- 1.31/sql/backup/sql_backup.cc 2007-06-23 23:38:58 +02:00
+++ 1.32/sql/backup/sql_backup.cc 2007-06-23 23:38:58 +02:00
@@ -10,11 +10,14 @@
- Handle SHOW BACKUP ARCHIVE command.
- Add database list to the backup/restore summary.
- Implement meta-data freeze during backup/restore.
- - Error feedback to client.
- Improve logic selecting backup driver for a table.
- Handle other types of meta-data in Backup_info methods.
- - Handle item dependencied when adding new items.
+ - Handle item dependencies when adding new items.
+ - Lock I_S tables when reading table list and similar (is it needed?)
+ - When reading table list from I_S tables, use select conditions to
+ limit amount of data read. (check prepare_select_* functions in sql_help.cc)
- Handle other kinds of backup locations (far future).
+ - Complete feedback given by BACKUP/RESTORE statements (send_summary function).
*/
#include "../mysql_priv.h"
@@ -27,25 +30,40 @@
#include "be_default.h"
#include "debug.h"
-//int sortcmp(const String *s,const String *t, CHARSET_INFO *cs);
-
namespace backup {
-// TODO: handle charsets consistently
-CHARSET_INFO *default_charset= table_alias_charset;
+// Helper functions
+
+static IStream* open_for_read(const Location&);
+static OStream* open_for_write(const Location&);
+
+/*
+ Report errors. The main error code and optional arguments for its description
+ are given plus a logger object which can contain stored errors.
+ */
+static int report_errors(THD*,int, Logger&,...);
-IStream* open_for_read(const Location&);
-OStream* open_for_write(const Location&);
+/*
+ Check if info object is valid. If not, report error to client.
+ */
+static int check_info(THD*,Backup_info&);
+static int check_info(THD*,Restore_info&);
-bool send_summary(THD*,const Backup_info&);
-bool send_summary(THD*,const Restore_info&);
+static bool send_summary(THD*,const Backup_info&);
+static bool send_summary(THD*,const Restore_info&);
+#ifdef DBUG_BACKUP
+// Flag used for testing error reporting
+bool test_error_flag= FALSE;
+#endif
}
/**
Call backup kernel API to execute backup related SQL statement.
@param lex results of parsing the statement.
+
+ @note This function sends response to the client (ok, result set or error).
*/
int
@@ -59,9 +77,12 @@
backup::Location *loc= backup::Location::find(lex->backup_dir);
if (!loc)
- DBUG_RETURN(-1);
+ {
+ my_error(ER_BACKUP_INVALID_LOC,MYF(0),lex->backup_dir);
+ DBUG_RETURN(ER_BACKUP_INVALID_LOC);
+ }
- int res= -1;
+ int res= 0;
switch (lex->sql_command) {
@@ -71,30 +92,68 @@
backup::IStream *stream= backup::open_for_read(*loc);
if (!stream)
- DBUG_RETURN(-2);
-
- backup::Restore_info info(*stream);
-
- if (lex->sql_command == SQLCOM_SHOW_ARCHIVE)
{
- // FIXME: implement this
- // res= mysql_show_archive(thd,info);
- ::send_ok(thd);
+ res= -1;
+ my_error(ER_BACKUP_READ_LOC,MYF(0),loc->describe());
+ goto finish_restore;
}
else
{
- // TODO: freeze all DDL operations
+ backup::Restore_info info(*stream);
- info.restore_all_dbs();
- res= mysql_restore(thd,info,*stream);
+ if (check_info(thd,info))
+ {
+ res= -2;
+ goto finish_restore;
+ }
+
+ if (lex->sql_command == SQLCOM_SHOW_ARCHIVE)
+ {
+ my_error(ER_NOT_ALLOWED_COMMAND,MYF(0));
+ /*
+ Uncomment when mysql_show_archive() is implemented
+
+ res= mysql_show_archive(thd,info);
+ */
+ }
+ else
+ {
+ info.report_error(backup::log_level::INFO,ER_BACKUP_RESTORE_START);
+
+ // TODO: freeze all DDL operations here
+
+ info.save_errors();
+ info.restore_all_dbs();
+
+ if (check_info(thd,info))
+ {
+ res= -3;
+ goto finish_restore;
+ }
+
+ info.clear_saved_errors();
+
+ if (mysql_restore(thd,info,*stream))
+ {
+ res= -4;
+ report_errors(thd,ER_BACKUP_RESTORE,info);
+ }
+ else
+ {
+ info.report_error(backup::log_level::INFO,ER_BACKUP_RESTORE_DONE);
+ info.total_size += info.header_size;
+ send_summary(thd,info);
+ }
+
+ // TODO: unfreeze DDL here
+ }
+ } // if (!stream)
- info.total_size += info.header_size;
- // TODO: unfreeze DDL
+ finish_restore:
- send_summary(thd,info);
- }
+ if (stream)
+ stream->close();
- stream->close();
break;
}
@@ -103,64 +162,113 @@
backup::OStream *stream= backup::open_for_write(*loc);
if (!stream)
- DBUG_RETURN(-2);
-
- backup::Backup_info info(thd);
-
- // TODO: freeze all DDL operations
-
- if (lex->db_list.is_empty())
- info.add_all_dbs(); // backup all databases
+ {
+ res= -5;
+ my_error(ER_BACKUP_WRITE_LOC,MYF(0),loc->describe());
+ goto finish_backup;
+ }
else
- info.add_dbs(lex->db_list); // backup databases specified by user
-
- info.close();
-
- res= mysql_backup(thd,info,*stream);
-
- // TODO: unfreeze DDL
+ {
+ backup::Backup_info info(thd);
- send_summary(thd,info);
+ if (check_info(thd,info))
+ {
+ res= -6;
+ goto finish_backup;
+ }
+
+ info.report_error(backup::log_level::INFO,ER_BACKUP_BACKUP_START);
+
+ // TODO: freeze all DDL operations here
+
+ info.save_errors();
+
+ if (lex->db_list.is_empty())
+ {
+ info.write_message(backup::log_level::INFO,"Backing up all databases");
+ info.add_all_dbs(); // backup all databases
+ }
+ else
+ {
+ info.write_message(backup::log_level::INFO,"Backing up selected databases");
+ info.add_dbs(lex->db_list); // backup databases specified by user
+ }
+
+ if (check_info(thd,info))
+ {
+ res= -7;
+ goto finish_backup;
+ }
+
+ info.close();
+
+ if (check_info(thd,info))
+ {
+ res= -8;
+ goto finish_backup;
+ }
+
+ info.clear_saved_errors();
+
+ if (mysql_backup(thd,info,*stream))
+ {
+ res= -9;
+ report_errors(thd,ER_BACKUP_BACKUP,info);
+ }
+ else
+ {
+ info.report_error(backup::log_level::INFO,ER_BACKUP_BACKUP_DONE);
+ send_summary(thd,info);
+ }
+
+ // TODO: unfreeze DDL here
+ } // if (!stream)
- // TODO: report errors to the client
+ finish_backup:
- stream->close();
+ if (stream)
+ stream->close();
+#ifdef DBUG_BACKUP
backup::IStream *is= backup::open_for_read(*loc);
if (is)
{
dump_stream(*is);
is->close();
}
+#endif
break;
}
- default: DBUG_ASSERT(FALSE);
+ default:
+ /*
+ execute_backup_command() should be called with correct command id
+ from the parser. If not, we fail on this assertion.
+ */
+ DBUG_ASSERT(FALSE);
}
loc->free();
-
- //::send_ok(thd);
DBUG_RETURN(res);
}
+
/*************************************************
*
* BACKUP
*
*************************************************/
-
// Declarations for functions used in backup operation
namespace backup {
// defined in meta_backup.cc
-bool write_meta_data(THD*, Backup_info&, OStream&);
+int write_meta_data(THD*, Backup_info&, OStream&);
// defined in data_backup.cc
-bool write_table_data(THD*, Backup_info&, OStream&);
+int write_table_data(THD*, Backup_info&, OStream&);
} // backup namespace
@@ -175,49 +283,55 @@
{
DBUG_ENTER("mysql_backup");
+ // This function should not be called with invalid backup info.
DBUG_ASSERT(info.is_valid());
size_t start_bytes= s.bytes;
+ int res= 0;
BACKUP_SYNC("backup_meta");
DBUG_PRINT("backup",("Writing image header"));
- info.save(s);
+ if (info.save(s))
+ {
+ res= -1;
+ goto finish;
+ }
DBUG_PRINT("backup",("Writing meta-data"));
- backup::write_meta_data(thd,info,s);
+ if (backup::write_meta_data(thd,info,s))
+ {
+ res= -2;
+ goto finish;
+ }
DBUG_PRINT("backup",("Writing table data"));
BACKUP_SYNC("backup_data");
- if(!backup::write_table_data(thd,info,s))
- goto error;
+ if (backup::write_table_data(thd,info,s))
+ {
+ res= -3;
+ goto finish;
+ }
DBUG_PRINT("backup",("Backup done."));
BACKUP_SYNC("backup_done");
info.total_size= s.bytes - start_bytes;
- goto end;
-
- error:
-
- DBUG_PRINT("backup",("Error creating backup image"));
-
- end:
-
- DBUG_RETURN(0);
+ finish:
+ DBUG_RETURN(res);
}
/*
Logic for selecting image format for a given table location:
1. If tables from that location have been already stored in one of the
- sub-images then choose that subimage.
+ sub-images then choose that sub-image.
2. If location has "native" backup format, put it in a new sub-image with
that format.
@@ -245,6 +359,13 @@
{ return m_hton; }
};
+/**
+ Find image to which given table can be added and add it.
+
+ Creates new images as needed.
+
+ @returns Position of the image found in @c images[] table or -1 on error.
+ */
int Backup_info::find_image(const Backup_info::Table_ref &tbl)
{
DBUG_ENTER("Backup_info::find_image");
@@ -252,6 +373,7 @@
const ::handlerton *hton= tbl.hton();
Image_info *img;
+ // If table has no handlerton something is really bad - we crash here
DBUG_ASSERT(hton);
DBUG_PRINT("backup",("Adding table %s.%s (%s,%p) to archive.",
@@ -269,7 +391,7 @@
// Point 3: try existing images but not the default one.
- for (uint no=0; no < img_count && no < 256 ; ++no)
+ for (uint no=0; no < img_count && no < MAX_IMAGES ; ++no)
{
if (default_image_no >= 0 && no == (uint)default_image_no)
continue;
@@ -287,11 +409,17 @@
{
uint no= img_count;
- DBUG_ASSERT(no<256);
+ // We handle at most MAX_IMAGES many images
+ DBUG_ASSERT(no<MAX_IMAGES);
img= images[no]= new Native_image(*this,hton);
- DBUG_ASSERT(images[no]);
+ if (!img)
+ {
+ report_error(ER_OUT_OF_RESOURCES);
+ DBUG_RETURN(-1);
+ }
+
DBUG_PRINT("backup",("%s image added to archive",img->name()));
img_count++;
@@ -311,7 +439,13 @@
default_image_no= img_count;
img= images[default_image_no]= new Default_image(*this);
- DBUG_ASSERT(img);
+
+ if (!img)
+ {
+ report_error(ER_OUT_OF_RESOURCES);
+ DBUG_RETURN(-1);
+ }
+
DBUG_PRINT("backup",("Default image added to archive"));
img_count++;
}
@@ -319,7 +453,8 @@
if (img->accept(tbl,hton))
DBUG_RETURN(default_image_no);
- DBUG_PRINT("backup",("Table ignored."));
+ Table_ref::describe_buf buf;
+ report_error(ER_BACKUP_NO_BACKUP_DRIVER,tbl.describe(buf,sizeof(buf)));
DBUG_RETURN(-1);
}
@@ -338,7 +473,7 @@
TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st);
/**
- Create @c Backup_info structure and prepare it for populationg with meta-data
+ Create @c Backup_info structure and prepare it for populating with meta-data
items.
When adding a complete database to the archive, all its tables are added.
@@ -351,6 +486,11 @@
m_items(NULL), m_last_item(NULL), m_last_db(NULL)
{
i_s_tables= get_schema_table(m_thd, ::get_schema_table(SCH_TABLES));
+ if (!i_s_tables)
+ {
+ report_error(ER_BACKUP_LIST_TABLES);
+ m_state= ERROR;
+ }
}
Backup_info::~Backup_info()
@@ -380,6 +520,8 @@
*/
bool Backup_info::close()
{
+ bool ok= is_valid();
+
if(i_s_tables)
::free_tmp_table(m_thd,i_s_tables);
i_s_tables= NULL;
@@ -387,7 +529,18 @@
if (m_state == INIT)
m_state= READY;
- return TRUE; //open_tables();
+ return ok;
+}
+
+int Backup_info::save(OStream &s)
+{
+ if (OK != Archive_info::save(s))
+ {
+ report_error(ER_BACKUP_WRITE_HEADER);
+ return -1;
+ }
+
+ return 0;
}
/**
@@ -416,10 +569,12 @@
/**
Add to archive all databases in the list.
*/
-bool Backup_info::add_dbs(List<LEX_STRING> &dbs)
+int Backup_info::add_dbs(List<LEX_STRING> &dbs)
{
List_iterator<LEX_STRING> it(dbs);
+ int res= 0;
LEX_STRING *s;
+ String unknown_dbs; // comma separated list of databases which don't exist
while ((s= it++))
{
@@ -427,21 +582,43 @@
if (db_exists(db))
{
+ // In case of error, we just compose unknown_dbs list
+ if (res)
+ continue;
+
Db_item *it= add_db(db);
if (!it)
- return FALSE;
-
- bool res= add_db_items(*it);
-
- if (!res)
- return FALSE;
+ {
+ res= -1;
+ goto finish;
+ }
+
+ if (add_db_items(*it))
+ {
+ res= -2;
+ goto finish;
+ }
}
else
- DBUG_PRINT("backup",("warning: skipping unknown database %s",s->str));
+ {
+ if (res)
+ unknown_dbs.append(",");
+ else
+ res= -3;
+ unknown_dbs.append(db.name());
+ }
}
- return TRUE;
+ if (res == -3)
+ report_error(ER_BAD_DB_ERROR,unknown_dbs.c_ptr());
+
+ finish:
+
+ if (res)
+ m_state= ERROR;
+
+ return res;
}
static const LEX_STRING is_string = { C_STRING_WITH_LEN("information_schema") };
@@ -451,26 +628,32 @@
Add to archive all instance's databases (except the internal ones).
*/
-bool Backup_info::add_all_dbs()
+int Backup_info::add_all_dbs()
{
+ DBUG_PRINT("backup", ("Reading databases from I_S"));
+
::TABLE *db_table = get_schema_table(m_thd, ::get_schema_table(SCH_SCHEMATA));
if (!db_table)
{
- DBUG_PRINT("backup",("Can't open I_S.SCHEMATA table!"));
- return FALSE;
+ report_error(ER_BACKUP_LIST_DBS);
+ return -1;
}
::handler *ha = db_table->file;
// TODO: locking
- DBUG_PRINT("backup", ("Adding all databases to the archive."));
-
const Db_ref is_db(is_string);
const Db_ref mysql_db(mysql_string);
+ int res= 0;
- ha->ha_rnd_init(TRUE);
+ if (ha->ha_rnd_init(TRUE))
+ {
+ res= -2;
+ report_error(ER_BACKUP_LIST_DBS);
+ goto finish;
+ }
while (!ha->rnd_next(db_table->record[0]))
{
@@ -487,21 +670,31 @@
Db_item *it= add_db(db);
if (!it)
- return FALSE;
-
- bool res= add_db_items(*it);
+ {
+ res= -3;
+ goto finish;
+ }
- if (!res)
- return FALSE;
+ if (add_db_items(*it))
+ {
+ res= -4;
+ goto finish;
+ }
}
DBUG_PRINT("backup", ("No more databases in I_S"));
ha->ha_rnd_end();
- db_table->file->close();
+ finish:
+
+ if (ha)
+ ha->close();
+
+ if (res)
+ m_state= ERROR;
- return TRUE;
+ return res;
}
/**
@@ -511,8 +704,21 @@
Backup_info::add_db(const backup::Db_ref &db)
{
StringPool::Key key= db_names.add(db.name());
+
+ if (!key.is_valid())
+ {
+ report_error(ER_OUT_OF_RESOURCES);
+ return NULL;
+ }
+
Db_item *di= new Db_item(*this,key);
+ if (!di)
+ {
+ report_error(ER_OUT_OF_RESOURCES);
+ return NULL;
+ }
+
// insert db item before all table items
if (!m_last_db)
@@ -524,6 +730,7 @@
}
else
{
+ // since m_last_db is not NULL, m_items list can't be empty
DBUG_ASSERT(m_items);
di->next= m_last_db->next;
@@ -538,40 +745,66 @@
/**
Add to archive all items belonging to a given database.
*/
-bool Backup_info::add_db_items(Db_item &db)
+int Backup_info::add_db_items(Db_item &db)
{
- DBUG_ASSERT(is_valid());
-
- // TODO: add other items (views, stored routines,...)
+ DBUG_ASSERT(m_state == INIT); // should be called before structure is closed
+ DBUG_ASSERT(is_valid()); // should be valid
+ DBUG_ASSERT(i_s_tables->file); // i_s_tables should be opened
// add all tables from db.
::handler *ha= i_s_tables->file;
- // TODO: do it properly: use SELECT conditions
+ /*
+ If error debugging is switched on (see debug.h) then I_S.TABLES access
+ error will be triggered when backing up database whose name starts with 'a'.
+ */
+ TEST_ERROR_IF(db.name().ptr()[0]=='a');
+
+ if (ha->ha_rnd_init(TRUE) || TEST_ERROR)
+ {
+ report_error(ER_BACKUP_LIST_DB_TABLES,db.name().ptr());
+ return -1;
+ }
- ha->ha_rnd_init(TRUE);
+ int res= 0;
while (!ha->rnd_next(i_s_tables->record[0]))
{
const Backup_info::Table_ref t(i_s_tables);
- if( t.db() == db )
+ if (!t.hton())
+ {
+ Table_ref::describe_buf buf;
+ report_error(ER_NO_STORAGE_ENGINE,t.describe(buf,sizeof(buf)));
+ res= -2;
+ break;
+ }
+
+ if (t.db() == db)
{
DBUG_PRINT("backup", ("Found table %s for database %s",
t.name().ptr(), t.db().name().ptr()));
// add_table method selects/creates sub-image appropriate for storing given table
Table_item *ti= add_table(db,t);
- DBUG_ASSERT(ti);
-
- add_table_items(*ti);
+ if (!ti)
+ {
+ res= -3;
+ break;
+ }
+
+ if (add_table_items(*ti))
+ {
+ res= -4;
+ break;
+ }
}
}
ha->ha_rnd_end();
- return TRUE;
+ return res;
}
/// Gets table identity from a row of I_S.TABLES table
@@ -591,7 +824,6 @@
name_lex.length= engine_name.length();
m_hton= ::ha_resolve_by_name(::current_thd,&name_lex);
- DBUG_ASSERT(m_hton);
}
/**
@@ -602,16 +834,48 @@
Backup_info::add_table(Db_item &db,
const Table_ref &t)
{
- int no= find_image(t);
+ int no= find_image(t); // Note: find_image reports errors
+
+ /*
+ If error debugging is switched on (see debug.h) then any table whose
+ name starts with 'a' will trigger "no backup driver" error.
+ */
+ TEST_ERROR_IF(t.name().ptr()[0]=='a');
+
+ if (no < 0 || TEST_ERROR)
+ return NULL;
+
+ /*
+ If image was found then images[no] should point at the Image_info
+ object
+ */
+ Image_info *img= images[no];
+ DBUG_ASSERT(img);
+
+ /*
+ If error debugging is switched on (see debug.h) then any table whose
+ name starts with 'b' will trigger error when added to backup image.
+ */
+ TEST_ERROR_IF(t.name().ptr()[0]=='b');
- DBUG_ASSERT(no >= 0 && images[no]);
+ int tno= img->tables.add(t);
+ if (tno < 0 || TEST_ERROR)
+ {
+ Table_ref::describe_buf buf;
+ report_error(ER_BACKUP_NOT_ACCEPTED,img->name(),t.describe(buf,sizeof(buf)));
+ return NULL;
+ }
- int tno= images[no]->tables.add(t);
- DBUG_ASSERT(tno >= 0);
table_count++;
Table_item *ti= new Table_item(*this,no,tno);
+ if (!ti)
+ {
+ report_error(ER_OUT_OF_RESOURCES);
+ return NULL;
+ }
+
// add to the end of items list
ti->next= NULL;
@@ -627,14 +891,15 @@
/**
Add to archive all items belonging to a given table.
*/
-bool Backup_info::add_table_items(Table_item&)
+int Backup_info::add_table_items(Table_item&)
{
- // FIXME: complete this
- return TRUE;
+ // TODO: Implement when we handle per-table meta-data.
+ return 0;
}
} // backup namespcae
+
/*************************************************
*
* RESTORE
@@ -645,15 +910,21 @@
namespace backup {
+// defined in meta_backup.cc
bool restore_meta_data(THD*, Restore_info&, IStream&);
+
+// defined in data_backup.cc
bool restore_table_data(THD*, Restore_info&, IStream&);
} // backup namespace
-// pre: archive info has been already read and the stream is positioned
-// at archive meta-data
+/**
+ Restore items saved in backup archive.
+ @pre archive info has been already read and the stream is positioned
+ at archive's meta-data
+*/
int mysql_restore(THD *thd, backup::Restore_info &info, backup::IStream &s)
{
DBUG_ENTER("mysql_restore");
@@ -662,20 +933,19 @@
DBUG_PRINT("restore",("Restoring meta-data"));
- bool res= backup::restore_meta_data(thd, info, s);
- DBUG_ASSERT(res);
+ if (backup::restore_meta_data(thd, info, s))
+ DBUG_RETURN(-1);
DBUG_PRINT("restore",("Restoring table data"));
- res= backup::restore_table_data(thd,info,s);
- DBUG_ASSERT(res);
+ if (backup::restore_table_data(thd,info,s))
+ DBUG_RETURN(-2);
DBUG_PRINT("restore",("Done."));
info.total_size= s.bytes - start_bytes;
DBUG_RETURN(0);
-
}
/*************************************************
@@ -694,7 +964,11 @@
{ return SERVER_FILE; }
File_loc(const char *p)
- { path.append(p); } // files_charset_info
+ { path.append(p); }
+
+ const char* describe()
+ { return path.c_ptr(); }
+
};
@@ -708,10 +982,11 @@
const File_loc &f= static_cast<const File_loc&>(loc);
S *s= new S(f.path);
- if (s)
- s->open();
+ if (s && s->open())
+ return s;
- return s;
+ delete s;
+ return NULL;
}
default:
@@ -754,7 +1029,6 @@
return new File_loc(where.str);
}
-
} // backup namespace
@@ -769,6 +1043,56 @@
namespace backup {
/**
+ Report errors.
+
+ Current implementation reports the last error saved in the logger if it exist.
+ Otherwise it reports error given by @c error_code.
+ */
+int report_errors(THD *thd,int error_code, Logger &log, ...)
+{
+ MYSQL_ERROR *error= log.last_saved_error();
+
+ if (error && !util::report_mysql_error(thd,error,error_code))
+ if (error->code)
+ error_code= error->code;
+ else // there are no error information in the logger - report error_code
+ {
+ char buf[ERRMSGSIZE + 20];
+ va_list args;
+ va_start(args,log);
+
+ my_vsnprintf(buf,sizeof(buf),ER_SAFE(error_code),args);
+ my_printf_error(error_code,buf,MYF(0));
+
+ va_end(args);
+ }
+
+ return error_code;
+}
+
+
+template<class Info>
+static
+int check_info(THD *thd, Info &info, int error_code)
+{
+ if (info.is_valid())
+ return OK;
+
+ return report_errors(thd,error_code,info);
+}
+
+template int check_info(THD*,Backup_info&,int);
+template int check_info(THD*,Restore_info&,int);
+
+inline
+int check_info(THD *thd, Backup_info &info)
+{ return check_info<Backup_info>(thd,info,ER_BACKUP_BACKUP_PREPARE); }
+
+inline
+int check_info(THD *thd, Restore_info &info)
+{ return check_info<Restore_info>(thd,info,ER_BACKUP_RESTORE_PREPARE); }
+
+/**
Send a summary of the backup/restore operation to the client.
The data about the operation is taken from filled @c Archive_info
@@ -778,6 +1102,7 @@
TODO: Add list of databases in the output.
*/
+static
bool send_summary(THD *thd, const Archive_info &info, bool backup)
{
Protocol *protocol= ::current_thd->protocol; // client comms
@@ -806,32 +1131,35 @@
Loop through the catalog and list the contents by
database.
- List<schema_ref> cat1= *catalog;
- List_iterator<schema_ref> cat(cat1);
- schema_ref *tmp;
-
- for (Archive_info::Db_iterator it(info); it; ++it)
- while ((tmp= cat++))
- {
- uint howmuch= 0;
-
- for (Archive_info::DItem_iterator dit(it); dit; ++dit)
- howmuch++;
-
- my_snprintf(buf,sizeof(buf),"%s %3u %s in database %s.",
- backup? "Backed up" : "Restored",
- howmuch, howmuch != 1 ? "tables" : " table",
- it->name().c_ptr());
+ Enable this code when functions for browsing archive contents are
+ implemented.
+
+ List<schema_ref> cat1= *catalog;
+ List_iterator<schema_ref> cat(cat1);
+ schema_ref *tmp;
+
+ for (Archive_info::Db_iterator it(info); it; ++it)
+ while ((tmp= cat++))
+ {
+ uint howmuch= 0;
+
+ for (Archive_info::DItem_iterator dit(it); dit; ++dit)
+ howmuch++;
+ my_snprintf(buf,sizeof(buf),"%s %3u %s in database %s.",
+ backup? "Backed up" : "Restored",
+ howmuch, howmuch != 1 ? "tables" : " table",
+ it->name().c_ptr());
+
+ protocol->prepare_for_resend();
+ protocol->store(buf,system_charset_info);
+ protocol->write();
+ }
+
+ my_snprintf(buf,sizeof(buf)," ");
protocol->prepare_for_resend();
protocol->store(buf,system_charset_info);
protocol->write();
- }
-
- my_snprintf(buf,sizeof(buf)," ");
- protocol->prepare_for_resend();
- protocol->store(buf,system_charset_info);
- protocol->write();
*/
my_snprintf(buf,sizeof(buf)," header = %-8lu bytes",(unsigned long)info.header_size);
@@ -873,10 +1201,6 @@
/// Open given table in @c INFORMATION_SCHEMA database.
-
-// TODO: Use select condition to get only selected rows from a table
-// CHECK: prepare_select_* functions (sql_help.cc)
-
TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st)
{
TABLE *t;
@@ -884,7 +1208,7 @@
bzero( &arg, sizeof(TABLE_LIST) );
- /* set context for create_schema_table call */
+ // set context for create_schema_table call
arg.schema_table= st;
arg.alias= NULL;
arg.select_lex= NULL;
@@ -894,8 +1218,8 @@
if( !t ) return NULL; // error!
/*
- Temporarily set thd->lex->wild to NULL to keep st->fill_table
- happy :)
+ Temporarily set thd->lex->wild to NULL to keep st->fill_table
+ happy :)
*/
LEX local_lex= *thd->lex;
@@ -904,16 +1228,19 @@
thd->lex= &local_lex;
local_lex.wild = NULL;
+ // set thd->lex->sql_command to something neutral (see, make_db_list/get_index_file_values).
local_lex.sql_command = enum_sql_command(0);
- // Note: set thd->lex->sql_command to something neutral (see, make_db_list/get_index_file_values).
-
/* context for fill_table */
arg.table= t;
+ /*
+ Question: is it correct to fill I_S table each time we use it or should it
+ be filled only once?
+ */
st->fill_table(thd,&arg,NULL); // NULL = no select condition
- /* restore wild field */
+ // restore wild field
thd->lex= saved_lex;
return t;
@@ -921,10 +1248,11 @@
/// Build linked @c TABLE_LIST list from a list stored in @c Table_list object.
-// FIXME: build list with the same order as in input
-// Actually, should work fine with reversed list as long as we use the reversed
-// list both in table writing and reading.
-
+/*
+ FIXME: build list with the same order as in input
+ Actually, should work fine with reversed list as long as we use the reversed
+ list both in table writing and reading.
+ */
TABLE_LIST *build_table_list(const Table_list &tables, thr_lock_type lock)
{
TABLE_LIST *tl= NULL;
@@ -932,7 +1260,7 @@
for( uint tno=0; tno < tables.count() ; tno++ )
{
TABLE_LIST *ptr= (TABLE_LIST*)sql_alloc(sizeof(TABLE_LIST));
- DBUG_ASSERT(ptr);
+ DBUG_ASSERT(ptr); // FIXME: report error instead
bzero(ptr,sizeof(TABLE_LIST));
Table_ref tbl= tables[tno];
--- 1.5/sql/backup/api_types.h 2007-06-23 23:38:58 +02:00
+++ 1.6/sql/backup/api_types.h 2007-06-23 23:38:58 +02:00
@@ -129,8 +129,14 @@
bool operator!=(const Table_ref &db) const
{ return ! this->operator==(db); }
- // Produce string identifying the table (e.g. for error reporting)
- //operator String() const;
+ typedef char describe_buf[512];
+
+ /// Produce string identifying the table (e.g. for error reporting)
+ const char* describe(char *buf, size_t len) const
+ {
+ my_snprintf(buf,len,"%s.%s",db().name().ptr(),name().ptr());
+ return buf;
+ }
protected:
--- 1.6/sql/backup/archive.cc 2007-06-23 23:38:58 +02:00
+++ 1.7/sql/backup/archive.cc 2007-06-23 23:38:58 +02:00
@@ -10,12 +10,15 @@
TODO:
- Add to Archive_info storage for other meta-data items.
- - Make existing storage solutions more rational.
+ - Make existing storage solutions more rational (e.g., string pool).
- Make reading code resistant to unknown image formats or meta-data types
(or, assume it is handled by format version number).
- Improve Image_info::Tables implementation (use some existing data structure).
- - Add more information to backup archive header.
-
+ - Add more information to backup archive header , for example server's version
+ string.
+ - Handle backward compatibility (new code reads archive with earlier version
+ number)
+ - Add to Archive_info methods for browsing contents of the archive.
*/
#include "backup_engine.h"
@@ -76,14 +79,11 @@
@c Image_info::write_description() method.
For list of databases and tables the format in which they are saved is
- defined by @c StringPool::save() and @c Archive_info::Tbles::save() methods
+ defined by @c StringPool::save() and @c Archive_info::Tables::save() methods
respectively.
-
- TODO: Add more info to the header. E.g. version number of the server on
- which archive was created.
*/
-stream_result::value Archive_info::save(OStream &s)
+result_t Archive_info::save(OStream &s)
{
DBUG_ENTER("Archive_info::save");
@@ -92,135 +92,210 @@
res= s.write2int(ver);
if (res != stream_result::OK)
- DBUG_RETURN(res);
+ {
+ DBUG_PRINT("backup",("Can't write archive version number (stream_res=%d)",(int)res));
+ DBUG_RETURN(ERROR);
+ }
- // list of images
+ // write list of images
DBUG_PRINT("backup",(" writing image list"));
uint ino;
Image_info *img;
- for (ino=0; ino <= img_count ; ++ino)
+ for (ino=0; ino < img_count ; ++ino)
if ((img= images[ino]))
{
DBUG_PRINT("backup",(" %2d: %s image",ino,img->name()));
- res= img->write_description(s);
- if (res != stream_result::OK)
- DBUG_RETURN(res);
+ if (ERROR == img->write_description(s))
+ {
+ DBUG_PRINT("backup",("Can't write description of %s image (#%d)",
+ img->name(),ino));
+ DBUG_RETURN(ERROR);
+ }
}
+ // close the header chunk
res= s.end_chunk();
if (res != stream_result::OK)
- DBUG_RETURN(res);
+ {
+ DBUG_PRINT("backup",("Error when closing image list chunk (stream_res=%d)",(int)res));
+ DBUG_RETURN(ERROR);
+ }
- // catalogue
- DBUG_PRINT("backup",(" writing archive catalog"));
- // db names
- res= db_names.save(s);
- if (res != stream_result::OK)
- DBUG_RETURN(res);
+ // write catalogue
+ DBUG_PRINT("backup",(" writing archive catalogue"));
+
+ // db names (one chunk)
+ if (ERROR == db_names.save(s)) // note: this closes the chunk
+ {
+ DBUG_PRINT("backup",("Error saving pool of db names (stream_res=%d)",(int)res));
+ DBUG_RETURN(ERROR);
+ }
- // table lists
- for (ino=0; ino <= img_count ; ++ino)
+ // table lists (one chunk per image/list)
+ for (ino=0; ino < img_count ; ++ino)
if ((img= images[ino]))
{
DBUG_PRINT("backup",(" saving %s image's tables",img->name()));
- res= img->tables.save(s);
- if (res != stream_result::OK)
- DBUG_RETURN(res);
+ if (ERROR == img->tables.save(s))
+ {
+ DBUG_PRINT("backup",("Error saving tables (stream_res=%d)",(int)res));
+ DBUG_RETURN(ERROR);
+ }
}
header_size= s.bytes - start_bytes;
- DBUG_RETURN(res);
+ DBUG_RETURN(OK);
}
/**
Fill @c Archive_info structure reading data from backup archive header and
catalogue.
+
+ @returns OK or ERROR
*/
-stream_result::value Archive_info::read(IStream &s)
+result_t Archive_info::read(IStream &s)
{
DBUG_ENTER("Archive_info::read");
size_t start_bytes= s.bytes;
- stream_result::value res;
+ result_t res;
version_t ver;
- // read version number
- res= s.read2int(ver);
- if (res != stream_result::OK)
- DBUG_RETURN(stream_result::ERROR); // neither stream nor chunk should end here
+ /*
+ We read archive's header which starts with archive format version number.
+ If we can't read the version number (end of stream or no data in the chunk)
+ there is something wrong and we signal error.
+ */
+
+ stream_result::value rres= s.read2int(ver);
+ if ( rres != stream_result::OK)
+ {
+ DBUG_PRINT("restore",("Error reading archive version number"
+ " (stream_res=%d)",(int)rres));
+ DBUG_RETURN(ERROR);
+ }
if (ver != Archive_info::ver)
- DBUG_RETURN(stream_result::ERROR); // wrong version number
- // (in future handle backward compatibility)
+ {
+ DBUG_PRINT("restore",("Backup archive version %d not supported",ver));
+ DBUG_RETURN(ERROR);
+ }
+
+ /*
+ What follows (until the end of the data chunk) is a list of entries
+ describing data images of the archive. It is read using
+ Image_info::create_from_stream() function which returns DONE when end of
+ chunk is reached.
+ */
DBUG_PRINT("restore",(" reading image list"));
- uint ino;
- Image_info *img;
+ uint ino= 0;
- for (ino=0 ;
- ino<256 && (img= Image_info::create_from_stream(*this,s)) ;
- ino++)
- {
- images[ino]= img;
- if (!img)
- DBUG_RETURN(stream_result::ERROR);
- DBUG_PRINT("restore",(" %2d: %s image",ino,img->name()));
- }
+ do
+ {
+ Image_info *img;
+
+ res= Image_info::create_from_stream(*this,s,img);
+
+ if (res == OK)
+ {
+ DBUG_ASSERT(img);
+ DBUG_PRINT("restore",(" %2d: %s image",ino,img->name()));
+ images[ino++]= img;
+ }
+
+ } while (res == OK && ino < MAX_IMAGES);
img_count= ino;
- table_count= 0;
- if (res == stream_result::EOS) // empty archive
- DBUG_RETURN(res);
+ // If res != DONE we haven't reached end of the chunk - something is wrong
+ if (res != DONE)
+ {
+ DBUG_PRINT("restore",("Error when reading image list (%d images read)",
+ img_count));
+ return ERROR;
+ }
+
+ /*
+ Next chunk starts archive's catalogue. We proceed with reading it.
+ Note that the catalogue should always contain at least one chunk (db names
+ pool) and hence we should not hit end of stream here.
+ */
- // move to the catalogue
- res= s.next_chunk();
- if (res != stream_result::OK)
- DBUG_RETURN(stream_result::ERROR); // neither stream nor chunk should end here
+ table_count= 0;
- DBUG_PRINT("restore",(" reading catalog (%d images)",img_count));
+ if (s.next_chunk() != stream_result::OK)
+ {
+ DBUG_PRINT("restore",("Can't proceed to the catalogue"));
+ DBUG_RETURN(ERROR);
+ }
+
+ DBUG_PRINT("restore",(" reading catalogue (%d images)",img_count));
- // read db_names pool
+ /*
+ First chunk of the catalogue contains db names pool - we read it and
+ proceed to the next chunk. We should never hit end of stream here and
+ so the only acceptable result of db.names.read() is OK.
+ */
res= db_names.read(s);
- if (res != stream_result::OK)
- DBUG_RETURN(stream_result::ERROR); // neither stream nor chunk should end here
+ if (res != OK)
+ {
+ DBUG_PRINT("restore",("Can't read db names pool (res=%d)",(int)res));
+ DBUG_RETURN(ERROR);
+ }
- // read tables
+ /*
+ The following chunks contain lists of tables for each image. There are
+ as many lists as there are images (possibly 0) and each list occupies
+ one chunk.
+ */
for (uint ino=0; ino < img_count; ++ino)
{
- img= images[ino];
- DBUG_PRINT("restore",(" reading %s image's tables (ino=%d)",img->name(),ino));
- res= img->tables.read(s);
- if (ino < img_count-1 && res != stream_result::OK ||
- res == stream_result::ERROR)
- DBUG_RETURN(stream_result::ERROR); // neither stream nor chunk should end here
+ Image_info *img= images[ino];
+
+ DBUG_PRINT("restore",(" reading %s image's tables (#%d)",img->name(),ino));
+
+ /*
+ There should be as many lists in the stream as there were images in the
+ header. Thus we should never hit end of stream here.
+ */
+ res= img->tables.read(s); // note: proceeds to the next chunk in the stream
+
+ if (res != OK)
+ {
+ DBUG_PRINT("restore",("Can't read table list for %s image (#%d)",
+ img->name(),ino));
+ DBUG_RETURN(ERROR); // neither stream nor chunk should end here
+ }
+
table_count+= img->tables.count();
+
DBUG_PRINT("restore",(" finished reading tables"));
}
header_size= s.bytes - start_bytes;
- DBUG_RETURN(res);
+ DBUG_RETURN(OK);
}
} // backup namespace
+
/**********************************
Write/read image descriptions
**********************************/
-
namespace backup {
/**
- Write entry describing (format of) a table data image.
+ Write entry describing (format of) a backup driver's image.
Entry has the form:
<pre>
@@ -231,41 +306,59 @@
is determined by @c X::do_write_description() method where X is a subclass of
Image_info corresponding to given image type.
*/
-stream_result::value
+result_t
Image_info::write_description(OStream &s)
{
- // TODO: write description length here
- s.writebyte(type());
- s.write2int(ver);
+ // TODO: to handle unknown description formats, write description length here
+
+ stream_result::value res= s.writebyte(type());
+
+ if (res != stream_result::OK)
+ return ERROR;
+
+ res= s.write2int(ver);
+
+ if (res != stream_result::OK)
+ return ERROR;
+
return do_write_description(s);
}
/**
Create @c Image_info instance from a saved entry describing it.
- TODO: Handle unknown image types (always skip correct amount of bytes).
+ @retval OK
+ @retval DONE end of chunk/stream hit
+ @retval ERROR
*/
-Image_info* Image_info::create_from_stream(Archive_info &info, IStream &s)
+result_t
+Image_info::create_from_stream(Archive_info &info, IStream &s, Image_info* &ptr)
{
uint ver;
byte t;
- if (s.readbyte(t) != stream_result::OK)
- return NULL;
+ stream_result::value res= s.readbyte(t);
- if (s.read2int(ver) != stream_result::OK)
- return NULL;
+ // if we are at end of data chunk or stream, we should tell the caller
+ if (res != stream_result::OK)
+ return (result_t)res;
+
+ res= s.read2int(ver);
+
+ if (res != stream_result::OK)
+ return ERROR;
switch (image_type(t)) {
case DEFAULT_IMAGE:
- return Default_image::create_from_stream(ver,info,s);
+ return Default_image::create_from_stream(ver,info,s,ptr);
case NATIVE_IMAGE:
- return Native_image::create_from_stream(ver,info,s);
+ return Native_image::create_from_stream(ver,info,s,ptr);
default:
- return NULL;
+ DBUG_PRINT("restore",("Unknown image type %d",t));
+ return ERROR;
}
}
@@ -296,17 +389,20 @@
respectively.
@see @c write_meta_data() for information about the format of the meta-data
- section of bakcup archive.
- */
-stream_result::value Archive_info::Item::save(THD *thd, OStream &s)
+ section of backup archive.
+*/
+result_t
+Archive_info::Item::save(THD *thd, OStream &s)
{
byte b= meta().type();
stream_result::value res=s.writebyte(b);
- TEST_WR_RES(res);
- res= save_id(s);
- TEST_WR_RES(res);
+ if (res != stream_result::OK)
+ return ERROR;
+
+ if (ERROR == save_id(s))
+ return ERROR;
return meta().save(thd,s);
}
@@ -315,90 +411,110 @@
Create meta-data item from a saved entry.
This function reads the type byte and calls @c create_from_stream method of
- corresponding class to create the item.
+ corresponding class to create the item. It stores pointer to the created
+ item in @c ptr argument.
- TODO: handle unknow entry types (so that they can be skipped).
+ @retval OK if new item was created
+ @retval DONE if end of chunk/stream was reached
+ @retval ERROR if error has happened
*/
-Archive_info::Item*
+result_t
Archive_info::Item::create_from_stream(const Archive_info &info,
- IStream &s)
+ IStream &s, Item* &ptr)
{
byte b;
- Item *i;
- stream_result::value res;
- res= s.readbyte(b);
+ stream_result::value res= s.readbyte(b);
+
+ if (res != stream_result::OK)
+ return (result_t)res;
- if (res!=stream_result::OK)
- return NULL;
+ ptr= NULL;
+
+ result_t res1;
switch (meta::Item::enum_type(b)) {
case meta::Item::DB:
- i= Db_item::create_from_stream(info,s);
+ res1= Db_item::create_from_stream(info,s,ptr);
break;
case meta::Item::TABLE:
- i= Table_item::create_from_stream(info,s);
+ res1= Table_item::create_from_stream(info,s,ptr);
break;
- default: return NULL;
+ default: return ERROR;
}
- if (i == NULL)
- return NULL;
-
- res= i->meta().read(s);
- TEST_RD_RES(res);
+ /*
+ Note that create_from_stream() should return OK - end of data should not
+ happen here.
+ */
+ if (res1 != OK || ptr == NULL)
+ return ERROR;
- return i;
+ return ptr->meta().read(s);
}
// Db items
-stream_result::value Archive_info::Db_item::save_id(OStream &s)
+result_t Archive_info::Db_item::save_id(OStream &s)
{
uint k= key;
DBUG_PRINT("backup",(" saving db-item (%d)",k));
- return s.writeint(k);
+ return stream_result::OK == s.writeint(k) ? OK : ERROR;
}
-Archive_info::Db_item*
-Archive_info::Db_item::create_from_stream(const Archive_info &i,IStream &s)
+result_t
+Archive_info::Db_item::create_from_stream(const Archive_info &i,
+ IStream &s,
+ Archive_info::Item* &ptr)
{
uint k;
- stream_result::value res;
+ stream_result::value res= s.readint(k);
- res= s.readint(k);
- TEST_RD_RES(res);
+ if (res != stream_result::OK)
+ return (result_t)res;
- return new Db_item(i,k);
+ return (ptr= new Db_item(i,k)) ? OK : ERROR;
}
// Table items
-stream_result::value Archive_info::Table_item::save_id(OStream &s)
+result_t Archive_info::Table_item::save_id(OStream &s)
{
DBUG_PRINT("backup",(" saving table-item (%d,%d)",img,pos));
stream_result::value res= s.writeint(img);
- TEST_WR_RES(res);
- return s.writeint(pos);
+
+ if (res != stream_result::OK)
+ return ERROR;
+
+ res= s.writeint(pos);
+
+ if (res != stream_result::OK)
+ return ERROR;
+
+ return OK;
}
-Archive_info::Table_item*
-Archive_info::Table_item::create_from_stream(const Archive_info &i,IStream &s)
+result_t
+Archive_info::Table_item::create_from_stream(const Archive_info &i,
+ IStream &s,
+ Archive_info::Item* &ptr)
{
uint img,no;
- stream_result::value res;
+ stream_result::value res= s.readint(img);
- res= s.readint(img);
- TEST_RD_IRES(res);
+ if (res != stream_result::OK)
+ return (result_t)res;
res= s.readint(no);
- TEST_RD_RES(res);
- return new Table_item(i,img,no);
+ if (res != stream_result::OK)
+ return ERROR;
+
+ return (ptr= new Table_item(i,img,no)) ? OK : ERROR;
}
} // backup namespace
@@ -427,32 +543,44 @@
}
};
-Image_info::Tables::~Tables()
+/// Empty the list.
+void Image_info::Tables::clear()
{
- for( node *ptr= m_head ; ptr ; )
+ for (node *ptr= m_head; ptr;)
{
node *n=ptr;
ptr= n->next;
delete n;
- };
+ }
+
+ m_head= m_last= NULL;
+ m_count= 0;
}
+/**
+ Add a table to the list.
+
+ @returns Position of the table or -1 if error
+ */
int Image_info::Tables::add(const backup::Table_ref &t)
{
StringPool::Key k= m_db_names.add(t.db().name());
- if( !k.is_valid() ) return -1;
+ if (!k.is_valid())
+ return -1;
return add(k,t.name());
}
+/// Add table at given position.
int Image_info::Tables::add(const StringPool::Key &k, const String &name)
{
node *n= new node(*this,k,name);
- if( !n ) return -1;
+ if (!n)
+ return -1;
- if( m_head == NULL )
+ if (m_head == NULL)
{
m_count=1;
m_head= m_last= n;
@@ -467,6 +595,11 @@
return m_count-1;
}
+/**
+ Locate table at given position.
+
+ @returns Pointer to table's list node or NULL if position is not occupied
+ */
Image_info::Tables::node*
Image_info::Tables::find_table(uint pos) const
{
@@ -474,7 +607,7 @@
node *ptr;
- for( ptr= m_head ; ptr && pos ; ptr= ptr->next )
+ for (ptr= m_head; ptr && pos; ptr= ptr->next)
pos--;
//if( !ptr ) ptr= m_last;
@@ -482,6 +615,7 @@
return ptr;
}
+/// Return table at a given position.
inline
Table_ref Image_info::Tables::operator[](uint pos) const
{
@@ -511,22 +645,38 @@
The format used assumes that a pool of database names is stored elsewhere.
Thus for each table only the key of the database is stored as var-length
- integer followed by table name.
+ integer followed by table name. Empty list is saved as single NIL value.
The list is stored in a single stream chunk which determines its end.
+
+ @returns OK or ERROR
*/
-stream_result::value
+result_t
Image_info::Tables::save(OStream &s)
{
+ DBUG_ENTER("Image_info::Tables::save");
stream_result::value res;
- for( Tables::node *n= m_head ; n ; n= n->next )
+ if (count() == 0)
{
- res= s.writeint(n->db);
- res= s.writestr(n->name);
- };
+ res= s.writenil();
+ if (res != stream_result::OK)
+ DBUG_RETURN(ERROR);
+ }
+ else
+ for (Tables::node *n= m_head ; n ; n= n->next)
+ {
+ res= s.writeint(n->db);
+ if (res != stream_result::OK)
+ DBUG_RETURN(ERROR);
+
+ res= s.writestr(n->name);
+ if (res != stream_result::OK)
+ DBUG_RETURN(ERROR);
+ };
- return s.end_chunk();
+ res= s.end_chunk();
+ DBUG_RETURN(res == stream_result::ERROR ? ERROR : OK);
}
/**
@@ -534,37 +684,65 @@
@pre Stream is positioned at the first entry of the saved list.
@post Stream is positioned at the beginning of next chunk or at its end.
+
+ @retval OK
+ @retval DONE end of stream or chunk hit (nothing has been read)
+ @retval ERROR
*/
-stream_result::value
+result_t
Image_info::Tables::read(IStream &s)
{
DBUG_ENTER("Image_info::Tables::read");
stream_result::value res;
+ uint k,tno=0;
- do
- {
- String name;
- uint k,tno;
+ /*
+ Read first entry - if it is NIL, we have empty list. Otherwise it should
+ be db index of the first table.
+ */
- res= s.readint(k);
- res= s.readstr(name);
+ res= s.readint(k);
- if( res == stream_result::OK )
+ // If unexpected result, report an error or end of stream/chunk
+ if (res != stream_result::OK && res != stream_result::NIL)
+ return (result_t)res;
+
+ // empty the list
+ clear();
+
+ if (res == stream_result::OK) // this is non-empty list
+ do
{
+ String name;
+
+ res= s.readstr(name);
+ if (res != stream_result::OK)
+ break;
+
tno= add(k,name);
DBUG_PRINT("restore",("got next table %s.%s (pos %d, dbkey %d)",
- (*this)[tno].db().name().ptr(),
- (*this)[tno].name().ptr(),tno,k));
+ (*this)[tno].db().name().ptr(),
+ (*this)[tno].name().ptr(),tno,k));
+ res= s.readint(k);
}
- }
- while (res == stream_result::OK);
+ while (res == stream_result::OK);
+ else
+ /*
+ If we have read NIL value, pretend we are at the end of chunk so that
+ no errors are reported below.
+ */
+ res= stream_result::EOC;
- DBUG_ASSERT(res == stream_result::EOC || res == stream_result::EOS);
+ // we should be now at end of chunk/stream
+ if (res != stream_result::EOC && res != stream_result::EOS)
+ {
+ DBUG_PRINT("restore",("Error when reading table no %d in table list",tno));
+ DBUG_RETURN(ERROR);
+ }
res= s.next_chunk();
-
- DBUG_RETURN(res);
+ DBUG_RETURN(res == stream_result::ERROR ? ERROR : OK);
}
} // backup namespace
@@ -577,69 +755,51 @@
namespace backup {
-Backup_driver* Native_image::get_backup_driver()
-{
- Backup_driver *drv;
-
- result_t res= m_be->get_backup(Driver::PARTIAL,tables,drv);
-
- DBUG_ASSERT(res == OK);
- DBUG_ASSERT(drv);
-
- return drv;
-}
-
-Restore_driver* Native_image::get_restore_driver()
-{
- Restore_driver *drv;
-
- result_t res= m_be->get_restore(ver,Driver::PARTIAL,tables,drv);
-
- DBUG_ASSERT(res == OK);
- DBUG_ASSERT(drv);
-
- return drv;
-}
-
/*
For native image its format (apart from the version number) is determined
by the storage engine whose backup driver created it. Thus we save the name
of storage engine.
- TODO: add more information here. Eg the version number of the storage engine.
+ TODO: add more information here. E.g. the version number of the storage engine.
*/
-stream_result::value
+result_t
Native_image::do_write_description(OStream &s)
{
- String name(::ha_resolve_storage_engine_name(m_hton),default_charset);
- return s.writestr(name);
+ String name(::ha_resolve_storage_engine_name(m_hton),&::my_charset_bin);
+ return stream_result::OK == s.writestr(name) ? OK : ERROR;
}
-Native_image*
-Native_image::create_from_stream(version_t ver, Archive_info &info, IStream &s)
+result_t
+Native_image::create_from_stream(version_t ver,
+ Archive_info &info,
+ IStream &s, Image_info* &img)
{
String name;
- Native_image *img;
+ stream_result::value res= s.readstr(name);
- if( s.readstr(name) == stream_result::OK )
- {
- LEX_STRING name_lex;
-
- name_lex.str= const_cast<char*>(name.ptr());
- name_lex.length= name.length();
+ if (res != stream_result::OK)
+ return (result_t)res;
- ::handlerton *hton= ::ha_resolve_by_name(::current_thd,&name_lex);
+ LEX_STRING name_lex= {name.c_ptr(), name.length()};
- img= new Native_image(info,hton);
- DBUG_ASSERT(ver <= img->ver); // TODO: deal with different versions
+ ::handlerton *hton= ::ha_resolve_by_name(::current_thd,&name_lex);
+ if (!hton)
+ return ERROR;
- img->ver= ver;
+ img= new Native_image(info,hton);
+ if (!img)
+ return ERROR;
- return img;
+ if (ver > img->ver)
+ {
+ DBUG_PRINT("restore",("Restore diver ver %d can't read image version %d",
+ img->ver,ver));
+ return ERROR;
}
- else
- return NULL;
+
+ img->ver= ver;
+
+ return OK;
}
} // backup namespace
-
--- 1.11/sql/backup/archive.h 2007-06-23 23:38:58 +02:00
+++ 1.12/sql/backup/archive.h 2007-06-23 23:38:58 +02:00
@@ -4,7 +4,8 @@
/**
@file
- Data types used to read, write and manipulate backup archives.
+ Data types used to represent contents of a backup archive and to read/write
+ its description (catalogue)
*/
#include <backup/api_types.h>
@@ -18,7 +19,8 @@
// Forward declaration for a class describing an image inside backup archive.
class Image_info;
-typedef Image_info* Img_list[256]; ///< List (vector) of image descriptions.
+#define MAX_IMAGES 256
+typedef Image_info* Img_list[MAX_IMAGES]; ///< List (vector) of image descriptions.
/**
Describes contents of a backup archive.
@@ -27,7 +29,7 @@
all items stored in the archive (currently only databases and tables). It also
determines how to save and read the catalogue to/from a backup stream.
- Only item names are stored in the catalouge. Other item data is stored
+ Only item names are stored in the catalogue. Other item data is stored
in the meta-data part of an archive and in case of tables, their data is
stored in images created by backup drivers.
@@ -42,8 +44,6 @@
When reading or writing backup archive, statistics about the size of its parts
is stored in the members of this class for later reporting.
-
- TODO: Define methods for browsing contents of the archive.
*/
struct Archive_info
{
@@ -62,7 +62,9 @@
class Db_item;
class Table_item;
- /*
+ /*
+ Classes which might be used to implement contents browsing.
+
class Item_iterator; // for iterating over all meta-data items
class Db_iterator; // iterates over databases in archive
class Ditem_iterator; // iterates over per-db items
@@ -71,16 +73,14 @@
Img_list images; ///< list of archive's images
/// Write archive's header and save the catalogue.
- stream_result::value save(OStream&);
+ result_t save(OStream&);
/// Read the header and catalogue from a stream.
- stream_result::value read(IStream&);
+ result_t read(IStream&);
virtual ~Archive_info();
protected:
- // Item *m_items;
-
Archive_info():
img_count(0), table_count(0),
total_size(0), header_size(0), meta_size(0), data_size(0)
@@ -103,7 +103,7 @@
An instance of this class:
- informs about the type of image,
- - stores list of tables whose data is keept in the image,
+ - stores list of tables whose data is kept in the image,
- provides methods for creating backup and restore drivers to write/read the
image,
- determines which tables can be stored in the image,
@@ -117,25 +117,38 @@
virtual image_type type() const =0; ///< Return type of the image.
version_t ver; ///< Image format version.
+ /// Check if instance was correctly constructed
+ virtual bool is_valid() =0;
/// Create backup driver for the image.
- virtual Backup_driver* get_backup_driver() =0;
+ virtual result_t get_backup_driver(Backup_driver*&) =0;
/// Create restore driver for the image.
- virtual Restore_driver* get_restore_driver() =0;
+ virtual result_t get_restore_driver(Restore_driver*&) =0;
size_t init_size; ///< Size of the initial data transfer (estimate). This is
- ///< meaningfull only after a call to get_backup_driver().
+ ///< meaningful only after a call to get_backup_driver().
/// Write header entry describing the image.
- stream_result::value write_description(OStream&);
- /// Create instance of @c Image_info described by an entry in backup stream.
- static Image_info* create_from_stream(Archive_info&, IStream&);
+ result_t write_description(OStream&);
+
+ /**
+ Create instance of @c Image_info described by an entry in backup stream.
+
+ @retval OK entry successfully read
+ @retval DONE end of chunk or stream has been reached.
+ @retval ERROR an error was detected
+ */
+ static result_t create_from_stream(Archive_info&, IStream&, Image_info*&);
/// Determine if a table stored in given engine can be saved in this image.
virtual bool accept(const Table_ref&, const ::handlerton*) =0;
- /// Return name identifying the image in debug messages.
+ /**
+ Return name identifying the image in debug messages.
+
+ The name should fit into "%s backup/restore driver" pattern.
+ */
virtual const char* name() const
- { return "<Unknown Driver>"; }
+ { return "<Unknown>"; }
virtual ~Image_info()
{}
@@ -153,9 +166,11 @@
m_db_names(db_names),
m_head(NULL),m_last(NULL),m_count(0)
{}
- ~Tables();
+
+ ~Tables() { clear(); }
int add(const backup::Table_ref&);
+ void clear();
backup::Table_ref operator[](uint pos) const;
//::TABLE_LIST* get_table_ptr(uint pos) const;
@@ -163,8 +178,8 @@
uint count() const
{ return m_count; }
- stream_result::value save(OStream&);
- stream_result::value read(IStream&);
+ result_t save(OStream&);
+ result_t read(IStream&);
private:
@@ -194,7 +209,7 @@
Method redefined in subclasses corresponding to different image types.
*/
- virtual stream_result::value do_write_description(OStream&) =0;
+ virtual result_t do_write_description(OStream&) =0;
};
/**
@@ -222,21 +237,21 @@
{
protected:
- const Archive_info *const m_info; ///< Pointer to an archive info instance to
- ///< which this item belongs.
+ /// Pointer to @c Archive_info instance to which this item belongs.
+ const Archive_info *const m_info;
Item *next; ///< Used to create a linked list of all meta-data items.
public:
virtual ~Item() {}
- virtual meta::Item& meta() =0; ///< Return reference to the @c meta::Item
- ///< instance.
+ /// Returns reference to the corresponding @c meta::Item instance.
+ virtual meta::Item& meta() =0;
- stream_result::value save(THD*,OStream&); ///< Write entry describing the item.
+ result_t save(THD*,OStream&); ///< Write entry describing the item.
- /// Create item from a stream entry.
- static Item* create_from_stream(const Archive_info&, IStream&);
+ // Create item from a saved entry.
+ static result_t create_from_stream(const Archive_info&, IStream&, Item*&);
class Iterator;
@@ -246,7 +261,7 @@
{}
/// Save data identifying the item inside the archive.
- virtual stream_result::value save_id(OStream&) =0;
+ virtual result_t save_id(OStream&) =0;
friend class Archive_info;
friend class Backup_info;
@@ -260,7 +275,7 @@
Usage:
<pre>
Item *head;
- for (Item::Iterator it(i); it ; it++)
+ for (Item::Iterator it(head); it ; it++)
{
it->archive_item_method()
}
@@ -268,7 +283,7 @@
or
<pre>
Item *head, *p;
- Item::iterator it(head);
+ Item::Iterator it(head);
while ((p=it++))
{
@@ -331,9 +346,10 @@
public:
- stream_result::value save_id(OStream&);
+ result_t save_id(OStream&);
/// Create instance reading its identity from a stream.
- static Db_item* create_from_stream(const Archive_info&,IStream&);
+ static
+ result_t create_from_stream(const Archive_info&,IStream&, Archive_info::Item*&);
friend class Archive_info;
friend class Backup_info;
@@ -359,6 +375,8 @@
img(no), pos(tno)
{}
+ public:
+
meta::Item& meta()
{ return *this; }
@@ -369,19 +387,11 @@
const Db_ref in_db()
{ return m_info->images[img]->tables[pos].db(); }
- public:
-
- stream_result::value save_id(OStream&);
+ result_t save_id(OStream&);
/// Create instance reading its identity from a stream.
- static Table_item* create_from_stream(const Archive_info&,IStream&);
+ static
+ result_t create_from_stream(const Archive_info&,IStream&, Archive_info::Item*&);
- // Implementation of methods used in meta::Table.
-/*
- TABLE_LIST *open(THD*)
- { return m_info->images[img]->tables.get_table_ptr(pos); }
- virtual bool close()
- { return TRUE; }
-*/
friend class Backup_info;
};
@@ -404,7 +414,7 @@
const ::handlerton *m_hton; ///< Pointer to storage engine.
Engine *m_be; ///< Pointer to the native backup engine.
- char m_name[256]; ///< Used to identify image in debug messages.
+ const char *m_name; ///< Used to identify image in debug messages.
public:
@@ -413,26 +423,39 @@
{
DBUG_ASSERT(hton);
DBUG_ASSERT(hton->get_backup_engine);
- hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
- DBUG_ASSERT(m_be);
- ver= m_be->version();
+ hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
- my_snprintf(m_name,sizeof(m_name),"%s's driver",
- ::ha_resolve_storage_engine_name(hton));
+ if(m_be)
+ {
+ ver= m_be->version();
+ m_name= ::ha_resolve_storage_engine_name(hton);
+ }
}
+ bool is_valid()
+ { return m_be != NULL; }
+
image_type type() const
{ return NATIVE_IMAGE; }
const char* name() const
{ return m_name; }
- Backup_driver* get_backup_driver();
- Restore_driver* get_restore_driver();
+ result_t get_backup_driver(Backup_driver* &drv)
+ {
+ DBUG_ASSERT(m_be);
+ return m_be->get_backup(Driver::PARTIAL,tables,drv);
+ }
+
+ result_t get_restore_driver(Restore_driver* &drv)
+ {
+ DBUG_ASSERT(m_be);
+ return m_be->get_restore(ver,Driver::PARTIAL,tables,drv);
+ }
- stream_result::value do_write_description(OStream&);
- static Native_image* create_from_stream(version_t,Archive_info&,IStream&);
+ result_t do_write_description(OStream&);
+ static result_t create_from_stream(version_t,Archive_info&,IStream&,Image_info*&);
bool accept(const Table_ref&, const ::handlerton *hton)
{ return hton == m_hton; }; // this assumes handlertons are single instance objects!
--- 1.7/sql/backup/backup_kernel.h 2007-06-23 23:38:58 +02:00
+++ 1.8/sql/backup/backup_kernel.h 2007-06-23 23:38:58 +02:00
@@ -7,8 +7,10 @@
#include <backup/logger.h>
-// Called from the big switch in mysql_execute_command() to execute
-// backup related statement
+/*
+ Called from the big switch in mysql_execute_command() to execute
+ backup related statement
+ */
int execute_backup_command(THD*, LEX*);
/**
@@ -47,9 +49,10 @@
struct Location
{
- // as we don't use rtti we need these to find out real type
- // of location object.
-
+ /*
+ As we don't use rtti we need these to find out the real type
+ of a location object.
+ */
enum enum_type {SERVER_FILE, INVALID};
bool read;
@@ -62,6 +65,10 @@
virtual void free()
{ delete this; } // use destructor to free resources
+ /// Describe location for debug purposes
+ virtual const char* describe()
+ { return "Invalid location"; }
+
/**
Interpret string passed to BACKUP/RESTORE statement as backup location
and construct corresponding Location object.
@@ -79,7 +86,7 @@
When Backup_info object is created it is empty and ready for adding items
to it. Methods @c add_table() @c add_db(), @c add_dbs() and @c add_all_dbs()
can be used for that purpose (currently only databases and tables are
- supported). After populationg info object with items it should be "closed"
+ supported). After populating info object with items it should be "closed"
with a call to @c close() method. After that it is ready for use as a
description of backup archive to be created.
@@ -101,24 +108,36 @@
Backup_info(THD*);
~Backup_info();
- bool add_dbs(List<LEX_STRING>&);
- bool add_all_dbs();
-
- bool close();
- //bool close_tables();
-
- bool is_valid() const
+ bool is_valid()
{
+ bool ok= TRUE;
+
switch (m_state) {
+ case ERROR:
+ ok= FALSE;
+
case INIT:
- return i_s_tables != NULL;
+ ok= (i_s_tables != NULL);
break;
- default: return TRUE;
+ default:
+ ok= TRUE;
}
+
+ if (!ok)
+ m_state= ERROR;
+
+ return ok;
}
+ int save(OStream&);
+
+ int add_dbs(List<LEX_STRING>&);
+ int add_all_dbs();
+
+ bool close();
+
class Item_iterator; // for iterating over all meta-data items
private:
@@ -126,13 +145,10 @@
/// State of the info structure.
enum {INIT, // structure ready for filling
READY, // structure ready for backup (tables opened)
- DONE // tables are closed
+ DONE, // tables are closed
+ ERROR
} m_state;
- /* Find image to which given table can be added and add it.
- Creates new images as needed.
- Returns -1 on error.
- */
int find_image(const Table_ref&);
int default_image_no; ///< Position of the default image in @c images list, -1 if not used.
@@ -140,14 +156,12 @@
Db_item* add_db(const backup::Db_ref&);
Table_item* add_table(Db_item&, const Table_ref&);
- bool add_db_items(Db_item&);
- bool add_table_items(Table_item&);
+ int add_db_items(Db_item&);
+ int add_table_items(Table_item&);
THD *m_thd;
TABLE *i_s_tables;
- //bool open_tables();
-
Item *m_items;
Item *m_last_item;
Item *m_last_db;
@@ -177,19 +191,25 @@
class Restore_info: public Archive_info, public Logger
{
+ bool m_valid;
+
public:
- Restore_info(IStream &s)
+ Restore_info(IStream &s): m_valid(TRUE)
{
- stream_result::value res= read(s);
- DBUG_ASSERT(res != stream_result::ERROR);
+ result_t res= read(s);
+ if (res == ERROR)
+ {
+ report_error(ER_BACKUP_READ_HEADER);
+ m_valid= FALSE;
+ }
}
bool is_valid() const
- { return TRUE; }
+ { return m_valid; }
- bool restore_all_dbs()
- { return TRUE; }
+ int restore_all_dbs()
+ { return 0; }
/// Determine if given item is selected for restore.
bool selected(const Archive_info::Item&)
@@ -197,4 +217,5 @@
};
} // backup namespace
+
#endif
--- 1.5/sql/backup/be_default.h 2007-06-23 23:38:59 +02:00
+++ 1.6/sql/backup/be_default.h 2007-06-23 23:38:59 +02:00
@@ -325,28 +325,33 @@
Default_image(Archive_info &info): Image_info(info)
{ ver= 1; }
+ bool is_valid()
+ { return TRUE; }
+
image_type type() const
{ return DEFAULT_IMAGE; }
const char* name() const
- { return "Default driver"; }
+ { return "Default"; }
bool accept(const Table_ref&, const ::handlerton*)
{ return TRUE; }; // accept all tables
- Backup_driver* get_backup_driver()
- { return new default_backup::Backup(tables,::current_thd); }
+ result_t get_backup_driver(Backup_driver* &ptr)
+ { return (ptr= new default_backup::Backup(tables,::current_thd)) ? OK : ERROR; }
- Restore_driver* get_restore_driver()
- { return new default_backup::Restore(tables,::current_thd); }
+ result_t get_restore_driver(Restore_driver* &ptr)
+ { return (ptr= new default_backup::Restore(tables,::current_thd)) ? OK : ERROR; }
- stream_result::value do_write_description(OStream&)
- { return stream_result::OK; }; // nothing to write
+ result_t do_write_description(OStream&)
+ { return OK; } // nothing to write
- static Default_image*
- create_from_stream(version_t ver, Archive_info &info,
- IStream&)
- { return new Default_image(info); }; // TODO: check format version
+ static result_t
+ create_from_stream(version_t, Archive_info &info, IStream&,
+ Image_info* &ptr)
+ {
+ return (ptr= new Default_image(info)) ? OK : ERROR;
+ }
};
} // backup namespace
--- 1.18/sql/backup/data_backup.cc 2007-06-23 23:38:59 +02:00
+++ 1.19/sql/backup/data_backup.cc 2007-06-23 23:38:59 +02:00
@@ -10,6 +10,28 @@
backed up.
*/
+/*
+ TODO
+
+ - Implement better scheduling strategy in Scheduler::step
+
+ The driver with least data read is chosen. If there are several,
+ they are polled in round-robin fashion.
+
+ - For consistency, always use OStream for data serialization (?)
+
+ - In Backup_pump::pump(), handle DONE response from backup driver.
+
+ - In case of backup/restore of empty archive, check that all resources
+ are correctly deallocated.
+
+ - Try to announce block size when initializing restore driver with begin()
+ method (is it possible?).
+
+ - If an error from driver is ignored (and operation retried) leave trace
+ of the error in the log.
+ */
+
#include "backup_engine.h"
#include "stream.h"
#include "backup_kernel.h"
@@ -35,6 +57,7 @@
FINISHING, ///< Final data transfer (phase 7).
DONE, ///< Backup complete.
SHUT_DOWN, ///< After @c end() call.
+ CANCELLED, ///< After cancelling backup process.
ERROR,
MAX };
@@ -54,6 +77,7 @@
name[FINISHING]= "FINISHING";
name[DONE]= "DONE";
name[SHUT_DOWN]= "SHUT DOWN";
+ name[CANCELLED]= "CANCELLED";
name[ERROR]= "ERROR";
}
};
@@ -87,7 +111,8 @@
result_t write_buf(const Buffer&);
result_t drop_buf(Buffer&);
- Block_writer(size_t size, OStream &s): m_str(s), buf_size(size)
+ Block_writer(size_t size, OStream &s):
+ m_str(s), buf_size(size)
{}
private:
@@ -104,7 +129,7 @@
Poll backup driver for backup data and send it to a stream. Monitors stages
of the backup process, keeps track of closed streams etc.
- Usage: Initialize using begin() method, then call pump() metod repeatedly.
+ Usage: Initialize using begin() method, then call pump() method repeatedly.
The state member informs about the current state of the backup process. When
done, call end() method. Methods prepare(), lock() and unlock() are forwarded
to backup driver to implement multi-engine synchronization.
@@ -126,23 +151,34 @@
Backup_pump(uint, Image_info&, Block_writer&);
~Backup_pump();
- size_t pump();
+ bool is_valid()
+ { return m_drv && state != backup_state::ERROR; }
+
+ int pump(size_t*);
- bool begin();
- bool end();
- bool prepare();
- bool lock();
- bool unlock();
+ int begin();
+ int end();
+ int prepare();
+ int lock();
+ int unlock();
+ int cancel();
/// Return the backup driver used by the pump.
Backup_driver &drv() const
- { return *m_drv; }
+ {
+ DBUG_ASSERT(m_drv);
+ return *m_drv;
+ }
+
+ void set_logger(Logger *log)
+ { m_log= log; }
private:
static const uint m_buf_size= 2048; ///< Size of buffers used for data transfers.
static const uint get_buf_retries= 3; /**< if block writer has no buffers, retry
this many times before giving up */
+ Logger *m_log; ///< Used to report errors if not NULL.
uint m_drv_no; ///< The number of this image in the backup archive.
Backup_driver *m_drv; ///< Pointer to the backup driver.
Block_writer &m_bw; ///< Block writer used for writing data blocks.
@@ -157,7 +193,7 @@
*/
byte *m_buf_head;
- uint m_buf_retries; ///< how many times failed to get a buffer from blick writer
+ uint m_buf_retries; ///< how many times failed to get a buffer from block writer
bool get_buf();
bool drop_buf();
@@ -192,13 +228,12 @@
public:
- void add(Pump*);
- void step();
+ int add(Pump*);
+ int step();
- void prepare();
- void lock();
- void unlock();
- void close();
+ int prepare();
+ int lock();
+ int unlock();
uint init_count; ///< no. drivers sending init data
uint prepare_count; ///< no. drivers preparing for lock
@@ -210,28 +245,31 @@
bool is_empty() const
{ return m_count == 0; }
- ~Scheduler() { close(); }
+ ~Scheduler() { cancel_backup(); }
private:
LIST *m_pumps, *m_last;
+ Logger *m_log; ///< used to report errors if not NULL
uint m_count; ///< current number of pumps
size_t m_total; ///< accumulated position of all drivers
size_t m_init_left; ///< how much of init data is left (estimate)
uint m_known_count; ///< no. drivers which can estimate init data size
OStream &m_str; ///< stream to which we write
+ bool cancelled; ///< true if backup process was cancelled
- Scheduler(OStream &s):
+ Scheduler(OStream &s, Logger *log):
init_count(0), prepare_count(0), finish_count(0),
- m_pumps(NULL), m_last(NULL),
+ m_log(log), m_pumps(NULL), m_last(NULL),
m_count(0), m_total(0), m_init_left(0), m_known_count(0),
- m_str(s)
+ m_str(s), cancelled(FALSE)
{}
void move_pump_to_end(const Pump_iterator&);
void remove_pump(Pump_iterator&);
+ void cancel_backup();
- friend bool write_table_data(THD*, Backup_info&, OStream&);
+ friend int write_table_data(THD*, Backup_info&, OStream&);
};
/**
@@ -256,34 +294,28 @@
};
-
-#define CHECK_STATE(P,S) \
- do { \
- if( (P).state != (S) ) \
- DBUG_PRINT("backup",("unexpected state %d of backup pump",(S))); \
- DBUG_ASSERT( (P).state == (S) ); \
- } while(0)
-
-
/**
Save data from tables being backed up.
Function initializes and controls backup drivers which create the image
of table data. Currently single thread is used and drivers are polled in
a round robin fashion.
+
+ @returns 0 on success.
*/
-bool write_table_data(THD*, Backup_info &info, OStream &s)
+int write_table_data(THD*, Backup_info &info, OStream &s)
{
DBUG_ENTER("backup::write_table_data");
info.data_size= 0;
if (info.img_count==0 || info.table_count==0) // nothing to backup
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(0);
- Scheduler sch(s); // scheduler instance
+ Scheduler sch(s,&info); // scheduler instance
List<Scheduler::Pump> inactive; // list of images not yet being created
size_t max_init_size=0; // keeps maximal init size for images in inactive list
+ int res= 0; // 0 or error code to return
size_t start_bytes= s.bytes;
@@ -300,13 +332,22 @@
Scheduler::Pump *p= new Scheduler::Pump(no,*i,s);
- DBUG_ASSERT(p);
+ if (!p || !p->is_valid())
+ {
+ info.report_error(ER_OUT_OF_RESOURCES);
+ res= -1;
+ goto error;
+ }
size_t init_size= p->init_size;
if (init_size == Driver::UNKNOWN_SIZE)
{
- sch.add(p);
+ if (sch.add(p))
+ {
+ res= -2;
+ goto error;
+ }
}
else
{
@@ -317,6 +358,9 @@
}
}
+ /*
+ Each driver should be either in the scheduler or on inactive list.
+ */
DBUG_ASSERT( !sch.is_empty() || !inactive.is_empty() );
DBUG_PRINT("backup/data",("%u drivers initialized, %u inactive",
@@ -331,8 +375,9 @@
BACKUP_SYNC("data_init");
/*
- poll "at end" drivers activating inactive ones on the way
- note: if scheduler is empty and there are images with non-zero
+ Poll "at end" drivers activating inactive ones on the way.
+
+ Note: if scheduler is empty and there are images with non-zero
init size (max_init_size > 0) then enter the loop as one such image
will be added to the scheduler inside.
*/
@@ -363,57 +408,97 @@
max_init_size= second_max;
- sch.add(p1);
+ if (sch.add(p1))
+ {
+ return -3;
+ goto error;
+ }
}
// poll drivers
- sch.step();
+ if (sch.step())
+ {
+ res= -4;
+ goto error;
+ }
}
- // start "at begin" drivers
- DBUG_PRINT("backup/data",("- activating \"at begin\" drivers"));
+ {
+ // start "at begin" drivers
+ DBUG_PRINT("backup/data",("- activating \"at begin\" drivers"));
- List_iterator<Scheduler::Pump> it1(inactive);
- Scheduler::Pump *p;
+ List_iterator<Scheduler::Pump> it1(inactive);
+ Scheduler::Pump *p;
- while ((p= it1++))
- sch.add(p);
+ while ((p= it1++))
+ if (sch.add(p))
+ {
+ res= -5;
+ goto error;
+ };
- while (sch.init_count > 0)
- sch.step();
+ while (sch.init_count > 0)
+ if (sch.step())
+ {
+ res= -6;
+ goto error;
+ };
+
+ // prepare for VP
+ DBUG_PRINT("backup/data",("-- PREPARE PHASE --"));
+ BACKUP_SYNC("data_prepare");
- // prepare for VP
- DBUG_PRINT("backup/data",("-- PREPARE PHASE --"));
- BACKUP_SYNC("data_prepare");
+ if (sch.prepare())
+ {
+ res= -7;
+ goto error;
+ }
- sch.prepare();
+ while (sch.prepare_count > 0)
+ if (sch.step())
+ {
+ res= -8;
+ goto error;
+ }
- while (sch.prepare_count > 0)
- sch.step();
+ // VP creation
+ DBUG_PRINT("backup/data",("-- SYNC PHASE --"));
+ BACKUP_SYNC("data_lock");
+ if (sch.lock())
+ {
+ res= -9;
+ goto error;
+ }
- // VP creation
- DBUG_PRINT("backup/data",("-- SYNC PHASE --"));
- BACKUP_SYNC("data_lock");
+ BACKUP_SYNC("data_unlock");
+ if (sch.unlock())
+ {
+ res= -10;
+ goto error;
+ }
- sch.lock();
+ // get final data from drivers
+ DBUG_PRINT("backup/data",("-- FINISH PHASE --"));
+ BACKUP_SYNC("data_finish");
- BACKUP_SYNC("data_unlock");
- sch.unlock();
+ while (sch.finish_count > 0)
+ if (sch.step())
+ {
+ res= -11;
+ goto error;
+ }
- // get final data from drivers
- DBUG_PRINT("backup/data",("-- FINISH PHASE --"));
- BACKUP_SYNC("data_finish");
+ DBUG_PRINT("backup/data",("-- DONE --"));
+ }
- while (sch.finish_count > 0)
- sch.step();
+ info.data_size= s.bytes - start_bytes;
- DBUG_PRINT("backup/data",("-- DONE --"));
- sch.close();
+ DBUG_RETURN(0);
- info.data_size= s.bytes - start_bytes;
+ error:
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(res ? res : -12);
}
} // backup namespace
@@ -463,29 +548,24 @@
Method updates statistics of number of drivers in each phase which is used
to detect end of a backup process.
*/
-void Scheduler::step()
+int Scheduler::step()
{
- /* find pump with least data read.
-
- Pick next driver to pump data from. The driver with least data read
- is choosen. If there are several, they are polled in round-robin
- fashion.
-
- TODO: implement this
- */
+ // Pick next driver to pump data from.
Pump_iterator p(*this);
/*
+ An attempt to implement more advanced scheduling strategy (not working).
+
for (Pump_ptr it(m_pumps); it; ++it)
if ( !p || it->pos() < p->pos() )
p= it;
*/
- if (!p)
+ if (!p) // No active pumps
{
init_count= prepare_count= finish_count= 0; // safety
- return;
+ return 0;
}
move_pump_to_end(p);
@@ -494,13 +574,18 @@
backup_state::value before_state= p->state;
- size_t howmuch= p->pump();
+ size_t howmuch;
+ int res= p->pump(&howmuch);
backup_state::value after_state= p->state;
+ // The error state should be set iff error was reported
+ DBUG_ASSERT(!(res && after_state != backup_state::ERROR));
+ DBUG_ASSERT(!(!res && after_state == backup_state::ERROR));
+
// update statistics
- if (howmuch > 0)
+ if (!res && howmuch > 0)
{
m_total += howmuch;
@@ -546,8 +631,12 @@
break;
case backup_state::DONE:
+ p->end();
+
case backup_state::ERROR:
remove_pump(p);
+ if (res)
+ cancel_backup(); // we hit an error - bail out
break;
default: break;
@@ -557,15 +646,22 @@
m_count, init_count, prepare_count, finish_count));
}
+ return res ? -1 : 0;
}
-/// Add backup pump to the scheduler.
-void Scheduler::add(Pump *p)
+/**
+ Add backup pump to the scheduler.
+
+ The pump is initialized with begin() call. In case of error, it is deleted.
+ */
+int Scheduler::add(Pump *p)
{
size_t avg= m_count? m_total/m_count + 1 : 0;
- bool res;
- DBUG_ASSERT(p);
+ if (!p) // no pump to add
+ return 0;
+
+ p->set_logger(m_log);
p->start_pos= avg;
DBUG_PRINT("backup/data",("Adding %s to scheduler (at pos %lu)",
@@ -578,8 +674,11 @@
m_count++;
m_total += avg;
- res= p->begin();
- DBUG_ASSERT(res);
+ if (p->begin())
+ goto error;
+
+ // in case of error, above call should return non-zero code (and report error)
+ DBUG_ASSERT(p->state != backup_state::ERROR);
if (p->init_size != Driver::UNKNOWN_SIZE)
{
@@ -607,11 +706,20 @@
DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
m_count, init_count, prepare_count, finish_count));
DBUG_PRINT("backup/data",("total init data size estimate: %lu",(unsigned long)m_init_left));
+
+ return 0;
+
+ error:
+
+ delete p;
+ cancel_backup();
+ return -1;
}
/// Move backup pump to the end of scheduler's list.
void Scheduler::move_pump_to_end(const Pump_iterator &p)
{
+ // The pump to move is in the m_pumps list so the list can't be empty.
DBUG_ASSERT(m_pumps);
if (m_last != p.el)
{
@@ -643,63 +751,92 @@
if (p)
{
- p->end();
+ // destructor calls driver's free() method
delete static_cast<Pump*>(p.el->data);
my_free((::gptr)p.el,MYF(0));
}
}
+/// Shut down backup process.
+void Scheduler::cancel_backup()
+{
+ if (cancelled)
+ return;
+
+ // shutdown any remaining drivers
+ while (m_count && m_pumps)
+ {
+ Pump_iterator p(*this);
+ p->cancel();
+ remove_pump(p);
+ }
+
+ cancelled= TRUE;
+}
+
+
/// Start prepare phase for all drivers.
-void Scheduler::prepare()
+int Scheduler::prepare()
{
+ DBUG_ASSERT(!cancelled);
+ // we should start prepare phase only when init phase is finished
DBUG_ASSERT(init_count==0);
DBUG_PRINT("backup/data",("calling prepare() for all drivers"));
- for(Pump_iterator it(*this); it; ++it)
+ for (Pump_iterator it(*this); it; ++it)
{
- it->prepare();
+ if (it->prepare())
+ {
+ cancel_backup();
+ return -1;
+ }
if (it->state == backup_state::PREPARING)
prepare_count++;
}
DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
m_count, init_count, prepare_count, finish_count));
+ return 0;
}
/// Lock all drivers.
-void Scheduler::lock()
+int Scheduler::lock()
{
- DBUG_ASSERT(prepare_count==0);
+ DBUG_ASSERT(!cancelled);
+ // lock only when init and prepare phases are finished
+ DBUG_ASSERT(init_count==0 && prepare_count==0);
DBUG_PRINT("backup/data",("calling lock() for all drivers"));
- for(Pump_iterator it(*this); it; ++it)
- it->lock();
+ for (Pump_iterator it(*this); it; ++it)
+ if (it->lock())
+ {
+ cancel_backup();
+ return -1;
+ }
DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
m_count, init_count, prepare_count, finish_count));
+ return 0;
}
/// Unlock all drivers.
-void Scheduler::unlock()
+int Scheduler::unlock()
{
+ DBUG_ASSERT(!cancelled);
DBUG_PRINT("backup/data",("calling unlock() for all drivers"));
+
for(Pump_iterator it(*this); it; ++it)
{
- it->unlock();
+ if (it->unlock())
+ {
+ cancel_backup();
+ return -1;
+ }
if (it->state == backup_state::FINISHING)
finish_count++;
}
-}
-/// Shut down backup process.
-void Scheduler::close()
-{
- // shutdown any remaining drivers
- while (m_count && m_pumps)
- {
- Pump_iterator p(*this);
- remove_pump(p);
- }
+ return 0;
}
@@ -721,9 +858,10 @@
1+img.tables.count(),
FALSE); // not thread safe
m_name= img.name();
- m_drv= img.get_backup_driver();
- DBUG_ASSERT(m_drv);
- init_size= m_drv->init_size();
+ if (ERROR == img.get_backup_driver(m_drv) || !m_drv)
+ state= backup_state::ERROR;
+ else
+ init_size= m_drv->init_size();
}
Backup_pump::~Backup_pump()
@@ -734,64 +872,115 @@
}
/// Initialize backup driver.
-bool Backup_pump::begin()
+int Backup_pump::begin()
{
state= backup_state::INIT;
DBUG_PRINT("backup/data",(" %s enters INIT state",m_name));
- return m_drv->begin(m_buf_size) == backup::OK;
+
+ if (ERROR == m_drv->begin(m_buf_size))
+ {
+ state= backup_state::ERROR;
+ // We check if logger is always setup. Later the assertion can
+ // be replaced with "if (m_log)"
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_INIT_BACKUP_DRIVER,m_name);
+ return -1;
+ }
+
+ return 0;
}
/// Shut down the driver.
-bool Backup_pump::end()
+int Backup_pump::end()
{
if (state != backup_state::SHUT_DOWN)
{
DBUG_PRINT("backup/data",(" shutting down %s",m_name));
- m_drv->end();
+
+ if (ERROR == m_drv->end())
+ {
+ state= backup_state::ERROR;
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_STOP_BACKUP_DRIVER,m_name);
+ return -1;
+ }
+
state= backup_state::SHUT_DOWN;
}
- return TRUE;
+
+ return 0;
}
/// Start prepare phase for the driver.
-bool Backup_pump::prepare()
+int Backup_pump::prepare()
{
result_t res= m_drv->prelock();
- state= backup_state::PREPARING;
-
switch (res) {
case READY:
state= backup_state::READY;
+ break;
case OK:
- res= OK;
+ state= backup_state::PREPARING;
break;
+ case ERROR:
default:
- break;
-
+ state= backup_state::ERROR;
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_PREPARE_DRIVER, m_name);
+ return -1;
}
DBUG_PRINT("backup/data",(" preparing %s, goes to %s state",
m_name,backup_state::name[state]));
- return res == backup::OK;
+ return 0;
}
/// Request VP from the driver.
-bool Backup_pump::lock()
+int Backup_pump::lock()
{
DBUG_PRINT("backup/data",(" locking %s",m_name));
- return m_drv->lock() == backup::OK;
+ if (ERROR == m_drv->lock())
+ {
+ state= backup_state::ERROR;
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_CREATE_VP,m_name);
+ return -1;
+ }
+
+ return 0;
}
/// Unlock the driver after VP creation.
-bool Backup_pump::unlock()
+int Backup_pump::unlock()
{
DBUG_PRINT("backup/data",(" unlocking %s, goes to FINISHING state",m_name));
state= backup_state::FINISHING;
- return m_drv->unlock() == backup::OK; // FIXME: better handling of errors
+ if (ERROR == m_drv->unlock())
+ {
+ state= backup_state::ERROR;
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_UNLOCK_DRIVER,m_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int Backup_pump::cancel()
+{
+ if (ERROR == m_drv->cancel())
+ {
+ state= backup_state::ERROR;
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_CANCEL_BACKUP,m_name);
+ return -1;
+ }
+ state= backup_state::CANCELLED;
+ return 0;
}
/**
@@ -810,18 +999,27 @@
</pre>
*/
-// FIXME: return number of bytes *written* to stream.
+// Consider: report number of bytes *written* to stream.
-size_t Backup_pump::pump()
+int Backup_pump::pump(size_t *howmuch)
{
+ // pumping not allowed in these states
+ DBUG_ASSERT(state != backup_state::INACTIVE);
+ DBUG_ASSERT(state != backup_state::SHUT_DOWN);
+ DBUG_ASSERT(state != backup_state::CANCELLED);
+
+ // we have detected error before - report it once more
+ if (state == backup_state::ERROR)
+ return -1;
- if (state == backup_state::INACTIVE
- || state == backup_state::ERROR
- || state == backup_state::DONE )
+ // we are done and thus there is nothing to do
+ if (state == backup_state::DONE)
return 0;
backup_state::value before_state= state;
- size_t howmuch= 0;
+
+ if (howmuch)
+ *howmuch= 0;
if (all_streams_closed())
{
@@ -839,6 +1037,9 @@
state= backup_state::DONE;
break;
+ case backup_state::ERROR:
+ return ERROR;
+
default: break;
}
}
@@ -846,8 +1047,10 @@
{
if (mode == READING)
{
- // If m_buf_head is NULL then a new request to the driver
- // should be made. We should allocate a new output buffer.
+ /*
+ If m_buf_head is NULL then a new request to the driver
+ should be made. We should allocate a new output buffer.
+ */
if (!m_buf_head)
switch (m_bw.get_buf(m_buf)) {
@@ -855,20 +1058,20 @@
case Block_writer::OK:
m_buf_retries= 0;
m_buf_head= m_buf.data;
- m_buf.data+= 4; // leave space for image and stram numbers
+ m_buf.data+= 4; // leave space for image and stream numbers
m_buf.size-= 4;
break;
case Block_writer::NO_RES:
- if (++m_buf_retries > get_buf_retries)
- state= backup_state::ERROR;
- return 0;
+ if (++m_buf_retries <= get_buf_retries)
+ return 0; // we shall try again
case Block_writer::ERROR:
default:
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_GET_BUF);
state= backup_state::ERROR;
- return 0;
-
+ return -2;
}
DBUG_ASSERT(m_buf_head);
@@ -907,7 +1110,6 @@
if ( m_buf.size > 0 )
{
- // TODO: use OStream interface for consistency
m_buf.size+= 4;
int2store(m_buf.data,m_drv_no+1);
int2store(m_buf.data+2,m_buf.table_no);
@@ -924,16 +1126,15 @@
case ERROR:
default:
-
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_GET_DATA,m_name);
state= backup_state::ERROR;
+ return -3;
case BUSY:
m_bw.drop_buf(m_buf);
m_buf_head=NULL; // thus a new request will be made
-
- // TODO: case DONE:
-
}
} // if (mode == READING)
@@ -944,17 +1145,20 @@
case Block_writer::OK:
- howmuch= m_buf.size;
+ if (howmuch)
+ *howmuch= m_buf.size;
+
DBUG_PRINT("backup/data",(" added %lu bytes from %s to archive (drv_no=%u, table_no=%u)",
(unsigned long)howmuch, m_name, m_drv_no, m_buf.table_no));
-
mode= READING;
break;
case Block_writer::ERROR:
+ DBUG_ASSERT(m_log);
+ m_log->report_error(ER_BACKUP_WRITE_DATA, m_name, m_buf.table_no);
state= backup_state::ERROR;
- break;
+ return -4;
default: // retry write
break;
@@ -967,12 +1171,12 @@
DBUG_PRINT("backup/data",(" %s changes state %s->%s",
m_name,backup_state::name[before_state],
backup_state::name[state]));
-
- return howmuch;
+ return 0;
}
} // backup namespace
+
/***********************************************
DATA RESTORE
@@ -981,25 +1185,30 @@
namespace backup {
+
/**
Read backup image data from a backup stream and forward it to restore drivers.
*/
-bool restore_table_data(THD*, Restore_info &info, IStream &s)
+int restore_table_data(THD*, Restore_info &info, IStream &s)
{
DBUG_ENTER("restore::restore_table_data");
enum { READING, SENDING, DONE, ERROR } state= READING;
- // FIXME: when selective restores are implemented, check that
- // nothing was *selected* to be restored.
-
if (info.img_count==0 || info.table_count==0) // nothing to restore
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(0);
- result_t res;
- Restore_driver* drv[256];
+ Restore_driver* drv[MAX_IMAGES];
- DBUG_ASSERT(info.img_count < 256);
+ if (info.img_count > MAX_IMAGES)
+ {
+ info.report_error(ER_BACKUP_TOO_MANY_IMAGES, info.img_count, MAX_IMAGES);
+ DBUG_RETURN(-1);
+ }
+
+ // Initializing restore drivers
+ result_t res;
+ int ret=0;
for (uint no=0; no < info.img_count; ++no)
{
@@ -1011,144 +1220,212 @@
if (!img)
continue;
- drv[no]= img->get_restore_driver();
- DBUG_ASSERT(drv[no]);
+ res= img->get_restore_driver(drv[no]);
+ if (res == backup::ERROR)
+ {
+ info.report_error(ER_BACKUP_CREATE_RESTORE_DRIVER,img->name());
+ ret= -2;
+ goto error;
+ };
- res= drv[no]->begin(0); // TODO: provide correct size
- DBUG_ASSERT(res == OK);
+ res= drv[no]->begin(0);
+ if (res == backup::ERROR)
+ {
+ info.report_error(ER_BACKUP_INIT_RESTORE_DRIVER,img->name());
+ ret= -3;
+ goto error;
+ }
}
- Buffer buf;
- uint img_no=0;
- uint repeats=0, errors= 0;
-
- static const uint MAX_ERRORS= 3;
- static const uint MAX_REPEATS= 7;
-
- size_t start_bytes= s.bytes;
-
- // main data reading loop
-
- while ( state != DONE && state != ERROR )
{
- switch (state) {
+ Buffer buf;
+ uint img_no=0;
+ uint repeats=0, errors= 0;
- case READING:
+ static const uint MAX_ERRORS= 3;
+ static const uint MAX_REPEATS= 7;
- switch (s >> buf) {
+ size_t start_bytes= s.bytes;
- case stream_result::EOS:
- state= DONE;
- break;
+ Restore_driver *drvr= NULL; // pointer to the current driver
+ Image_info *img= NULL; // corresponding restore image object
- case stream_result::OK:
- state= SENDING;
- break;
+ // main data reading loop
- default:
- state= ERROR;
- break;
-
- }
+ while ( state != DONE && state != ERROR )
+ {
+ switch (state) {
- if (state != SENDING)
- break;
+ case READING:
- DBUG_ASSERT(buf.data);
+ switch ((int)(s >> buf)) {
- img_no= uint2korr(buf.data);
- buf.table_no= uint2korr(buf.data+2);
- buf.data += 4;
- buf.size -= 4;
+ case stream_result::EOS:
+ state= DONE;
+ break;
- DBUG_PRINT("restore",("Got %lu bytes of subimage %u data (stream %u)",
- (unsigned long)buf.size, img_no, buf.table_no));
+ case stream_result::OK:
+ state= SENDING;
+ break;
- case SENDING:
+ case stream_result::ERROR:
+ info.report_error(ER_BACKUP_READ_DATA);
+ default:
+ state= ERROR;
+ ret= -4;
+ goto error;
- if( img_no < 1 || img_no > info.img_count || !drv[img_no-1] )
- {
- DBUG_PRINT("restore",("Skipping data for subimage %u",img_no));
- state= READING;
- break;
- }
+ }
- /*
- If the global variable "backup_sleep" has been set, use that value
- to cause the kernel to sleep in seconds.
- */
- if (backup_sleep)
- sleep(backup_sleep);
+ if (state != SENDING)
+ break;
- switch( drv[img_no-1]->send_data(buf) ) {
+ DBUG_ASSERT(buf.data);
- case backup::OK:
- switch (s.next_chunk()) {
+ img_no= uint2korr(buf.data);
+ buf.table_no= uint2korr(buf.data+2);
+ buf.data += 4;
+ buf.size -= 4;
- case stream_result::OK:
+ if (img_no < 1 || img_no > info.img_count || !(drvr= drv[img_no-1]))
+ {
+ DBUG_PRINT("restore",("Skipping data for image #%u",img_no));
state= READING;
- repeats= 0;
break;
+ }
+
+ img= info.images[img_no-1];
+ // Each restore driver should have corresponding Image_info object.
+ DBUG_ASSERT(img);
+
+ DBUG_PRINT("restore",("Got %lu bytes of %s image data (for table #%u)",
+ (unsigned long)buf.size, img->name(), buf.table_no));
+
+ case SENDING:
+
+ /*
+ If we are here, the img pointer should point at the image for which
+ we have next data block and drvr at its restore driver.
+ */
+ DBUG_ASSERT(img && drvr);
+
+ /*
+ If the global variable "backup_sleep" has been set, use that value
+ to cause the kernel to sleep in seconds.
+ */
+ if (backup_sleep)
+ sleep(backup_sleep);
+
+ switch( drvr->send_data(buf) ) {
+
+ case backup::OK:
+ switch ((int)s.next_chunk()) {
+
+ case stream_result::OK:
+ state= READING;
+ img= NULL;
+ drvr= NULL;
+ repeats= 0;
+ break;
+
+ case stream_result::EOS:
+ state= DONE;
+ break;
+
+ case stream_result::ERROR:
+ info.report_error(ER_BACKUP_NEXT_CHUNK);
+ default:
+ state= ERROR;
+ ret= -5;
+ goto error;
+ }
- case stream_result::EOS:
- state= DONE;
break;
- default:
- state= ERROR;
+ case backup::ERROR:
+ if( errors > MAX_ERRORS )
+ {
+ info.report_error(ER_BACKUP_SEND_DATA, buf.table_no, img->name());
+ state= ERROR;
+ ret= -6;
+ goto error;
+ }
+ errors++;
break;
+ case backup::PROCESSING:
+ case backup::BUSY:
+ default:
+ if( repeats > MAX_REPEATS )
+ {
+ info.report_error(ER_BACKUP_SEND_DATA_RETRY, repeats, img->name());
+ state= ERROR;
+ ret= -7;
+ goto error;
+ }
+ repeats++;
+ break;
}
+ default:
break;
+ } // switch(state)
- case backup::ERROR:
- if( errors > MAX_ERRORS )
- state= ERROR;
- errors++;
- break;
+ } // main reading loop
- case backup::PROCESSING:
- case backup::BUSY:
- default:
- if( repeats > MAX_REPEATS )
- {
- DBUG_PRINT("restore",("Quiting after 7 failed send_data() attempts"));
- state= ERROR;
- }
- repeats++;
- break;
+ DBUG_PRINT("restore",("End of backup stream"));
+ if (state != DONE)
+ DBUG_PRINT("restore",("state is %d",state));
- }
+ info.data_size= s.bytes - start_bytes;
+ }
- default:
- break;
+ { // Shutting down drivers
+
+ String bad_drivers;
+
+ for (uint no=0; no < info.img_count; ++no)
+ {
+ if (!drv[no])
+ continue;
- } // switch(state)
+ DBUG_PRINT("restore",("Shutting down restore driver %s",
+ info.images[no]->name()));
+ res= drv[no]->end();
+ if (res == backup::ERROR)
+ {
+ ret= -8;
+ state= ERROR;
- } // main reading loop
+ if (!bad_drivers.is_empty())
+ bad_drivers.append(",");
+ bad_drivers.append(info.images[no]->name());
+ }
+ drv[no]->free();
+ }
- DBUG_PRINT("restore",("End of backup stream"));
- if (state != DONE)
- DBUG_PRINT("restore",("state is %d",state));
+ if (!bad_drivers.is_empty())
+ info.report_error(ER_BACKUP_STOP_RESTORE_DRIVERS, bad_drivers.c_ptr());
+ }
- info.data_size= s.bytes - start_bytes;
+ DBUG_RETURN(state == ERROR ? -9 : 0);
- for(uint no=0; no < info.img_count; ++no)
+ error:
+
+ DBUG_PRINT("restore",("Cancelling restore process"));
+
+ for (uint no=0; no < info.img_count; ++no)
{
if (!drv[no])
continue;
- DBUG_PRINT("restore",("Shutting down driver %u",no));
- if (drv[no]->end() != backup::OK)
- DBUG_ASSERT(0);
+ drv[no]->cancel();
drv[no]->free();
}
- DBUG_RETURN(state != ERROR);
+ DBUG_RETURN(ret);
}
-
} // backup namespace
@@ -1165,7 +1442,7 @@
The buffer size is given by @c buf.size member.
- Current implementation tries to allocate the data transfer bufer in the
+ Current implementation tries to allocate the data transfer buffer in the
stream. It can handle only one buffer at a time.
@returns @c NO_RES if buffer can not be allocated, @c OK otherwise.
@@ -1188,7 +1465,7 @@
Write block of data to stream.
The buffer containing data must be obtained from a previous @c get_buf() call.
- After this call, bufer is returned to the buffer pool and can be reused for
+ After this call, buffer is returned to the buffer pool and can be reused for
other transfers.
*/
Block_writer::result_t
@@ -1216,5 +1493,3 @@
}
} // backup namespace
-
-
--- 1.3/sql/backup/debug.h 2007-06-23 23:38:59 +02:00
+++ 1.4/sql/backup/debug.h 2007-06-23 23:38:59 +02:00
@@ -3,7 +3,10 @@
#define BACKUP_SYNC_TIMEOUT 300
-// TODO: decide how to configure DeBUG_BACKUP
+/*
+ TODO
+ - decide how to configure DEBUG_BACKUP
+ */
#ifndef DBUG_OFF
# define DBUG_BACKUP
@@ -11,7 +14,55 @@
#ifdef DBUG_BACKUP
-// TODO: set correct info in thd->proc_info
+/*
+ Macros for debugging error (or other) conditions. Usage:
+
+ TEST_ERROR_IF(<condition deciding if TEST_ERROR should be true>);
+
+ if (<other conditions> || TEST_ERROR)
+ {
+ <report error>
+ }
+
+ The additional TEST_ERROR condition will be set only if "backup_error_test"
+ error injection is set in the server.
+
+ Notes:
+ - Whenever TEST_ERROR is used in a condition, TEST_ERROR_IF() should
+ be called before - otherwise TEST_ERROR might be unintentionally TRUE.
+ - This mechanism is not thread safe.
+ */
+
+namespace backup {
+ extern bool test_error_flag;
+}
+
+#define TEST_ERROR backup::test_error_flag
+// FIXME: DBUG_EXECUTE_IF below doesn't work
+#define TEST_ERROR_IF(X) \
+ do { \
+ backup::test_error_flag= FALSE; \
+ DBUG_EXECUTE_IF("backup_error_test", backup::test_error_flag= (X);); \
+ } while(0)
+
+/*
+ Macros for creating synchronization points in tests.
+
+ Usage
+
+ In the backup code:
+ BACKUP_SYNC("<synchronization point name>");
+
+ In a client:
+ SELECT get_lock("<synchronization point name>",<timeout>);
+ ...
+ SELECT release_lock("<synchronization point name>");
+
+ If the lock is kept by a client, server code will wait on the corresponding
+ BACKUP_SYNC() until it is released.
+
+ Consider: set thd->proc_info when waiting on lock
+ */
#define BACKUP_SYNC(S) \
do { \
@@ -22,6 +73,8 @@
#else
#define BACKUP_SYNC(S)
+#define TEST_ERROR FALSE
+#define TEST_ERROR_IF(X)
#endif
--- New file ---
+++ sql/backup/error.h 07/06/23 23:38:48
#ifndef _BACKUP_ERROR_H
#define _BACKUP_ERROR_H
namespace util {
/**
Report error stored in MYSQL_ERROR structure to a client.
If @c err doesn't contain any error code, the given error code is reported.
@returns 0 if error was reported, non-zero otherwise.
*/
inline
int report_mysql_error(THD*, MYSQL_ERROR *err, int code= 0)
{
DBUG_ASSERT(err);
if (err->level == MYSQL_ERROR::WARN_LEVEL_END
&& !err->msg && !err->code ) // err doesn't store any error
return -1;
switch (err->level) {
case MYSQL_ERROR::WARN_LEVEL_ERROR:
return my_printf_error(err->code ? err->code : code, err->msg, MYF(0));
default: // Q: What to do with warnings and notes? push them... ?
return -1;
}
}
} // util namespace
#endif
--- 1.1/sql/backup/logger.cc 2007-06-23 23:38:59 +02:00
+++ 1.2/sql/backup/logger.cc 2007-06-23 23:38:59 +02:00
@@ -4,34 +4,6 @@
namespace backup {
-/**
- Output message registered in errmsg.txt database.
-
- @param level level of the message (INFO,WARNING,ERROR)
- @param error_code code assigned to the message in errmsg.txt
-
- If the message contains placeholders, additioal arguments provide
- values to be put there.
-
- @returns 0 on success.
- */
-int Logger::report_error(log_level::value level, int error_code, ...)
-{
- const char *format;
- char buf[ERRMSGSIZE + 20];
- va_list args;
-
- if (error_code < ER_ERROR_FIRST || error_code > ER_ERROR_LAST)
- format= ER(ER_UNKNOWN_ERROR);
- else
- format= ER(error_code);
-
- va_start(args,error_code);
- my_vsnprintf(buf,sizeof(buf),format,args);
- va_end(args);
-
- return write_message(level,error_code,buf);
-}
/**
Output message on a given level.
@@ -45,26 +17,65 @@
for other messages set to 0
@param msg message text
- @returns 0 on succes.
+ @returns 0 on success.
*/
int Logger::write_message(log_level::value level, int error_code,
const char *msg)
{
switch (level) {
case log_level::ERROR:
+ if (m_save_errors)
+ errors.push_front(new MYSQL_ERROR(::current_thd,error_code,
+ MYSQL_ERROR::WARN_LEVEL_ERROR,msg));
sql_print_error(msg);
+ DBUG_PRINT("backup log",("[ERROR] %s",msg));
return 0;
case log_level::WARNING:
sql_print_warning(msg);
+ DBUG_PRINT("backup log",("[Warning] %s",msg));
return 0;
case log_level::INFO:
sql_print_information(msg);
+ DBUG_PRINT("backup log",("[Info] %s",msg));
return 0;
default: return -1;
}
}
+
+/**
+ Output message registered in errmsg.txt database.
+
+ @param level level of the message (INFO,WARNING,ERROR)
+ @param error_code code assigned to the message in errmsg.txt
+
+ If the message contains placeholders, additional arguments provide
+ values to be put there.
+
+ @returns 0 on success.
+ */
+int Logger::v_report_error(log_level::value level, int error_code, va_list args)
+{
+ return v_write_message(level,error_code,ER_SAFE(error_code),args);
+}
+
+/**
+ Output unregistered message.
+
+ Format string is given explicitly as an argument.
+
+ Note: no localization support.
+ */
+int Logger::v_write_message(log_level::value level, int error_code,
+ const char *format, va_list args)
+{
+ char buf[ERRMSGSIZE + 20];
+
+ my_vsnprintf(buf,sizeof(buf),format,args);
+ return write_message(level,0,buf);
+}
+
} // backup namespace
--- 1.1/sql/backup/logger.h 2007-06-23 23:38:59 +02:00
+++ 1.2/sql/backup/logger.h 2007-06-23 23:38:59 +02:00
@@ -1,11 +1,18 @@
#ifndef _BACKUP_LOGGER_H
#define _BACKUP_LOGGER_H
+#include <backup/error.h>
+
+
namespace backup {
/// Logging levels for messages generated by backup system
struct log_level {
- enum value { INFO, WARNING, ERROR };
+ enum value {
+ INFO= MYSQL_ERROR::WARN_LEVEL_NOTE,
+ WARNING= MYSQL_ERROR::WARN_LEVEL_WARN,
+ ERROR= MYSQL_ERROR::WARN_LEVEL_ERROR
+ };
};
@@ -24,20 +31,90 @@
{
public:
+ Logger(): m_save_errors(FALSE)
+ {}
+
int write_message(log_level::value level , int error_code, const char *msg);
- int report_error(log_level::value level, int error_code, ...);
+ int write_message(log_level::value level, const char *msg, ...)
+ {
+ va_list args;
+
+ va_start(args,msg);
+ int res= v_write_message(level, 0, msg, args);
+ va_end(args);
+
+ return res;
+ }
- /// Calls @c report_error() with log_level::ERROR.
+ /// Reports error with log_level::ERROR.
int report_error(int error_code, ...)
{
va_list args;
va_start(args,error_code);
- int res= report_error(log_level::ERROR, error_code, args);
+ int res= v_report_error(log_level::ERROR, error_code, args);
va_end(args);
+
+ return res;
+ }
+
+ /// Reports error with registered error description string.
+ int report_error(log_level::value level, int error_code, ...)
+ {
+ va_list args;
+
+ va_start(args,error_code);
+ int res= v_report_error(level, error_code, args);
+ va_end(args);
+
return res;
}
+
+ /// Reports error with given description.
+ int report_error(const char *format, ...)
+ {
+ va_list args;
+
+ va_start(args,format);
+ int res= v_write_message(log_level::ERROR, 0, format, args);
+ va_end(args);
+
+ return res;
+ }
+
+ /// Request that all reported errors are saved in the logger.
+ void save_errors()
+ {
+ if (m_save_errors)
+ return;
+ clear_saved_errors();
+ m_save_errors= TRUE;
+ }
+
+ /// Stop saving errors.
+ void stop_save_errors()
+ {
+ if (!m_save_errors)
+ return;
+ m_save_errors= FALSE;
+ }
+
+ /// Delete all saved errors to free resources.
+ void clear_saved_errors()
+ { errors.delete_elements(); }
+
+ /// Return a pointer to most recent saved error.
+ MYSQL_ERROR *last_saved_error()
+ { return errors.head(); }
+
+ private:
+
+ List<MYSQL_ERROR> errors; ///< Used to store saved errors
+ bool m_save_errors; ///< Flag telling if errors should be saved
+
+ int v_report_error(log_level::value,int,va_list);
+ int v_write_message(log_level::value,int, const char*,va_list);
};
--- 1.2/sql/backup/map.h 2007-06-23 23:38:59 +02:00
+++ 1.3/sql/backup/map.h 2007-06-23 23:38:59 +02:00
@@ -26,13 +26,13 @@
bool occupied(const Key&) const;
size_t count() const { return m_count; };
- Map(): m_count(0) {};
+ Map(): m_count(0) {}
#ifdef MAP_DEBUG
void print();
#endif
- ~Map() { clear(); };
+ ~Map() { clear(); }
void clear();
@@ -69,16 +69,18 @@
}
template<class D, class K>
+inline
typename Map<D,K>::El &
Map<D,K>::operator[](const Key &k) const
{
- if( !occupied(k) )
+ if (!occupied(k))
return const_cast<El&>(D::null);
return *(entries[k].el);
}
template<class D, class K>
+inline
typename Map<D,K>::Key
Map<D,K>::add(const El &e)
{
@@ -87,6 +89,7 @@
}
template<class D, class K>
+inline
typename Map<D,K>::Key
Map<D,K>::find(const El &e) const
{
@@ -94,15 +97,16 @@
return const_cast<Map<D,K>*>(this)->find_el(k,e);
}
-// Assumption, k is valid.
+// PRE: k is valid.
template<class D, class K>
+inline
typename Map<D,K>::Key
Map<D,K>::find_el(const Key &k, const El &e, bool insert)
{
El *x= entries[k].el;
- if( !x )
- if( insert )
+ if (!x)
+ if (insert)
{
set(k,e);
return k;
@@ -111,41 +115,43 @@
int res;
- if( (res= D::cmp(e,*x)) == 0 )
+ if ((res= D::cmp(e,*x)) == 0)
return k;
Key &k1 = res>0 ? entries[k].bigger : entries[k].smaller;
- if( K::valid_key(k1) )
+ if (K::valid_key(k1))
return find_el(k1,e);
Key k2;
- if( K::valid_key(k2= find_free_loc()) )
+ if (K::valid_key(k2= find_free_loc()))
{
k1= k2;
set(k2,e);
- };
+ }
return k2;
}
template<class D, class K>
+inline
typename Map<D,K>::Key
Map<D,K>::find_free_loc() const
{
- if( m_count >= K::size )
+ if (m_count >= K::size)
return K::null;
- for(uint k=0 ; k < size ; k++ )
- if( entries[k].el == NULL )
+ for (uint k=0; k < size; k++)
+ if (entries[k].el == NULL)
return k;
return K::null;
}
-// Assumption: k is valid
+// PRE k is valid
template<class D, class K>
+inline
void Map<D,K>::set(const Key &k, const El &e)
{
entries[k]= e;
@@ -153,10 +159,11 @@
}
template<class D, class K>
+inline
void Map<D,K>::clear()
{
- for(uint i=0 ; i < size ; i++)
- if( entries[i].el )
+ for (uint i=0; i < size; i++)
+ if (entries[i].el)
delete entries[i].el;
}
@@ -186,7 +193,8 @@
key8 &key8::operator=(unsigned int x)
{
val= x & 0xFF;
- for( int bits= sizeof(unsigned int) ; bits > 8 ; bits-= 8 )
+ // simple hashing
+ for (int bits= sizeof(unsigned int) ; bits > 8 ; bits-= 8)
{
x >>= 8;
val ^= x &0xFF;
--- 1.17/sql/backup/meta_backup.cc 2007-06-23 23:38:59 +02:00
+++ 1.18/sql/backup/meta_backup.cc 2007-06-23 23:38:59 +02:00
@@ -1,33 +1,42 @@
/**
@file
- Code to save/read meta-data image.
+ Functions used by kernel to save/restore meta-data
*/
/*
TODO:
- Handle events, routines, triggers and other item types (use Chuck's code).
+
+ - When saving database info, save its default charset and collation.
+ Alternatively, save a complete "CREATE DATABASE ..." statement with all
+ clauses which should work even when new clauses are added in the future.
+
+ - In silent_exec_query() - reset, collect and report errors from statement
+ execution.
+
+ - In the same function: investigate what happens if query triggers error -
+ there were signals that the code might hang in that case (some clean-up
+ needed?)
*/
#include <mysql_priv.h>
#include <sql_show.h>
-//#include <event_scheduler.h>
-//#include <sp.h>
-//#include <sql_trigger.h>
-//#include <log.h>
+/*
+ Might be needed for other meta-data items
+
+#include <event_scheduler.h>
+#include <sp.h>
+#include <sql_trigger.h>
+#include <log.h>
+*/
#include "backup_aux.h"
#include "backup_kernel.h"
#include "meta_backup.h"
-/*******************************************************
-
- Functions used by kernel to save/restore meta-data
-
- *******************************************************/
-
namespace backup {
/**
@@ -44,9 +53,10 @@
@note Meta-data description is added to the current chunk of the stream which
is then closed.
+
+ @returns 0 on success, error code otherwise.
*/
-bool
-write_meta_data(THD *thd, Backup_info &info, OStream &s)
+int write_meta_data(THD *thd, Backup_info &info, OStream &s)
{
DBUG_ENTER("backup::write_meta_data");
@@ -54,15 +64,23 @@
for (Backup_info::Item_iterator it(info); it; it++)
{
- stream_result::value res= it->save(thd,s); // this calls Archive_info::Item::save() method
- DBUG_ASSERT(res != stream_result::ERROR);
+ result_t res= it->save(thd,s); // this calls Archive_info::Item::save() method
+
+ if (res != OK)
+ {
+ meta::Item::description_buf buf;
+ info.report_error(ER_BACKUP_WRITE_META,
+ it->meta().describe(buf,sizeof(buf)));
+ DBUG_RETURN(-1);
+ }
}
- s.end_chunk();
+ if (stream_result::ERROR == s.end_chunk())
+ DBUG_RETURN(-2);
info.meta_size= s.bytes - start_bytes;
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(0);
}
@@ -70,27 +88,32 @@
Read meta-data items from a stream and create them if they are selected
for restore.
- @pre Stream is at the beginning of the meta-data chunk.
- @post Stream is at the beginning of the first chunk after the meta-data one.
+ @pre Stream is at the beginning of a saved meta-data chunk.
+ @post Stream is at the beginning of the next chunk.
*/
-bool
+int
restore_meta_data(THD *thd, Restore_info &info, IStream &s)
{
+ DBUG_ENTER("restore_meta_data");
Archive_info::Item *it; // save pointer to item read form the stream
Db_ref curr_db; // remember the current database
+ result_t res;
size_t start_bytes= s.bytes;
- while ((it= Archive_info::Item::create_from_stream(info,s))) // read next item from the stream
+ // read items from the stream until error or end of data is reached.
+ while (OK == (res= Archive_info::Item::create_from_stream(info,s,it)))
{
DBUG_PRINT("restore",(" got next meta-item."));
+
if (info.selected(*it)) // if the item was selected for restore ...
{
DBUG_PRINT("restore",(" creating it!"));
- // change the current database if we are going to create a per-db item
- // and we are not already in the correct one.
-
+ /*
+ change the current database if we are going to create a per-db item
+ and we are not already in the correct one.
+ */
const Db_ref db= it->meta().in_db();
if (db.is_valid() && (!curr_db.is_valid() || db != curr_db))
@@ -100,19 +123,37 @@
change_db(thd,db);
}
- bool res= it->meta().create(thd); // This calls method create() in the
- // appropriate subclass of meta::Item class
- DBUG_ASSERT(res); // TODO: handle errors!
+ if (OK != (res= it->meta().create(thd)))
+ {
+ meta::Item::description_buf buf;
+ info.report_error(ER_BACKUP_CREATE_META,
+ it->meta().describe(buf,sizeof(buf)));
+ DBUG_RETURN(-1);
+ }
delete it;
}
}
- stream_result::value res= s.next_chunk();
+ // We should reach end of chunk now - if not something went wrong
+
+ if (res != DONE)
+ {
+ info.report_error(ER_BACKUP_READ_META);
+ DBUG_RETURN(-2);
+ }
+
+ DBUG_ASSERT(res == DONE);
+
+ if (stream_result::ERROR == s.next_chunk())
+ {
+ info.report_error(ER_BACKUP_NEXT_CHUNK);
+ DBUG_RETURN(-3);
+ };
info.meta_size= s.bytes - start_bytes;
- return res != stream_result::ERROR;
+ DBUG_RETURN(0);
}
} // backup namespace
@@ -126,7 +167,7 @@
namespace backup {
-bool silent_exec_query(THD*, String&);
+int silent_exec_query(THD*, String&);
/**
Write data needed to restore an item.
@@ -135,17 +176,19 @@
This statement is constructed using @c meta::X::build_create_stmt() method
where meta::X is the class representing the item. The method stores statement
in the @c create_stmt member.
+
+ @returns OK or ERROR
*/
-stream_result::value meta::Item::save(THD *thd, OStream &s)
+result_t
+meta::Item::save(THD *thd, OStream &s)
{
create_stmt.free();
- bool res= build_create_stmt(thd);
-
- DBUG_ASSERT(res);
+ if (build_create_stmt(thd))
+ return ERROR;
- return s.writestr(create_stmt);
+ return stream_result::OK == s.writestr(create_stmt) ? OK : ERROR;
}
/**
@@ -153,10 +196,18 @@
By default, the CREATE statement is read and stored in the @c create_stmt
member.
+
+ @retval OK everything went ok
+ @retval DONE end of data chunk detected
+ @retval ERROR error has happened
*/
-stream_result::value meta::Item::read(IStream &s)
+result_t
+meta::Item::read(IStream &s)
{
- return s.readstr(create_stmt);
+ stream_result::value res= s.readstr(create_stmt);
+
+ // Saved string should not be NIL
+ return res == stream_result::NIL ? ERROR : (result_t)res;
}
/**
@@ -164,13 +215,19 @@
Default implementation executes the statement stored in the @c create_stmt
member.
+
+ @returns OK or ERROR
*/
-bool meta::Item::create(THD *thd)
+result_t
+meta::Item::create(THD *thd)
{
if (create_stmt.is_empty())
- { return FALSE; }
+ { return ERROR; }
- return drop(thd) && silent_exec_query(thd,create_stmt);
+ if (ERROR == drop(thd))
+ return ERROR;
+
+ return silent_exec_query(thd,create_stmt) ? ERROR : OK;
}
/**
@@ -182,12 +239,20 @@
</pre>
strings @<object type> and @<name> are returned by @c X::sql_object_name()
and @c X::sql_name() methods of the class @c meta::X representing the item.
- If neccessary, method @c drop() can be overwriten in a specialized class
+ If necessary, method @c drop() can be overwritten in a specialized class
corresponding to a given type of meta-data item.
+
+ @returns OK or ERROR
*/
-bool meta::Item::drop(THD *thd)
+result_t
+meta::Item::drop(THD *thd)
{
const char *ob= sql_object_name();
+
+ /*
+ An item class should define object name for DROP statement
+ or redefine drop() method.
+ */
DBUG_ASSERT(ob);
String drop_stmt;
@@ -197,7 +262,7 @@
drop_stmt.append(" IF EXISTS ");
drop_stmt.append(sql_name());
- return silent_exec_query(thd,drop_stmt);
+ return silent_exec_query(thd,drop_stmt) ? ERROR : OK;
};
/**** SAVE/RESTORE DATABASES ***********************************/
@@ -207,14 +272,11 @@
Currently we don't save anything. A database is always created using
"CREATE DATABASE @<name>" statement.
-
- TODO: Save info about db charset and collation. Alternatively, save a complete
- "CREATE DATABASE ..." statement with all clauses which should work even when
- new clauses are added in the future.
*/
-stream_result::value meta::Db::save(THD*,OStream&)
+result_t
+meta::Db::save(THD*,OStream&)
{
- return stream_result::OK;
+ return OK;
}
/**
@@ -223,25 +285,26 @@
Nothing to read. We just build the "CREATE DATABASE ..." statement in
@c create_stmt.
*/
-stream_result::value meta::Db::read(IStream&)
+result_t
+meta::Db::read(IStream&)
{
create_stmt.append("CREATE DATABASE ");
create_stmt.append(sql_name());
- return stream_result::OK;
+ return OK;
}
/**** SAVE/RESTORE TABLES ***************************************/
/**
- Build a CREATE steatement for a table.
+ Build a CREATE statement for a table.
We use @c store_create_info() function defined in the server. For that
we need to open the table. After building the statement the table is closed to
save resources. Actually, all tables of the thread are closed as we use
@c close_thread_tables() function.
*/
-bool meta::Table::build_create_stmt(THD *thd)
+int meta::Table::build_create_stmt(THD *thd)
{
TABLE_LIST t, *tl= &t;
@@ -252,13 +315,23 @@
uint cnt;
int res= ::open_tables(thd,&tl,&cnt,0);
- DBUG_ASSERT(res==0);
+
+ if (res)
+ {
+ DBUG_PRINT("backup",("Can't open table %s to save its description (error=%d)",
+ t.alias,res));
+ return res;
+ }
res= ::store_create_info(thd,&t,&create_stmt,NULL);
+ if (res)
+ DBUG_PRINT("backup",("Can't get CREATE statement for table %s (error=%d)",
+ t.alias,res));
+
::close_thread_tables(thd);
- return res == 0;
+ return res;
}
} // backup namespace
@@ -274,11 +347,12 @@
/// Execute SQL query without sending anything to client.
-// Change net.vio idea taken from execute_init_command in sql_parse.cc
-// TODO: investigate what happens if query triggers error - there were
-// signals that the code might hang in that case (some clean-up needed?)
+/*
+ Note: the change net.vio idea taken from execute_init_command in
+ sql_parse.cc
+ */
-bool silent_exec_query(THD *thd, String &query)
+int silent_exec_query(THD *thd, String &query)
{
Vio *save_vio= thd->net.vio;
@@ -297,19 +371,18 @@
::mysql_parse(thd,thd->query,thd->query_length);
+ thd->net.vio= save_vio;
+
if (thd->query_error)
{
DBUG_PRINT("restore",
("error executing query %s!", thd->query));
- DBUG_PRINT("restore",
- ("last error (%d): %s",thd->net.last_errno,
- thd->net.last_error));
- return FALSE;
+ DBUG_PRINT("restore",("last error (%d): %s",thd->net.last_errno
+ ,thd->net.last_error));
+ return thd->net.last_errno ? thd->net.last_errno : -1;
}
- thd->net.vio= save_vio;
-
- return TRUE;
+ return 0;
}
} // backup namespace
@@ -317,7 +390,7 @@
/**********************************************************
- Old code to intorporate into above framework: handle
+ Old code to incorporate into above framework: handles
events, routines and triggers
**********************************************************/
--- 1.1/sql/backup/meta_backup.h 2007-06-23 23:38:59 +02:00
+++ 1.2/sql/backup/meta_backup.h 2007-06-23 23:38:59 +02:00
@@ -10,6 +10,8 @@
#include <backup/api_types.h>
#include <backup/stream.h>
+#define META_ITEM_DESCRIPTION_LEN 128
+
namespace backup {
namespace meta {
@@ -45,16 +47,25 @@
{ return Db_ref(); }
/// Save data needed to create the item.
- virtual stream_result::value save(THD*,OStream&);
+ virtual result_t save(THD*,OStream&);
/// Read data saved by @c save() method.
- virtual stream_result::value read(IStream&);
+ virtual result_t read(IStream&);
/// Destroy the item if it exists.
- virtual bool drop(THD*);
+ virtual result_t drop(THD*);
/// Create the item.
- virtual bool create(THD*);
+ virtual result_t create(THD*);
+
+ typedef char description_buf[META_ITEM_DESCRIPTION_LEN+1];
+
+ /// Describe item for log purposes.
+ virtual const char* describe(char *buf, size_t buf_len)
+ {
+ my_snprintf(buf,buf_len,"%s %s",sql_object_name(),sql_name());
+ return buf;
+ }
protected:
@@ -75,8 +86,12 @@
virtual const char* sql_name() const =0;
/// Store in @c create_stmt a DDL statement which will create the item.
- virtual bool build_create_stmt(THD*)
- { return FALSE; }
+ /*
+ We give a default implementation because an item can not use create
+ statements and then it doesn't have to worry about this method.
+ */
+ virtual int build_create_stmt(THD*)
+ { return -1; }
};
@@ -92,8 +107,8 @@
{ return "DATABASE"; }
// Overwrite default implementations.
- stream_result::value save(THD*,OStream&);
- stream_result::value read(IStream&);
+ result_t save(THD*,OStream&);
+ result_t read(IStream&);
};
@@ -108,7 +123,7 @@
const char* sql_object_name() const
{ return "TABLE"; }
- bool build_create_stmt(THD*);
+ int build_create_stmt(THD*);
};
} // meta namespace
--- 1.7/sql/backup/stream.cc 2007-06-23 23:38:59 +02:00
+++ 1.8/sql/backup/stream.cc 2007-06-23 23:38:59 +02:00
@@ -2,9 +2,17 @@
#include "stream.h"
+/*
+ TODO
+
+ - blocking of OStream output when data window is allocated.
+ - use my_read instead of read - need to know how to detect EOF.
+
+ */
namespace backup {
+/************** IStream *************************/
Window::Result Window::set_length(const size_t len)
{
@@ -59,8 +67,6 @@
/************** OStream *************************/
-// TODO: blocking of other stream operations when window is allocated.
-
byte* OStream::get_window(const size_t len)
{
if (m_blocked || m_end+len > last_byte)
@@ -187,8 +193,7 @@
size_t len= next_chunk_len+2;
- // TODO: Learn how to read stream with EOF detection using my_read
- long int howmuch= 0; // POSIX ssize_t not defined in Win :|
+ long int howmuch= 0; // POSIX ssize_t not defined on win platform :|
while (len > 0 && (howmuch= ::read(m_fd,m_buf,len)) > 0)
len-= howmuch;
@@ -219,6 +224,8 @@
return howmuch==0 ? stream_result::EOS : stream_result::OK;
}
+#ifdef DBUG_BACKUP
+
// Show data chunks in a backup stream;
void dump_stream(IStream &s)
@@ -247,9 +254,9 @@
if (res == stream_result::EOS)
DBUG_PRINT("stream",("=========="));
else
- DBUG_PRINT("stream",("== ERROR: %d",res));
+ DBUG_PRINT("stream",("== ERROR: %d",(int)res));
}
-} // backup namespace
-
+#endif
+} // backup namespace
--- 1.9/sql/backup/stream.h 2007-06-23 23:38:59 +02:00
+++ 1.10/sql/backup/stream.h 2007-06-23 23:38:59 +02:00
@@ -2,13 +2,13 @@
#define _BACKUP_STREAM_H_
#include <backup/api_types.h> // for Buffer definition
+#include <backup/debug.h> // for definition of DBUG_BACKUP
-/***********************************************************
-
- Generic Stream Intrface for serializing basic types
- (ints, strings etc).
+/**
+ @file
- ***********************************************************/
+ Generic Stream Interface for serializing basic types (integers, strings etc).
+ */
namespace util
{
@@ -23,10 +23,11 @@
/*
Stream window: an abstract object which can be used to access a stream
- of bytes through a window in process address space. The methods give pointer to the
- beginning of the window and its current size. There is a request for enlarging the
- window. We can also move the window beginning to the right (decreasing window size).
- If some bytes move outside the window, they can't be accesed any more.
+ of bytes through a window in process address space. The methods give pointer
+ to the beginning of the window and its current size. There is a request for
+ enlarging the window. We can also move the window beginning to the right
+ (decreasing window size). If some bytes move outside the window, they can't
+ be accessed any more.
================================================> (stream of bytes)
@@ -58,15 +59,15 @@
byte* head() const; // pointer to start of the window
byte* end() const; // pointer to the end of the window
size_t length() const; // current length of the window (in bytes)
- Result set_length(const size_t size); // extend the window to have (at least) given length
- Result move(const off_t offset); // move window header
+ Result set_length(const size_t size); // extend the window to have (at least) given length
+ Result move(const off_t offset); // move window header
//void init();
//void done();
operator int(const Result&); // interpret the result: is it error or other
common situation (end of data).
- Note: window can be used for reading or writting.
+ Note: window can be used for reading or writing.
*/
@@ -76,7 +77,7 @@
These classes provide interface for serializing basic data
(numbers, strings) using host independent format.
- Interface is parametrized by a class implementing stream window.
+ Interface is parametrised by a class implementing stream window.
Instance of the window class is used to read/write stream data.
*/
@@ -97,7 +98,12 @@
Result read4int(ulong &x);
Result readint(ulong &x);
Result readint(uint &x)
- { ulong y; Result res= readint(y); x= y; return res; };
+ {
+ ulong y;
+ Result res= readint(y);
+ x= y;
+ return res;
+ }
Result readstr(String &s);
@@ -122,19 +128,19 @@
Result writeint(const ulong x);
Result writestr(const String &s);
Result writestr(const char *s)
- { return writestr(String(s,table_alias_charset)); };
+ { return writestr(String(s,table_alias_charset)); }
Result writenil();
};
-
template<class SW>
+inline
typename IStream<SW>::Result
IStream<SW>::readbyte(byte &x)
{
Result res;
- if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(1)) != stream_result::OK)
return res;
x= *m_win.head();
@@ -143,12 +149,13 @@
}
template<class SW>
+inline
typename OStream<SW>::Result
OStream<SW>::writebyte(const byte x)
{
Result res;
- if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(1)) != stream_result::OK)
return res;
(*m_win.head())= x;
@@ -156,14 +163,14 @@
return m_win.move(1);
}
-//inline
template<class SW>
+inline
typename IStream<SW>::Result
IStream<SW>::read2int(uint &x)
{
Result res;
- if( (res= m_win.set_length(2)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(2)) != stream_result::OK)
return res;
x= uint2korr(m_win.head());
@@ -171,17 +178,14 @@
return m_win.move(2);
}
-//inline
template<class SW>
+inline
typename OStream<SW>::Result
OStream<SW>::write2int(const int x)
{
Result res;
- if( x >= (1<<16) )
- return Result(stream_result::ERROR);
-
- if( (res= m_win.set_length(2)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(2)) != stream_result::OK)
return res;
int2store(m_win.head(),x);
@@ -190,14 +194,14 @@
}
-//inline
template<class SW>
+inline
typename IStream<SW>::Result
IStream<SW>::read4int(ulong &x)
{
Result res;
- if( (res= m_win.set_length(4)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(4)) != stream_result::OK)
return res;
x= uint4korr(m_win.head());
@@ -205,17 +209,14 @@
return m_win.move(4);
}
-//inline
template<class SW>
+inline
typename OStream<SW>::Result
OStream<SW>::write4int(const ulong x)
{
Result res;
-// if( x >= (1<<32) )
-// return Result(stream_result::ERROR);
-
- if( (res= m_win.set_length(4)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(4)) != stream_result::OK)
return res;
int4store(m_win.head(),x);
@@ -224,15 +225,16 @@
}
-// write/read numnber using variable-length encoding
+// write/read number using variable-length encoding
template<class SW>
+inline
typename IStream<SW>::Result
IStream<SW>::readint(ulong &x)
{
Result res;
- if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(1)) != stream_result::OK)
return res;
x= *m_win.head();
@@ -253,66 +255,67 @@
default:
return Result(stream_result::OK);
- };
+ }
}
template<class SW>
+inline
typename OStream<SW>::Result
OStream<SW>::writeint(const ulong x)
{
Result res;
- if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(1)) != stream_result::OK)
return res;
- if( x < 251 )
+ if (x < 251)
return writebyte((byte)x);
- if( x < (1<<16) )
+ if (x < (1UL<<16))
{
res= writebyte(252);
- return res == Result(stream_result::OK) ? write2int(x) : res;
- };
+ return res == stream_result::OK ? write2int(x) : res;
+ }
res= writebyte(253);
- return res == Result(stream_result::OK) ? write4int(x) : res;
+ return res == stream_result::OK ? write4int(x) : res;
}
// Write/read string using "length coded string" format
template<class SW>
+inline
typename IStream<SW>::Result
IStream<SW>::readstr(String &s)
{
Result res;
uint len;
- if( (res= readint(len)) != Result(stream_result::OK) )
+ if ((res= readint(len)) != stream_result::OK)
return res;
- if( (res= m_win.set_length(len)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(len)) != stream_result::OK)
return res;
s.free();
- s.copy(reinterpret_cast<const char*>(m_win.head()),
- len,
- table_alias_charset);
+ s.copy((const char*)m_win.head(), len, &::my_charset_bin);
return m_win.move(len);
}
template<class SW>
+inline
typename OStream<SW>::Result
OStream<SW>::writestr(const String &s)
{
Result res;
uint len= s.length();
- if( (res= writeint(len)) != Result(stream_result::OK) )
+ if ((res= writeint(len)) != stream_result::OK)
return res;
- if( (res= m_win.set_length(len)) != Result(stream_result::OK) )
+ if ((res= m_win.set_length(len)) != stream_result::OK)
return res;
memcpy(m_win.head(), s.ptr(), len);
@@ -321,6 +324,7 @@
}
template<class SW>
+inline
typename OStream<SW>::Result
OStream<SW>::writenil()
{
@@ -336,9 +340,9 @@
Backup Stream Interface
The stream is organized as a sequence of chunks each of which
- can have different length. When stream is read chunk boundraries
+ can have different length. When stream is read chunk boundaries
are detected. If this happens, next_chunk() member must be called
- in order to access data in next chunk. When writting to a stream,
+ in order to access data in next chunk. When writing to a stream,
data is appended to the current chunk. End_chunk() member closes
the current chunk and starts a new one.
@@ -348,15 +352,35 @@
struct stream_result
{
- enum value {
+ enum {
OK= util::stream_result::OK,
NIL= util::stream_result::NIL,
ERROR= util::stream_result::ERROR,
- EOS, // end of stream
- EOC // end of chunk
+ EOC, // end of chunk
+ EOS // end of stream
+ };
+
+ class value
+ {
+ int m_val;
+
+ public:
+
+ value(): m_val(OK) {}
+ value(const int val): m_val(val) {}
+
+ operator int() { return m_val; }
+
+ operator result_t()
+ {
+ return m_val == stream_result::ERROR ? backup::ERROR :
+ m_val == stream_result::EOC || m_val == stream_result::EOS ?
+ backup::DONE : backup::OK;
+ }
};
};
+
/**
Implementation of stream window interface to be used by util::{I,O}Stream
templates.
@@ -454,12 +478,12 @@
int m_fd;
String m_path;
- int m_flags; ///< flags used when openning the file
+ int m_flags; ///< flags used when opening the file
};
/**
- Implement backup stream which writes data to a file.
+ Implements backup stream which writes data to a file.
This class inherits from util::OStream which defines methods for serialization of
basic datatypes (strings and integer). It also implements the concept of data chunks. Data
@@ -468,7 +492,7 @@
A client of this class can ask an instance for an output buffer with <code>get_window()</code>
method. After filling the buffer its contents can be written to the stream. This is to avoid
- double buffering and unneccessary copying of data. However, once an output buffer is allocated,
+ double buffering and unnecessary copying of data. However, once an output buffer is allocated,
all output to the stream is blocked until the buffer is written with <code>write_window()</code>
or dropped with <code>drop_window()</code>.
*/
@@ -482,10 +506,10 @@
OStream instance uses an output buffer of fixed size inherited from Window class. The size of
a chunk is limited by the size of this buffer as a whole chunk is stored inside the buffer
- before writting to the file.
+ before writing to the file.
- Writting to the file happens when the current chunk is closed with <code>end_chunk()</code>
- method. At the time of writting the output, buffer contents is as follows:
+ writing to the file happens when the current chunk is closed with <code>end_chunk()</code>
+ method. At the time of writing the output, buffer contents is as follows:
====================== <- m_buf
2 bytes for chunk size
@@ -560,12 +584,12 @@
/**
- Implement backup stream reading data from a file.
+ Implements backup stream reading data from a file.
This class inherits from util::IStream which defines methods for serialization of
basic datatypes (strings and integer). It also handles chunk boundaries as created by
the OStream class. When reading data at the end of a data chunk, <code>stream_result::EOC</code>
- is returned. To access data in next chunck, <code>next_chunk()</code> must be called.
+ is returned. To access data in next chunk, <code>next_chunk()</code> must be called.
*/
/*
@@ -669,26 +693,13 @@
friend class stream_instances;
};
+#ifdef DBUG_BACKUP
+
// Function for debugging backup stream implementation.
void dump_stream(IStream &);
+#endif
} // backup namespace
-
-#define TEST_STR_RES(H,R) \
- do { \
- if ( (R) != stream_result::OK ) \
- DBUG_PRINT(H,("stream op result= %d",(R))); \
- DBUG_ASSERT( (R) != stream_result::ERROR ); \
- } while(0)
-#define TEST_STR_OK(H,R) \
- do { \
- if ( (R) != stream_result::OK ) \
- DBUG_PRINT(H,("stream op result= %d",(R))); \
- DBUG_ASSERT( (R) == stream_result::OK || (R) == stream_result::NIL ); \
- } while(0)
-#define TEST_WR_RES(R) TEST_STR_OK("backup",(R))
-#define TEST_RD_IRES(R) TEST_STR_OK("restore",(R))
-#define TEST_RD_RES(R) TEST_STR_RES("restore",(R))
#endif /*BACKUP_STREAM_H_*/
--- 1.3/sql/backup/string_pool.cc 2007-06-23 23:38:59 +02:00
+++ 1.4/sql/backup/string_pool.cc 2007-06-23 23:38:59 +02:00
@@ -8,7 +8,7 @@
String *StringDom::create(const Element &s)
{
- String *ns= new (current_thd->mem_root) String();
+ String *ns= new (::current_thd->mem_root) String();
ns->copy(s);
return ns;
}
@@ -22,69 +22,216 @@
// Serialization of StringPool
-stream_result::value StringPool::save(OStream &str)
-{
- String buf;
- uint skip=0 , i=0;
+/*
+ Pool
+
+ 0: "foo"
+ 1: -
+ 2: -
+ 3: "bar"
+ 4: "baz"
+ ...
+
+ is saved as
+
+ | foo | NIL : 2 | bar | baz | ...
+
+ where the (NIL:2) entry describes a "hole" in the pool of width 2.
- // TODO: error checking
+ Empty pool is saved as | NIL : 0 |.
+*/
- for(i=0 ; i < count() ; i++ )
+result_t StringPool::save(OStream &str)
+{
+ DBUG_ENTER("StringPool::save()");
+
+ if (count() == 0) // empty pool
{
- if( entries[i].el != NULL )
- {
- if( skip > 0 )
- str.writeint(skip);
- skip= 0;
- str.writestr(*entries[i].el);
- continue;
- };
+ DBUG_PRINT("string_pool",("saving empty pool"));
+ if (stream_result::ERROR == str.writenil()
+ || stream_result::ERROR == str.writeint(0))
+ DBUG_RETURN(ERROR);
+ }
+ else
+ {
+ String buf;
+ uint skip=0, i=0;
- if( skip == 0 )
- str.writenil();
+ DBUG_PRINT("string_pool",("saving pool of size %d",count()));
- skip++;
+ for (i=0 ; TRUE ; ++i)
+ {
+ if (i >= count() || entries[i].el != NULL) // non-empty or last entry
+ {
+ /*
+ If skip > 0 this entry closes a hole of skip entries.
+ Write hole width to finish its description.
+ */
+ if (skip > 0)
+ {
+ DBUG_PRINT("string_pool",("noting hole of width %d",skip));
+ if (stream_result::ERROR == str.writeint(skip))
+ {
+ DBUG_PRINT("string_pool",("error when writing hole width"));
+ DBUG_RETURN(ERROR);
+ }
+ }
+
+ if (i >= count())
+ break;
+
+ skip= 0; // the hole has ended now
+
+ DBUG_PRINT("string_pool",("writing entry %s at position %d",
+ (entries[i].el)->c_ptr(),i));
+ // save the entry
+ if (stream_result::ERROR == str.writestr(*entries[i].el))
+ {
+ DBUG_PRINT("string_pool",("error when writing string"));
+ DBUG_RETURN(ERROR);
+ }
+
+ continue;
+ }
+
+ // we have an empty entry, if skip == 0 it starts a new hole which
+ // is marked by NIL value
+
+ if( skip == 0 )
+ if (stream_result::ERROR == str.writenil())
+ DBUG_RETURN(ERROR);
- };
+ skip++;
+ }
+
+ } // if (count()==0) else ...
- if( skip > 0 )
- str.writeint(skip);
+ DBUG_PRINT("string_pool",("saved"));
- return str.end_chunk();
+ DBUG_RETURN(str.end_chunk() == stream_result::ERROR ? ERROR : OK);
}
-stream_result::value StringPool::read(IStream &str)
+/**
+ @retval OK
+ @retval DONE end of stream/chunk hit
+ @retval ERROR
+ */
+result_t StringPool::read(IStream &str)
{
+ DBUG_ENTER("StringPool::read");
+
stream_result::value res;
String buf;
- uint skip, i=0;
+ uint skip= 0, i=0;
+
+ /*
+ Read first entry. If it is NIL then we either have an empty pool
+ or a pool which starts with a hole. This is decided by the number following
+ NIL value.
+ */
+
+ res= str.readstr(buf);
+
+ if (res != stream_result::OK && res != stream_result::NIL)
+ {
+ // In that case there is an error or we hit end of data
+ if (res != stream_result::ERROR)
+ DBUG_PRINT("string_pool",("End of data hit"));
+ DBUG_RETURN((result_t)res);
+ }
clear();
- do
+ if (res == stream_result::NIL)
+ {
+ res= str.readint(skip);
+
+ if (res != stream_result::OK)
+ DBUG_RETURN(ERROR);
+
+ if (skip == 0) // this is empty pool - nothing more to read
+ {
+ DBUG_PRINT("string_pool",("Found empty pool"));
+ goto finish;
+ }
+
+ DBUG_PRINT("sting_pool",("Starts with hole of width %d",skip));
+
+ /*
+ this is non-empty pool starting with a hole - read the following
+ string
+ */
+
+ res= str.readstr(buf);
+
+ if (res == stream_result::ERROR)
+ {
+ DBUG_PRINT("string",("Error reading string"));
+ DBUG_RETURN(ERROR);
+ }
+ }
+
+ /*
+ If we are here, we have a non-empty pool and are going to populate it
+ with strings read from the stream. The first entry is already read.
+ */
+
+ while (res == stream_result::OK)
{
- if( i >= size ) break;
+ /*
+ If skip > 0 then we have a hole of that size preceding the next
+ string entry.
+ */
+ if (skip > 0)
+ i+= skip;
+ skip= 0;
+
+ if (i >= size) // Not enough space to store the string
+ {
+ DBUG_PRINT("string_pool",("Not enough space to store string at pos %d"
+ " (size=%d)",i,size));
+ DBUG_RETURN(ERROR);
+ }
+
+ DBUG_PRINT("string_pool",("Adding string %s at position %d",buf.c_ptr(),i));
+ // store the string and increase position
+ set(i++,buf);
+
+ // read next entry from stream
res= str.readstr(buf);
- switch( res )
+
+ if (res == stream_result::NIL) // this is hole description
{
- case stream_result::OK:
- set(i++,buf);
- break;
-
- case stream_result::NIL:
- str.readint(skip);
- i += skip;
- break;
+ // read size of the hole
+ res= str.readint(skip);
+ DBUG_PRINT("string_pool",("Found hole of width %d",skip));
+
+ if (res != stream_result::OK)
+ {
+ DBUG_PRINT("string_pool",("Found hole - error reading its width"));
+ DBUG_RETURN(ERROR);
+ }
- default:
- break;
+ // read the following string
+ res= str.readstr(buf);
}
- } while( res == stream_result::OK );
+ }
+
+ // Now we should be at the end of chunk
+ if (res != stream_result::EOC && res != stream_result::EOS)
+ {
+ DBUG_PRINT("string_pool",("Error when reading strings (res=%d)",(int)res));
+ DBUG_RETURN(ERROR);
+ }
+
+ DBUG_PRINT("string_pool",("Finished reading strings"));
+
+ finish:
res= str.next_chunk();
- return res;
+ DBUG_RETURN(res == stream_result::ERROR ? ERROR: OK);
}
} // backup namespace
--- 1.2/sql/backup/string_pool.h 2007-06-23 23:38:59 +02:00
+++ 1.3/sql/backup/string_pool.h 2007-06-23 23:38:59 +02:00
@@ -4,14 +4,24 @@
#include "map.h"
#include "stream.h"
-namespace util {
+/**
+ @file
-/************************************************************
+ Implementation of a string pool which is a collection of
+ String objects indexed by 8 bit keys.
+ */
+
+/*
+ TODO
+
+ - change memory allocation scheme
+ - use mysql library functions
+ - simplify by not using Map template
+ */
- StringPool class
- String pool = map from key8 -> Strings
+namespace util {
- ************************************************************/
+/// A domain of String values which can by used by Map template.
struct StringDom
{
@@ -35,13 +45,14 @@
namespace backup {
-// Add serialization to util::StringPool
-
+/**
+ Adds serialization to util::StringPool class
+ */
class StringPool: public util::StringPool
{
public:
- stream_result::value save(OStream&);
- stream_result::value read(IStream&);
+ result_t save(OStream&);
+ result_t read(IStream&);
};
} // backup namespace
--- 1.33/mysql-test/r/backup.result 2007-06-23 23:38:59 +02:00
+++ 1.34/mysql-test/r/backup.result 2007-06-23 23:38:59 +02:00
@@ -64,7 +64,6 @@
total 3159 bytes
DROP DATABASE db1;
DROP DATABASE db2;
-SHOW BACKUP 'test.ba';
USE mysql;
RESTORE DATABASE FROM 'test.ba';
Restore Summary
--- 1.32/mysql-test/t/backup.test 2007-06-23 23:38:59 +02:00
+++ 1.33/mysql-test/t/backup.test 2007-06-23 23:38:59 +02:00
@@ -99,7 +99,7 @@
DROP DATABASE db1;
DROP DATABASE db2;
-SHOW BACKUP 'test.ba';
+# SHOW BACKUP 'test.ba';
disconnect backup;
| Thread |
|---|
| • bk commit into 5.1 tree (rafal:1.2537) | rsomla | 23 Jun |