#At file:///home/stewart/mysql/stew-encrypted-backup/
2651 Stewart Smith 2008-07-10 [merge]
merge
added:
sql/backup/be_logical.h
modified:
sql/backup/Makefile.am
sql/backup/backup_kernel.h
sql/backup/be_default.cc
sql/backup/be_default.h
sql/backup/be_snapshot.cc
sql/backup/be_snapshot.h
sql/backup/data_backup.cc
sql/backup/image_info.h
sql/backup/kernel.cc
storage/myisam/mi_examine_log.c
storage/myisam/myisam_backup_engine.cc
storage/myisam/myisamdef.h
=== modified file 'sql/backup/Makefile.am'
--- a/sql/backup/Makefile.am 2008-06-26 16:20:30 +0000
+++ b/sql/backup/Makefile.am 2008-07-07 12:51:56 +0000
@@ -61,6 +61,7 @@ noinst_HEADERS = \
backup_info.h \
restore_info.h \
be_native.h \
+ be_logical.h \
be_default.h \
be_snapshot.h \
be_nodata.h \
=== modified file 'sql/backup/backup_kernel.h'
--- a/sql/backup/backup_kernel.h 2008-06-26 12:10:46 +0000
+++ b/sql/backup/backup_kernel.h 2008-07-07 12:51:56 +0000
@@ -115,6 +115,14 @@ class Backup_restore_ctx: public backup:
void disable_fkey_constraints();
int restore_triggers_and_events();
+ /**
+ Indicates if tables have been locked with @c lock_tables_for_restore()
+ */
+ bool m_tables_locked;
+
+ int lock_tables_for_restore();
+ int unlock_tables();
+
friend class Backup_info;
friend class Restore_info;
friend int backup_init();
=== modified file 'sql/backup/be_default.cc'
--- a/sql/backup/be_default.cc 2008-07-01 20:32:27 +0000
+++ b/sql/backup/be_default.cc 2008-07-08 19:58:46 +0000
@@ -80,33 +80,6 @@ using backup::Buffer;
using namespace backup;
-Engine::Engine(THD *t_thd)
-{
- m_thd= t_thd;
-}
-
-/**
- * Create a default backup backup driver.
- *
- * Given a list of tables to be backed-up, create instance of backup
- * driver which will create backup image of these tables.
- *
- * @param tables (in) list of tables to be backed-up.
- * @param eng (out) pointer to backup driver instance.
- *
- * @retval ERROR if cannot create backup driver class.
- * @retval OK on success.
- */
-result_t Engine::get_backup(const uint32, const Table_list &tables,
- Backup_driver* &drv)
-{
- DBUG_ENTER("Engine::get_backup");
- Backup *ptr= new default_backup::Backup(tables, m_thd, TL_READ_NO_INSERT);
- if (!ptr)
- DBUG_RETURN(ERROR);
- drv= ptr;
- DBUG_RETURN(OK);
-}
Backup::Backup(const Table_list &tables, THD *t_thd, thr_lock_type lock_type)
:Backup_thread_driver(tables)
@@ -122,8 +95,28 @@ Backup::Backup(const Table_list &tables,
Create a TABLE_LIST * list for iterating through the tables.
Initialize the list for opening the tables in read mode.
*/
- locking_thd->tables_in_backup= build_table_list(tables, lock_type);
- all_tables= locking_thd->tables_in_backup;
+ all_tables= (TABLE_LIST*)my_malloc(tables.count()*sizeof(TABLE_LIST),
+ MYF(MY_WME | MY_ZEROFILL));
+ DBUG_ASSERT(all_tables); // TODO: report error instead
+
+ for (uint i=0; i < tables.count(); ++i)
+ {
+ Table_ref tbl= tables[i];
+ TABLE_LIST &tl= all_tables[i];
+
+ tl.alias= tl.table_name= const_cast<char*>(tbl.name().ptr());
+ tl.db= const_cast<char*>(tbl.db().name().ptr());
+ tl.lock_type= lock_type;
+
+ // link previous entry to this one
+ if (i > 0)
+ {
+ all_tables[i-1].next_global= all_tables[i-1].next_local=
+ all_tables[i-1].next_name_resolution_table= &tl;
+ }
+ }
+
+ locking_thd->tables_in_backup= all_tables;
init_phase_complete= FALSE;
locks_acquired= FALSE;
hdl= NULL;
@@ -368,25 +361,21 @@ result_t Backup::get_data(Buffer &buf)
*/
case GET_NEXT_TABLE:
{
- mode= READ_RCD;
- int res= next_table();
- /*
- If no more tables in list, tell backup algorithm we're done else
- fall through to reading mode.
- */
- if (res)
+ if (tbl_num >= m_tables.count())
{
buf.last= TRUE;
buf.size= 0;
buf.table_num= 0;
- DBUG_RETURN(OK);
- }
- else
- {
- start_tbl_read(cur_table);
- tbl_num++;
- buf.table_num= tbl_num;
+ DBUG_RETURN(DONE);
}
+
+ cur_table= all_tables[tbl_num++].table;
+ DBUG_ASSERT(cur_table); // tables should be opened at that time
+ read_set= cur_table->read_set;
+ start_tbl_read(cur_table);
+
+ buf.table_num= tbl_num;
+ mode= READ_RCD;
}
/*
@@ -579,31 +568,9 @@ result_t Backup::get_data(Buffer &buf)
DBUG_RETURN(OK);
}
-/**
- * Create a default backup restore driver.
- *
- * Given a list of tables to be restored, create instance of restore
- * driver which will restore these tables from a backup image.
- *
- * @param version (in) version of the backup image.
- * @param tables (in) list of tables to be restored.
- * @param eng (out) pointer to restore driver instance.
- *
- * @retval ERROR if cannot create restore driver class.
- * @retval OK on success.
- */
-result_t Engine::get_restore(version_t, const uint32,
- const Table_list &tables, Restore_driver* &drv)
-{
- DBUG_ENTER("Engine::get_restore");
- Restore *ptr= new default_backup::Restore(tables, m_thd);
- if (!ptr)
- DBUG_RETURN(ERROR);
- drv= ptr;
- DBUG_RETURN(OK);
-}
-Restore::Restore(const Table_list &tables, THD *t_thd) :Restore_driver(tables)
+Restore::Restore(const backup::Logical_snapshot &snap, THD *t_thd)
+ :Restore_driver(snap.get_table_list()), m_snap(snap)
{
DBUG_PRINT("default_backup",("Creating restore driver"));
m_thd= t_thd; /* save current thread */
@@ -611,12 +578,6 @@ Restore::Restore(const Table_list &table
tbl_num= 0; /* set table number to 0 */
mode= INITIALIZE; /* initialize write */
- /*
- Create a TABLE_LIST * list for iterating through the tables.
- Initialize the list for opening the tables in write mode.
- */
- tables_in_backup= build_table_list(tables, TL_WRITE);
- all_tables= tables_in_backup;
for (int i=0; i < MAX_FIELDS; i++)
blob_ptrs[i]= 0;
blob_ptr_index= 0;
@@ -651,23 +612,7 @@ result_t Restore::cleanup()
* @retval OK rows deleted.
* @retval ERROR problem with deleting rows.
*/
-result_t Restore::truncate_table(TABLE *tbl)
-{
- int last_write_res;
- DBUG_ENTER("Default_backup::truncate_table)");
- DBUG_ASSERT(tbl->file);
- hdl= tbl->file;
- last_write_res= cur_table->file->ha_delete_all_rows();
- /*
- Check to see if delete all rows was ok. Ignore if the handler
- doesn't support it.
- */
- if ((last_write_res != 0) && (last_write_res != HA_ERR_WRONG_COMMAND))
- DBUG_RETURN(ERROR);
- num_rows= 0;
- DBUG_RETURN(OK);
-}
/**
* @brief End restore process.
@@ -679,7 +624,6 @@ result_t Restore::truncate_table(TABLE *
result_t Restore::end()
{
DBUG_ENTER("Restore::end");
- close_thread_tables(m_thd);
DBUG_RETURN(OK);
}
@@ -692,42 +636,7 @@ result_t Restore::end()
* @retval 0 no errors.
* @retval -1 no more tables in list.
*/
-int Restore::next_table()
-{
- DBUG_ENTER("Restore::next_table()");
- if (cur_table == NULL)
- cur_table= tables_in_backup->table;
- else
- {
- /*
- Restore original timestamp autoset type.
- */
- if (cur_table && cur_table->timestamp_field)
- cur_table->timestamp_field_type= old_tm;
- tables_in_backup= tables_in_backup->next_global;
- if (tables_in_backup != NULL)
- {
- DBUG_ASSERT(tables_in_backup->table);
- cur_table= tables_in_backup->table;
- }
- else
- {
- cur_table= NULL;
- DBUG_RETURN(-1);
- }
- }
- /*
- Save original timestamp autoset type and switch to TIMESTAMP_NO_AUTO_SET
- so that any restored data with timestamp fields will not have their values
- reset on write.
- */
- if (cur_table && cur_table->timestamp_field)
- {
- old_tm= cur_table->timestamp_field->get_auto_set_type();
- cur_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- }
- DBUG_RETURN(0);
-}
+
/**
* @brief Unpack the data for a row in the table.
@@ -759,7 +668,6 @@ uint Restore::unpack(byte *packed_row)
error= unpack_row(NULL, cur_table, n_fields, packed_row, &cols, &cur_row_end, &length);
if (!use_bitbuf)
bitmap_free(&cols);
- num_rows++;
}
DBUG_RETURN(error);
}
@@ -809,57 +717,11 @@ result_t Restore::send_data(Buffer &buf)
switch (mode) {
/*
- Nothing to do in Initialize, continue to GET_NEXT_TABLE.
+ Nothing to do in Initialize, continue to WRITE_RCD.
*/
case INITIALIZE:
/*
- If class has been initialized and we need to read the next table,
- advance the current table pointer and initialize read process.
- */
- case GET_NEXT_TABLE:
- {
- int res;
-
- mode= WRITE_RCD;
- /*
- It is possible for the backup system to send data to this
- engine out of sequence from the table list. When a non-sequential
- access is detected, start the table list at the beginning and
- find the table in question. This is needed if any tables (more
- than MAX_RETRIES are empty!
- */
- if ((tbl_num + 1) == buf.table_num) //do normal sequential lookup
- res= next_table();
- else //do linear search
- {
- uint i= 0;
-
- cur_table= NULL;
- tables_in_backup= all_tables;
- do
- {
- i++;
- res= next_table();
- }
- while ((i != buf.table_num) && !res);
- tbl_num= i - 1;
- }
- if (res)
- {
- buf.last= TRUE;
- buf.size= 0;
- buf.table_num= 0;
- DBUG_RETURN(OK);
- }
- else
- {
- truncate_table(cur_table); /* delete all rows from table */
- tbl_num++;
- }
- }
-
- /*
Write a row to the table from the data in the buffer.
*/
case WRITE_RCD:
@@ -867,17 +729,32 @@ result_t Restore::send_data(Buffer &buf)
cur_blob= 0;
max_blob_size= 0;
+ // We don't process any data on stream #0
+ if (buf.table_num == 0)
+ DBUG_RETURN(OK);
+
+ // We only proccess data for the tables we are restoring.
+ if (buf.table_num > m_tables.count())
+ DBUG_RETURN(OK);
+
/*
- If the table number is different from the stream number, we're
- receiving data from the backup for a different table. Set the mode to
- get the next table in the list.
+ Get the opened table instance corresponding to buf.table_num. Note that
+ tables are opened (and locked) by the kernel.
*/
- if (tbl_num != buf.table_num)
- {
- mode= GET_NEXT_TABLE;
- DBUG_RETURN(PROCESSING);
- }
- else
+ cur_table= m_snap.get_opened_table(buf.table_num - 1);
+ DBUG_ASSERT(cur_table); // All tables we are processing should be opened.
+
+ /*
+ Change timestamp autoset type to TIMESTAMP_NO_AUTO_SET
+ so that any restored data with timestamp fields will not have their values
+ reset on write.
+ */
+ if (cur_table->timestamp_field)
+ cur_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ hdl= cur_table->file;
+ DBUG_ASSERT(hdl); // table should be opened
+
{
uint32 size= buf.size - META_SIZE;
block_type= *buf.data;
=== modified file 'sql/backup/be_default.h'
--- a/sql/backup/be_default.h 2008-06-12 19:54:19 +0000
+++ b/sql/backup/be_default.h 2008-07-07 12:51:56 +0000
@@ -3,6 +3,7 @@
#include <backup_engine.h>
#include <backup/image_info.h> // to define default backup image class
+#include <backup/be_logical.h>
#include <backup/buffer_iterator.h>
#include <backup/be_thread.h>
@@ -31,42 +32,6 @@ const byte BLOB_FIRST= (3U<<1); // First
const byte BLOB_DATA= (3U<<2); // Intermediate data block for blob buffer
const byte BLOB_LAST= (3U<<3); // Last data block in buffer for blob buffer
-/**
- * @class Engine
- *
- * @brief Encapsulates default online backup/restore functionality.
- *
- * This class is used to initiate the default backup algorithm, which is used
- * by the backup kernel to create a backup image of data stored in any
- * engine that does not have a native backup driver. It may also be used as
- * an option by the user.
- *
- * Using this class, the caller can create an instance of the default backup
- * backup and restore class. The backup class is used to backup data for a
- * list of tables. The restore class is used to restore data from a
- * previously created default backup image.
- */
-class Engine: public Backup_engine
-{
- public:
- Engine(THD *t_thd);
-
- /*
- Return version of backup images created by this engine.
- */
- const version_t version() const { return 0; };
- result_t get_backup(const uint32, const Table_list &tables,
- Backup_driver* &drv);
- result_t get_restore(const version_t ver, const uint32, const Table_list &tables,
- Restore_driver* &drv);
-
- /*
- Free any resources allocated by the default backup engine.
- */
- void free() { delete this; }
- private:
- THD *m_thd; ///< Pointer to the current thread.
-};
/**
* @class Backup
@@ -87,7 +52,7 @@ class Backup: public Backup_thread_drive
virtual ~Backup()
{
cleanup();
- backup::free_table_list(all_tables);
+ my_free(all_tables, MYF(0));
};
size_t size() { return UNKNOWN_SIZE; };
size_t init_size() { return 0; };
@@ -134,7 +99,7 @@ class Backup: public Backup_thread_drive
result_t start_tbl_read(TABLE *tbl);
int next_table();
BACKUP_MODE mode; ///< Indicates which mode the code is in
- int tbl_num; ///< The index of the current table.
+ ulong tbl_num; ///< The index of the current table.
uint *cur_blob; ///< The current blob field.
uint *last_blob_ptr; ///< Position of last blob field.
MY_BITMAP *read_set; ///< The file read set.
@@ -162,11 +127,10 @@ class Restore: public Restore_driver
{
public:
enum has_data_info { YES, WAIT, EOD };
- Restore(const Table_list &tables, THD *t_thd);
+ Restore(const backup::Logical_snapshot &info, THD *t_thd);
virtual ~Restore()
{
cleanup();
- backup::free_table_list(all_tables);
};
result_t begin(const size_t) { return backup::OK; };
result_t end();
@@ -177,7 +141,6 @@ class Restore: public Restore_driver
cleanup();
return backup::OK;
}
- TABLE_LIST *get_table_list() { return all_tables; }
void free() { delete this; };
private:
@@ -196,8 +159,10 @@ class Restore: public Restore_driver
WRITE_BLOB_BUFFER ///< Buffer blobs mode
} RESTORE_MODE;
- result_t truncate_table(TABLE *tbl);
- int next_table();
+ /**
+ Reference to the corresponding logical snapshot object.
+ */
+ const backup::Logical_snapshot &m_snap;
RESTORE_MODE mode; ///< Indicates which mode the code is in
uint tbl_num; ///< The index of the current table.
uint32 max_blob_size; ///< The total size (sum of parts) for the blob.
@@ -207,12 +172,9 @@ class Restore: public Restore_driver
uint *last_blob_ptr; ///< Position of last blob field.
Buffer_iterator rec_buffer; ///< Buffer iterator for windowing records
Buffer_iterator blob_buffer; ///< Buffer iterator for windowing BLOB fields
- TABLE_LIST *tables_in_backup; ///< List of tables used in backup.
byte *blob_ptrs[MAX_FIELDS]; ///< List of blob pointers used
int blob_ptr_index; ///< Position in blob pointer list
THD *m_thd; ///< Pointer to current thread struct.
- TABLE_LIST *all_tables; ///< Reference to list of tables used.
- ulonglong num_rows; ///< Number of rows in table
timestamp_auto_set_type old_tm;///< Save old timestamp auto set type.
my_bool m_cleanup; ///< Is call to cleanup() needed?
@@ -232,13 +194,13 @@ namespace backup {
class Logger;
-class Default_snapshot: public Snapshot_info
+class Default_snapshot: public Logical_snapshot
{
public:
- Default_snapshot(Logger&) :Snapshot_info(1) // current version number is 1
+ Default_snapshot(Logger&) :Logical_snapshot(1) // current version number is 1
{}
- Default_snapshot(Logger&, const version_t ver) :Snapshot_info(ver)
+ Default_snapshot(Logger&, const version_t ver) :Logical_snapshot(ver)
{}
enum_snap_type type() const
@@ -268,10 +230,10 @@ class Default_snapshot: public Snapshot_
TL_READ_NO_INSERT)) ? OK : ERROR; }
result_t get_restore_driver(Restore_driver* &ptr)
- { return (ptr= new default_backup::Restore(m_tables, ::current_thd)) ? OK : ERROR; }
+ { return (ptr= new default_backup::Restore(*this, ::current_thd)) ? OK : ERROR; }
bool is_valid(){ return TRUE; };
-
+
};
} // backup namespace
=== added file 'sql/backup/be_logical.h'
--- a/sql/backup/be_logical.h 1970-01-01 00:00:00 +0000
+++ b/sql/backup/be_logical.h 2008-07-07 12:51:56 +0000
@@ -0,0 +1,56 @@
+#ifndef _BE_LOGICAL_H_
+#define _BE_LOGICAL_H_
+
+/**
+ @file
+
+ This header contains definitions needed for "logical" backup/restore
+ drivers which access tables using handlerton interface. The built-in
+ drivers are of this type.
+*/
+
+#include <backup/image_info.h> // For definition of Snapshot_info
+
+class Backup_restore_ctx;
+
+namespace backup {
+
+/**
+ Extends Snapshot_info with methods for accessing tables opened in the server.
+*/
+class Logical_snapshot :public Snapshot_info
+{
+public:
+
+ Logical_snapshot(version_t ver) :Snapshot_info(ver) {}
+ TABLE* get_opened_table(ulong pos) const;
+ const Table_list& get_table_list() const;
+};
+
+/**
+ Get opened TABLE structure for the table at position @c pos.
+
+ This method should be called only after tables have been opened and locked
+ by the backup kernel.
+*/
+inline
+TABLE *Logical_snapshot::get_opened_table(ulong pos) const
+{
+ Image_info::Table *t= get_table(pos);
+
+ // If we have table at pos, then the m_table pointer should be set for it.
+ DBUG_ASSERT(!t || t->m_table);
+
+ return t ? t->m_table->table : NULL;
+}
+
+inline
+const Table_list& Logical_snapshot::get_table_list() const
+{
+ return m_tables;
+}
+
+} // backup namespace
+
+
+#endif /*BE_LOGICAL_H_*/
=== modified file 'sql/backup/be_snapshot.cc'
--- a/sql/backup/be_snapshot.cc 2008-06-25 13:39:04 +0000
+++ b/sql/backup/be_snapshot.cc 2008-07-07 12:51:56 +0000
@@ -56,27 +56,6 @@ using backup::Buffer;
using namespace backup;
/**
- * Create a snapshot backup backup driver.
- *
- * Given a list of tables to be backed-up, create instance of backup
- * driver which will create backup image of these tables.
- *
- * @param tables (in) list of tables to be backed-up.
- * @param eng (out) pointer to backup driver instance.
- *
- * @retval Error code or backup::OK on success.
- */
-result_t Engine::get_backup(const uint32, const Table_list &tables, Backup_driver* &drv)
-{
- DBUG_ENTER("Engine::get_backup");
- Backup *ptr= new snapshot_backup::Backup(tables, m_thd);
- if (!ptr)
- DBUG_RETURN(ERROR);
- drv= (backup::Backup_driver *)ptr;
- DBUG_RETURN(OK);
-}
-
-/**
Cleanup backup
This method provides a means to stop a current backup by allowing
@@ -167,29 +146,6 @@ result_t Backup::get_data(Buffer &buf)
return(res);
}
-/**
- * Create a snapshot backup restore driver.
- *
- * Given a list of tables to be restored, create instance of restore
- * driver which will restore these tables from a backup image.
- *
- * @param version (in) version of the backup image.
- * @param tables (in) list of tables to be restored.
- * @param eng (out) pointer to restore driver instance.
- *
- * @retval Error code or backup::OK on success.
- */
-result_t Engine::get_restore(version_t, const uint32, const Table_list &tables,
-Restore_driver* &drv)
-{
- DBUG_ENTER("Engine::get_restore");
- Restore *ptr= new snapshot_backup::Restore(tables, m_thd);
- if (!ptr)
- DBUG_RETURN(ERROR);
- drv= ptr;
- DBUG_RETURN(OK);
-}
-
} /* snapshot_backup namespace */
=== modified file 'sql/backup/be_snapshot.h'
--- a/sql/backup/be_snapshot.h 2008-06-12 17:43:47 +0000
+++ b/sql/backup/be_snapshot.h 2008-07-07 12:51:56 +0000
@@ -15,36 +15,6 @@ using backup::Table_ref;
using backup::Buffer;
/**
- * @class Engine
- *
- * @brief Encapsulates snapshot online backup/restore functionality.
- *
- * This class is used to initiate the snapshot backup algorithm, which is used
- * by the backup kernel to create a backup image of data stored in any
- * engine that does not have a native backup driver but supports consisten reads.
- * It may also be used as an option by the user.
- *
- * Using this class, the caller can create an instance of the snapshot backup
- * backup and restore class. The backup class is used to backup data for a
- * list of tables. The restore class is used to restore data from a
- * previously created snapshot backup image.
- */
-class Engine: public Backup_engine
-{
- public:
- Engine(THD *t_thd) { m_thd= t_thd; }
-
- /// Return version of backup images created by this engine.
- const version_t version() const { return 0; };
- result_t get_backup(const uint32, const Table_list &tables, Backup_driver*
-&drv);
- result_t get_restore(const version_t ver, const uint32, const Table_list &tables,
- Restore_driver* &drv);
- private:
- THD *m_thd; ///< Pointer to the current thread.
-};
-
-/**
* @class Backup
*
* @brief Contains the snapshot backup algorithm backup functionality.
@@ -100,8 +70,8 @@ class Backup: public default_backup::Bac
class Restore: public default_backup::Restore
{
public:
- Restore(const Table_list &tables, THD *t_thd)
- :default_backup::Restore(tables, t_thd){};
+ Restore(const backup::Logical_snapshot &snap, THD *t_thd)
+ :default_backup::Restore(snap, t_thd){};
virtual ~Restore(){};
void free() { delete this; };
};
@@ -117,13 +87,13 @@ class Restore: public default_backup::Re
namespace backup {
-class CS_snapshot: public Snapshot_info
+class CS_snapshot: public Logical_snapshot
{
public:
- CS_snapshot(Logger&) :Snapshot_info(1) // current version number is 1
+ CS_snapshot(Logger&) :Logical_snapshot(1) // current version number is 1
{}
- CS_snapshot(Logger&, version_t ver) :Snapshot_info(ver)
+ CS_snapshot(Logger&, version_t ver) :Logical_snapshot(ver)
{}
enum_snap_type type() const
@@ -143,7 +113,7 @@ class CS_snapshot: public Snapshot_info
{ return (ptr= new snapshot_backup::Backup(m_tables, ::current_thd)) ? OK : ERROR; }
result_t get_restore_driver(Restore_driver* &ptr)
- { return (ptr= new snapshot_backup::Restore(m_tables, ::current_thd)) ? OK : ERROR; }
+ { return (ptr= new snapshot_backup::Restore(*this, ::current_thd)) ? OK : ERROR; }
bool is_valid(){ return TRUE; };
=== modified file 'sql/backup/data_backup.cc'
--- a/sql/backup/data_backup.cc 2008-07-02 07:53:34 +0000
+++ b/sql/backup/data_backup.cc 2008-07-07 12:51:56 +0000
@@ -288,58 +288,6 @@ class Scheduler::Pump: public Backup_pum
{ return start_pos + bytes_in; }
};
-/*
- Collect tables from default and snapshot for open and lock tables.
- There should be at most only 1 of each driver.
-*/
-int get_default_snapshot_tables(backup::Backup_driver *backup_drv,
- backup::Restore_driver *restore_drv,
- TABLE_LIST **tables,
- TABLE_LIST **tables_last)
-{
- TABLE_LIST *table_list= *tables;
- TABLE_LIST *table_list_last= *tables_last;
-
- DBUG_ENTER("backup::get_default_snapshot_tables");
- /*
- If the table list is defined and the last pointer is
- defined then we are seeing a duplicate of either default
- or snapshot drivers. There should be at most 1 of each.
- */
- if (table_list && table_list_last->next_global)
- {
- DBUG_PRINT("restore",("Duplicate default or snapshot subimage"));
- DBUG_RETURN(ERROR);
- }
- /*
- If the table list is empty, use the first one and loop
- until the end then record the end of the first one.
- */
- if (!table_list)
- {
- if (backup_drv)
- table_list= ((default_backup::Backup *)backup_drv)->get_table_list();
- else if (restore_drv)
- table_list= ((default_backup::Restore *)restore_drv)->get_table_list();
- else
- DBUG_RETURN(ERROR);
- *tables= table_list;
- table_list_last= table_list;
- while (table_list_last->next_global != NULL)
- table_list_last= table_list_last->next_global;
- *tables_last= table_list_last;
- }
- else
- if (backup_drv)
- (*tables_last)->next_global=
- ((default_backup::Backup *)backup_drv)->get_table_list();
- else if (restore_drv)
- (*tables_last)->next_global=
- ((default_backup::Restore *)restore_drv)->get_table_list();
- else
- DBUG_RETURN(ERROR);
- DBUG_RETURN(0);
-}
/**
Commit Blocker
@@ -1370,11 +1318,6 @@ int restore_table_data(THD *thd, Restore
Restore_driver* drv[256];
- TABLE_LIST *table_list= 0;
- TABLE_LIST *table_list_last= 0;
- List<obs::Obj> tables_to_lock;
- obs::Name_locker *table_name_locker= new obs::Name_locker(thd);
-
if (info.snap_count() > 256)
{
info.m_ctx.fatal_error(ER_BACKUP_TOO_MANY_IMAGES, info.snap_count(), 256);
@@ -1399,56 +1342,8 @@ int restore_table_data(THD *thd, Restore
{
info.m_ctx.fatal_error(ER_BACKUP_CREATE_RESTORE_DRIVER, snap->name());
goto error;
- };
-
- /*
- Collect tables from default and snapshot for open and lock tables.
- There should be at most only 1 of each driver.
- */
- if ((snap->type() == Snapshot_info::DEFAULT_SNAPSHOT) ||
- (snap->type() == Snapshot_info::CS_SNAPSHOT))
- get_default_snapshot_tables(NULL, (default_backup::Restore *)drv[n],
- &table_list, &table_list_last);
- /*
- Collect tables from all drivers for name locking.
- */
- Image_info::Tables *snap_table_list= snap->get_table_list();
- for (uint i= 0; i < snap_table_list->count(); i++)
- {
- Image_info::Table *tbl= snap_table_list->get_table(i);
- tables_to_lock.push_front(tbl->m_obj_ptr);
- }
- }
-
- /*
- Open tables for default and snapshot drivers.
- */
- if (table_list)
- {
- table_list->lock_type= TL_WRITE;
- query_cache.invalidate_locked_for_write(table_list);
-
- // The lex needs to be cleaned up between consecutive calls to
- // open_and_lock_tables. Otherwise, open_and_lock_tables will try to open
- // previously opened views and crash.
- ::current_thd->lex->cleanup_after_one_table_open();
- if (open_and_lock_tables(::current_thd, table_list))
- {
- info.m_ctx.fatal_error(ER_BACKUP_OPEN_TABLES, "restore");
- DBUG_RETURN(backup::ERROR);
- }
- if (table_list_last)
- table_list_last->next_global= NULL; // break lists
- }
-
- /*
- Apply name locks to all tables used.
- */
- if (table_name_locker->get_name_locks(&tables_to_lock, TL_WRITE))
- {
- info.m_ctx.fatal_error(ER_BACKUP_OBTAIN_NAME_LOCK_FAILED);
- goto error;
- }
+ };
+ }
// Initialize the drivers.
for (uint n=0; n < info.snap_count(); ++n)
@@ -1614,37 +1509,12 @@ int restore_table_data(THD *thd, Restore
info.m_ctx.report_error(ER_BACKUP_STOP_RESTORE_DRIVERS, bad_drivers.c_ptr());
}
- /*
- Release name locks on driver tables.
- */
- if (table_name_locker->release_name_locks())
- info.m_ctx.fatal_error(ER_BACKUP_RELEASE_NAME_LOCK_FAILED);
- delete table_name_locker;
-
- /*
- Close all tables if default or snapshot driver used.
- */
- if (table_list)
- close_thread_tables(::current_thd);
-
- { // If auto commit is turned off, be sure to commit the transaction
- THD *thd=::current_thd;
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
- {
- if (ha_autocommit_or_rollback(thd, 0)) state=ERROR;
- if (end_active_trans(thd)) state=ERROR;
- }
- }
-
DBUG_RETURN(state == ERROR ? backup::ERROR : 0);
error:
DBUG_PRINT("restore",("Cancelling restore process"));
- if (table_name_locker)
- delete table_name_locker;
-
for (uint n=0; n < info.snap_count(); ++n)
{
if (!drv[n])
=== modified file 'sql/backup/image_info.h'
--- a/sql/backup/image_info.h 2008-07-01 20:32:27 +0000
+++ b/sql/backup/image_info.h 2008-07-07 12:51:56 +0000
@@ -5,6 +5,8 @@
#include <backup_stream.h> // for st_bstream_* types
#include <backup/backup_aux.h> // for Map template
+class Backup_restore_ctx;
+
namespace backup {
/********************************************************************
@@ -13,8 +15,8 @@ namespace backup {
********************************************************************/
-
class Snapshot_info;
+class Logical_snapshot;
/**
Describes contents of a backup image.
@@ -140,6 +142,7 @@ public: // public interface
// friends
friend class Snapshot_info;
+ friend class backup::Logical_snapshot; // needs access to Tables class
};
Image_info::Obj* find_obj(const Image_info &info,
@@ -257,7 +260,7 @@ class Snapshot_info
virtual ~Snapshot_info();
- Image_info::Tables *get_table_list() { return &m_tables; }
+ Image_info::Table* get_table(ulong pos) const;
protected:
@@ -268,8 +271,7 @@ class Snapshot_info
// Methods for adding and accessing tables stored in the table list.
int add_table(Image_info::Table &t, ulong pos);
- Image_info::Table* get_table(ulong pos);
-
+
// IMPLEMENTATION
Image_info::Tables m_tables; ///< List of tables stored in this image.
@@ -487,6 +489,7 @@ class Image_info::Table
{
const Db &m_db; ///< The database to which this table belongs.
Table *next_table; ///< Used to crate a linked list of tables in a database.
+ TABLE_LIST *m_table; ///< If not NULL, points at opened table.
public:
@@ -498,11 +501,14 @@ class Image_info::Table
friend class Db;
friend class Dbobj_iterator;
+ friend class Logical_snapshot; // reads m_table
+ friend class ::Backup_restore_ctx; // sets m_table
};
inline
Image_info::Table::Table(const Db &db, const ::String &name)
- :Table_ref(db.name(), Image_info::Obj::m_name), m_db(db), next_table(NULL)
+ :Table_ref(db.name(), Image_info::Obj::m_name), m_db(db), next_table(NULL),
+ m_table(NULL)
{
bzero(&base, sizeof(base));
base.base.type= BSTREAM_IT_TABLE;
@@ -1073,7 +1079,7 @@ int Snapshot_info::add_table(Image_info:
/// Get table at a given position
inline
-Image_info::Table* Snapshot_info::get_table(ulong pos)
+Image_info::Table* Snapshot_info::get_table(ulong pos) const
{
return m_tables.get_table(pos);
}
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-07-01 13:34:36 +0000
+++ b/sql/backup/kernel.cc 2008-07-08 19:58:46 +0000
@@ -194,6 +194,8 @@ execute_backup_command(THD *thd, LEX *le
res= context.do_restore();
+ DEBUG_SYNC(thd, "restore_before_end");
+
if (res)
DBUG_RETURN(send_error(context, ER_BACKUP_RESTORE));
@@ -328,7 +330,8 @@ backup::Mem_allocator *Backup_restore_ct
Backup_restore_ctx::Backup_restore_ctx(THD *thd)
:Logger(thd), m_state(CREATED), m_thd_options(thd->options),
- m_error(0), m_path(NULL), m_remove_loc(FALSE), m_stream(NULL), m_catalog(NULL)
+ m_error(0), m_path(NULL), m_remove_loc(FALSE), m_stream(NULL),
+ m_catalog(NULL), m_tables_locked(FALSE)
{
/*
Check for progress tables.
@@ -631,6 +634,91 @@ Backup_restore_ctx::prepare_for_restore(
return info;
}
+/*
+ Lock tables being restored.
+
+ Backup kernel ensures that all tables being restored are exclusively locked.
+
+ We use open_and_lock_tables() for locking. This is a temporary solution until
+ a better mechanism is devised - open_and_lock_tables() is not good if there
+ are many tables to be processed.
+
+ The built-in restore drivers need to open tables to write rows to them. Since
+ we have opened tables here, we store pointers to opened TABLE_LIST structures
+ in the restore catalogue so that the built-in drivers can access them later.
+
+ @todo Replace open_and_lock_tables() by a lighter solution.
+ @todo Hide table locking behind the server API.
+*/
+int Backup_restore_ctx::lock_tables_for_restore()
+{
+ TABLE_LIST *tables= NULL;
+
+ /*
+ Iterate over all tables in all snapshots and create a linked TABLE_LIST
+ for call to open_and_lock_tables(). Store pointers to TABLE_LIST structures
+ in the restore catalogue for later access to opened tables.
+ */
+
+ for (uint s= 0; s < m_catalog->snap_count(); ++s)
+ {
+ backup::Snapshot_info *snap= m_catalog->m_snap[s];
+
+ for (ulong t=0; t < snap->table_count(); ++t)
+ {
+ TABLE_LIST *ptr= (TABLE_LIST*)alloc_root(m_thd->mem_root,
+ sizeof(TABLE_LIST));
+ DBUG_ASSERT(ptr); // FIXME: report error instead
+ bzero(ptr, sizeof(TABLE_LIST));
+
+ backup::Image_info::Table *tbl= snap->get_table(t);
+
+ ptr->alias= ptr->table_name= const_cast<char*>(tbl->name().ptr());
+ ptr->db= const_cast<char*>(tbl->db().name().ptr());
+ ptr->lock_type= TL_WRITE;
+
+ tbl->m_table= ptr;
+
+ ptr->next_global= ptr->next_local= ptr->next_name_resolution_table= tables;
+ tables= ptr;
+ }
+ }
+
+ /*
+ Open and lock the tables.
+
+ Note: simple_open_n_lock_tables() must be used here since we don't want
+ to do derived tables processing. Processing derived tables even leads
+ to crashes as those reported in BUG#34758.
+ */
+ if (simple_open_n_lock_tables(m_thd,tables))
+ {
+ fatal_error(ER_BACKUP_OPEN_TABLES,"RESTORE");
+ return m_error;
+ }
+
+ m_tables_locked= TRUE;
+ return 0;
+}
+
+/**
+ Unlock tables which were locked by @c lock_tables_for_restore.
+ */
+int Backup_restore_ctx::unlock_tables()
+{
+ // Do nothing if tables are not locked.
+ if (!m_tables_locked)
+ return 0;
+
+ DBUG_PRINT("restore",("unlocking tables"));
+
+ close_thread_tables(m_thd);
+ m_tables_locked= FALSE;
+
+ return 0;
+}
+
+
/**
Destroy a backup/restore context.
@@ -652,6 +740,19 @@ int Backup_restore_ctx::close()
time_t when= my_time(0);
+ // If auto commit is turned off, be sure to commit the transaction
+ // TODO: move it to the big switch, case: MYSQLCOM_BACKUP?
+
+ if (m_thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ {
+ ha_autocommit_or_rollback(m_thd, 0);
+ end_active_trans(m_thd);
+ }
+
+ // unlock tables if they are still locked
+
+ unlock_tables();
+
// unfreeze meta-data
obs::ddl_blocker_disable();
@@ -889,12 +990,28 @@ int Backup_restore_ctx::do_restore()
It should be fixed inside object services implementation and then the
following line should be removed.
*/
+ close_thread_tables(m_thd);
m_thd->main_da.reset_diagnostics_area();
+ if (lock_tables_for_restore()) // reports errors
+ DBUG_RETURN(m_error);
+
// Here restore drivers are created to restore table data
if (restore_table_data(m_thd, info, s)) // reports errors
DBUG_RETURN(ER_BACKUP_RESTORE);
+ unlock_tables();
+
+ /*
+ Re-create all triggers and events (it was not done in @c bcat_create_item()).
+
+ Note: it is important to do that after tables are unlocked, otherwise
+ creation of these objects will fail.
+ */
+
+ if (restore_triggers_and_events())
+ DBUG_RETURN(ER_BACKUP_RESTORE);
+
DBUG_PRINT("restore",("Done."));
if (read_summary(info, s))
@@ -904,18 +1021,12 @@ int Backup_restore_ctx::do_restore()
}
/*
- Re-create all triggers and events (it was not done in @c bcat_create_item()).
- */
-
- if (restore_triggers_and_events())
- DBUG_RETURN(ER_BACKUP_RESTORE);
-
- /*
FIXME: this call is here because object services doesn't clean the
statement execution context properly, which leads to assertion failure.
It should be fixed inside object services implementation and then the
following line should be removed.
*/
+ close_thread_tables(m_thd);
m_thd->main_da.reset_diagnostics_area();
report_stats_post(info);
=== modified file 'storage/myisam/mi_examine_log.c'
--- a/storage/myisam/mi_examine_log.c 2008-07-01 20:32:27 +0000
+++ b/storage/myisam/mi_examine_log.c 2008-07-08 20:18:10 +0000
@@ -841,21 +841,14 @@ static my_bool cmp_filename(struct file_
/**
- Closes a table but, if physical log, does not update its state on disk.
+ Closes a table but, if physical log, updates the share from disk first.
mi_close() calls mi_state_info_write() if the table is corrupted.
This can happen for example is the table is from an online backup which
made a copy of its data file and only its index' header.
But in that case, if we have executed some MI_LOG_WRITE_BYTES_MYI commands,
- the state in memory is older than the state on disk, so we do not want to
- call mi_state_info_write(), it would cancel what we have just done.
- The solution is to mark the table as "read only" before mi_close().We have
- no problem with updating the MYISAM_SHARE structure as we are not
- multi-threaded (i.e. nobody uses the share while we are changing it to
- read-only). It is also not a problem if this "read only" influences
- next users of this same share, as a backup log contains all index header
- writes logged, and so all next users can skip calling
- mi_state_info_write() too.
+ the state in memory is older than the state on disk, so we update the
+ share from disk.
@return Operation status
@retval 0 ok
@@ -865,6 +858,15 @@ static my_bool cmp_filename(struct file_
static int mi_close_care_state(MI_INFO *info)
{
if (!update_index_on_close)
- info->s->mode= O_RDONLY;
+ {
+ MYISAM_SHARE *share;
+ my_bool old_myisam_single_user;
+
+ share= info->s;
+ old_myisam_single_user= myisam_single_user;
+ myisam_single_user= FALSE;
+ (void) mi_state_info_read_dsk(share->kfile, &share->state, 1);
+ myisam_single_user= old_myisam_single_user;
+ }
return mi_close(info);
}
=== modified file 'storage/myisam/myisam_backup_engine.cc'
--- a/storage/myisam/myisam_backup_engine.cc 2008-07-01 20:32:27 +0000
+++ b/storage/myisam/myisam_backup_engine.cc 2008-07-08 20:18:10 +0000
@@ -1594,6 +1594,7 @@ result_t Log_restore::post_restore()
mi_exl.max_files= open_files_limit;
if (mi_examine_log(&mi_exl))
SET_STATE_TO_ERROR_AND_DBUG_RETURN;
+
DBUG_RETURN(backup::OK);
}
@@ -1696,6 +1697,107 @@ result_t Table_restore::close()
if ((dfile_restore.close_file() != backup::OK) |
(kfile_restore.close_file() != backup::OK))
SET_STATE_TO_ERROR_AND_DBUG_RETURN;
+
+ /*
+ CAUTION! Ugliest hack ever!
+ This hack tries to recover from bypassing the MyISAM interface
+ by the MyISAM restore driver.
+ The situation is so:
+ The backup kernel opens and locks the tables in backup.
+ But the MyISAM restore driver does not use the open MI_INFO
+ instance. Instead it opens another instance, duplicates its
+ file descriptors, and closes the instance. Then it uses the
+ duplicate file descriptors to write directly ("physically")
+ to the data and index files.
+ Among the writes are chunks of data from the index file, which
+ overwrite the index header with the state info.
+ In this function, called after all data have been written, the
+ duplicate file descriptors are closed (above). Now the index
+ and data files have the contents they ought to have.
+ Everything would be fine if no instance of the table would be
+ open at the time. Then a new open would read all table info from
+ disk and everybody would be happy.
+ However, the backup kernel still has the table open. Parts of
+ the index file are cached in the open MYISAM_SHARE object.
+ If the backup kernel would close the tables, this old information
+ would be written to the index file, which crashes the table.
+ This hack tries to solve the problem by loading the share with
+ information from the index file. At first, we open a new MI_INFO
+ instance from the table. This open does not read the state info
+ from the file because another instance is already open from the
+ same table. But the open gives us access to the share.
+ We do then explicitly call mi_state_info_read_dsk(), which is
+ the function that loads the share from the index file at an
+ initial open. Well, not exactly. At open a similar function is
+ used, after the index header has been read by a direct read.
+ But the mentioned function includes both, read and share load.
+ Another small problem is that the function doesn't do anything
+ if external locking is disabled. It assumes that no external
+ (or bypassing) writes happen to the files. Since we did exactly
+ this, we must pretend that we are doing external locking. The
+ function uses the variable 'myisam_single_user' for the
+ decision. So we temporarily change it.
+ Now we can close the new table instance. This won't write the
+ state again, because is is not the last open instance.
+ But since the share does now cache the new values from the
+ index file, the backup kernel's close writes the correct
+ information back to the file.
+ */
+ {
+ MI_INFO *mi_info;
+ MYISAM_SHARE *share;
+ my_bool old_myisam_single_user;
+
+ mi_info= mi_open(file_name.ptr(), O_RDWR, HA_OPEN_FOR_REPAIR);
+ if (mi_info == NULL)
+ goto err;
+ share= mi_info->s;
+ DBUG_PRINT("myisam_backup", ("share data_file: %lu",
+ (ulong) share->state.state.data_file_length));
+ old_myisam_single_user= myisam_single_user;
+ myisam_single_user= FALSE;
+ if (mi_state_info_read_dsk(share->kfile,&share->state,1))
+ {
+ myisam_single_user= old_myisam_single_user;
+ goto err;
+ }
+ myisam_single_user= old_myisam_single_user;
+ DBUG_PRINT("myisam_backup", ("share data_file: %lu",
+ (ulong) share->state.state.data_file_length));
+ /*
+ Now follows the most dirty part of the hack.
+ We have correct information in the share, but the instance that
+ holds the lock on the table has a local copy of the state.
+ We must find this instance and fix the local info.
+ Fortunately there is a state pointer, which can be set to the
+ share. This invalidates the instance's local copy.
+ We need to acquire share->intern_lock when traversing the list
+ of open MyISAM instances.
+ */
+ {
+ LIST *list_element ;
+ pthread_mutex_lock(&share->intern_lock);
+ for (list_element= myisam_open_list;
+ list_element;
+ list_element= list_element->next)
+ {
+ MI_INFO *tmpinfo= (MI_INFO*) list_element->data;
+ if (tmpinfo->s == share)
+ tmpinfo->state= &share->state.state;
+ }
+ pthread_mutex_unlock(&share->intern_lock);
+ }
+ if (mi_close(mi_info))
+ goto err;
+ goto end;
+
+ err:
+ SET_STATE_TO_ERROR_AND_DBUG_RETURN;
+
+ end :
+ do {} while (0); /* Empty statement, syntactically required. */
+ }
+
DBUG_RETURN(backup::OK);
}
=== modified file 'storage/myisam/myisamdef.h'
--- a/storage/myisam/myisamdef.h 2008-05-09 10:27:23 +0000
+++ b/storage/myisam/myisamdef.h 2008-07-07 14:51:02 +0000
@@ -510,7 +510,9 @@ extern pthread_mutex_t THR_LOCK_myisam_l
/* Some extern variables */
+C_MODE_START
extern LIST *myisam_open_list;
+C_MODE_END
extern uchar NEAR myisam_file_magic[],NEAR myisam_pack_file_magic[];
extern uint NEAR myisam_read_vec[],NEAR myisam_readnext_vec[];
extern uint myisam_quick_table_bits;
| Thread |
|---|
| • bzr commit into mysql-6.0-backup branch (stewart:2651) | Stewart Smith | 10 Jul |