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-21 00:48:24+02:00, rafal@quant.(none) +25 -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:
- Many error messages are ad-hoc and not entered into errmsg.txt
database.
- 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).
- There is no policy for return error codes.
- More extensive testing of error handling logic is needed (WL#3324).
mysql-test/r/backup.result@stripped, 2007-06-21 00:48:18+02:00, rafal@quant.(none) +0 -1
Result update.
mysql-test/r/backup_errors.result@stripped, 2007-06-21 00:48:21+02:00, rafal@quant.(none) +24 -0
Results for backup_errors test
mysql-test/r/backup_errors.result@stripped, 2007-06-21 00:48:21+02:00, rafal@quant.(none) +0 -0
mysql-test/r/backup_no_data.result@stripped, 2007-06-21 00:48:18+02:00, rafal@quant.(none) +0 -1
Result update.
mysql-test/t/backup.test@stripped, 2007-06-21 00:48:18+02:00, rafal@quant.(none) +1 -1
Disable SHOW BACKUP which is not yet implemented.
mysql-test/t/backup_errors.opt@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +1 -0
Error injection option for backup_errors test.
mysql-test/t/backup_errors.opt@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +0 -0
mysql-test/t/backup_errors.test@stripped, 2007-06-21 00:48:21+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-21 00:48:21+02:00, rafal@quant.(none) +0 -0
mysql-test/t/backup_no_data.test@stripped, 2007-06-21 00:48:18+02:00, rafal@quant.(none) +1 -3
Disable SHOW BACKUP which is not yet implemented.
sql/backup/archive.cc@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +337 -182
- 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-21 00:48:19+02:00, rafal@quant.(none) +61 -40
- change methods so that they return result_t values
sql/backup/backup_kernel.h@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +42 -18
- 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-21 00:48:19+02:00, rafal@quant.(none) +15 -10
Adapt Default_image implementation to new signature of Image_info.
sql/backup/data_backup.cc@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +453 -219
- add new CANCELED 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
sql/backup/debug.h@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +13 -0
Add error injection macros for testing error code.
sql/backup/error.h@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +34 -0
Defines helper function report_mysql_error(),
sql/backup/error.h@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +0 -0
sql/backup/logger.cc@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +39 -28
Add new methods for error reporting.
sql/backup/logger.h@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +74 -3
- add new methods for error reporting
- add methods for saving messages reported with the Logger class
sql/backup/meta_backup.cc@stripped, 2007-06-21 00:48:19+02:00, rafal@quant.(none) +105 -42
- 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-21 00:48:20+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-21 00:48:20+02:00, rafal@quant.(none) +365 -104
- 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-21 00:48:20+02:00, rafal@quant.(none) +1 -1
stream_result::value needs explicit cast to int type.
sql/backup/stream.h@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +43 -23
Type stream_result::value changed to define cast to type result_t.
sql/backup/string_pool.cc@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +185 -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-21 00:48:20+02:00, rafal@quant.(none) +4 -2
Change methods to return result_t value.
sql/share/errmsg.txt@stripped, 2007-06-21 00:48:20+02:00, rafal@quant.(none) +24 -0
Added few messages for backup error reporting.
sql/sql_parse.cc@stripped, 2007-06-21 00:48:19+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-21 00:48:30 +02:00
+++ 1.637/sql/sql_parse.cc 2007-06-21 00:48:30 +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-21 00:48:30 +02:00
+++ 1.142/sql/share/errmsg.txt 2007-06-21 00:48:30 +02:00
@@ -3187,6 +3187,8 @@
+ER_CANT_OPEN_TABLE
+ eng "Can't open table: '%-.64s'"
ER_CANT_REOPEN_TABLE
@@ -6053,3 +6055,25 @@
ER_BINLOG_PURGE_EMFILE
eng "Too many files opened, please execute the command again"
+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
+ eng "Can't read backup location '%-.64s'"
+ER_BACKUP_WRITE
+ eng "Can't write to backup location '%-.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"
--- 1.3/mysql-test/r/backup_no_data.result 2007-06-21 00:48:30 +02:00
+++ 1.4/mysql-test/r/backup_no_data.result 2007-06-21 00:48:30 +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-21 00:48:30 +02:00
+++ 1.3/mysql-test/t/backup_no_data.test 2007-06-21 00:48:30 +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/21 00:48:21
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/21 00:48:20
--loose-debug=d,backup_error_test
--- New file ---
+++ mysql-test/t/backup_errors.test 07/06/21 00:48:21
DROP DATABASE IF EXISTS adb;
DROP DATABASE IF EXISTS bdb;
# non-existent backup archive
-- exec rm -f $MYSQLTEST_VARDIR/master-data/test.ba
-- error 1590
RESTORE DATABASE FROM 'test.ba';
CREATE DATABASE adb;
CREATE DATABASE bdb;
# bad archive name
-- error 1591
BACKUP DATABASE adb TO '';
# non-existent database
DROP DATABASE IF EXISTS foo;
DROP DATABASE IF EXISTS bar;
-- error 1587
BACKUP DATABASE foo TO 'test.ba';
-- error 1587
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-21 00:48:30 +02:00
+++ 1.32/sql/backup/sql_backup.cc 2007-06-21 00:48:30 +02:00
@@ -27,25 +27,42 @@
#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.
+
+ This function sends response to the client (ok, result set or error).
+
+ @returns 0 on success.
*/
int
@@ -59,7 +76,10 @@
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;
@@ -71,30 +91,59 @@
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= ER_BACKUP_READ;
+ my_error(res,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 ((res= check_info(thd,info)))
+ goto finish_restore;
- info.total_size += info.header_size;
- // TODO: unfreeze DDL
- send_summary(thd,info);
- }
+ if (lex->sql_command == SQLCOM_SHOW_ARCHIVE)
+ {
+ // FIXME: implement this
+ my_error(ER_NOT_ALLOWED_COMMAND,MYF(0));
+ // res= mysql_show_archive(thd,info);
+ }
+ else
+ {
+ info.report_error(backup::log_level::INFO,ER_BACKUP_RESTORE_START);
+
+ // TODO: freeze all DDL operations
+
+ info.save_errors();
+ info.restore_all_dbs();
+
+ if ((res= check_info(thd,info)))
+ goto finish_restore;
+
+ info.clear_saved_errors();
+
+ if ((res= mysql_restore(thd,info,*stream)))
+ {
+ 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
+ }
+ } // if (!stream)
+
+ finish_restore:
+
+ if (stream)
+ stream->close();
- stream->close();
break;
}
@@ -103,45 +152,82 @@
backup::OStream *stream= backup::open_for_write(*loc);
if (!stream)
- DBUG_RETURN(-2);
-
- backup::Backup_info info(thd);
+ {
+ res= ER_BACKUP_WRITE;
+ my_error(res,MYF(0),loc->describe());
+ goto finish_backup;
+ }
+ else
+ {
+ backup::Backup_info info(thd);
- // TODO: freeze all DDL operations
+ if ((res= check_info(thd,info)))
+ goto finish_backup;
- if (lex->db_list.is_empty())
- info.add_all_dbs(); // backup all databases
- else
- info.add_dbs(lex->db_list); // backup databases specified by user
+ info.report_error(backup::log_level::INFO,ER_BACKUP_BACKUP_START);
- info.close();
+ // TODO: freeze all DDL operations
- res= mysql_backup(thd,info,*stream);
+ info.save_errors();
- // TODO: unfreeze DDL
+ 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 ((res= check_info(thd,info)))
+ goto finish_backup;
+
+ info.close();
+
+ if ((res= check_info(thd,info)))
+ goto finish_backup;
+
+ info.clear_saved_errors();
+
+ if ((res= mysql_backup(thd,info,*stream)))
+ {
+ report_errors(thd,ER_BACKUP_BACKUP,info);
+ }
+ else
+ {
+ info.report_error(backup::log_level::INFO,ER_BACKUP_BACKUP_DONE);
+ send_summary(thd,info);
+ }
- send_summary(thd,info);
+ // TODO: unfreeze DDL
+ } // 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);
}
@@ -157,10 +243,10 @@
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,41 +261,39 @@
{
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 ((res= info.save(s)))
+ goto finish;
DBUG_PRINT("backup",("Writing meta-data"));
- backup::write_meta_data(thd,info,s);
+ if ((res= backup::write_meta_data(thd,info,s)))
+ goto finish;
DBUG_PRINT("backup",("Writing table data"));
BACKUP_SYNC("backup_data");
- if(!backup::write_table_data(thd,info,s))
- goto error;
+ if ((res= backup::write_table_data(thd,info,s)))
+ 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"));
+ finish:
- end:
-
- DBUG_RETURN(0);
+ DBUG_RETURN(res);
}
@@ -252,6 +336,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 +354,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 +372,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 +402,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 +416,8 @@
if (img->accept(tbl,hton))
DBUG_RETURN(default_image_no);
- DBUG_PRINT("backup",("Table ignored."));
+ report_error("No backup driver found for table %s.%s",
+ tbl.db().name().ptr(), tbl.name().ptr());
DBUG_RETURN(-1);
}
@@ -351,6 +449,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("Can't acces server's table information");
+ m_state= ERROR;
+ }
}
Backup_info::~Backup_info()
@@ -380,6 +483,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 +492,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("Error writing backup archive header");
+ return -1;
+ }
+
+ return 0;
}
/**
@@ -416,10 +532,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 error= 0;
LEX_STRING *s;
+ String unknown_dbs; // comma separated list of databases which don't exist
while ((s= it++))
{
@@ -427,21 +545,40 @@
if (db_exists(db))
{
+ // In case of error, we just compose unknown_dbs list
+ if (error)
+ continue;
+
Db_item *it= add_db(db);
if (!it)
- return FALSE;
+ {
+ error= ER_BACKUP_BACKUP_PREPARE;
+ goto finish;
+ }
- bool res= add_db_items(*it);
-
- if (!res)
- return FALSE;
+ if ((error= add_db_items(*it)))
+ goto finish;
}
else
- DBUG_PRINT("backup",("warning: skipping unknown database %s",s->str));
+ {
+ if (error)
+ unknown_dbs.append(",");
+ else
+ error= ER_BAD_DB_ERROR;
+ unknown_dbs.append(db.name());
+ }
}
- return TRUE;
+ if (error==ER_BAD_DB_ERROR)
+ report_error(ER_BAD_DB_ERROR,unknown_dbs.c_ptr());
+
+ finish:
+
+ if (error)
+ m_state= ERROR;
+
+ return error;
}
static const LEX_STRING is_string = { C_STRING_WITH_LEN("information_schema") };
@@ -451,26 +588,33 @@
Add to archive all instance's databases (except the internal ones).
*/
-bool Backup_info::add_all_dbs()
+int Backup_info::add_all_dbs()
{
+ int error= 0;
+
+ 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("Can't access list of databases");
+ 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);
- ha->ha_rnd_init(TRUE);
+ if (ha->ha_rnd_init(TRUE))
+ {
+ error= -2;
+ report_error("Can't read list of databases");
+ goto finish;
+ }
while (!ha->rnd_next(db_table->record[0]))
{
@@ -487,21 +631,28 @@
Db_item *it= add_db(db);
if (!it)
- return FALSE;
-
- bool res= add_db_items(*it);
+ {
+ error= -3;
+ goto finish;
+ }
- if (!res)
- return FALSE;
+ if ((error= add_db_items(*it)))
+ 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 (error)
+ m_state= ERROR;
- return TRUE;
+ return error;
}
/**
@@ -511,8 +662,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 +688,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,9 +703,11 @@
/**
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());
+ 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
// TODO: add other items (views, stored routines,...)
@@ -550,28 +717,45 @@
// TODO: do it properly: use SELECT conditions
- ha->ha_rnd_init(TRUE);
+ TEST_ERROR_IF(db.name().ptr()[0]=='a');
+
+ if (ha->ha_rnd_init(TRUE) || TEST_ERROR)
+ {
+ report_error("Can't acces table list when collecting meta-data items"
+ " of database '%s'",db.name().ptr());
+ return -1;
+ }
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())
+ {
+ report_error("Can't locate storage engine of table %s.%s",
+ t.db().name().ptr(), t.name().ptr());
+ return -2;
+ }
+
+ 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);
+ if (!ti)
+ return -3;
- add_table_items(*ti);
+ int res= add_table_items(*ti);
+ if (res)
+ return res;
}
}
ha->ha_rnd_end();
- return TRUE;
+ return 0;
}
/// Gets table identity from a row of I_S.TABLES table
@@ -591,7 +775,6 @@
name_lex.length= engine_name.length();
m_hton= ::ha_resolve_by_name(::current_thd,&name_lex);
- DBUG_ASSERT(m_hton);
}
/**
@@ -602,16 +785,37 @@
Backup_info::add_table(Db_item &db,
const Table_ref &t)
{
- int no= find_image(t);
+ int no= find_image(t); // Consider: reporting errors inside find image...
+
+ TEST_ERROR_IF(t.name().ptr()[0]=='a');
+
+ if (no < 0 || !images[no] || TEST_ERROR)
+ {
+ report_error("Can't find backup driver for table '%s.%s'",
+ t.db().name().ptr(),t.name().ptr());
+ return NULL;
+ }
- DBUG_ASSERT(no >= 0 && images[no]);
+ TEST_ERROR_IF(t.name().ptr()[0]=='b');
int tno= images[no]->tables.add(t);
- DBUG_ASSERT(tno >= 0);
+ if (tno < 0 || TEST_ERROR)
+ {
+ report_error("Table '%s.%s' can't be added to %s's image",
+ t.db().name().ptr(),t.name().ptr(),images[no]->name());
+ return NULL;
+ }
+
table_count++;
Table_item *ti= new Table_item(*this,no,tno);
+ if (!ti)
+ {
+ report_error(ER_OUT_OF_RESOURCES);
+ return NULL;
+ }
+
// add to the end of items list
ti->next= NULL;
@@ -627,10 +831,10 @@
/**
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;
+ return 0;
}
} // backup namespcae
@@ -658,17 +862,18 @@
{
DBUG_ENTER("mysql_restore");
+ int error= 0;
size_t start_bytes= s.bytes;
DBUG_PRINT("restore",("Restoring meta-data"));
- bool res= backup::restore_meta_data(thd, info, s);
- DBUG_ASSERT(res);
+ if ((error= backup::restore_meta_data(thd, info, s)))
+ DBUG_RETURN(error);
DBUG_PRINT("restore",("Restoring table data"));
- res= backup::restore_table_data(thd,info,s);
- DBUG_ASSERT(res);
+ if ((error= backup::restore_table_data(thd,info,s)))
+ DBUG_RETURN(error);
DBUG_PRINT("restore",("Done."));
@@ -695,6 +900,10 @@
File_loc(const char *p)
{ path.append(p); } // files_charset_info
+
+ const char* describe()
+ { return path.c_ptr(); }
+
};
@@ -708,10 +917,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:
@@ -769,6 +979,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 +1038,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
@@ -932,7 +1193,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.6/sql/backup/archive.cc 2007-06-21 00:48:30 +02:00
+++ 1.7/sql/backup/archive.cc 2007-06-21 00:48:30 +02:00
@@ -10,11 +10,12 @@
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.
+ - Decide how to report errors in Archive_info methods and friends.
*/
@@ -83,7 +84,7 @@
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,120 +93,196 @@
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 descrition 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
+
+ // write catalogue
DBUG_PRINT("backup",(" writing archive catalog"));
- // db names
- res= db_names.save(s);
- if (res != stream_result::OK)
- DBUG_RETURN(res);
+ // 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));
+ // TODO: in future handle backward compatibility
+ 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
@@ -220,7 +297,7 @@
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 +308,61 @@
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);
+
+ stream_result::value res= s.writebyte(type());
+
+ if (res != stream_result::OK)
+ return ERROR;
+
+ res= s.write2int(ver);
+
+ if (res != stream_result::OK)
+ return ERROR;
+
return do_write_description(s);
}
/**
Create @c Image_info instance from a saved entry describing it.
+ @retval OK
+ @retval DONE end of chunk/stream hit
+ @retval ERROR
+
TODO: Handle unknown image types (always skip correct amount of bytes).
*/
-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 we are at end of data chunk or stream, we should inform here
+ if (res != stream_result::OK)
+ return (result_t)res;
+
+ res= s.read2int(ver);
- if (s.read2int(ver) != stream_result::OK)
- return NULL;
+ 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;
}
}
@@ -297,16 +394,19 @@
@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)
+*/
+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 +415,112 @@
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.
+
+ @retval OK if new item was created
+ @retval DONE if end of chunk/stream was reached
+ @retval ERROR if error has happened
TODO: handle unknow entry types (so that they can be skipped).
*/
-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,21 +549,25 @@
}
};
-Image_info::Tables::~Tables()
+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;
}
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());
}
@@ -450,9 +576,10 @@
{
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;
@@ -474,7 +601,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;
@@ -511,22 +638,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);
- return s.end_chunk();
+ res= s.writestr(n->name);
+ if (res != stream_result::OK)
+ DBUG_RETURN(ERROR);
+ };
+
+ res= s.end_chunk();
+ DBUG_RETURN(res == stream_result::ERROR ? ERROR : OK);
}
/**
@@ -534,37 +677,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.readint(k);
- res= s.readstr(name);
+ // If unexpected result, report an error or end of stream/chunk
+ if (res != stream_result::OK && res != stream_result::NIL)
+ return (result_t)res;
- if( res == stream_result::OK )
+ // 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,30 +748,6 @@
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
@@ -608,37 +755,45 @@
TODO: add more information here. Eg 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;
-
- if( s.readstr(name) == stream_result::OK )
- {
- LEX_STRING name_lex;
+ stream_result::value res= s.readstr(name);
- 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));
+ // TODO: deal with different versions
+ return ERROR;
}
- else
- return NULL;
+
+ img->ver= ver;
+
+ return OK;
}
} // backup namespace
--- 1.11/sql/backup/archive.h 2007-06-21 00:48:31 +02:00
+++ 1.12/sql/backup/archive.h 2007-06-21 00:48:31 +02:00
@@ -18,7 +18,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.
@@ -71,9 +72,9 @@
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();
@@ -117,18 +118,27 @@
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().
/// 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 successfuly 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;
@@ -153,9 +163,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 +175,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 +206,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;
};
/**
@@ -233,10 +245,10 @@
virtual meta::Item& meta() =0; ///< Return reference to the @c meta::Item
///< instance.
- 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 stream entry.
+ static result_t create_from_stream(const Archive_info&, IStream&, Item*&);
class Iterator;
@@ -246,7 +258,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 +272,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 +280,7 @@
or
<pre>
Item *head, *p;
- Item::iterator it(head);
+ Item::Iterator it(head);
while ((p=it++))
{
@@ -331,9 +343,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 +372,8 @@
img(no), pos(tno)
{}
+ public:
+
meta::Item& meta()
{ return *this; }
@@ -369,19 +384,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;
};
@@ -413,26 +420,40 @@
{
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();
+ my_snprintf(m_name,sizeof(m_name),"%s's driver",
+ ::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-21 00:48:31 +02:00
+++ 1.8/sql/backup/backup_kernel.h 2007-06-21 00:48:31 +02:00
@@ -62,6 +62,9 @@
virtual void free()
{ delete this; } // use destructor to free resources
+ virtual const char* describe()
+ { return "Invalid location"; }
+
/**
Interpret string passed to BACKUP/RESTORE statement as backup location
and construct corresponding Location object.
@@ -101,24 +104,37 @@
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();
+ //bool close_tables();
+
class Item_iterator; // for iterating over all meta-data items
private:
@@ -126,7 +142,8 @@
/// 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.
@@ -140,8 +157,8 @@
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;
@@ -177,19 +194,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("Can't read backup archive 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 +220,5 @@
};
} // backup namespace
+
#endif
--- 1.5/sql/backup/be_default.h 2007-06-21 00:48:31 +02:00
+++ 1.6/sql/backup/be_default.h 2007-06-21 00:48:31 +02:00
@@ -325,6 +325,9 @@
Default_image(Archive_info &info): Image_info(info)
{ ver= 1; }
+ bool is_valid()
+ { return TRUE; }
+
image_type type() const
{ return DEFAULT_IMAGE; }
@@ -334,19 +337,21 @@
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-21 00:48:31 +02:00
+++ 1.19/sql/backup/data_backup.cc 2007-06-21 00:48:31 +02:00
@@ -35,6 +35,7 @@
FINISHING, ///< Final data transfer (phase 7).
DONE, ///< Backup complete.
SHUT_DOWN, ///< After @c end() call.
+ CANCELED, ///< After cancelling backup process.
ERROR,
MAX };
@@ -54,6 +55,7 @@
name[FINISHING]= "FINISHING";
name[DONE]= "DONE";
name[SHUT_DOWN]= "SHUT DOWN";
+ name[CANCELED]= "CANCELED";
name[ERROR]= "ERROR";
}
};
@@ -87,7 +89,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:
@@ -126,23 +129,34 @@
Backup_pump(uint, Image_info&, Block_writer&);
~Backup_pump();
- size_t pump();
+ bool is_valid()
+ { return m_drv && state != backup_state::ERROR; }
- bool begin();
- bool end();
- bool prepare();
- bool lock();
- bool unlock();
+ int pump(size_t*);
+
+ int begin();
+ int end();
+ int prepare();
+ int lock();
+ int unlock();
+ int cancel();
/// Return the backup driver used by the pump.
Backup_driver &drv() const
- { 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.
@@ -192,13 +206,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 +223,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 canceled; ///< true if backup process was canceled
- 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), canceled(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 +272,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 or error code to return
size_t start_bytes= s.bytes;
@@ -300,13 +310,18 @@
Scheduler::Pump *p= new Scheduler::Pump(no,*i,s);
- DBUG_ASSERT(p);
+ if (!p || !p->is_valid())
+ {
+ info.report_error(ER_OUT_OF_RESOURCES);
+ goto error;
+ }
size_t init_size= p->init_size;
if (init_size == Driver::UNKNOWN_SIZE)
{
- sch.add(p);
+ if ((res= sch.add(p)))
+ goto error;
}
else
{
@@ -317,6 +332,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",
@@ -363,57 +381,70 @@
max_init_size= second_max;
- sch.add(p1);
+ if ((res= sch.add(p1)))
+ goto error;
}
// poll drivers
- sch.step();
+ if ((res= sch.step()))
+ goto error;
}
- // start "at begin" drivers
- DBUG_PRINT("backup/data",("- activating \"at begin\" drivers"));
-
- List_iterator<Scheduler::Pump> it1(inactive);
- Scheduler::Pump *p;
+ {
+ // start "at begin" drivers
+ DBUG_PRINT("backup/data",("- activating \"at begin\" drivers"));
- while ((p= it1++))
- sch.add(p);
+ List_iterator<Scheduler::Pump> it1(inactive);
+ Scheduler::Pump *p;
- while (sch.init_count > 0)
- sch.step();
+ while ((p= it1++))
+ if ((res= sch.add(p)))
+ goto error;
- // prepare for VP
- DBUG_PRINT("backup/data",("-- PREPARE PHASE --"));
- BACKUP_SYNC("data_prepare");
+ while (sch.init_count > 0)
+ if ((res= sch.step()))
+ goto error;
- sch.prepare();
+ // prepare for VP
+ DBUG_PRINT("backup/data",("-- PREPARE PHASE --"));
+ BACKUP_SYNC("data_prepare");
- while (sch.prepare_count > 0)
- sch.step();
+ if ((res= sch.prepare()))
+ goto error;
- // VP creation
- DBUG_PRINT("backup/data",("-- SYNC PHASE --"));
- BACKUP_SYNC("data_lock");
+ while (sch.prepare_count > 0)
+ if ((res= sch.step()))
+ goto error;
- sch.lock();
+ // VP creation
+ DBUG_PRINT("backup/data",("-- SYNC PHASE --"));
+ BACKUP_SYNC("data_lock");
+ if ((res= sch.lock()))
+ goto error;
- BACKUP_SYNC("data_unlock");
- sch.unlock();
+ BACKUP_SYNC("data_unlock");
+ if ((res= sch.unlock()))
+ goto error;
- // get final data from drivers
- DBUG_PRINT("backup/data",("-- FINISH PHASE --"));
- BACKUP_SYNC("data_finish");
+ // get final data from drivers
+ DBUG_PRINT("backup/data",("-- FINISH PHASE --"));
+ BACKUP_SYNC("data_finish");
- while (sch.finish_count > 0)
- sch.step();
+ while (sch.finish_count > 0)
+ if ((res= sch.step()))
+ goto error;
- DBUG_PRINT("backup/data",("-- DONE --"));
- sch.close();
+ DBUG_PRINT("backup/data",("-- DONE --"));
+ }
info.data_size= s.bytes - start_bytes;
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(0);
+
+ error:
+
+ DBUG_RETURN(res ? res : -1);
}
} // backup namespace
@@ -463,7 +494,7 @@
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.
@@ -482,10 +513,10 @@
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,10 +525,15 @@
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)
@@ -546,6 +582,7 @@
break;
case backup_state::DONE:
+ p->end();
case backup_state::ERROR:
remove_pump(p);
break;
@@ -557,15 +594,25 @@
m_count, init_count, prepare_count, finish_count));
}
+ if (res) // we hit an error - bail out
+ cancel_backup();
+
+ return res;
}
-/// Add backup pump to the scheduler.
-void Scheduler::add(Pump *p)
+/**
+ Add backup pump to the scheduler.
+
+ In case of error, the pump 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 +625,13 @@
m_count++;
m_total += avg;
- res= p->begin();
- DBUG_ASSERT(res);
+ int res= 0;
+
+ if ((res= 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 +659,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 res;
}
/// 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 +704,98 @@
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 (canceled)
+ return;
+
+ // shutdown any remaining drivers
+ while (m_count && m_pumps)
+ {
+ Pump_iterator p(*this);
+ p->cancel();
+ remove_pump(p);
+ }
+
+ canceled= TRUE;
+}
+
+
/// Start prepare phase for all drivers.
-void Scheduler::prepare()
+int Scheduler::prepare()
{
+ DBUG_ASSERT(!canceled);
+ // 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)
+ int res= 0;
+
+ for (Pump_iterator it(*this); it; ++it)
{
- it->prepare();
+ if ((res= it->prepare()))
+ {
+ cancel_backup();
+ return res;
+ }
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(!canceled);
+ // 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();
+ int res= 0;
+
+ for (Pump_iterator it(*this); it; ++it)
+ if ((res= it->lock()))
+ {
+ cancel_backup();
+ return res;
+ }
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(!canceled);
DBUG_PRINT("backup/data",("calling unlock() for all drivers"));
+
+ int res= 0;
+
for(Pump_iterator it(*this); it; ++it)
{
- it->unlock();
+ if ((res= it->unlock()))
+ {
+ cancel_backup();
+ return res;
+ }
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 +817,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 +831,117 @@
}
/// 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("Can't initialize backup driver %s",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("Can't close backup driver %s",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("Error when preparing backup driver %s for"
+ " synchronization (result=%d)", m_name, (int)res);
+ 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("Can't create VP of %s image",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("Can't unlock backup driver %s",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("Error when cancelling backup process of %s image",m_name);
+ return -1;
+ }
+ state= backup_state::CANCELED;
+ return 0;
}
/**
@@ -810,18 +960,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::CANCELED);
- if (state == backup_state::INACTIVE
- || state == backup_state::ERROR
- || state == backup_state::DONE )
+ // we have detected error before - report it once more
+ if (state == backup_state::ERROR)
+ return -1;
+
+ // 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 +998,9 @@
state= backup_state::DONE;
break;
+ case backup_state::ERROR:
+ return ERROR;
+
default: break;
}
}
@@ -860,15 +1022,16 @@
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("Can't get stream window for writing %s backup"
+ " image data", m_name);
state= backup_state::ERROR;
- return 0;
-
+ return -1;
}
DBUG_ASSERT(m_buf_head);
@@ -924,8 +1087,11 @@
case ERROR:
default:
-
+ DBUG_ASSERT(m_log);
+ m_log->report_error("Error when polling data from backup driver %s",
+ m_name);
state= backup_state::ERROR;
+ return -1;
case BUSY:
@@ -944,17 +1110,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("Error when writing %s backup image data", m_name);
state= backup_state::ERROR;
- break;
+ return -1;
default: // retry write
break;
@@ -968,7 +1137,7 @@
m_name,backup_state::name[before_state],
backup_state::name[state]));
- return howmuch;
+ return 0;
}
} // backup namespace
@@ -983,8 +1152,11 @@
/**
Read backup image data from a backup stream and forward it to restore drivers.
+
+ @returns 0 on success.
*/
-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");
@@ -994,12 +1166,19 @@
// 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];
+
+ if (info.img_count > MAX_IMAGES)
+ {
+ info.report_error("Too many images in backup archive (%d vs. %d)",
+ info.img_count,MAX_IMAGES);
+ DBUG_RETURN(-1);
+ }
- DBUG_ASSERT(info.img_count < 256);
+ // Initializing restore drivers
for (uint no=0; no < info.img_count; ++no)
{
@@ -1011,143 +1190,198 @@
if (!img)
continue;
- drv[no]= img->get_restore_driver();
- DBUG_ASSERT(drv[no]);
+ if (backup::ERROR == img->get_restore_driver(drv[no]))
+ {
+ info.report_error("Can't create restore driver for %s image",img->name());
+ goto error;
+ };
res= drv[no]->begin(0); // TODO: provide correct size
- DBUG_ASSERT(res == OK);
+ if (res == backup::ERROR)
+ {
+ info.report_error("Can't initialize restore driver for %s image",img->name());
+ 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;
+ {
+ Buffer buf;
+ uint img_no=0;
+ uint repeats=0, errors= 0;
- size_t start_bytes= s.bytes;
+ static const uint MAX_ERRORS= 3;
+ static const uint MAX_REPEATS= 7;
- // main data reading loop
+ size_t start_bytes= s.bytes;
- while ( state != DONE && state != ERROR )
- {
- switch (state) {
+ // main data reading loop
- case READING:
+ while ( state != DONE && state != ERROR )
+ {
+ switch (state) {
- switch (s >> buf) {
+ case READING:
- case stream_result::EOS:
- state= DONE;
- break;
+ switch ((int)(s >> buf)) {
- case stream_result::OK:
- state= SENDING;
- break;
+ case stream_result::EOS:
+ state= DONE;
+ break;
- default:
- state= ERROR;
- break;
+ case stream_result::OK:
+ state= SENDING;
+ break;
- }
+ case stream_result::ERROR:
+ info.report_error("Error reading backup stream");
+ default:
+ state= ERROR;
+ goto error;
- if (state != SENDING)
- break;
+ }
- DBUG_ASSERT(buf.data);
+ if (state != SENDING)
+ break;
- img_no= uint2korr(buf.data);
- buf.table_no= uint2korr(buf.data+2);
- buf.data += 4;
- buf.size -= 4;
+ DBUG_ASSERT(buf.data);
- DBUG_PRINT("restore",("Got %lu bytes of subimage %u data (stream %u)",
- (unsigned long)buf.size, img_no, buf.table_no));
+ img_no= uint2korr(buf.data);
+ buf.table_no= uint2korr(buf.data+2);
+ buf.data += 4;
+ buf.size -= 4;
- case SENDING:
+ DBUG_PRINT("restore",("Got %lu bytes of %s image data (for table #%u)",
+ (unsigned long)buf.size,
+ info.images[img_no-1]->name(),
+ buf.table_no));
- 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;
- }
+ case SENDING:
- /*
- 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( img_no < 1 || img_no > info.img_count || !drv[img_no-1] )
+ {
+ DBUG_PRINT("restore",("Skipping data for image #%u",img_no));
+ state= READING;
+ break;
+ }
- switch( drv[img_no-1]->send_data(buf) ) {
+ /*
+ 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( drv[img_no-1]->send_data(buf) ) {
+
+ case backup::OK:
+ switch ((int)s.next_chunk()) {
+
+ case stream_result::OK:
+ state= READING;
+ repeats= 0;
+ break;
+
+ case stream_result::EOS:
+ state= DONE;
+ break;
+
+ case stream_result::ERROR:
+ info.report_error("Error when switching to next data chunk in backup stream");
+ default:
+ state= ERROR;
+ goto error;
- case backup::OK:
- switch (s.next_chunk()) {
+ }
- case stream_result::OK:
- state= READING;
- repeats= 0;
break;
- case stream_result::EOS:
- state= DONE;
+ case backup::ERROR:
+ // TODO: inform about ignored errors
+ if( errors > MAX_ERRORS )
+ {
+ info.report_error("Error when sending data to restore driver: %s",
+ info.images[img_no-1]->name());
+ state= ERROR;
+ goto error;
+ }
+ errors++;
break;
+ case backup::PROCESSING:
+ case backup::BUSY:
default:
- state= ERROR;
+ if( repeats > MAX_REPEATS )
+ {
+ info.report_error("Can't send data block to restoredriver: %s",
+ info.images[img_no-1]->name());
+ state= ERROR;
+ goto error;
+ }
+ repeats++;
break;
}
+ default:
break;
- case backup::ERROR:
- if( errors > MAX_ERRORS )
- state= ERROR;
- errors++;
- break;
+ } // switch(state)
- 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;
+ } // main reading loop
- }
+ DBUG_PRINT("restore",("End of backup stream"));
+ if (state != DONE)
+ DBUG_PRINT("restore",("state is %d",state));
- default:
- break;
+ info.data_size= s.bytes - start_bytes;
+ }
- } // switch(state)
+ { // Shutting down drivers
- } // main reading loop
+ String bad_drivers;
- DBUG_PRINT("restore",("End of backup stream"));
- if (state != DONE)
- DBUG_PRINT("restore",("state is %d",state));
+ for (uint no=0; no < info.img_count; ++no)
+ {
+ if (!drv[no])
+ continue;
- info.data_size= s.bytes - start_bytes;
+ DBUG_PRINT("restore",("Shutting down restore driver %s",
+ info.images[no]->name()));
+ if (drv[no]->end() == backup::ERROR)
+ {
+ char buf[16];
+ my_snprintf(buf,sizeof(buf),"%d",no);
- for(uint no=0; no < info.img_count; ++no)
+ state= ERROR;
+ if (!bad_drivers.is_empty())
+ bad_drivers.append(",");
+ bad_drivers.append(buf);
+ }
+ drv[no]->free();
+ }
+
+ if (!bad_drivers.is_empty())
+ info.report_error("Error when shutting down restore drivers no %s",
+ bad_drivers.c_ptr());
+ }
+
+ DBUG_RETURN(state == ERROR ? -1 : 0);
+
+ 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(-2);
}
-
} // backup namespace
--- 1.3/sql/backup/debug.h 2007-06-21 00:48:31 +02:00
+++ 1.4/sql/backup/debug.h 2007-06-21 00:48:31 +02:00
@@ -11,6 +11,17 @@
#ifdef DBUG_BACKUP
+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 { \
+ DBUG_EXECUTE_IF("backup_error_test", backup::test_error_flag= (X);); \
+ } while(0)
+
// TODO: set correct info in thd->proc_info
#define BACKUP_SYNC(S) \
@@ -22,6 +33,8 @@
#else
#define BACKUP_SYNC(S)
+#define TEST_ERROR FALSE
+#define TEST_ERROR_IF(X)
#endif
--- New file ---
+++ sql/backup/error.h 07/06/21 00:48:20
#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-21 00:48:31 +02:00
+++ 1.2/sql/backup/logger.cc 2007-06-21 00:48:31 +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.
@@ -52,19 +24,58 @@
{
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, additioal 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 explicitely 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-21 00:48:31 +02:00
+++ 1.2/sql/backup/logger.h 2007-06-21 00:48:31 +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,9 +31,21 @@
{
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.
int report_error(int error_code, ...)
@@ -34,10 +53,62 @@
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;
+ }
+
+ 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;
}
+
+ 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;
+ }
+
+ void save_errors()
+ {
+ if (m_save_errors)
+ return;
+ clear_saved_errors();
+ m_save_errors= TRUE;
+ }
+
+ void stop_save_errors()
+ {
+ if (!m_save_errors)
+ return;
+ m_save_errors= FALSE;
+ }
+
+ void clear_saved_errors()
+ { errors.delete_elements(); }
+
+ MYSQL_ERROR *last_saved_error()
+ { return errors.head(); }
+
+ private:
+
+ List<MYSQL_ERROR> errors;
+ bool m_save_errors;
+
+ int v_report_error(log_level::value,int,va_list);
+ int v_write_message(log_level::value,int, const char*,va_list);
};
--- 1.17/sql/backup/meta_backup.cc 2007-06-21 00:48:31 +02:00
+++ 1.18/sql/backup/meta_backup.cc 2007-06-21 00:48:31 +02:00
@@ -44,9 +44,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 +55,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("Error when saving meta-data of %s",
+ 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);
}
@@ -73,17 +82,21 @@
@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.
*/
-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!"));
@@ -100,19 +113,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("Error when creating %s",
+ 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("Error when reading meta-data item info");
+ DBUG_RETURN(-2);
+ }
+
+ DBUG_ASSERT(res == DONE);
+
+ if (stream_result::ERROR == s.next_chunk())
+ {
+ info.report_error("Error when closing chunk after reading meta-data info");
+ DBUG_RETURN(-3);
+ };
info.meta_size= s.bytes - start_bytes;
- return res != stream_result::ERROR;
+ DBUG_RETURN(0);
}
} // backup namespace
@@ -126,7 +157,7 @@
namespace backup {
-bool silent_exec_query(THD*, String&);
+int silent_exec_query(THD*, String&);
/**
Write data needed to restore an item.
@@ -135,17 +166,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 +186,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 +205,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; }
+
+ if (ERROR == drop(thd))
+ return ERROR;
- return drop(thd) && silent_exec_query(thd,create_stmt);
+ return silent_exec_query(thd,create_stmt) ? ERROR : OK;
}
/**
@@ -184,11 +231,15 @@
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
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();
- DBUG_ASSERT(ob);
+ DBUG_ASSERT(ob); // an item should define object name for DROP statement
+ // or redefine drop() method.
String drop_stmt;
@@ -197,7 +248,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 ***********************************/
@@ -212,9 +263,10 @@
"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,11 +275,12 @@
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;
}
@@ -241,7 +294,7 @@
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 +305,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
@@ -277,8 +340,9 @@
// 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?)
+// TODO: reset and collect errors from thread execution.
-bool silent_exec_query(THD *thd, String &query)
+int silent_exec_query(THD *thd, String &query)
{
Vio *save_vio= thd->net.vio;
@@ -297,19 +361,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
--- 1.1/sql/backup/meta_backup.h 2007-06-21 00:48:31 +02:00
+++ 1.2/sql/backup/meta_backup.h 2007-06-21 00:48:31 +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 default implementation because an item can not use create
+ statements and then it doesn't have to worry about that 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-21 00:48:31 +02:00
+++ 1.8/sql/backup/stream.cc 2007-06-21 00:48:31 +02:00
@@ -247,7 +247,7 @@
if (res == stream_result::EOS)
DBUG_PRINT("stream",("=========="));
else
- DBUG_PRINT("stream",("== ERROR: %d",res));
+ DBUG_PRINT("stream",("== ERROR: %d",(int)res));
}
} // backup namespace
--- 1.9/sql/backup/stream.h 2007-06-21 00:48:31 +02:00
+++ 1.10/sql/backup/stream.h 2007-06-21 00:48:31 +02:00
@@ -134,7 +134,7 @@
{
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();
@@ -148,7 +148,7 @@
{
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;
@@ -163,7 +163,7 @@
{
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());
@@ -181,7 +181,7 @@
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);
@@ -197,7 +197,7 @@
{
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());
@@ -215,7 +215,7 @@
// 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);
@@ -232,7 +232,7 @@
{
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();
@@ -262,7 +262,7 @@
{
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 )
@@ -271,11 +271,11 @@
if( x < (1<<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;
}
@@ -288,10 +288,10 @@
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();
@@ -309,10 +309,10 @@
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);
@@ -348,15 +348,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.
@@ -681,14 +701,14 @@
DBUG_PRINT(H,("stream op result= %d",(R))); \
DBUG_ASSERT( (R) != stream_result::ERROR ); \
} while(0)
-#define TEST_STR_OK(H,R) \
- do { \
+#define TEST_STR_OK(H,R,X) \
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))
+ return X; \
+ }
+#define TEST_WR_RES(R,X) TEST_STR_OK("backup",(R),(X))
#define TEST_RD_IRES(R) TEST_STR_OK("restore",(R))
-#define TEST_RD_RES(R) TEST_STR_RES("restore",(R))
+#define TEST_RD_RES(R,X) TEST_STR_OK("restore",(R),(X))
#endif /*BACKUP_STREAM_H_*/
--- 1.3/sql/backup/string_pool.cc 2007-06-21 00:48:31 +02:00
+++ 1.4/sql/backup/string_pool.cc 2007-06-21 00:48:31 +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,215 @@
// Serialization of StringPool
-stream_result::value StringPool::save(OStream &str)
-{
- String buf;
- uint skip=0 , i=0;
+/*
+ Pool
+
+ 0: "foo"
+ 1: -
+ 3: -
+ 4: "bar"
+ 5: "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 with 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 is closed 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 thee 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-21 00:48:31 +02:00
+++ 1.3/sql/backup/string_pool.h 2007-06-21 00:48:31 +02:00
@@ -37,11 +37,13 @@
// Add serialization to util::StringPool
+// FIXME: what happens when saving/reading empty pool?
+
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-21 00:48:31 +02:00
+++ 1.34/mysql-test/r/backup.result 2007-06-21 00:48:31 +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-21 00:48:31 +02:00
+++ 1.33/mysql-test/t/backup.test 2007-06-21 00:48:31 +02:00
@@ -99,7 +99,7 @@
DROP DATABASE db1;
DROP DATABASE db2;
-SHOW BACKUP 'test.ba';
+# SHOW BACKUP 'test.ba';
disconnect backup;