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-03-20 17:38:14+01:00, rafal@quant.(none) +29 -0
WL#3327: Reorganization of backup prototype code.
BitKeeper/deleted/.del-backup.h@stripped, 2007-03-20 15:49:43+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/backup.h
BitKeeper/deleted/.del-backup0.h@stripped, 2007-03-20 15:49:45+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/backup0.h
BitKeeper/deleted/.del-backup_alg.cc@stripped, 2007-03-20 15:48:25+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/backup_alg.cc
BitKeeper/deleted/.del-backup_util.cc@stripped, 2007-03-20 15:46:46+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/backup_util.cc
BitKeeper/deleted/.del-backup_util.h@stripped, 2007-03-20 15:48:08+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/backup_util.h
BitKeeper/deleted/.del-default_driver.h@stripped, 2007-03-20 15:51:57+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/default_driver.h
BitKeeper/deleted/.del-restore_alg.cc@stripped, 2007-03-20 15:48:25+01:00, rafal@quant.(none) +0 -0
Delete: sql/backup/restore_alg.cc
sql/backup/Makefile.am@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +19 -13
Changed code organization.
sql/backup/README@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +36 -0
Information for code reader.
sql/backup/README@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +0 -0
sql/backup/api_types.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +149 -0
(Abstract) types used in the API's (table and db name, table list).
sql/backup/api_types.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +0 -0
sql/backup/archive.cc@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +78 -0
Data structures describing backup archive.
sql/backup/archive.cc@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +0 -0
sql/backup/archive.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +305 -0
Data structures describing backup archive.
sql/backup/archive.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +0 -0
sql/backup/backup_aux.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +7 -442
new file: auxiliary definitions
sql/backup/backup_aux.h@stripped, 2007-03-20 16:01:46+01:00, rafal@quant.(none) +0 -0
Rename: sql/backup/backup_private.h -> sql/backup/backup_aux.h
sql/backup/backup_engine.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +428 -0
Backup engine interface.
sql/backup/backup_engine.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +0 -0
sql/backup/backup_kernel.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +17 -0
Interface to backup kernel functionality.
sql/backup/backup_kernel.h@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +0 -0
sql/backup/be_default.cc@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +3 -2
Default backup engine implementation (was backup_driver.cc).
sql/backup/be_default.cc@stripped, 2007-03-20 15:51:17+01:00, rafal@quant.(none) +0 -0
Rename: sql/backup/default_driver.cc -> sql/backup/be_default.cc
sql/backup/be_default.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +332 -0
Default backup engine implementation (was backup_driver.h).
sql/backup/be_default.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/data_backup.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +961 -0
Code interacting with backup/restore drivers to create backup image of table data.
sql/backup/data_backup.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/map.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +220 -0
Associative array datatype template (used to implement StringPool).
sql/backup/map.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/meta_backup.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +446 -0
Code for (re-)storing meta-data.
sql/backup/meta_backup.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/sql_backup.cc@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +38 -363
new file: Implementation of BACKUP/RESTORE SQL statements.
sql/backup/sql_backup.cc@stripped, 2007-03-20 16:02:33+01:00, rafal@quant.(none) +0 -0
Rename: sql/backup/backup.cc -> sql/backup/sql_backup.cc
sql/backup/stream.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +70 -0
Backup stream implementation.
sql/backup/stream.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/stream.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +776 -0
Backup stream interface.
sql/backup/stream.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/string_pool.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +92 -0
Dynamic array of strings.
sql/backup/string_pool.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/string_pool.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +48 -0
Dynamic array of strings.
sql/backup/string_pool.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/tables.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +160 -0
Data structures for storing table references and lists of them.
sql/backup/tables.cc@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/backup/tables.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +107 -0
Data structures for storing table references and lists of them.
sql/backup/tables.h@stripped, 2007-03-20 17:38:11+01:00, rafal@quant.(none) +0 -0
sql/handler.h@stripped, 2007-03-20 17:38:09+01:00, rafal@quant.(none) +2 -2
Changed code organization.
sql/sql_parse.cc@stripped, 2007-03-20 17:38:10+01:00, rafal@quant.(none) +1 -1
Changed code organization.
# 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
--- 1.254/sql/handler.h 2007-03-20 17:38:22 +01:00
+++ 1.255/sql/handler.h 2007-03-20 17:38:22 +01:00
@@ -14,7 +14,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef EMBEDDED_LIBRARY
-#include "backup0.h"
+#include "backup/api_types.h"
#endif
/* Definitions for parameters to do with handler-routines */
@@ -694,8 +694,8 @@
/*
Backup API hook
*/
+ backup_factory *get_backup_engine;
- Backup_result_t (*get_backup_engine)(handlerton *hton, Backup_engine* &be);
#endif
};
--- 1.631/sql/sql_parse.cc 2007-03-20 17:38:22 +01:00
+++ 1.632/sql/sql_parse.cc 2007-03-20 17:38:22 +01:00
@@ -29,7 +29,7 @@
#include "event_data_objects.h"
#ifndef EMBEDDED_LIBRARY
-#include "backup.h"
+#include "backup/backup_kernel.h"
#endif
#include "sql_trigger.h"
--- 1.6/sql/backup/default_driver.cc 2007-03-20 17:38:23 +01:00
+++ 1.8/sql/backup/be_default.cc 2007-03-20 17:38:23 +01:00
@@ -15,8 +15,9 @@
*/
#include "mysql_priv.h"
-#include "backup.h"
-#include "backup_private.h"
+#include "backup_engine.h"
+#include "tables.h" // for build_table_list()
+#include "be_default.h"
/*
TODO list:
--- 1.11/sql/backup/backup.cc 2007-03-20 17:38:23 +01:00
+++ 1.13/sql/backup/sql_backup.cc 2007-03-20 17:38:23 +01:00
@@ -1,10 +1,10 @@
-#include "mysql_priv.h"
-
-#include "backup.h"
-#include "backup_private.h"
+#include "../mysql_priv.h"
+#include "archive.h"
+#include "backup_aux.h"
//#define TEST yes
+
namespace backup {
CHARSET_INFO *default_charset= table_alias_charset;
@@ -136,44 +136,6 @@
}
-
-// Table_ref implementation which gets table identity from a filled record
-// of I_S.TABLES table.
-// field positions must be synchronized with defs in sql_show.cc
-
-class Table: public Table_ref
-{
- String m_db_name;
- String m_name;
- const ::handlerton *m_hton;
-
- public:
-
- Table(TABLE *t): Table_ref(m_db_name, m_name)
- {
- String engine_name;
-
- t->field[1]->val_str(&m_db_name);
- t->field[2]->val_str(&m_name);
- t->field[4]->val_str(&engine_name);
-
- LEX_STRING name_lex;
-
- name_lex.str= const_cast<char*>(engine_name.ptr());
- name_lex.length= engine_name.length();
-
- m_hton= ::ha_resolve_by_name(::current_thd,&name_lex);
- DBUG_ASSERT(m_hton);
- }
-
- const ::handlerton *hton() const
- { return m_hton; }
-
- operator bool() const
- { return TRUE; }
-};
-
-
// Collect tables to be backed-up and fill Backup_info structure describing backup image.
// Currently we try to backup all tables in 'test' database (but only tables for which
// appropriate backup engine is found will be included in the image).
@@ -318,6 +280,7 @@
#endif
}
+} // backup namespcae
/*************************************************
*
@@ -325,6 +288,8 @@
*
*************************************************/
+namespace backup {
+
bool read_header(IStream&,Restore_info&);
bool create_all_tables(THD*,const Restore_info&,IStream&);
bool restore_table_data(const Restore_info&,IStream&);
@@ -434,351 +399,61 @@
}
+} // backup namespace
-//////////////////////////////////////////////////////////////////////////////
-
-// Serialization of backup image info
-
-bool write_header(const Backup_info &info, OStream &str)
-{
- DBUG_ENTER("backup::write_header");
- stream_result::value res= str.write2int(info.ver);
- TEST_WR_RES(res);
-
- List_iterator<Backup_subimage> it(const_cast<List<Backup_subimage>&>(info.images));
- Backup_subimage *img;
-
- // List of sub-images
-
- uint ino;
-
- for( ino=0 ; (img= it++) ; ino++ )
- {
- DBUG_PRINT("backup",(" adding %s image to catalog",img->name()));
- img->write_description(str); // write description of sub-image format
- }
-
- str.end_chunk();
-
- if( ino == 0 )
- DBUG_RETURN(TRUE);
-
- DBUG_PRINT("backup",(" ==="));
-
- // write catalogs
-
- it.rewind();
-
- for( ino=0 ; (img= it++) ; ino++ )
- {
- str << img->tables; // write list of tables stored in the image
- }
-
- DBUG_PRINT("backup",("catalog written"));
-
- DBUG_RETURN(TRUE);
-}
-
-bool read_header(IStream &str, Restore_info &info)
-{
- DBUG_ENTER("restore::read_header");
-
- stream_result::value res= str.read2int(info.ver);
- TEST_RD_RES(res);
-
- Restore_subimage *img;
- uint ino;
-
- for (ino=0 ; (img= Restore_subimage::create_from_stream(ino,str)); ino++)
- {
- DBUG_PRINT("restore",(" got %s image",img->name()));
- info.images.push_back(img);
- }
-
- info.img_count= ino;
-
- res= str.next_chunk(); // move to next_chunk
-
- if( ino == 0 || res != stream_result::OK )
- DBUG_RETURN(res == stream_result::OK);
-
- // read list of tables
-
- List_iterator<Restore_subimage> it(info.images);
-
- for( ino=0 ; (img= it++) ; ino++ )
- {
- res= (str >> img->tables); // read list of tables stored in the image
- TEST_RD_RES(res);
- info.table_count+= img->tables.count();
- };
-
- DBUG_PRINT("restore",("catalog read"));
-
- DBUG_RETURN(TRUE);
-}
-
-// Write/read description of a sub-image. The exact format is determined by specific
-// classes derived from Backup_subimage.
-
-bool Backup_subimage::write_description(OStream &s)
-{
- // TODO: write description length here
- s.write2int(ver());
- s.writebyte(type());
- return do_write_description(s);
-}
-
-// TODO: Handle different and unknown types (always skip correct amount of bytes).
-
-Restore_subimage*
-Restore_subimage::create_from_stream(uint no, IStream &s)
-{
- version_t ver;
- //stream_result::value res= s.read2int(ver);
-
- if (s.read2int(ver) != stream_result::OK)
- return NULL;
-
- byte t;
- //res= s.read_byte(t);
-
- if (s.readbyte(t) != stream_result::OK)
- return NULL;
-
- switch (image_type(t)) {
-
- case DEFAULT_IMAGE:
- return Default_restore_info::create_from_stream(no, ver,s);
-
- case NATIVE_IMAGE:
- return Native_restore_info::create_from_stream(no, ver,s);
-
- default:
- return NULL;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-
-// Writing/reading table definitions
-// This implementation uses temporary formad to be replaced by DDL statements later.
-
-bool write_table_definitions(THD *thd, const Backup_info &info, OStream &s)
-{
- DBUG_ENTER("backup::write_table_definitions");
- DBUG_ASSERT(thd);
-
- List_iterator<Backup_subimage> it(const_cast< List<Backup_subimage>& >(info.images));
- Backup_subimage *img;
-
- while ( (img= it++) )
- {
- TABLE_LIST *tl= build_table_list(img->tables,TL_READ);
-
- DBUG_PRINT("backup", ( "calling open_and_lock_tables" ));
-
- if ( ::open_and_lock_tables(thd, tl) )
- {
- DBUG_PRINT("backup", ( "error!" ));
- DBUG_RETURN(FALSE);
- }
-
- DBUG_PRINT("backup", ( "saving table defs" ));
- stream_result::value res;
-
- for ( TABLE_LIST *ptr= tl; ptr ; ptr= ptr->next_global )
- {
- res= prototype::operator<<(s,*(ptr->table));
- TEST_WR_RES(res);
- }
-
- DBUG_PRINT("backup",("Table defs written, closing tables"));
- ::close_thread_tables(thd,FALSE,TRUE);
-
- // TODO: deallocate tl memory
-
- } // sub-image loop
-
- DBUG_PRINT("backup",("All table definitions written"));
-
- DBUG_RETURN(TRUE);
-}
-
-bool create_all_tables(THD *thd, const Restore_info &info, IStream &s)
-{
- DBUG_ENTER("restore::create_all_tables");
- DBUG_ASSERT(thd);
-
- int err;
- List_iterator<Restore_subimage> it(const_cast< List<Restore_subimage>& >(info.images));
- Restore_subimage *img;
-
- while ( (img= it++) ) // loop over sub-images
- {
- uint tno= img->tables.count();
- DBUG_PRINT("restore",("Creating %d tables forom %s image",tno,img->name()));
-
- TABLE_LIST *tl= build_table_list(img->tables,TL_READ);
-
- // remove tables which exist
-
- DBUG_PRINT("restore",(" dropping tables"));
- err= mysql_rm_table_part2_with_lock(thd, tl, TRUE, FALSE, TRUE); // = DROP TABLE IF EXISTS
- DBUG_PRINT("restore",(" result= %d",err));
-
- DBUG_ASSERT( err == 0 );
-
- // create tables
- // FIXME: correct order
-
- for( int i= tno-1; i >= 0 ; --i )
- {
- err= prototype::create_table_from_str(thd,s,img->tables[i]);
- DBUG_ASSERT( err == 0 );
- }
-
- // TODO: free tl memory
-
- } // sub-image loop
-
- DBUG_PRINT("restore",("All tables created"));
-
- DBUG_RETURN(TRUE);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Reading and writing list of tables from/to a stream.
-
-stream_result::value operator<<(OStream &str, const Tables &tl)
-{
- stream_result::value res;
-
- res= (str << tl.dbnames); // write the pool of dbnames
-
- for( Tables::node *n= tl.m_head ; n ; n= n->next )
- {
- res= str.write2int(n->db);
- res= str.writestr(n->name);
- };
-
- return str.end_chunk();
-}
-
-stream_result::value operator>>(IStream &str, Tables &tl)
-{
- stream_result::value res;
-
- res= (str >> tl.dbnames); // read the dbnames pool
-
- if( tl.dbnames.count() == 0 || res != stream_result::OK )
- return res;
-
- do
- {
- String name;
- uint k,tno;
-
- res= str.read2int(k);
- res= str.readstr(name);
- if( res == stream_result::OK )
- {
- tno= tl.add(k,name);
- DBUG_PRINT("restore",(" got next table %s.%s (pos %d, dbkey %d)",
- tl[tno].db().name().ptr(),
- tl[tno].name().ptr(),tno,k));
- }
- }
- while( res == stream_result::OK );
- if( res == stream_result::EOC )
- res= str.next_chunk();
+TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list); // defined in sql_show.cc
- return res;
-}
+namespace backup {
-// Serialization of StringPool
+// TODO: Use select condition to get only selected rows from a table
+// CHECK: prepare_select_* functions (sql_help.cc)
-stream_result::value operator>>(IStream &str, StringPool &p)
+TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st)
{
- stream_result::value res;
- String buf;
- uint skip, i=0;
+ TABLE *t;
+ TABLE_LIST arg;
- p.clear();
+ bzero( &arg, sizeof(TABLE_LIST) );
- do
- {
- if( i >= p.size ) break;
+ /* set context for create_schema_table call */
+ arg.schema_table= st;
+ arg.alias= NULL;
+ arg.select_lex= NULL;
- res= str.readstr(buf);
- switch( res )
- {
- case stream_result::OK:
- p.set(i++,buf);
- break;
-
- case stream_result::NIL:
- str.readint(skip);
- i += skip;
- break;
+ t= ::create_schema_table(thd,&arg); // Q: who deallocates *t ?
- default:
- break;
- }
- } while( res == stream_result::OK );
+ if( !t ) return NULL; // error!
- if( res == stream_result::EOC )
- res= str.next_chunk();
+ /*
+ Temporarily set thd->lex->wild to NULL to keep st->fill_table
+ happy :)
+ */
- return res;
-}
+ LEX local_lex= *thd->lex;
+ LEX *saved_lex= thd->lex;
-stream_result::value operator<<(OStream &str, const StringPool &p)
-{
- stream_result::value res;
- String buf;
- uint skip=0 , i=0;
+ thd->lex= &local_lex;
- // TODO: error checking
+ local_lex.wild = NULL;
+ local_lex.sql_command = enum_sql_command(0);
- for(i=0 ; i < p.count() ; i++ )
- {
- if( p.entries[i].el != NULL )
- {
- if( skip > 0 )
- str.writeint(skip);
- skip= 0;
- str.writestr(*p.entries[i].el);
- continue;
- };
-
- if( skip == 0 )
- str.writenil();
+ // Note: set thd->lex->sql_command to something neutral (see, make_db_list/get_index_file_values).
- skip++;
+ /* context for fill_table */
+ arg.table= t;
- };
+ st->fill_table(thd,&arg,NULL); // NULL = no select condition
- if( skip > 0 )
- str.writeint(skip);
+ /* restore wild field */
+ thd->lex= saved_lex;
- return str.end_chunk();
+ return t;
}
} // backup namespace
-/*
-
- Notes
-
- Check type: Table_ident (sql_parse.cc)
-
-
-*/
--- 1.2/sql/backup/Makefile.am 2007-03-20 17:38:23 +01:00
+++ 1.3/sql/backup/Makefile.am 2007-03-20 17:38:23 +01:00
@@ -24,20 +24,26 @@
-I$(top_srcdir)/regex
libbackup_la_SOURCES = \
- backup.cc \
- backup_alg.cc \
- restore_alg.cc \
- backup_prototype.cc \
- backup_util.cc \
- default_driver.cc
+ data_backup.cc \
+ meta_backup.cc \
+ sql_backup.cc \
+ be_default.cc \
+ archive.cc \
+ stream.cc \
+ tables.cc \
+ string_pool.cc
noinst_HEADERS = \
- backup.h \
- backup0.h \
- backup_private.h \
- backup_stream.h \
- backup_util.h \
- default_driver.h
+ api_types.h \
+ backup_kernel.h \
+ backup_engine.h \
+ backup_aux.h \
+ be_default.h \
+ archive.h \
+ stream.h \
+ tables.h \
+ string_pool.h \
+ map.h
DEFS = \
-DMYSQL_SERVER \
@@ -54,7 +60,7 @@
cd ..; rm -f $<; @LN_CP_F@ backup/$< $<
clean:
- rm -f *.bak
+ rm -f *.bak *.o *.lo *.loT
rm -f ../backup0.h
rm -f ../backup.h
--- New file ---
+++ sql/backup/README 07/03/20 17:38:10
Sources of the on-line backup system
====================================
Exported interfaces
-------------------
backup_kernel.h interface to backup kernel functionality
backup_engine.h backup engine interface
api_types.h (abstract) types used in the API's (table and db name,
table list)
Kernel implementation
---------------------
meta_backup.cc code for (re-)storing meta-data
data_backup.cc code interacting with backup/restore drivers to create
backup image of table data.
sql_backup.cc functions used in sql_parse.cc to execute BACKUP/RESTORE
SQL statements.
Backup engines
--------------
be_default.{cc,h} default backup engine.
Support datatypes and functions
-------------------------------
backup_aux.h auxiliary definitions.
archive.{cc,h} data structures describing backup archive.
stream.{cc,h} backup stream interface and implementation.
tables.{cc,h} data structures for storing table references and lists
of them.
string_pool.{cc,h} dynamic array of strings.
map.h associative array datatype template (used to implement
StringPool).
--- New file ---
+++ sql/backup/api_types.h 07/03/20 17:38:10
#ifndef _BACKUP_API_TYPES_H
#define _BACKUP_API_TYPES_H
extern const String my_null_string;
namespace backup {
typedef unsigned char byte;
enum result_t { OK=0, ERROR, WAIT, NOT_NOW, INIT_DONE, READY, END_BLOCK };
typedef uint version_t;
//@{
/**
* Classes Db_ref and Table_ref are used to identify databeses and tables
* inside mysql server instance.
*
* These classes abstract the way a table or database is identified inside mysqld,
* so that when this changes (introduction of global db/table ids, introduction
* of catalogs) it is easy to adapt backup code to the new identification schema.
*
* Regardless of the internal representation, classes provide methods returning
* db/table name as a String object. Also, each table belongs to some database
* and a method returnig Db_ref object identifying this database is present. For
* Db_ref objects there is catalog() method returning name of the catalog, but
* currently it always returns null string.
*
* Clases are implemented so that the memory for storing names can be allocated
* outside an instance. This is used in the Tables class implementing
* Table_list interface to share space used for storing database names among
* several Table_ref instances.
*
* Instances of Table_ref and Db_ref should be considered cheap to use, equivallent
* to using pointers or other base types. Currently, single instance of each class
* uses as much memory as a single pointer (+some external memory to store names
* which can be shared among different instances). The methods are inlined to avoid
* function call costs.
*/
class Db_ref
{
const String &m_name;
public:
const String& name() const
{ return m_name; }
const String& catalog() const
{ return my_null_string; }
protected:
// Constructors are made protected as clients of this class are
// not supposed to create instances (see comment inside Table_ref)
Db_ref(const String &name): m_name(name)
{}
Db_ref(const Db_ref &db): m_name(db.m_name)
{}
friend class Table_ref;
};
class Table_ref
{
const Db_ref m_db;
const String &m_name;
public:
const Db_ref& db() const
{ return m_db; }
const String& name() const
{ return m_name; }
// Produce string identifying the table (e.g. for error reporting)
//operator String() const;
protected:
/*
Constructor is made protected as it should not be used by
clients of this class -- they obtain already constructed
instances from the backup kernel via Table_list object passed
when creating backup/restore driver.
*/
Table_ref(const String &db, const String &name):
m_db(db), m_name(name)
{}
friend class Tables; // internal implementation of Table_list interface
};
//@}
/**
* @class Table_list
*
* @brief This abstract class defines interface used to access a list of
* tables (e.g. when such a list is passed to a backup/restore driver).
*
* Elements of the list can be accessed by index, counting from 0. E.g.
* @code
* Table_list &tables;
* Table_ref t2 = tables[1]; // t2 refers to the second element of the list.
* @endcode
*
* Interface is made abstract, so that different implementations can be
* used in the backup code. For example it is possible to create a class which
* adds this interface to a list of tables represented by a linked list of
* TABLE_LIST structures as used elsewhere in the code. On the other hand, much
* more space efficient implementations are possible, as for each table we need
* to store only table's identity (db/table name). In any case, the interface
* to the list reamins the same, as defined by this class.
*
* TODO: add iterators.
*/
class Table_list
{
public:
virtual ~Table_list() {}
/// Return reference to given list element. Elements are counted from 0.
virtual Table_ref operator[](uint pos) const =0;
/// Return number of elements in the list.
virtual uint count() const =0;
};
class Engine; // forward declaration
} // backup namespace
typedef backup::result_t Backup_result_t;
typedef backup::Engine Backup_engine;
typedef Backup_result_t backup_factory(::handlerton *,Backup_engine*&);
#endif
--- New file ---
+++ sql/backup/archive.cc 07/03/20 17:38:10
#include "../mysql_priv.h"
#include "archive.h"
#include "backup_aux.h"
// Implementation of default backup image type
namespace backup {
bool Default_backup_info::add(const Table_ref &tbl, const ::handlerton*)
{
// accept any table ...
DBUG_PRINT("backup",("adding table %s to default image",tbl.name().ptr()));
tables.add(tbl);
return TRUE;
}
bool Default_backup_info::do_write_description(OStream &)
{
// no extra information needed
// return(false);
return TRUE;
}
Default_restore_info*
Default_restore_info::create_from_stream(uint no, version_t ver, IStream &)
{
return new Default_restore_info(no, ver);
}
} // backup namespace
// Implementation of native backup image type
namespace backup {
bool Native_backup_info::add(const Table_ref &tbl, const ::handlerton *hton)
{
// Accept all tables stored in the storage engine creating this format.
if( hton == m_hton ) // this assumes handlertons are single instance objects!
{
DBUG_PRINT("backup",("adding table %s to %s image",tbl.name().ptr(),m_name));
tables.add(tbl);
return TRUE;
}
else
return FALSE;
}
bool Native_backup_info::do_write_description(OStream &s)
{
String name(::ha_resolve_storage_engine_name(m_hton),default_charset);
return s.writestr(name) == stream_result::OK;
}
Native_restore_info*
Native_restore_info::create_from_stream(uint no, version_t ver, IStream &s)
{
String name;
if( s.readstr(name) == stream_result::OK )
{
LEX_STRING name_lex;
name_lex.str= const_cast<char*>(name.ptr());
name_lex.length= name.length();
::handlerton *hton= ::ha_resolve_by_name(::current_thd,&name_lex);
return new Native_restore_info(no, hton,ver);
}
else
return NULL;
}
} // backup namespace
--- New file ---
+++ sql/backup/archive.h 07/03/20 17:38:10
#ifndef _BACKUP_ARCHIVE_H
#define _BACKUP_ARCHIVE_H
#include "backup_engine.h"
#include "tables.h"
#include "stream.h"
#include "be_default.h"
namespace backup {
// Data common to Backup_subimage and Restore_subimage
struct Subimage_info
{
enum image_type {NATIVE_IMAGE, DEFAULT_IMAGE};
uint no;
Tables tables;
Subimage_info(uint no): no(no)
{}
virtual version_t ver() const =0;
virtual image_type type() const =0;
virtual ~Subimage_info()
{}
virtual const char* name() const
{ return "<Unknown Driver>"; }
};
class Backup_subimage: public Subimage_info
{
protected:
Backup_driver *m_drv;
public:
size_t init_size;
Backup_subimage(uint no):
Subimage_info(no),
m_drv(NULL), init_size(Driver::UNKNOWN_SIZE)
{}
virtual bool add(const Table_ref &tbl, const ::handlerton *hton)
{ return FALSE; }
result_t create_driver()
{
result_t res= do_create_driver();
DBUG_ASSERT(res == OK);
DBUG_ASSERT(m_drv);
init_size= m_drv->init_size();
return res;
}
// TODO: change driver method to return pointer (to signal problems).
Backup_driver& driver()
{
if(!m_drv)
create_driver();
return *m_drv;
}
bool write_description(OStream &s);
protected:
virtual bool do_write_description(OStream&) =0;
virtual result_t do_create_driver() =0;
};
class Restore_subimage: public Subimage_info
{
protected:
Restore_driver *m_drv;
public:
version_t m_ver;
Restore_subimage(uint no, version_t ver):
Subimage_info(no), m_drv(NULL), m_ver(ver)
{}
version_t ver() const
{ return m_ver; }
result_t create_driver()
{
result_t res= do_create_driver();
DBUG_ASSERT(res == OK);
DBUG_ASSERT(m_drv);
return res;
}
Restore_driver* driver()
{
if(!m_drv)
create_driver();
return m_drv;
}
static Restore_subimage* create_from_stream(uint,IStream&);
protected:
virtual result_t do_create_driver() =0;
};
// Info about (contents of) a global backup image.
struct Image_info
{
version_t ver;
uint img_count; // number of subimages
uint table_count; // total number of tables
Image_info(version_t v): ver(v), img_count(0), table_count(0)
{}
};
struct Backup_info: public Image_info
{
List<Backup_subimage> images;
Backup_subimage *default_image;
Backup_info(): Image_info(7), default_image(NULL)
{}
// Add table stored in a given storage engine
bool add(const Table_ref &tbl, const ::handlerton *hton);
};
struct Restore_info: public Image_info
{
List<Restore_subimage> images;
Restore_info(): Image_info(7)
{}
};
} // backup namespace
namespace backup {
/************************************************************
Definitions of subimage types used
************************************************************/
// Defaul backup image: using default (blocking) backup/restore algorithm.
class Default_backup_info: public Backup_subimage
{
public:
Default_backup_info(uint no): Backup_subimage(no)
{}
version_t ver() const
{ return 1; }
image_type type() const
{ return DEFAULT_IMAGE; }
const char* name() const
{ return "Default driver"; }
result_t do_create_driver()
{
m_drv= new default_backup::Backup(tables, ::current_thd);
return m_drv? OK : ERROR;
}
bool add(const Table_ref&, const ::handlerton*);
bool do_write_description(OStream&);
};
class Default_restore_info: public Restore_subimage
{
public:
Default_restore_info(uint no, version_t ver): Restore_subimage(no, ver)
{}
image_type type() const
{ return DEFAULT_IMAGE; }
const char* name() const
{ return "Default driver"; }
result_t do_create_driver()
{
m_drv= new default_backup::Restore(tables, ::current_thd);
return m_drv? OK : ERROR;
}
static Default_restore_info* create_from_stream(uint, version_t, IStream&);
};
// Native backup image: created by backup-capable storage engine
class Native_backup_info: public Backup_subimage
{
const ::handlerton *m_hton;
Engine *m_be;
char m_name[256];
public:
Native_backup_info(uint no, const ::handlerton *hton):
Backup_subimage(no), m_hton(hton)
{
DBUG_ASSERT(hton);
DBUG_ASSERT(hton->get_backup_engine);
hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
DBUG_ASSERT(m_be);
my_snprintf(m_name,sizeof(m_name),"ARCHIVE's driver");
}
version_t ver() const
{ return m_be->version(); }
image_type type() const
{ return NATIVE_IMAGE; }
result_t do_create_driver()
{
result_t res= OK;
DBUG_ASSERT(m_be);
res= m_be->get_backup(tables,m_drv);
DBUG_ASSERT(res == OK);
DBUG_ASSERT(m_drv);
return res;
}
const char* name() const
{ return m_name; }
bool add(const Table_ref&, const ::handlerton*);
bool do_write_description(OStream&);
};
class Native_restore_info: public Restore_subimage
{
const ::handlerton *m_hton;
Engine *m_be;
public:
Native_restore_info(uint no, const ::handlerton *hton, version_t ver):
Restore_subimage(no,ver), m_hton(hton)
{
DBUG_ASSERT(hton);
DBUG_ASSERT(hton->get_backup_engine);
hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
DBUG_ASSERT(m_be);
}
image_type type() const
{ return NATIVE_IMAGE; }
result_t do_create_driver()
{
result_t res= OK;
DBUG_ASSERT(m_be);
res= m_be->get_restore(m_ver,tables,m_drv);
DBUG_ASSERT(res == OK);
DBUG_ASSERT(m_drv);
return res;
}
static Native_restore_info* create_from_stream(uint, version_t, IStream&);
};
} // backup namespace
#endif
--- New file ---
+++ sql/backup/backup_engine.h 07/03/20 17:38:10
#ifndef _BACKUP_ENGINE_API_H
#define _BACKUP_ENGINE_API_H
#include <backup/api_types.h>
#ifndef VIEW_ALGORITHM_UNDEFINED
#error backup.h must be included after table.h
#endif
namespace backup {
// Forward declarations
class Backup_driver;
class Restore_driver;
/**
* @class Engine
* @brief Encapsulates online backup/restore functionality.
*
* Any engine providing online backup creates an instance of this class,
* so called <em>backup engine</em>, which is used by backup kernel to
* create backup image of data stored in that engine or to restore data from
* a previously created backup.
*
* When a backup image of a given list of tables is to be created, the backup
* engine creates a <em>backup driver</em> object which performs this operation
* responding to the backup protocol of the backup kernel.
*
* Similar, when data is to be restored from a backup image, a <em>restore driver</em>
* object is created by backup engine. This object performs restore operation and
* communicates with backup kernel using the restore protocol.
*
* @note It is thinkable to have a backup engine independent from a storage
* engine but currently no use of such backup engines is planned.
*/
class Engine
{
public:
virtual ~Engine() {}
/// Return version of backup images created by this engine.
virtual const version_t version() const =0;
/**
* Create a backup driver.
*
* Given a list of tables to be backed-up, create instance of backup
* driver which will create backup image of these tables.
*
* @param tables (in) list of tables to be backed-up.
* @param eng (out) pointer to backup driver instance.
*
* @return Error code or <code>backup::OK</code> on success.
*/
virtual result_t get_backup(const Table_list &tables, Backup_driver* &drv) =0;
/**
* Create a restore driver.
*
* Given a list of tables to be restored, create instance of restore
* driver which will restore these tables from a backup image.
*
* @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.
*
* @return Error code or <code>backup::OK</code> on success.
*/
virtual result_t get_restore(version_t version,
const Table_list &tables, Restore_driver* &drv) =0;
/**
* Free any resources allocated by the backup engine.
*
* It is possible to delete the instance here since backup kernel
* will never use an instance after a call to <code>free()</code> method.
*
* Backup kernel does not assume that backup engine is allocated
* dynamically and therefore will never delete an instance it has obtained.
* However, it will call <code>free()</code> method when done with the instance.
* If the instance is dynamically alocated it should be deleted in this method.
*/
virtual void free() {};
};
/**
* @class Buffer
*
* @brief Used for data transfers between backup kernel and backup/restore drivers.
*
* Apart from allocated memory a Buffer object contains fields informing about its size
* and holding other information.
*
* As described in WL#3169 all blocks of backup image data are distributed among several
* streams corresponding to different tables being backed-up/restored. Field
* <code>stream_no</code> contains the number of the stream to which the data belongs.
*
* When backup driver fills buffers given by backup kernel with its backup image data it
* sets <code>last</code> field to <code>TRUE</code> to indicate that this is the last
* block of data in a given stream. Backup kernel knows that all data has been sent if
* it has received "last" blocks for all the streams forming backup image.
*
* @note Backup driver indicates how much data it has put into the buffer by updating
* its <code>size</code> field.
*/
struct Buffer
{
size_t size; ///< size of the buffer (of memory block pointed by data).
uint stream_no; ///< Number of the stream to which data in the buffer belongs.
bool last; ///< <code>TRUE</code> if this is last block of data in the stream.
byte *data; ///< Pointer to data area.
Buffer(): data(NULL),size(0),stream_no(0),last(TRUE)
{}
void reset(size_t len)
{
size= len;
stream_no= 0;
last= TRUE;
}
};
/**
* @class Driver
*
* @brief Common base for backup and restore drivers.
*
* This class contains methods which are common to both types of drivers.
*/
class Driver
{
public:
/// Construct from list of tables. The list is stored for future use.
Driver(const Table_list &tables):m_tables(tables) {};
virtual ~Driver() {}; // We want to inherit from this class.
//@{
/**
* Initialize or finalize backup/restore process.
*
* After return from <code>begin()</code> call, driver should be ready to
* serve requests for sending/receiving backup image data.
*
* Method <code>end()</code> is called when all data has been sent (from kernel
* to restore driver or from backup driver to kernel) so that the backup/restore
* process can be finalized inside the driver.
*
* @note <b>Important!</b>. All DDL operations on tables being backed-up are
* blocked in the server. An engine which can alter tables (e.g. NDB) should
* participate in this block by not allowing any such changes between calls to
* <code>begin()</code> and <code>end()</code>.
*/
virtual result_t begin() =0;
virtual result_t end() =0;
//@}
/// Cancel ongoing backup/restore process.
virtual result_t cancel() =0;
/**
* Free resources allocated by the driver.
*
* Driver can be deleted here. @see Engine::free()
*/
virtual void free() {};
/// Unknown size constant used for backup image size estimates.
static const size_t UNKNOWN_SIZE= static_cast<size_t>(-1);
protected:
/// Refers to the list of tables passed when the driver was created.
const Table_list &m_tables;
};
/**
* @class Backup_driver
*
* @brief Represents backup driver for backing-up a given list of tables.
*
* This class provides all the methods used to implement the backup protocol
* for communication between backup kernel and the driver. The most important
* method is <code>get_data()</code> which is used by the kernel to poll the
* backup image data and at the same time learn about state of the backup process.
*/
class Backup_driver: public Driver
{
public:
Backup_driver(const Table_list &tables):Driver(tables) {};
virtual ~Backup_driver() {}; // Each specific implementation will derive from this class.
/**
* @fn result_t get_data(Buffer &buf)
*
* @brief Accept a request for filling a buffer with backup image data or check
* status of a previously accepted request.
*
* This method is called by backup kernel when it wants to fill a buffer with (next)
* block of backup image data from the driver. Returned value is also used to inform
* kernel about state of the backup process (thus implements a communication channel
* from backup driver to the kernel).
*
* Backup driver can implement its own policy of handling these requests. It is possible
* that it returns immediately from the call and uses a separate thread to fill the
* buffer and it is also possible that the call blocks until the buffer is filled.
*
* If method returns <code>WAIT</code>, then it means that the request was
* accepted by the driver but is not completed yet. Further calls to <code>get_data()</code>
* with the same buffer as argument are needed to complete the request. When a request
* is complete, <code>get_data()</code> normally returns <code>OK</code>.
*
* Return values <code>INIT_DONE</code> and <code>READY</code> also
* signal completion of a request, but additionaly they are responses in the backup
* creation protocol used by the backup kernel (see WL#3569 and methods
* <code>prelock()</code> and <code>lock()</code> below).
*
* It is possible that no data is stored in the buffer as the result of a request. This
* can be used to signal <code>INIT_DONE</code> or <code>READY</code> without actually
* sending any data.
*
* As described in WL#3169 backup image data is distributed between streams corresponding
* to tables being backed-up. When completing a request, backup driver fills the number
* of stream to which given block of data belongs. The driver can send streams in any
* order it wishes including interleaving. Upon restore it is guaranteed that blocks
* from each stream are sent in the same order in which they were received when the backup
* image was created. However, order of the streams and interleaving can be changed.
*
* @param buf (in/out) buffer to be filled with backup data.
*
* @returns
*
* - OK : The request is completed - new data is in the buffer.
* - INIT_DONE : As above and additionally signals end of initial backup phase.
* - READY : As <code>OK</code> and additionally signals that driver is ready
* for creating a validity point of its backup image.
* - WAIT : The request was accepted but is not completed yet.
* - NOT_NOW : The request can not be accepted right now. A call to
* <code>get_data()</code> should be repeated later.
* - ERROR : An error has happened. The request is rejected or removed from the
* queue if it was accepted before.
*
* Additionaly the driver sets <code>size</code> field in the buffer object to the
* amount of data it has placed in it. This can be 0. It also fills the
* <code>stream_no</code> field to indicate which stream the data belongs to. If this
* is the last block of data in the stream the <code>last</code> field should be set
* to <code>TRUE</code>.
*
* @note If backup kernel calls <code>get_data()</code> when there is no more data
* to be sent, the driver should:
* -# set <code>buf.size</code> to 0,
* -# set <code>buf.last</code> to TRUE,
* -# return <code>OK</code>.
*/
virtual result_t get_data(Buffer &buf) =0;
/**
* @fn result_t prelock()
*
* @brief Prepare for synchronization of backup image.
*
* This method is called by backup kernel when all engines participating in creation of
* the backup have finished their initial data transfer. After this call the
* driver should prepare for the following <code>lock()</code> call from the kernel.
*
* It can do the preparations inside the <code>prelock()</code> method if it doesn't
* require too long time. In that case it should return <code>READY</code>.
*
* If the preparations require longer time (waiting for ongoing operations to finish)
* or sending additional data to the kernel then <code>prelock()</code> should return
* <code>OK</code>. Later on, the kernel will call <code>get_data()</code>
* method which can signal that driver is ready by returning <code>READY</code>.
*
* @returns
*
* - READY : The driver is ready for synchronization, i.e. it can accept the following
* <code>lock()</code> call.
*
* - OK : The driver is preparing for synchronization. Kernel should call
* <code>get_data()</code> and wait until driver is ready.
*
* - ERROR : The driver can not prepare for synchronization.
*/
virtual result_t prelock()
{ return READY; };
//@{
/**
* @ brief Methods for synchronization with backup images created by other engines.
*
* After sending <code>prelock()</code> requests to all backup drivers and receiving
* <code>READY</code> confirmations, backup kernell calls <code>lock()</code> method
* of each driver. The driver is supposed to do two things in response:
*
* -# Create a validity point of its backup image. The whole backup image should
* describe data at this exact point in time.
*
* -# Freeze its state until the following <code>unlock()</code> call. This means that
* from now on the data stored in the engine should not change in any way, so that
* the validity point remains valid during the time other engines create their own
* validity points.
*
* When all drivers have locked, backup kernel will call <code>unlock()</code> on all
* of them. After this call the driver should unfreeze. Kernel will continue polling
* backup data using <code>get_data()</code> method until driver signals that there
* is no more data to be sent.
*
* @note <b>Important!</b>. A call to <code>lock()</code> should return as quickly
* as possible. Ideally, only fast memory access and/or (non-blocking) mutex
* manipulations should happen but no writing/reading from disk. The backup kernel
* expects that this call will return in at most few seconds.
*
* @returns Error code or <code>OK</code> upon success.
*/
virtual result_t lock() =0;
virtual result_t unlock() =0;
//@}
/**
* Return estimate (in bytes) of the size of the backup image.
*
* This estimate is used by backup kernel to give backup progress feedback to
* users.
*
* If estimating the size is impossible or very costly, the driver can return
* <code>UNKNOWN_SIZE</code>.
*/
virtual size_t size() =0;
/**
* Estimate how much data will be sent in the initial phase of backup.
*
* This information is used by backup kernel to initialize backup engines of different
* types at correct times (see WL#3569 for details).
*
* For example the "at begin" engines which send all backup data in the final phase
* of backup should return 0 here. This ensures that they are initialized after any
* other engines have finished their initial data transfer.
*
* If estimating this size is impossible or very expensive, the driver can return
* <code>UNKNOWN_SIZE</code>. In that case it will be treated as "at end"
* driver and will be initialized before any other drivers.
*/
virtual size_t init_size() =0;
};
/**
* @class Restore_driver
*
* @brief Represents restore driver used for restoring a given list of tables.
*
* This class provides all the methods used to implement the restore protocol
* for communication between backup kernel and the driver. The most important
* method is <code>send_data()</code> which is used by the kernel to send
* backup image data to the driver.
*/
class Restore_driver: public Driver
{
public:
Restore_driver(const Table_list &tables):Driver(tables) {};
virtual ~Restore_driver() {};
/**
* @fn result_t send_data(Buffer &buf)
*
* @brief Request processing of next block of backup image data or check
* status of a previously accepted request.
*
* Upon restore, backup kernel calls this method periodically sending consecutive
* blocks of data from the backup image. The <code>stream_no</code> field in the
* buffer is set to indicate from which stream the data comes. Also,
* <code>buf.last</code> is <code>TRUE</code> if this is the last block in the stream.
* For each stream the blocks are guaranteed to be sent in the same order in which they
* were received when the backup image was created.
*
* Backup driver can implement its own policy of handling these requests. It is possible
* that it returns immediately from the call and uses a separate thread to process
* data in the buffer and it is also possible that the call blocks until all processing
* is done.
*
* Returning <code>OK</code> means that the data has been successfuly processed and
* the buffer can be re-used for further transfers. If method returns <code>WAIT</code>,
* it means that the request was accepted but is not completed yet. The buffer should
* not be used for other purposes until a further call to <code>get_data()</code> with
* the same buffer as argument returns <code>OK</code>.
*
* @param buf (in/out) buffer filled with backup data. Fields <code>size</code>,
* <code>stream_no</code> and <code>last</code> are set accordingly.
*
* @returns
*
* - OK : The data has been accepted and processed -- the buffer can be
* used for other transfers.
* - WAIT : The request was accepted but data is not processed yet -- it should
* stay in the buffer. Further calls to <code>send_data()</code> are
* needed to complete the request.
* - NOT_NOW : The request can not be processed right now. A call to
* <code>send_data()</code> should be repeated later.
* - ERROR : An error has happened. The request is rejected or removed from the
* queue if it was accepted before.
*/
virtual result_t send_data(Buffer &buf) =0;
};
} // backup namespace
// export Backup/Restore_driver classes to global namespace
using backup::Backup_driver;
using backup::Restore_driver;
#endif
--- New file ---
+++ sql/backup/backup_kernel.h 07/03/20 17:38:10
#ifndef _BACKUP_KERNEL_API_H
#define _BACKUP_KERNEL_API_H
#include <backup/api_types.h>
namespace backup {
//@{
/// Functions used to implement BACKUP/RESTORE SQL statements.
/// They are called from sql_parse.cc
int do_backup(THD*);
int do_restore(THD*);
//@}
} // backup namespace
#endif
--- New file ---
+++ sql/backup/be_default.h 07/03/20 17:38:11
#ifndef _DEFAULT_BACKUP_H
#define _DEFAULT_BACKUP_H
/**
* @file default_backup.h
*
* @brief Contains the default backup algorithm driver.
*
* This file contains the default backup algorithm (also called a "driver"
* in the online backup terminology. The default backup algorithm may be
* used in place of an engine-specific driver if one does not exist or if
* chosen by the user.
*
* The default backup algorithm is a blocking algorithm that locks all of
* the tables given at the start of the backup/restore process. Once all of
* the data is backed up or restored, the locks are removed. The default
* backup is a row-level backup and therefore does not backup the indexes
* or any of the engine-specific files.
*
* The classes in this file use the namespace "default_backup" to distinguish
* these classes from other backup drivers. The backup functionality is
* contained in the backup class shown below. Similarly, the restore
* functionality is contained in the restore class below.
*
* The format of the backup is written as a series of data blocks where the
* first block contains the record buffer (table->record[0]). This is
* followed by one or more blocks containing the blob (text) fields one per
* block. This sequence is repeated for each row in the table.
*
* blknum contents
* ------------------------------------------------------
* 1 (byte) copy record_buffer read from handler
* ------------------------------------------------------
* 2...n (uint32) size of blob data to read
* (byte) blob data for blob
* ------------------------------------------------------
*/
namespace default_backup {
using backup::byte;
using backup::result_t;
using backup::version_t;
using backup::Table_list;
using backup::Table_ref;
using backup::Buffer;
/**
* @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; };
/**
* 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.
*
* @return Error code or <code>backup::OK</code> on success.
*/
result_t get_backup(const Table_list &tables, Backup_driver* &drv);
/**
* 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.
*
* @return Error code or <code>backup::OK</code> on success.
*/
result_t get_restore(version_t ver, const Table_list &tables,
Restore_driver* &drv);
/**
* Free any resources allocated by the default backup engine.
*/
void free() { delete this; }
private:
THD *m_thd; ///Pointer to the current thread.
};
/**
* @class Backup
*
* @brief Contains the default backup algorithm backup functionality.
*
* The backup class is a row-level backup mechanism designed to perform
* a table scan on each table reading the rows and saving the data to the
* buffer from the backup algorithm.
*
* @see <backup driver>
*/
class Backup: public Backup_driver
{
public:
enum has_data_info { YES, WAIT, EOD };
Backup(const Table_list &tables, THD *t_thd);
virtual ~Backup();
size_t size() { return UNKNOWN_SIZE; };
size_t init_size() { return UNKNOWN_SIZE; };
/**
* @brief Start backup process.
*
* This method locks all of the tables for reading.
*
* @return
* backup::OK - all tables locked properly.
* backup::Error - problem with locking tables.
*/
result_t begin();
/**
* @brief End backup process.
*
* This method unlocks all of the tables.
*
* @return
* backup::OK - all tables unlocked.
*/
result_t end();
/**
* @brief Get the data for a row in the table.
* This method is the main method used in the backup operation. It is
* responsible for reading a row from the table and placing the data in
* the buffer (buf.data) and setting the correct attributes for processing
* (e.g., buf.size = size of record data).
*
* Control of the method is accomplished by using several modes that
* signal portions of the method to run. These modes are:
*
* initialized - Signals (when false) method to initialize variables
* and prepare for backup process. When true, indicates
* the initialization process is complete.
* get_next_table - Signals method to move to the next table in the list.
* May be used in conjunction with reading and read_blobs.
* reading - Signals method to read next row of data.
* read_blobs - Signals that row has blobs and to read blob data.
*
* @return
* backup::INIT_DODE - initialization phase complete.
* backup::OK - data read.
* backup::Error - problem with reading data.
*/
result_t get_data(Buffer &buf);
result_t lock();
result_t unlock();
result_t cancel() { return backup::OK; };
void free() { delete this; };
private:
/**
* @brief Start table read.
*
* This method saves the handler for the table and initializes the
* handler for reading.
*
* @return
* backup::OK - handler initialized properly.
* backup::Error - problem with hander initialization.
*/
result_t start_tbl_read(TABLE *tbl);
/**
* @brief End table read.
*
* This method signals the handler that the reading process is complete.
*
* @return
* backup::OK - handler read stopped properly.
* backup::Error - problem with hander.
*/
result_t end_tbl_read();
/**
* @brief Get next table in the list.
*
* This method iterates through the list of tables selecting the
* next table in the list and starting the read process.
*
* @return
* 0 - no errors.
* -1 - no more tables in list.
*/
int next_table();
int initialized; ///< Indicates code has been initialized.
int reading; ///< Indicates mode is read row.
int last_read_res; ///< The last result code from handler.
int tbl_num; ///< The index of the current table.
int read_blobs; ///< Indicates mode is read blob data.
int get_next_table; ///< Indicates get the next table in list.
TABLE *cur_table; ///< The table currently being read.
THD *m_thd; ///< Pointer to current thread struct.
handler *hdl; ///< Pointer to table handler.
uint *cur_blob; ///< The current blob field.
uint *last_blob_ptr; ///< Position of last blob field.
TABLE_LIST *tables_in_backup; ///< List of tables used in backup.
TABLE_LIST *all_tables; ///< Reference to list of tables used.
MY_BITMAP *read_set; ///< The file read set.
};
/**
* @class Restore
*
* @brief Contains the default backup algorithm restore functionality.
*
* The restore class is a row-level backup mechanism designed to restore
* data for each table by writing the data for the rows from the
* buffer given by the backup algorithm.
*
* @see <restore driver>
*/
class Restore: public Restore_driver
{
public:
enum has_data_info { YES, WAIT, EOD };
Restore(const Table_list &tables, THD *t_thd);
virtual ~Restore();
/**
* @brief Start restore process.
*
* This method locks all of the tables for writing.
*
* @return
* backup::OK - all tables locked properly.
* backup::Error - problem with locking tables.
*/
result_t begin();
/**
* @brief End restore process.
*
* This method unlocks all of the tables.
*
* @return
* backup::OK - all tables unlocked.
*/
result_t end();
/**
* @brief Restore the data for a row in the table.
*
* This method is the main method used in the restore operation. It is
* responsible for writing a row to the table.
*
* Control of the method is accomplished by using several modes that
* signal portions of the method to run. These modes are:
*
* initialized - Signals (when false) method to initialize variables
* and prepare for restore process. When true, indicates
* the initialization process is complete.
* get_next_table - Signals method to move to the next table in the list.
* May be used in conjunction with writing and write_blobs.
* writing - Signals method to write next row of data.
* write_blobs - Signals that row has blobs and to write blob data.
*
* @return
* backup::INIT_DODE - initialization phase complete.
* backup::OK - data written.
* backup::Error - problem with writing data.
*/
result_t send_data(Buffer &buf);
result_t cancel() { return backup::OK; };
void free() { delete this; };
private:
/**
* @brief Truncate table.
*
* This method saves the handler for the table and deletes all rows in
* the table.
*
* @return
* backup::OK - rows deleted.
* backup::Error - problem with deleting rows.
*/
result_t truncate_table(TABLE *tbl);
/**
* @brief Get next table in the list.
*
* This method iterates through the list of tables selecting the
* next table in the list and starting the read process.
*
* @return
* 0 - no errors.
* -1 - no more tables in list.
*/
int next_table();
int initialized; ///< Indicates code has been initialized.
int writing; ///< Indicates mode is write row.
int last_write_res; ///< The last result code from handler.
int tbl_num; ///< The index of the current table.
int write_blobs; ///< Indicates mode is write blob data.
int get_next_table; ///< Indicates get the next table in list.
TABLE *cur_table; ///< The table currently being read.
THD *m_thd; ///< Pointer to current thread struct.
handler *hdl; ///< Pointer to table handler.
uint *cur_blob; ///< The current blob field.
uint *last_blob_ptr; ///< Position of last blob field.
TABLE_LIST *tables_in_backup; ///< List of tables used in backup.
TABLE_LIST *all_tables; ///< Reference to list of tables used.
};
} // default_backup namespace
#endif
--- New file ---
+++ sql/backup/data_backup.cc 07/03/20 17:38:11
#include "../mysql_priv.h"
#include "backup_engine.h"
#include "stream.h"
#include "archive.h"
namespace backup {
struct backup_state {
enum value { INACTIVE,
INIT,
WAITING,
PREPARING,
READY,
FINISHING,
DONE,
ERROR,
MAX };
static const char* name[];
struct Initializer
{
Initializer()
{
name[INACTIVE]= "INACTIVE";
name[INIT]= "INIT";
name[WAITING]= "WAITING";
name[PREPARING]= "PREPARING";
name[READY]= "READY";
name[FINISHING]= "FINISHING";
name[DONE]= "DONE";
name[ERROR]= "ERROR";
}
};
private:
static Initializer init;
};
const char* backup_state::name[backup_state::MAX];
backup_state::Initializer init;
struct Block_writer
{
enum result_t { OK, NO_RES, ERROR };
result_t get_buf(Buffer &);
result_t write_buf(const Buffer&);
result_t drop_buf(Buffer&);
Block_writer(size_t size, OStream &s): m_str(s), buf_size(size)
{}
private:
// uint m_no;
OStream &m_str;
size_t buf_size;
};
/**
@class Backup_pump
Poll backup driver for backup data and send it to a stream. Monitors stages
of the backup process, keeps track of closed streams etc.
Usage: Initialize using begin() method, then call pump() metod repeatedly.
The state member informs about the current state of the backup process. When
done, call end() method. Methods prepare(), lock() and unlock() are forwarded
to backup driver to implement multi-engine synchronization.
@note Stream multiplexer is used to write data so that several backup pumps
can write to the same stream simultanously.
*/
struct Backup_pump
{
backup_state::value state;
enum { READING, WRITING } mode;
size_t init_size;
size_t bytes_in, bytes_out;
const char *m_name;
Backup_pump(Backup_subimage&, Block_writer&);
size_t pump();
bool prepare();
bool begin()
{
state= backup_state::INIT;
DBUG_PRINT("backup/data",(" %s enters INIT state",m_name));
return m_drv.begin() == backup::OK;
}
bool end()
{
m_drv.end();
m_drv.free();
return TRUE;
}
bool lock()
{
DBUG_PRINT("backup/data",(" locking %s",m_name));
return m_drv.lock() == backup::OK;
}
bool 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
}
Backup_driver &drv() const
{ return m_drv; }
private:
static const uint m_buf_size= 1024;
uint m_drv_no;
Backup_driver &m_drv;
Block_writer &m_bw;
Buffer m_buf;
byte *m_buf_head;
bool get_buf();
bool drop_buf();
bool write_buf();
MY_BITMAP m_closed_streams;
void mark_stream_closed(uint stream_no)
{
bitmap_set_bit(&m_closed_streams, stream_no);
}
bool all_streams_closed()
{
return bitmap_is_set_all(&m_closed_streams);
}
};
bool Backup_pump::prepare()
{
result_t res= m_drv.prelock();
state= backup_state::PREPARING;
switch (res) {
case READY:
state= backup_state::READY;
case OK:
res= OK;
break;
default:
break;
}
DBUG_PRINT("backup/data",(" preparing %s, goes to %s state",
m_name,backup_state::name[state]));
return res == backup::OK;
}
/**
@class Scheduler
Used to drive several backup pumps in a fair fashion. Also, keeps track of the
state of these pumps.
*/
struct Scheduler
{
void step();
void add(Backup_subimage&);
void prepare();
void lock();
void unlock();
void close() {}
uint init_count; ///< no. drivers sending init data
uint prepare_count; ///< no. drivers preparing for lock
uint finish_count; ///< no. drivers sending final data
size_t init_left() const
{ return m_known_count? m_init_left/m_known_count + 1 : 0; }
bool is_empty() const
{ return m_count == 0; }
private:
// Extend Backup_pump with information abouts its position relative
// to other pumps.
class Pump: public Backup_pump
{
size_t start_pos;
Block_writer bw;
Pump(OStream &s, Backup_subimage &img, size_t pos):
Backup_pump(img,bw), start_pos(pos), bw(1024,s)
{}
friend class Scheduler;
public:
size_t pos() const
{ return start_pos + bytes_in; }
};
struct Pump_ptr
{
LIST *el;
Pump* operator->()
{
return el? static_cast<Pump*>(el->data) : NULL;
}
void operator++()
{
if(el) el= el->next;
}
operator bool() const
{ return el && el->data; }
void operator=(const Pump_ptr &p)
{ el= p.el; }
Pump_ptr(): el(NULL)
{}
Pump_ptr(LIST *el): el(el)
{}
};
LIST *m_pumps, *m_last;
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;
void move_pump_to_end(const Pump_ptr &p)
{
DBUG_ASSERT(m_pumps);
if (m_last != p.el)
{
m_pumps= list_delete(m_pumps,p.el);
m_last->next= p.el;
p.el->prev= m_last;
p.el->next= NULL;
m_last= p.el;
}
}
void remove_pump(const Pump_ptr &p)
{
DBUG_ASSERT(p.el);
if (m_last == p.el)
m_last= m_last->prev;
if (m_pumps)
m_pumps= list_delete(m_pumps,p.el);
}
Scheduler(OStream &s):
init_count(0), prepare_count(0), finish_count(0),
m_pumps(NULL), m_last(NULL),
m_count(0), m_total(0), m_init_left(0), m_known_count(0),
m_str(s)
{}
friend bool write_table_data(const Backup_info&, OStream&);
};
#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)
// Poll all backup drivers involved in the backup image for their backup
// data and write to a backup stream.
// Currently only one driver is handled.
bool write_table_data(const Backup_info &info, OStream &s)
{
DBUG_ENTER("backup::write_table_data");
Scheduler sch(s); // scheduler instance
List<Backup_subimage> inactive; // list of images not yet being created
size_t max_init_size=0; // keeps maximal init size for images in inactive list
DBUG_PRINT("backup/data",("initializing scheduler"));
// add unknown "at end" drivers to scheduler, rest to inactive list
List_iterator<Backup_subimage> it(const_cast< List<Backup_subimage>& >(info.images));
Backup_subimage *img;
while ((img= it++))
{
DBUG_ASSERT( OK == img->create_driver() );
size_t init_size= img->init_size;
if (init_size == Driver::UNKNOWN_SIZE)
{
sch.add(*img);
}
else
{
if (init_size > max_init_size)
max_init_size= init_size;
inactive.push_back(img);
}
}
DBUG_ASSERT( !sch.is_empty() || !inactive.is_empty() );
DBUG_PRINT("backup/data",("%d drivers initialized, %d inactive",
sch.init_count,
inactive.elements));
DBUG_PRINT("backup/data",("%d drivers initialized, %d inactive",
sch.init_count,
inactive.elements));
DBUG_PRINT("backup/data",("-- INIT PHASE --"));
// poll "at end" drivers activating inactive ones on the way
// note: if scheduler is empty and there are images with non-zero
// init size (max_init_size > 0) then enter the loop as one such image
// will be added to the scheduler inside.
while (sch.init_count > 0 || sch.is_empty() && max_init_size > 0)
{
// add inactive image if it is a time for it
if (max_init_size > 0 && sch.init_left() <= max_init_size)
{
List_iterator<Backup_subimage> it(inactive);
Backup_subimage *p;
size_t second_max= 0;
max_init_size= 0;
img= NULL;
while ((p= it++))
{
if ( p->init_size >= max_init_size )
{
second_max= max_init_size;
max_init_size= p->init_size;
img= p;
}
}
max_init_size= second_max;
sch.add(*img);
}
sch.step();
}
// start "at begin" drivers
DBUG_PRINT("backup/data",("- activating \"at begin\" drivers"));
List_iterator<Backup_subimage> it1(inactive);
while ((img= it1++))
sch.add(*img);
while (sch.init_count > 0)
sch.step();
// prepare for VP
DBUG_PRINT("backup/data",("-- PREPARE PHASE --"));
sch.prepare();
while (sch.prepare_count > 0)
sch.step();
// VP creation
DBUG_PRINT("backup/data",("-- SYNC PHASE --"));
sch.lock();
sch.unlock();
// get final data from drivers
DBUG_PRINT("backup/data",("-- FINISH PHASE --"));
while (sch.finish_count > 0)
sch.step();
DBUG_PRINT("backup/data",("-- DONE --"));
sch.close();
DBUG_RETURN(TRUE);
}
/**************************************************
* Implementation of Scheduler
**************************************************/
void Scheduler::step()
{
/* find pump with least data read.
Pick next driver to pump data from. The driver with least data read
is choosen. If there are several, they are polled in round-robin
fashion.
TODO: implement this
*/
Pump_ptr p(m_pumps);
/*
for (Pump_ptr it(m_pumps); it; ++it)
if ( !p || it->pos() < p->pos() )
p= it;
*/
if (!p)
return;
move_pump_to_end(p);
const char *drv_name= p->m_name;
DBUG_PRINT("backup/data",("polling %s",drv_name));
backup_state::value before_state= p->state;
size_t howmuch= p->pump();
backup_state::value after_state= p->state;
// update statistics
if (howmuch > 0)
{
m_total += howmuch;
if (before_state == backup_state::INIT
&& p->init_size != Driver::UNKNOWN_SIZE)
m_init_left -= howmuch;
}
if (after_state != before_state)
{
switch (before_state) {
case backup_state::INIT:
if (init_count>0)
init_count--;
break;
case backup_state::PREPARING:
if (prepare_count>0)
prepare_count--;
break;
case backup_state::FINISHING:
if (finish_count>0)
finish_count--;
break;
default: break;
}
switch (after_state) {
case backup_state::INIT:
init_count++;
break;
case backup_state::PREPARING:
prepare_count++;
break;
case backup_state::FINISHING:
finish_count++;
break;
case backup_state::DONE:
remove_pump(p);
break;
default: break;
}
DBUG_PRINT("backup/data",("driver counts: total=%d, init=%d, prepare=%d, finish=%d.",
m_count, init_count, prepare_count, finish_count));
}
}
void Scheduler::add(Backup_subimage &img)
{
size_t avg= m_count? m_total/m_count + 1 : 0;
Pump *p= new Pump(m_str,img,avg);
DBUG_ASSERT(p);
DBUG_PRINT("backup/data",("Adding %s to scheduler (at pos %d)",
img.name(),avg));
m_pumps= list_cons(p,m_pumps);
if (!m_last)
m_last= m_pumps;
m_count++;
m_total += avg;
DBUG_ASSERT( p->begin() );
if (img.init_size != Driver::UNKNOWN_SIZE)
{
m_init_left += img.init_size;
m_known_count++;
}
switch (p->state) {
case backup_state::INIT:
init_count++;
break;
case backup_state::PREPARING:
prepare_count++;
break;
case backup_state::FINISHING:
finish_count++;
break;
default: break;
}
DBUG_PRINT("backup/data",("driver counts: total=%d, init=%d, prepare=%d, finish=%d.",
m_count, init_count, prepare_count, finish_count));
DBUG_PRINT("backup/data",("total init data size estimate: %d",m_init_left));
}
void Scheduler::prepare()
{
DBUG_ASSERT(init_count==0);
DBUG_PRINT("backup/data",("calling prepare() for all drivers"));
for(Pump_ptr it(m_pumps); it; ++it)
{
it->prepare();
if (it->state == backup_state::PREPARING)
prepare_count++;
}
DBUG_PRINT("backup/data",("driver counts: total=%d, init=%d, prepare=%d, finish=%d.",
m_count, init_count, prepare_count, finish_count));
}
void Scheduler::lock()
{
DBUG_ASSERT(prepare_count==0);
DBUG_PRINT("backup/data",("calling lock() for all drivers"));
for(Pump_ptr it(m_pumps); it; ++it)
it->lock();
DBUG_PRINT("backup/data",("driver counts: total=%d, init=%d, prepare=%d, finish=%d.",
m_count, init_count, prepare_count, finish_count));
}
void Scheduler::unlock()
{
DBUG_PRINT("backup/data",("calling unlock() for all drivers"));
for(Pump_ptr it(m_pumps); it; ++it)
{
it->unlock();
if (it->state == backup_state::FINISHING)
finish_count++;
}
}
/**************************************************
* Implementation of Backup_pump
**************************************************/
Backup_pump::Backup_pump(Backup_subimage &img, Block_writer &bw):
state(backup_state::INACTIVE), mode(READING),
init_size(img.init_size), bytes_in(0), bytes_out(0),
m_drv_no(img.no), m_drv(img.driver()), m_bw(bw), m_buf_head(NULL)
{
m_buf.data= NULL;
bitmap_init(&m_closed_streams,
NULL,
1+img.tables.count(),
FALSE); // not thread safe
m_name= img.name();
}
// FIXME: return number of bytes *written* to stream.
size_t Backup_pump::pump()
{
if (state == backup_state::INACTIVE
|| state == backup_state::ERROR
|| state == backup_state::DONE )
return 0;
backup_state::value before_state= state;
size_t howmuch= 0;
if (all_streams_closed())
{
switch (state) {
case backup_state::INIT:
state= backup_state::WAITING;
break;
case backup_state::PREPARING:
state= backup_state::READY;
break;
case backup_state::FINISHING:
state= backup_state::DONE;
break;
default: break;
}
}
else
{
if (mode == READING)
{
// ensure that we have a buffer ready for writing
if (!m_buf_head)
switch (m_bw.get_buf(m_buf)) {
case Block_writer::NO_RES:
return 0;
case Block_writer::ERROR:
state= backup_state::ERROR;
return 0;
default:
m_buf_head= m_buf.data;
m_buf.data+= 4; // leave space for image and stram numbers
m_buf.size-= 4;
break;
}
result_t res;
res= m_drv.get_data(m_buf);
switch (res) {
case INIT_DONE:
//if( state == backup_state::INIT )
state= backup_state::WAITING;
case READY:
//if( state == backup_state::WAITING || state == backup_state::INIT )
if (res == READY)
state= backup_state::READY;
case OK:
if (m_buf.last)
{
mark_stream_closed(m_buf.stream_no);
if (all_streams_closed())
DBUG_PRINT("backup/data",(" all streams of %s closed",m_name));
else
DBUG_PRINT("backup/data",(" stream %d closed",m_buf.stream_no));
}
m_buf.data= m_buf_head;
m_buf_head= NULL;
if ( m_buf.size > 0 )
{
// TODO: use OStream interface for consistency
m_buf.size+= 4;
int2store(m_buf.data,m_drv_no+1);
int2store(m_buf.data+2,m_buf.stream_no);
mode= WRITING;
}
else
m_bw.drop_buf(m_buf);
break;
case WAIT:
break;
case ERROR:
state= backup_state::ERROR;
case NOT_NOW:
m_bw.drop_buf(m_buf);
default: break;
}
} // if (mode == READING)
if (mode == WRITING && state != backup_state::ERROR)
{
switch (m_bw.write_buf(m_buf)) {
case Block_writer::OK:
howmuch= m_buf.size;
DBUG_PRINT("backup/data",(" added %d bytes from %s to archive (drv_no=%d, stream_no=%d)",
howmuch, m_name, m_drv_no, m_buf.stream_no));
mode= READING;
break;
case Block_writer::ERROR:
state= backup_state::ERROR;
break;
default: break;
}
}
}
if (state != before_state)
DBUG_PRINT("backup/data",(" %s changes state %s->%s",
m_name,backup_state::name[before_state],
backup_state::name[state]));
return howmuch;
}
/**************************************************
* Implementation of Block_writer
**************************************************/
Block_writer::result_t
Block_writer::get_buf(Buffer &buf)
{
buf.size= buf_size;
buf.data= m_str.get_window(buf.size);
if (!buf.data)
return NO_RES;
return OK;
}
Block_writer::result_t
Block_writer::write_buf(const Buffer &buf)
{
stream_result::value res= m_str.write_window(buf.size);
m_str.end_chunk();
return res == stream_result::OK ? OK : ERROR;
}
Block_writer::result_t
Block_writer::drop_buf(Buffer &buf)
{
m_str.drop_window();
buf.data= NULL;
buf.size= 0;
return OK;
}
} // backup namespace
namespace backup {
// Read backup image data from a backup stream and forward it to restore drivers.
// This simple implementation handles only one restore driver.
bool restore_table_data(const Restore_info &info, IStream &s)
{
DBUG_ENTER("restore::restore_table_data");
enum { READING, SENDING, DONE, ERROR } state= READING;
Restore_driver* drv[32];
uint drv_count;
List_iterator<Restore_subimage> it(const_cast<Restore_info&>(info).images);
Restore_subimage *img;
for(drv_count=0; (img= it++) && drv_count < 32 ; ++drv_count)
{
DBUG_PRINT("restore",("Creating %s (image %d)",img->name(), img->no+1));
drv[drv_count]= img->driver();
DBUG_ASSERT(drv[drv_count]);
DBUG_ASSERT( OK == drv[drv_count]->begin() );
}
Buffer buf;
uint img_no;
uint repeats=0, errors= 0;
static const uint MAX_ERRORS= 3;
static const uint MAX_REPEATS= 7;
// main data reading loop
while ( state != DONE && state != ERROR )
{
switch (state) {
case READING:
switch (s >> buf) {
case stream_result::EOS:
state= DONE;
break;
case stream_result::OK:
state= SENDING;
break;
default:
state= ERROR;
break;
}
if (state != SENDING)
break;
DBUG_ASSERT(buf.data);
img_no= uint2korr(buf.data);
buf.stream_no= uint2korr(buf.data+2);
buf.data += 4;
DBUG_PRINT("restore",("Got %d bytes of subimage %d data (stream %d)",buf.size, img_no, buf.stream_no));
case SENDING:
if( img_no < 1 || !drv[img_no-1] )
{
DBUG_PRINT("restore",("Skipping data for subimage %d",img_no));
state= READING;
break;
}
switch( drv[img_no-1]->send_data(buf) ) {
case backup::OK:
switch (s.next_chunk()) {
case stream_result::OK:
state= READING;
repeats= 0;
break;
case stream_result::EOS:
state= DONE;
break;
default:
state= ERROR;
break;
}
break;
case backup::ERROR:
if( errors > MAX_ERRORS )
state= ERROR;
errors++;
break;
case backup::WAIT:
case backup::NOT_NOW:
default:
if( repeats > MAX_REPEATS )
{
DBUG_PRINT("restore",("Quiting after 7 failed send_data() attempts"));
state= ERROR;
}
repeats++;
break;
}
default:
break;
} // switch(state)
} // main reading loop
DBUG_PRINT("restore",("End of backup stream"));
if (state != DONE)
DBUG_PRINT("restore",("state is %d",state));
for(uint no=0; no < drv_count; ++no)
{
DBUG_PRINT("restore",("Shutting down driver %d",no));
drv[no]->end();
drv[no]->free();
}
DBUG_RETURN(state != ERROR);
}
} // backup namespace
--- New file ---
+++ sql/backup/map.h 07/03/20 17:38:11
#ifndef _UTIL_MAP_H
#define _UTIL_MAP_H
namespace util {
/*******************************************************
Map (associative array) datatype implementation.
*******************************************************/
struct key8;
template<class D, class K=key8>
class Map
{
public:
typedef typename D::Element El;
typedef typename K::Key Key;
static const size_t size= K::size;
Key add(const El &);
El &operator[](const Key &) const;
Key find(const El &) const;
bool occupied(const Key &) const;
size_t count() const { return m_count; };
Map(): m_count(0) {};
#ifdef MAP_DEBUG
void print();
#endif
~Map() { clear(); };
void clear();
protected:
// Use binary search tree for storing entries.
struct node {
El *el;
Key bigger,smaller;
node(): el(NULL), bigger(K::null), smaller(K::null) {};
void operator=(const El &e)
{
if( el ) delete el;
el= D::create(e);
bigger= K::null;
smaller= K::null;
};
} entries[size];
uint m_count;
Key find_el(const Key &, const El &, bool insert= FALSE);
Key find_free_loc() const;
void set(const Key&, const El&);
};
template<class D, class K>
bool Map<D,K>::occupied(const Key &k) const
{
return K::valid_key(k) && entries[k].el != NULL;
}
template<class D, class K>
typename Map<D,K>::El &
Map<D,K>::operator[](const Key &k) const
{
if( !occupied(k) )
return const_cast<El&>(D::null);
return *(entries[k].el);
}
template<class D, class K>
typename Map<D,K>::Key
Map<D,K>::add(const El &e)
{
Key k= D::hash(e);
return find_el(k,e,TRUE);
}
template<class D, class K>
typename Map<D,K>::Key
Map<D,K>::find(const El &e) const
{
Key k= D::hash(e);
return const_cast<Map<D,K>*>(this)->find_el(k,e);
}
// Assumption, k is valid.
template<class D, class K>
typename Map<D,K>::Key
Map<D,K>::find_el(const Key &k, const El &e, bool insert)
{
El *x= entries[k].el;
if( !x )
if( insert )
{
set(k,e);
return k;
}
else return K::null;
int res;
if( (res= D::cmp(e,*x)) == 0 )
return k;
Key &k1 = res>0 ? entries[k].bigger : entries[k].smaller;
if( K::valid_key(k1) )
return find_el(k1,e);
Key k2;
if( K::valid_key(k2= find_free_loc()) )
{
k1= k2;
set(k2,e);
};
return k2;
}
template<class D, class K>
typename Map<D,K>::Key
Map<D,K>::find_free_loc() const
{
if( m_count >= K::size )
return K::null;
for(uint k=0 ; k < size ; k++ )
if( entries[k].el == NULL )
return k;
return K::null;
}
// Assumption: k is valid
template<class D, class K>
void Map<D,K>::set(const Key &k, const El &e)
{
entries[k]= e;
m_count++;
}
template<class D, class K>
void Map<D,K>::clear()
{
for(uint i=0 ; i < size ; i++)
if( entries[i].el )
delete entries[i].el;
}
// 8 bit keys
struct key8
{
typedef key8 Key;
static const size_t size= (size_t)255;
static const unsigned int null= 0xFF;
operator int() const { return val; };
bool is_valid() const { return key8::valid_key(*this); };
static bool valid_key(const Key &k) { return k.val != 0xFF; };
key8(): val(0xFF) {};
key8(unsigned int x) { operator=(x); };
Key &operator=(unsigned int x);
private:
unsigned char val;
};
inline
key8 &key8::operator=(unsigned int x)
{
val= x & 0xFF;
for( int bits= sizeof(unsigned int) ; bits > 8 ; bits-= 8 )
{
x >>= 8;
val ^= x &0xFF;
};
return *this;
}
#ifdef MAP_DEBUG
template<class D, class K>
void Map<D,K>::print()
{
for(uint i=0 ; i < K::size ; i++ )
{
node &n= entries[i];
if( n.el )
{
printf("entry %02d (%d,%d): ", i, (int)n.bigger, (int)n.smaller );
D::print(*n.el);
printf("\n");
}
}
}
#endif
} // util namespace
#endif
--- New file ---
+++ sql/backup/meta_backup.cc 07/03/20 17:38:11
#include "../mysql_priv.h"
#include "tables.h"
#include "archive.h"
#include "backup_aux.h"
namespace backup {
// Serialization of backup image info
bool write_header(const Backup_info &info, OStream &str)
{
DBUG_ENTER("backup::write_header");
stream_result::value res= str.write2int(info.ver);
TEST_WR_RES(res);
List_iterator<Backup_subimage> it(const_cast<List<Backup_subimage>&>(info.images));
Backup_subimage *img;
// List of sub-images
uint ino;
for( ino=0 ; (img= it++) ; ino++ )
{
DBUG_PRINT("backup",(" adding %s image to catalog",img->name()));
img->write_description(str); // write description of sub-image format
}
str.end_chunk();
if( ino == 0 )
DBUG_RETURN(TRUE);
DBUG_PRINT("backup",(" ==="));
// write catalogs
it.rewind();
for( ino=0 ; (img= it++) ; ino++ )
{
str << img->tables; // write list of tables stored in the image
}
DBUG_PRINT("backup",("catalog written"));
DBUG_RETURN(TRUE);
}
bool read_header(IStream &str, Restore_info &info)
{
DBUG_ENTER("restore::read_header");
stream_result::value res= str.read2int(info.ver);
TEST_RD_RES(res);
Restore_subimage *img;
uint ino;
for (ino=0 ; (img= Restore_subimage::create_from_stream(ino,str)); ino++)
{
DBUG_PRINT("restore",(" got %s image",img->name()));
info.images.push_back(img);
}
info.img_count= ino;
res= str.next_chunk(); // move to next_chunk
if( ino == 0 || res != stream_result::OK )
DBUG_RETURN(res == stream_result::OK);
// read list of tables
List_iterator<Restore_subimage> it(info.images);
for( ino=0 ; (img= it++) ; ino++ )
{
res= (str >> img->tables); // read list of tables stored in the image
TEST_RD_RES(res);
info.table_count+= img->tables.count();
};
DBUG_PRINT("restore",("catalog read"));
DBUG_RETURN(TRUE);
}
// Write/read description of a sub-image. The exact format is determined by specific
// classes derived from Backup_subimage.
bool Backup_subimage::write_description(OStream &s)
{
// TODO: write description length here
s.write2int(ver());
s.writebyte(type());
return do_write_description(s);
}
// TODO: Handle different and unknown types (always skip correct amount of bytes).
Restore_subimage*
Restore_subimage::create_from_stream(uint no, IStream &s)
{
version_t ver;
//stream_result::value res= s.read2int(ver);
if (s.read2int(ver) != stream_result::OK)
return NULL;
byte t;
//res= s.read_byte(t);
if (s.readbyte(t) != stream_result::OK)
return NULL;
switch (image_type(t)) {
case DEFAULT_IMAGE:
return Default_restore_info::create_from_stream(no, ver,s);
case NATIVE_IMAGE:
return Native_restore_info::create_from_stream(no, ver,s);
default:
return NULL;
}
}
//////////////////////////////////////////////////////////////////////////////
// Functions for writing/reading table defs
namespace prototype {
stream_result::value operator<<(OStream&,TABLE&);
int create_table_from_str(THD*,IStream&,const Table_ref&);
} // prototype namespace
// Writing/reading table definitions
// This implementation uses temporary formad to be replaced by DDL statements later.
bool write_table_definitions(THD *thd, const Backup_info &info, OStream &s)
{
DBUG_ENTER("backup::write_table_definitions");
DBUG_ASSERT(thd);
List_iterator<Backup_subimage> it(const_cast< List<Backup_subimage>& >(info.images));
Backup_subimage *img;
while ( (img= it++) )
{
TABLE_LIST *tl= build_table_list(img->tables,TL_READ);
DBUG_PRINT("backup", ( "calling open_and_lock_tables" ));
if ( ::open_and_lock_tables(thd, tl) )
{
DBUG_PRINT("backup", ( "error!" ));
DBUG_RETURN(FALSE);
}
DBUG_PRINT("backup", ( "saving table defs" ));
stream_result::value res;
for ( TABLE_LIST *ptr= tl; ptr ; ptr= ptr->next_global )
{
res= prototype::operator<<(s,*(ptr->table));
TEST_WR_RES(res);
}
DBUG_PRINT("backup",("Table defs written, closing tables"));
::close_thread_tables(thd,FALSE,TRUE);
// TODO: deallocate tl memory
} // sub-image loop
DBUG_PRINT("backup",("All table definitions written"));
DBUG_RETURN(TRUE);
}
bool create_all_tables(THD *thd, const Restore_info &info, IStream &s)
{
DBUG_ENTER("restore::create_all_tables");
DBUG_ASSERT(thd);
int err;
List_iterator<Restore_subimage> it(const_cast< List<Restore_subimage>& >(info.images));
Restore_subimage *img;
while ( (img= it++) ) // loop over sub-images
{
uint tno= img->tables.count();
DBUG_PRINT("restore",("Creating %d tables forom %s image",tno,img->name()));
TABLE_LIST *tl= build_table_list(img->tables,TL_READ);
// remove tables which exist
DBUG_PRINT("restore",(" dropping tables"));
err= mysql_rm_table_part2_with_lock(thd, tl, TRUE, FALSE, TRUE); // = DROP TABLE IF EXISTS
DBUG_PRINT("restore",(" result= %d",err));
DBUG_ASSERT( err == 0 );
// create tables
// FIXME: correct order
for( int i= tno-1; i >= 0 ; --i )
{
err= prototype::create_table_from_str(thd,s,img->tables[i]);
DBUG_ASSERT( err == 0 );
}
// TODO: free tl memory
} // sub-image loop
DBUG_PRINT("restore",("All tables created"));
DBUG_RETURN(TRUE);
}
} // backup namespace
/*
Functions for writing/reading table definitions to/from backup stream.
This code should be eventually replaced by DDL based solution
*/
namespace backup {
namespace prototype {
/// Write description of a single column
stream_result::value operator<<(OStream &s,Field &f)
{
byte type= f.type();
uint length= 0;
uint decimals= 0;
// Setup length and decimals
// FIXME: legth and decimals for colums of type DECIMAL (look into SHOW CREATE command)
if( type == MYSQL_TYPE_DECIMAL || type == MYSQL_TYPE_NEWDECIMAL )
length= f.field_length - f.decimals(); // TODO: length of strings, other anomalies?
else
length= f.field_length; // FIXME: what about string fields (with wide chars)?
if( type == MYSQL_TYPE_DECIMAL || type == MYSQL_TYPE_NEWDECIMAL
|| type == MYSQL_TYPE_FLOAT || type == MYSQL_TYPE_DOUBLE )
decimals= f.decimals();
DBUG_PRINT("backup",(" saving field %d %s (%d/%d)",type,f.field_name,length,decimals));
stream_result::value res;
res= s.writestr(String(f.field_name,default_charset));
TEST_WR_RES(res);
res= s.writebyte(type);
TEST_WR_RES(res);
res= s.write4int(length);
TEST_WR_RES(res);
res= s.write2int(decimals);
TEST_WR_RES(res);
res= s.writenil(); // FIXME: write f.comment (LEX_STRING)
TEST_WR_RES(res);
return res;
}
/// Extension of ::create_field structure with storage for field
/// name and comment.
struct Field_info: public ::create_field
{
String name;
String comment;
bool init(THD *thd,
::enum_field_types type,
uint length,
uint decimals)
{
char len_buf[32];
char dec_buf[32];
LEX_STRING comment_lex= { const_cast<char*>(comment.ptr()),
comment.length() };
int10_to_str(length,len_buf,1 /* unsigned */);
int10_to_str(decimals,dec_buf,1 /* unsigned */);
//my_snprintf(len_buf,sizeof(len_buf),"%d",length);
//my_snprintf(dec_buf,sizeof(dec_buf),"%d",decimals);
return ::create_field::init(thd,
const_cast<char*>(name.ptr()),
type,
len_buf,
dec_buf,
0, // fld_type_modifiers ?
NULL, // fld_default_value (TBD)
NULL, // fld_on_update_value (ignore)
&comment_lex,
NULL, // fld_change (ignore)
NULL, // List<String>* fld_interval_list ?
NULL, //&charset,
0 ); // fld_geom_type ?
}
};
/// Read colum description
Field_info *read_field(THD *thd, IStream &s)
{
uint dec;
ulong len;
byte b;
Field_info *fld= new Field_info();
DBUG_ASSERT(fld);
stream_result::value res;
res= s.readstr(fld->name);
if( res == stream_result::EOC || res == stream_result::EOS )
{
delete fld;
return NULL;
}
TEST_RD_RES(res);
res= s.readbyte(b);
TEST_RD_RES(res);
::enum_field_types type= ::enum_field_types(b);
res= s.read4int(len);
res= s.read2int(dec);
res= s.readstr(fld->comment);
DBUG_ASSERT( ! fld->init(thd, type, len, dec) );
return fld;
}
/// Write table definition taken from open TABLE structure
stream_result::value operator<<(OStream &s,TABLE &t)
{
DBUG_PRINT("backup",(" write def of table %s",t.alias));
// write name of the storage engine
String se_name(::ha_resolve_storage_engine_name(t.s->db_type), default_charset);
s.writestr(se_name);
// s.write2int(t.s->fields); // write number of columns in table
// dump column definitions
DBUG_PRINT("backup",(" dumping table cols"));
for( int i=0; t.field[i] ; ++i )
s << *(t.field[i]);
DBUG_PRINT("backup",(" table def written"));
return s.end_chunk();
}
/// Create table using table defintion read from a stream
int create_table_from_str(THD *thd, IStream &s, const Table_ref &tbl)
{
DBUG_ASSERT(thd);
DBUG_PRINT("restore",("Creating table %s.%s from stream",
tbl.db().name().ptr(), tbl.name().ptr()));
stream_result::value res;
String se_name;
res= s.readstr(se_name);
TEST_RD_RES(res);
HA_CREATE_INFO info;
bzero( &info, sizeof(HA_CREATE_INFO) );
LEX_STRING name_lex= { const_cast<char*>(se_name.ptr()), se_name.length() };
info.db_type= ::ha_resolve_by_name(thd,&name_lex);
DBUG_ASSERT( info.db_type );
// Read field descriptions
List< ::create_field > fields;
Field_info *fld;
uint fno= 0;
while ( (fld= prototype::read_field(thd,s)) )
{
fno++;
fields.push_back(fld);
}
s.next_chunk();
DBUG_PRINT("restore",(" %d columns read",fno));
// Setup keys - not implemented
List< ::Key > keys;
// Create the table
int err;
DBUG_PRINT("restore",(" calling mysql_create_table"));
err= mysql_create_table(thd, tbl.db().name().ptr(), tbl.name().ptr(),
&info, fields, keys, FALSE, 0, FALSE);
if( err )
{
DBUG_PRINT("restore",(" result= %d",err));
DBUG_ASSERT(FALSE);
}
// TODO: free memory
return err;
}
} // prototype namespace
} // backup namespace
--- New file ---
+++ sql/backup/stream.cc 07/03/20 17:38:11
#include "../mysql_priv.h"
#include "stream.h"
/// Static storage for the backup image.
backup::prototype::Chunk_storage
backup::prototype::Chunk_storage::m_instance;
namespace backup {
// Return instances of IStream/OStream which use the in-memory storage.
struct stream_instances {
static IStream istream;
static OStream ostream;
};
IStream stream_instances::istream(prototype::Chunk_storage::m_instance);
OStream stream_instances::ostream(prototype::Chunk_storage::m_instance);
IStream &open_for_read()
{
stream_instances::istream.rewind();
return stream_instances::istream;
}
OStream &open_for_write()
{
stream_instances::ostream.rewind();
return stream_instances::ostream;
}
// Show data chunks in a backup stream;
void dump_stream(IStream &s)
{
stream_result::value res;
byte b;
DBUG_PRINT("stream",("=========="));
do {
uint chunk_size;
for( chunk_size=0; (res= s.readbyte(b)) == stream_result::OK ; ++chunk_size );
DBUG_PRINT("stream",(" chunk size= %d",chunk_size));
if( res == stream_result::EOC )
{
DBUG_PRINT("stream",("----------"));
res= s.next_chunk();
}
} while ( res == stream_result::OK );
if (res == stream_result::EOS)
DBUG_PRINT("stream",("=========="));
else
DBUG_PRINT("stream",("== ERROR: %d",res));
}
} // backup namespace
--- New file ---
+++ sql/backup/stream.h 07/03/20 17:38:11
#ifndef _BACKUP_STREAM_H_
#define _BACKUP_STREAM_H_
#include "backup_engine.h" // for Buffer definition
/***********************************************************
Generic Stream Intrface for serializing basic types
(ints, strings etc).
***********************************************************/
namespace util
{
typedef unsigned char byte;
struct stream_result
{
enum value { OK=0, NIL, ERROR };
};
/*
Stream window: an abstract object which can be used to access a stream
of bytes through a window in process address space. The methods give pointer to the
beginning of the window and its current size. There is a request for enlarging the
window. We can also move the window beginning to the right (decreasing window size).
If some bytes move outside the window, they can't be accesed any more.
================================================> (stream of bytes)
|--------------------|
^
|
window header
1. Extend window:
|--------------------............|
^
|
window header
2. Move window header (shrinks window):
.....|---------------|
^
|
window header
Interface
---------
typename Result;
byte* head() const; // pointer to start of the window
byte* end() const; // pointer to the end of the window
size_t length() const; // current length of the window (in bytes)
Result set_length(size_t size); // extend the window to have (at least) given length
Result move(off_t offset); // move window header
//void init();
//void done();
operator int(const Result&); // interpret the result: is it error or other
common situation (end of data).
Note: window can be used for reading or writting.
*/
class Stream_win
{
protected:
byte* m_head;
size_t m_length;
public:
Stream_win(byte *ptr, size_t len=0): m_head(ptr), m_length(len)
{}
byte* head()
{ return m_head; }
byte* end()
{ return m_head+m_length-1; }
size_t length()
{ return m_length; }
};
/**
Stream classes.
These classes provide interface for serializing basic data
(numbers, strings) using host independent format.
Interface is parametrized by a class implementing stream window.
Instance of the window class is used to read/write stream data.
*/
template<class SWin>
class IStream
{
SWin &m_win;
public:
typedef typename SWin::Result Result;
IStream(SWin &swin): m_win(swin)
{}
Result skip(size_t off);
Result read(byte *buf,size_t len);
Result readbyte(byte &x);
Result read2int(int &x);
Result read2int(uint &x);
Result read4int(long &x);
Result read4int(ulong &x);
Result readint(ulong &x);
Result readint(uint &x)
{ ulong y; Result res= readint(y); x= y; return res; };
Result readstr(String &s);
};
template<class SWin>
class OStream
{
SWin &m_win;
public:
typedef typename SWin::Result Result;
OStream(SWin &swin): m_win(swin)
{}
Result write(byte *buf,size_t len);
Result writebyte(const byte x);
Result write2int(const int x);
Result write4int(const ulong x);
Result writeint(const ulong x);
Result writestr(const String &s);
Result writestr(const char *s)
{ return writestr(String(s,table_alias_charset)); };
Result writenil();
};
template<class SW>
typename IStream<SW>::Result
IStream<SW>::readbyte(byte &x)
{
Result res;
if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
return res;
x= *m_win.head();
return m_win.move(1);
}
template<class SW>
typename OStream<SW>::Result
OStream<SW>::writebyte(const byte x)
{
Result res;
if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
return res;
(*m_win.head())= x;
return m_win.move(1);
}
//inline
template<class SW>
typename IStream<SW>::Result
IStream<SW>::read2int(uint &x)
{
Result res;
if( (res= m_win.set_length(2)) != Result(stream_result::OK) )
return res;
x= uint2korr(m_win.head());
return m_win.move(2);
}
//inline
template<class SW>
typename OStream<SW>::Result
OStream<SW>::write2int(const int x)
{
Result res;
if( x >= (1<<16) )
return Result(stream_result::ERROR);
if( (res= m_win.set_length(2)) != Result(stream_result::OK) )
return res;
int2store(m_win.head(),x);
return m_win.move(2);
}
//inline
template<class SW>
typename IStream<SW>::Result
IStream<SW>::read4int(ulong &x)
{
Result res;
if( (res= m_win.set_length(4)) != Result(stream_result::OK) )
return res;
x= uint4korr(m_win.head());
return m_win.move(4);
}
//inline
template<class SW>
typename OStream<SW>::Result
OStream<SW>::write4int(const ulong x)
{
Result res;
// if( x >= (1<<32) )
// return Result(stream_result::ERROR);
if( (res= m_win.set_length(4)) != Result(stream_result::OK) )
return res;
int2store(m_win.head(),x);
return m_win.move(4);
}
// write/read numnber using variable-length encoding
template<class SW>
typename IStream<SW>::Result
IStream<SW>::readint(ulong &x)
{
Result res;
if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
return res;
x= *m_win.head();
m_win.move(1);
switch( x ) {
case 251:
return Result(stream_result::NIL);
case 252:
uint y;
res= read2int(y);
x= y;
return res;
case 253:
return read4int(x);
default:
return Result(stream_result::OK);
};
}
template<class SW>
typename OStream<SW>::Result
OStream<SW>::writeint(const ulong x)
{
Result res;
if( (res= m_win.set_length(1)) != Result(stream_result::OK) )
return res;
if( x < 251 )
return writebyte(x);
if( x < (1<<16) )
{
res= writebyte(252);
return res == Result(stream_result::OK) ? write2int(x) : res;
};
res= writebyte(253);
return res == Result(stream_result::OK) ? write4int(x) : res;
}
// Write/read string using "length coded string" format
template<class SW>
typename IStream<SW>::Result
IStream<SW>::readstr(String &s)
{
Result res;
uint len;
if( (res= readint(len)) != Result(stream_result::OK) )
return res;
if( (res= m_win.set_length(len)) != Result(stream_result::OK) )
return res;
s.copy(reinterpret_cast<const char*>(m_win.head()),
len,
table_alias_charset);
return m_win.move(len);
}
template<class SW>
typename OStream<SW>::Result
OStream<SW>::writestr(const String &s)
{
Result res;
uint len= s.length();
if( (res= writeint(len)) != Result(stream_result::OK) )
return res;
if( (res= m_win.set_length(len)) != Result(stream_result::OK) )
return res;
memcpy(m_win.head(), s.ptr(), len);
return m_win.move(len);
}
template<class SW>
typename OStream<SW>::Result
OStream<SW>::writenil()
{
return writebyte(251);
}
} // util namespace
/************************************************************
Backup Stream Interface
The stream is organized as a sequence of chunks each of which
can have differnet length. When stream is read chunk boundraries
are detected. If this happens, next_chunk() member must be called
in order to access data in next chunk. When writting to a stream,
data is appended to the current chunk. End_chunk() member closes
the current chunk and starts a new one.
************************************************************/
namespace backup {
struct stream_result
{
enum value {
OK= util::stream_result::OK,
NIL= util::stream_result::NIL,
ERROR= util::stream_result::ERROR,
EOS, // end of stream
EOC // end of chunk
};
};
/**
Ad hoc, in-memory storage for byte chunks.
For simplicity no dynamic memory is allocated. Instead we have a static
instance with contignous memory buffer of fixed size to keep all the data.
Information about chunk boundraries is kept in a separate array.
*/
namespace prototype {
struct Chunk_storage
{
static const size_t max_size= 16*1024;
static const uint max_chunk= 100;
byte data_buf[max_size];
byte *chunk_end[max_chunk]; ///< chunk_end[i] points at the first byte after chunk #i.
uint no_chunks;
size_t size; ///< how much data is kept in data[]
void clear() ///< Remove all chunks
{
size= 0;
no_chunks= 0;
}
bool add_chunk()
{
if (no_chunks >= max_chunk )
return false;
no_chunks++;
if (no_chunks == 1 )
chunk_end[0]= data_buf;
else
chunk_end[no_chunks-1]= chunk_end[no_chunks-2];
return true;
}
/// Memory window for accessing chunk data.
/// @see util::Stream
class Window;
/// Specializations of Window class used for reading and writting chunks
class IWindow;
class OWindow;
friend class Window;
friend class IWindow;
friend class OWindow;
Chunk_storage()
{ clear(); }
static Chunk_storage m_instance;
};
class Chunk_storage::Window: public util::Stream_win
{
public:
typedef stream_result::value Result;
size_t bytes;
public:
virtual ~Window() {}
byte *get_window(size_t len)
{
if (m_length > 0) // a window is already allocated
return NULL;
Result res= set_length(len);
return res == stream_result::OK ? m_head : NULL;
}
void drop_window()
{
set_length(0);
}
protected:
Window(Chunk_storage &st):
bytes(0), Stream_win(st.data_buf), m_storage(st)
{};
virtual Result set_length(size_t len) =0;
Result move(off_t off);
Chunk_storage &m_storage;
};
inline
stream_result::value
Chunk_storage::Window::move(off_t off)
{
Result result= stream_result::OK;
if ( off > m_length )
result= set_length(off);
if ( result == stream_result::OK )
{
bytes+= off;
m_head+= off;
m_length-= off;
}
return result;
}
class Chunk_storage::IWindow: public Chunk_storage::Window
{
uint chunk; ///< chunk currently read
public:
Result get_chunk();
Result next_chunk();
void close() {};
void rewind();
protected:
IWindow(Chunk_storage &st): Window(st), chunk(0)
{}
Result set_length(size_t len);
friend class util::IStream<IWindow>;
};
class Chunk_storage::OWindow: public Chunk_storage::Window
{
public:
Result end_chunk();
void close();
void rewind();
Result write_window(size_t len)
{
Result res= move(len);
drop_window();
return res;
}
protected:
OWindow(Chunk_storage &st): Window(st)
{
if( st.no_chunks > 0 )
m_head= st.chunk_end[st.no_chunks-1];
m_length= 0;
}
Result set_length(size_t len);
friend class util::OStream<OWindow>;
};
inline
stream_result::value
Chunk_storage::IWindow::set_length(size_t len)
{
if ( len < m_length )
return stream_result::OK;
if ( m_storage.no_chunks == 0 )
return stream_result::EOS;
m_length= len;
if ( m_head+m_length > m_storage.chunk_end[chunk] ) // we hit chunk boundraty
{
m_length= m_storage.chunk_end[chunk] - m_head;
return chunk >= m_storage.no_chunks-1 ? stream_result::EOS : stream_result::EOC;
}
return stream_result::OK;
}
inline
stream_result::value
Chunk_storage::IWindow::get_chunk()
{
if ( m_storage.no_chunks == 0 )
return stream_result::EOS;
m_length= m_storage.chunk_end[chunk] - m_head;
return stream_result::OK;
}
inline
stream_result::value
Chunk_storage::IWindow::next_chunk()
{
bytes+= m_length;
if ( chunk+1 >= m_storage.no_chunks )
return stream_result::EOS;
m_head= m_storage.chunk_end[chunk++];
m_length= 0;
return stream_result::OK;
}
inline
void Chunk_storage::IWindow::rewind()
{
chunk= 0;
m_head= m_storage.data_buf;
m_length= 0;
bytes= 0;
}
// pre: one more chunk avaliable in the stream
inline
stream_result::value
Chunk_storage::OWindow::set_length(size_t len)
{
// if ( len < m_length )
// return stream_result::OK;
m_length= len;
byte *last_byte= m_storage.data_buf+max_size;
byte *data_end= m_head+m_length;
if ( data_end > last_byte ) // exceed stream capacity
{
m_length= last_byte - m_head;
return stream_result::EOS;
}
return stream_result::OK;
}
// pre: there is one more chunk avaliable in the stream
inline
stream_result::value
Chunk_storage::OWindow::end_chunk()
{
uint chunk= m_storage.no_chunks++;
m_head= m_storage.chunk_end[chunk]= m_head+m_length;
m_length= 0;
if ( chunk >= m_storage.max_chunk )
return stream_result::EOS;
else
return stream_result::OK;
}
inline
void Chunk_storage::OWindow::close()
{
if( m_storage.chunk_end[m_storage.no_chunks-1] < m_head )
end_chunk();
}
inline
void Chunk_storage::OWindow::rewind()
{
m_storage.clear();
m_head= m_storage.data_buf;
m_length= 0;
}
} // prototype namespace
typedef prototype::Chunk_storage::IWindow IWindow;
typedef prototype::Chunk_storage::OWindow OWindow;
} // backup namespace
template class util::IStream< backup::IWindow >;
template class util::OStream< backup::OWindow >;
namespace backup {
/****************************************************
Definitions of input and output backup streams
****************************************************/
class IStream:
public IWindow,
public util::IStream< IWindow >
{
public:
typedef IWindow Base1;
typedef util::IStream< IWindow > Base2;
stream_result::value operator >>(String &str)
{
return readstr(str);
}
stream_result::value operator >>(Buffer &buf)
{
stream_result::value res= get_chunk();
if (res == stream_result::OK)
{
buf.data= m_head;
buf.size= m_length;
}
return res;
}
operator bool()
{ return TRUE; }
private:
IStream(prototype::Chunk_storage &cs):
Base1(cs), Base2(static_cast<IWindow&>(*this))
{}
friend class stream_instances;
};
class OStream:
public OWindow,
public util::OStream< OWindow >
{
public:
typedef OWindow Base1;
typedef util::OStream< OWindow > Base2;
stream_result::value operator <<(const String &str)
{
return writestr(str);
}
operator bool()
{ return TRUE; }
private:
OStream(prototype::Chunk_storage &cs):
Base1(cs), Base2(static_cast<OWindow&>(*this))
{}
friend class stream_instances;
};
IStream &open_for_read();
OStream &open_for_write();
// Function for debugging backup stream implementation.
void dump_stream(IStream &);
} // backup namespace
#define TEST_STR_RES(H,R) \
do { \
if ( (R) != stream_result::OK ) \
DBUG_PRINT(H,("stream op result= %d",(R))); \
DBUG_ASSERT( (R) == stream_result::OK ); \
} while(0)
#define TEST_WR_RES(R) TEST_STR_RES("backup",(R))
#define TEST_RD_RES(R) TEST_STR_RES("restore",(R))
#endif /*BACKUP_STREAM_H_*/
--- New file ---
+++ sql/backup/string_pool.cc 07/03/20 17:38:11
#include "../mysql_priv.h"
#include "string_pool.h"
extern const String my_null_string;
namespace util {
String *StringDom::create(const Element &s)
{
String *ns= new (current_thd->mem_root) String();
ns->copy(s);
return ns;
}
String &StringDom::null= const_cast< String& >(::my_null_string);
} // util namespace
namespace backup {
// Serialization of StringPool
stream_result::value operator>>(IStream &str, StringPool &p)
{
stream_result::value res;
String buf;
uint skip, i=0;
p.clear();
do
{
if( i >= p.size ) break;
res= str.readstr(buf);
switch( res )
{
case stream_result::OK:
p.set(i++,buf);
break;
case stream_result::NIL:
str.readint(skip);
i += skip;
break;
default:
break;
}
} while( res == stream_result::OK );
if( res == stream_result::EOC )
res= str.next_chunk();
return res;
}
stream_result::value operator<<(OStream &str, const StringPool &p)
{
stream_result::value res;
String buf;
uint skip=0 , i=0;
// TODO: error checking
for(i=0 ; i < p.count() ; i++ )
{
if( p.entries[i].el != NULL )
{
if( skip > 0 )
str.writeint(skip);
skip= 0;
str.writestr(*p.entries[i].el);
continue;
};
if( skip == 0 )
str.writenil();
skip++;
};
if( skip > 0 )
str.writeint(skip);
return str.end_chunk();
}
} // backup namespace
--- New file ---
+++ sql/backup/string_pool.h 07/03/20 17:38:11
#ifndef _STRING_POOL_H
#define _STRING_POOL_H
#include "map.h"
#include "stream.h"
namespace util {
/************************************************************
StringPool class
String pool = map from key8 -> Strings
************************************************************/
struct StringDom
{
typedef String Element;
static Element &null;
static Element *create(const Element &s);
static int cmp(const Element &x, const Element &y)
{ return stringcmp(&x,&y); }
static unsigned int hash(const Element &x)
{ return 0; }
};
template class Map<StringDom>;
typedef Map<StringDom> StringPool;
} // util namespace
namespace backup {
// Add serialization to util::StringPool
class StringPool: public util::StringPool
{
friend stream_result::value operator>>(IStream &str, StringPool &p);
friend stream_result::value operator<<(OStream &str, const StringPool &p);
};
} // backup namespace
#endif
--- New file ---
+++ sql/backup/tables.cc 07/03/20 17:38:11
#include "../mysql_priv.h"
#include "tables.h"
namespace backup {
int Tables::add(const Table_ref &t)
{
StringPool::Key k= dbnames.add(t.db().name());
if( !k.is_valid() ) return -1;
return add(k,t.name());
}
int Tables::add(const StringPool::Key &k, const String &name)
{
node *n= new node(k,name);
if( !n ) return -1;
if( m_head == NULL )
{
m_count=1;
m_head= m_last= n;
}
else
{
m_count++;
m_last->next= n;
m_last= n;
};
return m_count-1;
}
Table_ref Tables::operator[](uint pos) const
{
DBUG_ASSERT(pos < m_count);
node *ptr;
for( ptr= m_head ; ptr && pos ; ptr= ptr->next )
pos--;
if( !ptr ) ptr= m_last;
// can crash if m_last == NULL
/*
DBUG_PRINT("backup",("Tables::operator[%d]: dbkey %d: %s.%s",
pos,(int)ptr->db,
dbnames[ptr->db].ptr(), ptr->name.ptr() ));
*/
return Table_ref(dbnames[ptr->db],ptr->name);
}
Tables::Tables(): m_head(NULL),m_last(NULL),m_count(0)
{}
Tables::~Tables()
{
for( node *ptr= m_head ; ptr ; )
{
node *n=ptr;
ptr= n->next;
delete n;
};
}
// Reading and writing list of tables from/to a stream.
stream_result::value operator<<(OStream &str, const Tables &tl)
{
stream_result::value res;
res= (str << tl.dbnames); // write the pool of dbnames
for( Tables::node *n= tl.m_head ; n ; n= n->next )
{
res= str.write2int(n->db);
res= str.writestr(n->name);
};
return str.end_chunk();
}
stream_result::value operator>>(IStream &str, Tables &tl)
{
stream_result::value res;
res= (str >> tl.dbnames); // read the dbnames pool
if( tl.dbnames.count() == 0 || res != stream_result::OK )
return res;
do
{
String name;
uint k,tno;
res= str.read2int(k);
res= str.readstr(name);
if( res == stream_result::OK )
{
tno= tl.add(k,name);
DBUG_PRINT("restore",(" got next table %s.%s (pos %d, dbkey %d)",
tl[tno].db().name().ptr(),
tl[tno].name().ptr(),tno,k));
}
}
while( res == stream_result::OK );
if( res == stream_result::EOC )
res= str.next_chunk();
return res;
}
// Build linked TABLE_LIST list from a list stored in Tables object
// FIXME: build list with the same order as in input
// Actually, should work fine with reversed list as long as we use the reversed
// list both in table writing and reading.
TABLE_LIST *build_table_list(const Table_list &tables, thr_lock_type lock)
{
TABLE_LIST *tl= NULL;
for( uint tno=0; tno < tables.count() ; tno++ )
{
TABLE_LIST *ptr= (TABLE_LIST*)sql_alloc(sizeof(TABLE_LIST));
DBUG_ASSERT(ptr);
bzero(ptr,sizeof(TABLE_LIST));
Table_ref tbl= tables[tno];
ptr->alias= ptr->table_name= const_cast<char*>(tbl.name().ptr());
ptr->db= const_cast<char*>(tbl.db().name().ptr());
ptr->lock_type= lock;
// and add it to the list
ptr->next_global= ptr->next_local=
ptr->next_name_resolution_table= tl;
tl= ptr;
tl->table= ptr->table;
}
return tl;
}
} // backup namespace
--- New file ---
+++ sql/backup/tables.h 07/03/20 17:38:11
#ifndef _BACKUP_TABLES_H
#define _BACKUP_TABLES_H
#include "api_types.h"
#include "string_pool.h"
/************************************************************
Tables class implementing Table_list interface.
************************************************************/
namespace backup {
class Tables: public Table_list
{
public:
Tables(); // Create empty list.
~Tables();
/// Add table to the list.
int add(const Table_ref &t);
int add(const StringPool::Key &db, const String &name);
/// Return reference to given list element. Elements are counted from 0.
Table_ref operator[](uint pos) const;
/// Return number of elements in the list.
uint count() const
{ return m_count; }
private:
// Internal implementation.
struct node {
StringPool::Key db;
String name;
node *next;
node(const StringPool::Key &k, const String &nm): db(k), next(NULL)
{ name.copy(nm); }
};
node *m_head,*m_last;
uint m_count;
StringPool dbnames;
friend stream_result::value operator<<(OStream &str, const Tables &tl);
friend stream_result::value operator>>(IStream &str, Tables &tl);
};
TABLE_LIST *build_table_list(const Table_list&, thr_lock_type);
// Table_ref implementation which gets table identity from a filled record
// of I_S.TABLES table.
// field positions must be synchronized with defs in sql_show.cc
class Table: public Table_ref
{
String m_db_name;
String m_name;
const ::handlerton *m_hton;
public:
Table(TABLE *t): Table_ref(m_db_name, m_name)
{
String engine_name;
t->field[1]->val_str(&m_db_name);
t->field[2]->val_str(&m_name);
t->field[4]->val_str(&engine_name);
LEX_STRING name_lex;
name_lex.str= const_cast<char*>(engine_name.ptr());
name_lex.length= engine_name.length();
m_hton= ::ha_resolve_by_name(::current_thd,&name_lex);
DBUG_ASSERT(m_hton);
}
const ::handlerton *hton() const
{ return m_hton; }
operator bool() const
{ return TRUE; }
};
} // backup namespace
/*
Notes
Check type: Table_ident (sql_parse.cc)
*/
#endif
--- 1.7/sql/backup/backup_private.h 2007-03-20 17:38:23 +01:00
+++ 1.9/sql/backup/backup_aux.h 2007-03-20 17:38:23 +01:00
@@ -1,452 +1,17 @@
-#ifndef BACKUP_INT_H
-#define BACKUP_INT_H
+#ifndef _BACKUP_AUX_H
+#define _BACKUP_AUX_H
//#define USE_DEFAULT_BACKUP
-#include "backup_util.h"
-#include "backup_stream.h"
-#include "default_driver.h"
-
-#define TEST_STR_RES(H,R) \
- do { \
- if ( (R) != stream_result::OK ) \
- DBUG_PRINT(H,("stream op result= %d",(R))); \
- DBUG_ASSERT( (R) == stream_result::OK ); \
- } while(0)
-#define TEST_WR_RES(R) TEST_STR_RES("backup",(R))
-#define TEST_RD_RES(R) TEST_STR_RES("restore",(R))
-
-TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list); // defined in sql_show.cc
-
-
-/************************************************************
- Contents:
-
- - Auxiliary classes.
-
- - Classes for describing sub-images for backup/restore purposes.
-
- - Tables class implementing Table_list interface.
-
- ************************************************************/
-
-/************************************************************
-
- Auxiliary Declarations
-
- ************************************************************/
-
-namespace backup
-{
-
-/* This is probably obsolete
-struct Buf: public Buffer
-{
- operator bool() const;
- {
- return data != NULL;
- }
-
- Buf(); ///< Create uninitialized buffer with no memory allocated.
- Buf(uint size); ///< Create buffer with allocated memory of given size
-
- ~Buf();
- {
- if(data) my_free((gptr)data,MYF(0));
- }
-
-};
-*/
-
-// Add serialization to util::StringPool
-
-class StringPool: public util::StringPool
+inline
+bool operator==(const String &s1, const String &s2)
{
- friend stream_result::value operator>>(IStream &str, StringPool &p);
- friend stream_result::value operator<<(OStream &str, const StringPool &p);
-};
-
-// Trivial engine with no backup capabilities (for testing)
-
-namespace prototype {
-
-class Trivial_engine: public Engine
-{
- public:
-
- const version_t version() const
- { return 1; }
-
- result_t get_backup( const Table_list &tables, Backup_driver* &drv )
- { return ERROR; }
-
- result_t get_restore( version_t version,
- const Table_list &tables, Restore_driver* &drv )
- { return ERROR; }
-
- static Trivial_engine instance;
-};
-
-result_t get_trivial_engine(::handlerton*, Engine* &eng);
-
-} // prototype namespace
-
-// Functions for writing/reading table defs
-
-namespace prototype {
-
-stream_result::value operator<<(OStream&,TABLE&);
-int create_table_from_str(THD*,IStream&,const Table_ref&);
-
-} // prototype namespace
-
-} // backup namespace
-
-/************************************************************
-
- Tables class implementing Table_list interface.
-
- ************************************************************/
+ return stringcmp(&s1,&s2) == 0;
+}
namespace backup {
-TABLE_LIST *build_table_list(const Table_list&, thr_lock_type);
-
-class Tables: public Table_list
-{
- public:
-
- Tables(); // Create empty list.
- ~Tables();
-
- /// Add table to the list.
- int add(const Table_ref &t);
- int add(const StringPool::Key &db, const String &name);
-
- /// Return reference to given list element. Elements are counted from 0.
- Table_ref operator[](uint pos) const;
-
- /// Return number of elements in the list.
- uint count() const
- { return m_count; }
-
- private:
-
- // Internal implementation.
-
- struct node {
- StringPool::Key db;
- String name;
- node *next;
-
- node(const StringPool::Key &k, const String &nm): db(k), next(NULL)
- { name.copy(nm); }
- };
-
- node *m_head,*m_last;
- uint m_count;
-
- StringPool dbnames;
-
- friend stream_result::value operator<<(OStream &str, const Tables &tl);
- friend stream_result::value operator>>(IStream &str, Tables &tl);
-};
-
-} // backup namespace
-
-/************************************************************
-
- Classes for descsribing backup image
-
- ************************************************************/
-
-namespace backup {
-
-// Data common to Backup_subimage and Restore_subimage
-
-struct Subimage_info
-{
- enum image_type {NATIVE_IMAGE, DEFAULT_IMAGE};
-
- uint no;
- Tables tables;
-
- Subimage_info(uint no): no(no)
- {}
-
- virtual version_t ver() const =0;
- virtual image_type type() const =0;
-
- virtual ~Subimage_info()
- {}
-
- virtual const char* name() const
- { return "<Unknown Driver>"; }
-};
-
-class Backup_subimage: public Subimage_info
-{
- protected:
-
- Backup_driver *m_drv;
-
- public:
-
- size_t init_size;
-
- Backup_subimage(uint no):
- Subimage_info(no),
- m_drv(NULL), init_size(Driver::UNKNOWN_SIZE)
- {}
-
- virtual bool add(const Table_ref &tbl, const ::handlerton *hton)
- { return FALSE; }
-
- result_t create_driver()
- {
- result_t res= do_create_driver();
- DBUG_ASSERT(res == OK);
- DBUG_ASSERT(m_drv);
- init_size= m_drv->init_size();
-
- return res;
- }
-
- // TODO: change driver method to return pointer (to signal problems).
- Backup_driver& driver()
- {
- if(!m_drv)
- create_driver();
-
- return *m_drv;
- }
-
- bool write_description(OStream &s);
-
- protected:
-
- virtual bool do_write_description(OStream&) =0;
- virtual result_t do_create_driver() =0;
-
-};
-
-class Restore_subimage: public Subimage_info
-{
- protected:
-
- Restore_driver *m_drv;
-
- public:
-
- version_t m_ver;
-
- Restore_subimage(uint no, version_t ver):
- Subimage_info(no), m_drv(NULL), m_ver(ver)
- {}
-
- version_t ver() const
- { return m_ver; }
-
- result_t create_driver()
- {
- result_t res= do_create_driver();
- DBUG_ASSERT(res == OK);
- DBUG_ASSERT(m_drv);
- return res;
- }
-
- Restore_driver* driver()
- {
- if(!m_drv)
- create_driver();
-
- return m_drv;
- }
-
- static Restore_subimage* create_from_stream(uint,IStream&);
-
- protected:
-
- virtual result_t do_create_driver() =0;
-
-};
-
-
-// Info about (contents of) a global backup image.
-
-struct Image_info
-{
- version_t ver;
- uint img_count; // number of subimages
- uint table_count; // total number of tables
-
-
- Image_info(version_t v): ver(v), img_count(0), table_count(0)
- {}
-};
-
-struct Backup_info: public Image_info
-{
- List<Backup_subimage> images;
- Backup_subimage *default_image;
-
- Backup_info(): Image_info(7), default_image(NULL)
- {}
-
- // Add table stored in a given storage engine
- bool add(const Table_ref &tbl, const ::handlerton *hton);
-};
-
-struct Restore_info: public Image_info
-{
- List<Restore_subimage> images;
-
- Restore_info(): Image_info(7)
- {}
-};
-
-/************************************************************
-
- Definitions of subimage types used
-
- ************************************************************/
-// Defaul backup image: using default (blocking) backup/restore algorithm.
-
-
-class Default_backup_info: public Backup_subimage
-{
- public:
-
- Default_backup_info(uint no): Backup_subimage(no)
- {}
-
- version_t ver() const
- { return 1; }
-
- image_type type() const
- { return DEFAULT_IMAGE; }
-
- const char* name() const
- { return "Default driver"; }
-
- result_t do_create_driver()
- {
- m_drv= new default_backup::Backup(tables, ::current_thd);
-
- return m_drv? OK : ERROR;
- }
-
- bool add(const Table_ref&, const ::handlerton*);
- bool do_write_description(OStream&);
-};
-
-class Default_restore_info: public Restore_subimage
-{
- public:
-
- Default_restore_info(uint no, version_t ver): Restore_subimage(no, ver)
- {}
-
- image_type type() const
- { return DEFAULT_IMAGE; }
-
- const char* name() const
- { return "Default driver"; }
-
- result_t do_create_driver()
- {
- m_drv= new default_backup::Restore(tables, ::current_thd);
-
- return m_drv? OK : ERROR;
- }
-
- static Default_restore_info* create_from_stream(uint, version_t, IStream&);
-};
-
-// Native backup image: created by backup-capable storage engine
-
-class Native_backup_info: public Backup_subimage
-{
- const ::handlerton *m_hton;
- Engine *m_be;
-
- char m_name[256];
-
- public:
-
- Native_backup_info(uint no, const ::handlerton *hton):
- Backup_subimage(no), m_hton(hton)
- {
- DBUG_ASSERT(hton);
- DBUG_ASSERT(hton->get_backup_engine);
- hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
- DBUG_ASSERT(m_be);
-
- my_snprintf(m_name,sizeof(m_name),"ARCHIVE's driver");
- }
-
- version_t ver() const
- { return m_be->version(); }
-
- image_type type() const
- { return NATIVE_IMAGE; }
-
- result_t do_create_driver()
- {
- result_t res= OK;
-
- DBUG_ASSERT(m_be);
-
- res= m_be->get_backup(tables,m_drv);
-
- DBUG_ASSERT(res == OK);
- DBUG_ASSERT(m_drv);
-
- return res;
- }
-
- const char* name() const
- { return m_name; }
-
- bool add(const Table_ref&, const ::handlerton*);
- bool do_write_description(OStream&);
-};
-
-class Native_restore_info: public Restore_subimage
-{
- const ::handlerton *m_hton;
- Engine *m_be;
-
- public:
-
- Native_restore_info(uint no, const ::handlerton *hton, version_t ver):
- Restore_subimage(no,ver), m_hton(hton)
- {
- DBUG_ASSERT(hton);
- DBUG_ASSERT(hton->get_backup_engine);
- hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
- DBUG_ASSERT(m_be);
- }
-
- image_type type() const
- { return NATIVE_IMAGE; }
-
- result_t do_create_driver()
- {
- result_t res= OK;
-
- DBUG_ASSERT(m_be);
-
- res= m_be->get_restore(m_ver,tables,m_drv);
-
- DBUG_ASSERT(res == OK);
- DBUG_ASSERT(m_drv);
-
- return res;
- }
-
- static Native_restore_info* create_from_stream(uint, version_t, IStream&);
-};
+extern CHARSET_INFO *default_charset;
} // backup namespace
| Thread |
|---|
| • bk commit into 5.1 tree (rafal:1.2486) | rsomla | 20 Mar |