List:Commits« Previous MessageNext Message »
From:rsomla Date:February 5 2007 5:09pm
Subject:bk commit into 5.1 tree (rafal:1.2357)
View as plain text  
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-02-05 17:09:31+01:00, rafal@quant.(none) +17 -0
  This is an upgrade of the backup prototype to use current version of backup protocol.
Apart from 
  that:
  
  - backup kernel code is refactored into several files,
  - backup stream interface is defined and used for writing/reading backup image,
  - a format for backup image header is implemented.

  sql/Makefile.am@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +1 -1
    Backup implementation split among several files now. (In the future I (Rafal) 
    plan to move them to a separate directory),

  sql/backup.cc@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +452 -859
    Many parts moved to other backup_* files. Now this file contains: 
    - implementation of do_backup() and do_restore()
    - code for selecting backup engines used for storing tables,
    - code for writing/reading backup image header and table definitions. 

  sql/backup.h@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +468 -159
    Cleanup: this file contains only interface published to backup engine 
    implementors, all else is moved to backup_private.h and other local header files.

  sql/backup0.h@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +10 -1
    Minor fixes and adding return values of the updated protocol.

  sql/backup_alg.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +512 -0
    Implementation of backup algorithm: collecting data from backup drivers and writing to
backup 
    stream.

  sql/backup_alg.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/backup_private.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +299 -0
    Private definitions for the backup kernel

  sql/backup_private.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/backup_prototype.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +347 -0
    Code used in the prototype but which will probably be raplaced by something else in
the final 
    release.

  sql/backup_prototype.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/backup_stream.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +392 -0
    Definition of the backup stream type. Currently using an in-memory storage.

  sql/backup_stream.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/backup_util.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +144 -0
    Utility code. Now mainly implementation of Table_list class used inside backup kernel.

  sql/backup_util.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/backup_util.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +625 -0
    Definitions of types used in backup kernel but not related to backup as such. It
contains
    - Stream template providing serialization code for basic types (numbers and strings).
    - Map template implementing associative array.
    

  sql/backup_util.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/handler.h@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +1 -1
    Method name change

  sql/restore_alg.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +135 -0
    Implementation of restore algorithm (reading data from backup stream and sending them
to 
    restore drivers).

  sql/restore_alg.cc@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +0 -0

  sql/sql_parse.cc@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +1 -1
    Fixed compiler warning.

  storage/archive/abackup.cc@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +140 -111
    Upgrade to the current backup interface version.

  storage/archive/ha_archive.cc@stripped, 2007-02-05 17:09:27+01:00, rafal@quant.(none) +18
-21
    Move file extension defs to the header file (so that they can be used in abackup.cc)

  storage/archive/ha_archive.h@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +6 -0
    Move file extension defs to the header file (so that they can be used in abackup.cc)

  unittest/mysys/Makefile.am@stripped, 2007-02-05 17:09:28+01:00, rafal@quant.(none) +9 -2
    Removing string-t from build

# 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.159/sql/Makefile.am	2007-02-05 17:09:38 +01:00
+++ 1.160/sql/Makefile.am	2007-02-05 17:09:38 +01:00
@@ -106,7 +106,7 @@
                         event_queue.cc event_db_repository.cc events.cc \
 			sql_plugin.cc sql_binlog.cc \
 			sql_builtin.cc sql_tablespace.cc partition_info.cc \
-			backup.cc
+			backup.cc backup_util.cc backup_prototype.cc backup_alg.cc restore_alg.cc
 
 
 gen_lex_hash_SOURCES =	gen_lex_hash.cc

--- 1.243/sql/handler.h	2007-02-05 17:09:38 +01:00
+++ 1.244/sql/handler.h	2007-02-05 17:09:38 +01:00
@@ -702,7 +702,7 @@
      Backup API hook
    */
    
-   Backup_result_t (*backup_engine)(handlerton *hton, Backup_engine* &be);
+   Backup_result_t (*get_backup_engine)(handlerton *hton, Backup_engine* &be);
 };
 
 

--- 1.582/sql/sql_parse.cc	2007-02-05 17:09:38 +01:00
+++ 1.583/sql/sql_parse.cc	2007-02-05 17:09:38 +01:00
@@ -2745,7 +2745,7 @@
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
 	check_global_access(thd, FILE_ACL))
-      goto error; /* purecov: inspected */
+      goto error; */ /* purecov: inspected */
 /*    thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_backup_table(thd, first_table);
     select_lex->table_list.first= (byte*) first_table;

--- 1.106/storage/archive/ha_archive.cc	2007-02-05 17:09:38 +01:00
+++ 1.107/storage/archive/ha_archive.cc	2007-02-05 17:09:38 +01:00
@@ -121,10 +121,6 @@
 pthread_mutex_t archive_mutex;
 static HASH archive_open_tables;
 
-/* The file extension */
-#define ARZ ".ARZ"               // The data file
-#define ARN ".ARN"               // Files used during an optimize call
-#define ARM ".ARM"               // Meta file
 /*
   uchar + uchar + ulonglong + ulonglong + ulonglong + ulonglong + FN_REFLEN 
   + uchar
@@ -188,8 +184,8 @@
   archive_hton->panic=archive_db_end;
   archive_hton->flags=HTON_NO_FLAGS;
   
-  archive_backup::init();
-  archive_hton->backup_engine= archive_backup_engine;
+//  archive_backup::init();
+  archive_hton->get_backup_engine= archive_backup_engine;
   
   if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST))
     goto error;
@@ -220,7 +216,7 @@
 
 int archive_db_done(void *p)
 {
-  archive_backup::done();
+//  archive_backup::done();
     
   if (archive_inited)
   {
@@ -494,27 +490,28 @@
   int rc= 0;
   DBUG_ENTER("ha_archive::flush");
   
-  if(lock) pthread_mutex_lock(&archive_mutex);
+  if (lock) 
+    pthread_mutex_lock(&archive_mutex);
+  
+  if (share->archive_write_open)
+  {
+    DBUG_PRINT("archive", ("flushing table data"));
+    if (azflush(&(share->archive_write), Z_FULL_FLUSH)) // Z_SYNC_FLUSH or
Z_FULL_FLUSH
+      rc= 1;
+    share->forced_flushes++;
+  }
   
-  share->forced_flushes++;
-
   (void)write_meta_file(share->meta_file, share->rows_recorded,
                         share->auto_increment_value, 
                         share->forced_flushes, 
                         share->real_path, 
-                        share->crashed ? TRUE :FALSE);
-  
-  if( share->archive_write_open )
-  {
-    DBUG_PRINT("archive",("flushing data stream"));
-    if (azflush(&(share->archive_write),Z_FULL_FLUSH))
-      rc= 1;
-  };
+                        share->dirty);
   
-  if (my_close(share->meta_file, MYF(0)))
-    rc= 1;
+  //if (my_close(share->meta_file, MYF(0)))
+  //  rc= 1;
     
-  if(lock) pthread_mutex_unlock(&archive_mutex);
+  if (lock) 
+    pthread_mutex_unlock(&archive_mutex);
 
   DBUG_RETURN(rc);
 }

--- 1.53/storage/archive/ha_archive.h	2007-02-05 17:09:38 +01:00
+++ 1.54/storage/archive/ha_archive.h	2007-02-05 17:09:38 +01:00
@@ -20,7 +20,13 @@
 
 #include <zlib.h>
 #include "azlib.h"
+
 #include "backup.h"
+
+/* The file extension */
+#define ARZ ".ARZ"               // The data file
+#define ARN ".ARN"               // Files used during an optimize call
+#define ARM ".ARM"               // Meta file
 
 
 class ha_archive;

--- 1.6/unittest/mysys/Makefile.am	2007-02-05 17:09:38 +01:00
+++ 1.7/unittest/mysys/Makefile.am	2007-02-05 17:09:38 +01:00
@@ -1,11 +1,18 @@
 
 AM_CPPFLAGS      = @ZLIB_INCLUDES@ -I$(top_builddir)/include 
-AM_CPPFLAGS     += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap
+AM_CPPFLAGS     += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap
-I$(top_srcdir)/sql
 
 LDADD 		= $(top_builddir)/unittest/mytap/libmytap.a \
 		  $(top_builddir)/mysys/libmysys.a \
 		  $(top_builddir)/dbug/libdbug.a \
 		  $(top_builddir)/strings/libmystrings.a
 
-noinst_PROGRAMS  = bitmap-t base64-t my_atomic-t
+noinst_PROGRAMS  = bitmap-t base64-t my_atomic-t # string-t
 
+#string_t_SOURCES = string-t.cc 
+#string_t_LDADD = \
+#    $(top_builddir)/libmysqld/libmysqld.a \
+#		@mysql_plugin_libs@ 
+#		$(top_builddir)/vio/libvio.a \
+#		$(yassl_inc_libs)
+   
\ No newline at end of file

--- 1.4/sql/backup.cc	2007-02-05 17:09:38 +01:00
+++ 1.5/sql/backup.cc	2007-02-05 17:09:38 +01:00
@@ -1,1033 +1,626 @@
 #include "mysql_priv.h"
-#include "sql_show.h"
 
 #include "backup.h"
+#include "backup_private.h"
 
-TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list); // defined in sql_show.cc
+//#define TEST  yes
 
 namespace backup {
-  
-/* 
-  Data describing single table column. 
-  
-  Variable length values (field name and comment) are stored in a memory pool pointed 
-  by pool. 
+    
+CHARSET_INFO *default_charset= table_alias_charset;
 
-  This structure can be initialized from a Field structure (used to describe table
-  columns inside TABLE structures).
-*/
+/*************************************************
+ * 
+ *                 BACKUP 
+ * 
+ *************************************************/
+    
 
-struct Field_data
-{
-  enum_field_types  type;
-  uint              length;
-  uint              decimals;
+#define DBUG_ERROR(E) { my_error(ER_NO,MYF(0),"ala","ala"); \
+                         DBUG_RETURN(1); }
 
-  Field_data():pool(NULL) {}
-/*
-  Field_data(const Field_data &fd):
-    type(fd.type), length(fd.length), decimals(fd.decimals), pool(fd.pool)
-  { 
-    const_cast<Field_data&>(fd).pool= NULL; 
-  };
-  
-  ~Field_data()
-  {
-    if( pool ) 
-      my_free(pool,MYF(0));
-  };
-*/
-  Field_data& operator=(const Field &f);
-  void free()
-  {
-    if( pool ) 
-      my_free(pool,MYF(0));
-  }
 
-  private:
+//  Returns tmp table containing records from a given I_S table
+TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st);
 
-    byte*   pool;
+bool collect_tables_to_backup(THD*,Backup_info&);
+bool write_header(const Backup_info&,OStream&);
+bool write_table_definitions(THD*,const Backup_info&,OStream&);
+bool write_table_data(const Backup_info&,OStream&);
 
-    friend class Field_create;  // this class, defined below, is used to transform the 
-                                // column description to the format accepted by table
-                                // creating functions
-};  
+/*
+  Implements BACKUP command -- called from sql_parse.cc  
+*/
 
-Field_data &Field_data::operator=(const Field &f)
+int do_backup(THD *thd)
 {
+  DBUG_ENTER("backup::do_backup");
   
-  type=     f.type();
-  length=   0;   
-  decimals= 0;
+  bool stream_opened= FALSE;  
+
+  // Open backup stream...
+
+  OStream &backup_stream= open_for_write();
+
+  if(!(bool)backup_stream)
+    DBUG_RETURN(backup::ERROR);
+
+  stream_opened= TRUE;
+
+  Backup_info  info;
   
-  // FIXME: legth and decimals for colums of type DECIMAL (look into SHOW CREATE command)
+  if(!collect_tables_to_backup(thd,info))
+    goto error;
 
-  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)?
+  DBUG_PRINT("backup",("Writing image header"));
+  
+  write_header(info,backup_stream);
+  
+  DBUG_PRINT("backup",("Writing table definitons"));
 
-  if(    type == MYSQL_TYPE_DECIMAL || type == MYSQL_TYPE_NEWDECIMAL
-      || type == MYSQL_TYPE_FLOAT || type == MYSQL_TYPE_DOUBLE )
-    decimals= f.decimals();
+  if(!write_table_definitions(thd,info,backup_stream))
+    goto error;
     
-  char  *ptr = pool;
+
+  DBUG_PRINT("backup",("Writing backup images"));
+
+  if(!write_table_data(info,backup_stream))
+    goto error;
   
-  if( pool ) my_free(ptr,MYF(0));
+  DBUG_PRINT("backup",("Backup image written"));
+
+  goto end;
   
-  pool= ptr= my_malloc( strlen(f.field_name)+f.comment.length+2, MYF(0) );
+ error:
 
-  strncpy(ptr,f.field_name,strlen(f.field_name)+1);
-  strncpy(ptr+strlen(ptr)+1,f.comment.str,f.comment.length+1);
+  // FIXME: report errors to the client
  
-  // DBUG_PRINT("backup",(" saving field %d %s (%d/%d)",type,ptr,length,decimals));
-   
-  return *this;
-};
+ end:
 
-      
-/*
- * Structure for keeping per table metadata
- */      
-     
-struct Table_data
-{
-  char        name[32];     // table name
-  unsigned    fields_no;    // number of columns
-  Field_data  fields[100];  // column descriptions
-
-  Table_data():fields_no(0) {};
-  Table_data(const TABLE &tbl); // read data from open table structure
-  void free();
-};
+  if( stream_opened )
+    backup_stream.close();
 
-Table_data::Table_data(const TABLE &tbl)
-{
-  DBUG_PRINT("backup", ("saving fields of table %s", tbl.s->table_name.str)); 
-  
-  strncpy(name,tbl.s->table_name.str,32);
-  
-  fields_no= 0;
-    
-  for( fields_no=0 ; tbl.field[fields_no] && (fields_no < 100) ; fields_no++ )

-  {
-    //DBUG_PRINT("backup", ( "  adding field %d: %s", i, t->field[i]->field_name )
);   
-    fields[fields_no] = *tbl.field[fields_no];
-  }        
-}
+  send_ok(thd);
+  DBUG_RETURN(0);
 
-void Table_data::free()
-{
-  while( fields_no > 0 )
-    fields[--fields_no].free();
 }
 
 
-class Backup_stream 
+// 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
+// FIXME: use more direct access to a record (and its fields)
+
+struct Table: public Table_ref
 {
-  public:    
+  struct Db: public Db_ref
+  { 
+    String m_name;
+    const String &name() const { return m_name; };
+    Db(TABLE *t) { t->field[1]->val_str(&m_name); };
+  } m_db;
+  
+  String         m_name;
+  const ::handlerton   *m_hton;
 
-    static const uint DATA_LEN= 16*1024;
+  const String &name() const { return m_name; };
+  const Db_ref &db() const { return m_db; };
+  
+  Table(TABLE *t): m_db(t) 
+  { 
+    String engine_name;
+    t->field[2]->val_str(&m_name);
+    t->field[4]->val_str(&engine_name);
     
-    Backup_stream(byte buf[DATA_LEN]):stream(0),data(buf),pos(0) {};
-      
-    Backup_stream &operator[](uint no) { stream= no; return *this; };
+    LEX_STRING name_lex;
     
-    Backup_stream &operator<<(const Buffer &buf); 
-    Backup_stream &operator>>(Buffer &buf); 
-
-    void rewind() { pos= 0; };
-    void close() 
-    {
-      *reinterpret_cast<uint*>(data+pos)= 0;
-    };
-
-    uint size() const 
-    { return pos; };
-
-  private:
-        
-   uint stream;
-   byte *data;
-   uint pos;
-   
-   struct header 
-   {
-    uint size;
-    uint stream;
+    name_lex.str= const_cast<char*>(engine_name.ptr());
+    name_lex.length= engine_name.length();
     
-    header():size(0),stream(0) {};
-    header(const Buffer &buf):size(buf.size),stream(buf.stream_no) {};
-   };
+    m_hton= ::ha_resolve_by_name(::current_thd,&name_lex);
+    DBUG_ASSERT(m_hton);
     
-};
+    // FIXME: temporary debug hack -- add backup engine per force
+    // FIXME: the backup engine of ARCHIVE doesn't work
+    
+    //const_cast< ::handlerton* >(m_hton)->get_backup_engine= 
+    //  (prototype::get_trivial_engine);
+  };    
   
-struct Backup_image
-{
-  version_t     version;
-  Table_data    meta[10];
-  unsigned      howmuch;  // how many entries are saved in meta
-  Backup_stream str;      // stream for table data
-  
-  Backup_image();
-  ~Backup_image();
-  void add(const Table_data &td);
-};
-
-static Backup_image backup_image;
+  const ::handlerton *hton() const
+  { return m_hton; }
   
-/*************************************************
- * 
- *                 BACKUP 
- * 
- *************************************************/
-    
-/*
-  Returns tmp table containing records from a given I_S table
-*/
-TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st);
-
-// variant using schema table id
-inline
-TABLE* get_schema_table(THD *thd, enum enum_schema_tables which)
-{
-  if( which < SCH_END )
-    return get_schema_table(thd, ::get_schema_table(which));
-  else
-    return NULL;
-}
+  operator bool() const
+  { return TRUE; }
+};
 
-/*
- * Read backup image from backup driver and send it to the stream
- */ 
-int get_se_images(const Table_map&, Backup_stream&);
 
-#define DBUG_ERROR(E) { my_error(ER_NO,MYF(0),"ala","ala"); \
-                         DBUG_RETURN(1); }
+// 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).    
 
-        
-/*
-  Implements BACKUP command -- called from sql_parse.cc  
-
-  Currently we want to backup all tables stored inside the ARCHIVE engine.
-*/
-
-int do_backup(THD *thd)
+bool collect_tables_to_backup(THD *thd, Backup_info &info)
 {
-  DBUG_ENTER("backup::do_backup");
+  DBUG_ENTER("backup::collect_tables_to_backup");
   
-  int res;
+  // open and scan I_S.TABLES table
   
-  // open I_S.TABLES table
-  TABLE *tables = get_schema_table(thd,SCH_TABLES);
+  TABLE *i_s_tables = get_schema_table(thd, ::get_schema_table(SCH_TABLES) );
   
-  if( !tables ) 
+  if( !i_s_tables ) 
   {
     DBUG_PRINT("backup",("get_schema_table returned NULL!"));
-    DBUG_ERROR(backup::ERROR);
-  };
+    DBUG_ERROR(ERROR);
+  }
           
-  // scan TABLES table and select tables to be backed-up
-      
-  TABLE_LIST *tl= NULL; // for opening the tables
-
-  handler *ha = tables->file;
+  ::handler *ha = i_s_tables->file;
   
-  // TODO; locking 
+  // TODO: locking 
   ha->ha_rnd_init(TRUE);
-  
-  // loop through records in I_S.TABLES table and fill TABLE_LIST
-  while( ! ha->rnd_next(tables->record[0]) )
+
+  // TODO: use conditions, do it properly 
+  while( ! ha->rnd_next(i_s_tables->record[0]) )
   {
-    String buf;   // to read values of table's fields
+    Table tbl(i_s_tables);
     
-    // DBUG_PRINT("backup",("got next row from TABLES table"));
-      
-    // field positions must be synchronized with defs in sql_show.cc
-    Field*  db_f=     tables->field[1];
-    Field*  name_f=   tables->field[2]; 
-    Field*  engine_f= tables->field[4];
+    if( tbl && tbl.db().name() == String("test",default_charset) )  // TODO:
tbl.db() == test_db
+    {  
+      DBUG_PRINT("backup", ("Found table %s in test database",
+                             tbl.name().ptr()));
+                             
+      // Backup_info::add method selects/creates sub-image appropriate for storing given
table
+      info.add(tbl,tbl.hton()); 
+    }
+  } 
+  
+  DBUG_PRINT("backup", ("No more tables in I_S"));  
+
+  ha->ha_rnd_end();
+
+  // TODO: close i_s_tables
+  
+  DBUG_RETURN(TRUE);
+}
 
-    // select only tables stored in ARCHIVE engine
-    if( strncmp("ARCHIVE",engine_f->val_str(&buf)->ptr(),7) == 0 )
-    {
-      // fill TABLE_LIST element
-      
-      TABLE_LIST *ptr= (TABLE_LIST*)sql_alloc(sizeof(TABLE_LIST));
-      bzero(ptr,sizeof(TABLE_LIST));
 
-      String *name= name_f->val_str(&buf);        
-      //DBUG_PRINT("backup",("allocating %d (%d) bytes",name->length(),buf.length()));
-      ptr->alias= ptr->table_name= sql_alloc(name->length()+1);
-      strncpy(ptr->table_name,name->ptr(),name->length()+1);
-
-      DBUG_PRINT("backup",("adding %s to TABLE_LIST",ptr->alias));
-              
-      ptr->db= (char *)"test";
-      ptr->lock_type= TL_READ;
+/*
+   Logic for selecting sub-image format for a given location:
+   
+   1. If tables from that location have been already stored in one of the
+      sub-images then choose that subimage.
       
-      // and add it to the list
+   2. If location has "native" backup format, put it in a new sub-image with
+      that format.
       
-      ptr->next_global= ptr->next_local= 
-        ptr->next_name_resolution_table= tl;
-      tl= ptr;  
-    };      
-    
-  }; // loop through I_S.TABLES records
+   3. Otherwise check if one of the existing sub-images would accept table from
+      this location.
+      
+   4. When everything else fails, use default backup format.  
+ */ 
 
-  ha->ha_rnd_end();
-  
-  // Open the tables
-  DBUG_PRINT("backup", ( "calling open_and_lock_tables" )); 
+bool Backup_info::add(const Table_ref &tbl, const ::handlerton *hton)
+{
+   DBUG_ASSERT(hton);
 
-  if( open_and_lock_tables(thd, tl) ) 
-  {
-    DBUG_PRINT("backup", ( "error!" )); 
-    DBUG_ERROR(backup::ERROR);
-  };
+   DBUG_PRINT("backup",("Adding table %s.%s (%s,%p) to backup image.",
+                        tbl.db().name().ptr(),
+                        tbl.name().ptr(),
+                        ::ha_resolve_storage_engine_name(hton),
+                        hton->get_backup_engine) );
 
-  DBUG_PRINT("backup", ( "tables opened" )); 
+   // Point 3
 
-  // add each table to backup list and save its metadata 
-  
-  Table_map backup_tables; // tables to be backed-up
-    
-  for( TABLE_LIST *ptr= tl; ptr ; ptr= ptr->next_global )
-    if( ptr->table )
-    {
-      Table_ref  tr(*ptr);
-      Table_data td(*ptr->table);
-      DBUG_PRINT("backup",("adding %s to backup list",tr.name().ptr()));
-      backup_tables.add(tr);
-      backup_image.add(td);
-    };
- 
-  DBUG_PRINT("backup",("closing tables"));
-  close_thread_tables(thd,FALSE,TRUE);
-/*    
-    // dealocate resources
-    
-    for( TABLE_LIST *ptr= tl ; ptr ; )
-    {
-      TABLE_LIST *aux= ptr;
-      ptr= ptr->next_global;
-      free(aux->table_name);  // this crashes server
-      free(aux);
-    };     
-*/    
-  // collect backup image data
-  
-  DBUG_PRINT("backup",("getting backup images for %d tables",backup_tables.count()));
-  
-  if( backup_tables.count() > 0 )
-    res= get_se_images( backup_tables, backup_image.str ); 
-  backup_image.str.close(); 
-  
-  if( res )
-    DBUG_ERROR(backup::ERROR);
-    
-  DBUG_PRINT("backup",("backup image has %d bytes",backup_image.str.size())); 
-             
-  send_ok(thd);
-  DBUG_RETURN(0);
+   Backup_subimage *img;
+   List_iterator<Backup_subimage> it(images);
+      
+   while( (img= it++) )
+   {
+     // A sub-image decides if it can handle given table or not
+     if( img->add(tbl,hton) ) 
+       return TRUE;
+   }
+      
+   // Point 2
+   
+   if( hton->get_backup_engine )
+   {
+     DBUG_PRINT("backup",("creating subimage in native format"));
+     
+     img= new Native_backup_info(hton);
+     DBUG_ASSERT( img->add(tbl,hton) ); // native image should accept all tables from
its own engine
+          
+     images.push_back(img);
+     
+     return TRUE; 
+   }
 
+   DBUG_PRINT("backup",("Table ignored."));
+   return FALSE;
 }
 
 
-/*
- * Function get_se_images implements the backup protocol
- */
+/*************************************************
+ * 
+ *                 RESTORE 
+ * 
+ *************************************************/
 
-#define MAX_BUF_SIZE  10*1024
-#define MAX_ERRORS    3
+bool read_header(IStream&,Restore_info&);
+bool create_all_tables(THD*,const Restore_info&,IStream&);
+bool restore_table_data(const Restore_info&,IStream&);
 
-const LEX_STRING archive_str= { C_STRING_WITH_LEN("ARCHIVE") };
 
-int get_se_images(const Table_map &tables, Backup_stream &str)
+int do_restore(THD *thd) 
 {
-  enum { START, INIT, SYNC, FINAL, DONE } stage= START;
- 
-  DBUG_ENTER("backup: get_se_images");   
-
-  // get backup engine and backup driver
-
-  handlerton  *hton= ha_resolve_by_name(current_thd,&archive_str);
-  Engine      *be;
-  
-  if( !hton )
-  {
-    DBUG_PRINT("backup",("Can't get the handlerton!"));
-    DBUG_RETURN(1);
-  };
-  
-  if( !(hton->backup_engine) )
-  {
-    DBUG_PRINT("backup",("Engine doesn't support backup!"));
-    DBUG_RETURN(1);
-  };
+  DBUG_ENTER("backup::do_restore");
   
-  if( hton->backup_engine(hton,be) || !be )
-  {
-    DBUG_PRINT("backup", ("Can't get backup engine!") );
-    DBUG_RETURN(1);
-  };
-
-  DBUG_PRINT("backup", ("Got backup engine") );
+  Restore_info   info;
+  bool res;
   
-  backup_image.version= be->version();
-  uint streams= tables.count()+1; // number of streams in the image
+  IStream &backup_str= open_for_read();
   
-  // get a backup driver  
+  if( !(bool)backup_str )
+    goto error;
     
-  Engine::Backup *drv;
-  
-  if( be->get_backup(tables,drv) || !drv )
-  {
-    DBUG_PRINT("backup", ("Can't get backup driver!") );
-    be->free();
-    DBUG_RETURN(2);
-  };
-
-  DBUG_PRINT("backup", ("Got backup driver") );
+  dump_stream(backup_str); // for debug purposes -- grep for 'stream: ' in server trace
   
-  // allocate buffer
+  backup_str.rewind();  
   
-  size_t bufsize = drv->buffer_size();
-  
-  if( bufsize > MAX_BUF_SIZE ) bufsize= MAX_BUF_SIZE;
-  
-  Buffer buf(bufsize);
-  
-  if( !buf ) 
-  {
-    DBUG_PRINT("backup", ("Error allocationg buffer") );
-    drv->free();
-    be->free();
-    DBUG_RETURN(3);
-  };
-  
-  DBUG_PRINT("backup", ("Got a buffer") );
-
-  // initialize creation of backup image
-  
-  if( drv->start_backup() )
+  DBUG_ASSERT( read_header(backup_str,info) );
+    
+  DBUG_PRINT("restore",("Got backup image"));
+  DBUG_PRINT("restore",("== version: %d",info.ver));
   {
-    DBUG_PRINT("backup", ("Error initializing backup!") );
-    drv->free();
-    be->free();
-    DBUG_RETURN(3);
-  };
-        
-  // initial stage
-  
-  stage= INIT;
-  DBUG_PRINT("backup", ("initial stage") );
+    List_iterator<Restore_subimage> it(info.images);
+    Restore_subimage  *img;
+    
+    for( uint no=1 ; (img= it++) ; no++ )
+    {  
+      DBUG_PRINT("restore",(" image %d.",no));
+      for( uint tno=0 ; tno < img->tables.count() ; tno++ )
+        DBUG_PRINT("restore",("   table %s.%s",
+         img->tables[tno].db().name().ptr(),
+         img->tables[tno].name().ptr())
+        );
+    }
+  }
   
-  uint errors= 0, res;
+  DBUG_PRINT("restore",("== end of catalog"));
   
-  while( stage == INIT )
-  {
-    buf.reset();
-    res= drv->get_data(buf);
-    
-    DBUG_PRINT("backup", (" got data packet of size %d for stream %d
(res=%d)",buf.size,buf.stream_no,res) );    
-    
-    switch( res  )
-    {
-      case INIT_DONE: 
-        stage= SYNC;
-                  
-      case OK: 
-        str[buf.stream_no] << buf; 
-        if( buf.last ) // should not happen!
-          streams--;
-        errors= 0;
-        break;
-    
-      case ERROR:
-        errors++;
-        if( errors > MAX_ERRORS )
-        {
-          DBUG_PRINT("backup", ("Error getting backup image!") );
-          drv->cancel();
-          drv->free();
-          be->free();
-          return 4;          
-        };
-               
-       case WAIT: case NOT_NOW:  
-         break; // wait a bit?
-         
-    };
-  };
+  res= create_all_tables(thd,info,backup_str);  
+  DBUG_ASSERT(res);
   
-  // synchronization stage
-  stage= SYNC;
-  DBUG_PRINT("backup", ("synchronization stage") );
-  
-  drv->lock();
-  drv->cont();
-  
-  // final stage
-  stage= FINAL;
-  DBUG_PRINT("backup", ("final stage") );
-    
-  while( stage == FINAL )
-  {
-    buf.reset();
-    res= drv->get_data(buf);
-    
-    DBUG_PRINT("backup", (" got data packet of size %d for stream %d
(res=%d)",buf.size,buf.stream_no,res) );    
-    
-    switch( res )
-    {
-      case INIT_DONE: // can't happen!
-                  
-      case OK: 
-        str[buf.stream_no] << buf; 
-        // FIXME: watch for ends of all streams
-        if( buf.last ) 
-        {
-          streams--;
-          DBUG_PRINT("backup", (" end of stream %d (%d left)",buf.stream_no,streams) );  
 
-          if( !streams ) stage= DONE;
-        };
-        errors= 0;
-        break;
-    
-      case ERROR:
-        errors++;
-        if( errors > MAX_ERRORS )
-        {
-          DBUG_PRINT("backup", ("Error getting backup image!") );
-          drv->cancel();
-          drv->free();
-          be->free();
-          return 4;          
-        };
-               
-        case WAIT: case NOT_NOW:  
-          break;// wait a bit?
-    };    
-  };
+  DBUG_PRINT("restore",("All tables creating, restoring data"));
   
-  // backup ready!
-  DBUG_PRINT("backup", ("all table images collected") );    
+  res= restore_table_data(info,backup_str);
+  DBUG_ASSERT(res);
   
-  drv->free();
-  be->free();
+  DBUG_PRINT("restore",("Done."));
+ 
+ error: 
   
-  DBUG_RETURN(0);
+  send_ok(thd);
+  DBUG_RETURN(OK);
   
 }
 
-/*************************************************
- * 
- *                 RESTORE 
- * 
- *************************************************/
 
-/*
-  Create a single table using info stored in Table_data structure.
-  Drops the table if it exists.
-*/
-void restore_table(THD *thd, Table_data const &td);
+//////////////////////////////////////////////////////////////////////////////
 
-int do_restore(THD *thd) 
-{
-  DBUG_ENTER("backup::do_restore");
- 
-  DBUG_PRINT("backup",("backup image contains %d tables, %d bytes of table data",
-                       backup_image.howmuch,backup_image.str.size()));
-   
-  // create empty tables
-  
-  for( unsigned i=0; i<backup_image.howmuch ; i++ ) 
-  {
-    Table_data &td = backup_image.meta[i];
-    
-    DBUG_PRINT("backup", ( "restoring table %s (pos %d)", 
-               td.name, i ) );
-    restore_table(thd,td);  
-  };
- 
-  // Get restore engine
+// Serialization of backup image info
 
-  handlerton  *hton= ha_resolve_by_name(thd,&archive_str);
-  Engine      *be;
+bool write_header(const Backup_info &info, OStream &str)
+{ 
+  // FIXME: write errors
+
+  DBUG_PRINT("backup",("writing backup catalog to stream"));
+
+  str.write2int(info.ver);
   
-  if( !hton )
-  {
-    DBUG_PRINT("backup",("Can't get the handlerton!"));
-    DBUG_ERROR(backup::ERROR);
-  };
+  List_iterator<Backup_subimage>
it(const_cast<List<Backup_subimage>&>(info.images));
+  Backup_subimage  *img;
   
-  if( !(hton->backup_engine) )
-  {
-    DBUG_PRINT("backup",("Engine doesn't support backup!"));
-    DBUG_ERROR(backup::ERROR);
-  };
+  // List of sub-images
   
-  if( hton->backup_engine(hton,be) || !be )
-  {
-    DBUG_PRINT("backup", ("Can't get backup engine!") );
-    DBUG_ERROR(backup::ERROR);
-  };
-
-  DBUG_PRINT("backup", ("Got backup engine") );
-    
-  // collect list of tables for the driver
+  uint ino;
   
-  uint streams= backup_image.howmuch+1; // number of streams in the image
-    
-  Table_map tables;
-  for( uint n=0 ; n < backup_image.howmuch ; ++n )
+  for( ino=0 ; (img= it++) ; ino++ )
   {
-    tables.add(Table_ref("test",backup_image.meta[n].name));
-    DBUG_PRINT("backup",("Restoring table %s <- %d",
-                          tables[n].name().ptr(),
-                          n+1));
-  };
-  
-  // get a restore driver  
-    
-  Engine::Restore *drv;
+    DBUG_PRINT("backup",(" adding image to catalog"));
+    img->write_description(str);   // write description of sub-image format
+  }
   
-  if( be->get_restore(backup_image.version,tables,drv) || !drv )
-  {
-    DBUG_PRINT("backup", ("Can't get backup driver!") );
-    be->free();
-    DBUG_ERROR(2);
-  };
-
-  DBUG_PRINT("backup", ("Got backup driver") );
+  str.end_chunk();
   
-  // initialize creation of backup image
+  if( ino == 0 ) return TRUE;
   
-  if( drv->prepare() )
-  {
-    DBUG_PRINT("backup", ("Error initializing backup!") );
-    drv->free();
-    be->free();
-    DBUG_ERROR(3);
-  };
+  DBUG_PRINT("backup",(" ==="));
   
-  // pump data into driver
+  // write catalogs
   
-  backup_image.str.rewind();
-  Buffer buf;
-    
-  enum { SENDING, NEXT, DONE, ERROR } stage= NEXT;
-  uint errors= 0;
-  uint repeats= 0;
+  it.rewind();
   
-  while( stage != DONE && stage != ERROR )
+  for( ino=0 ; (img= it++) ; ino++ )
   {
-    if( stage == NEXT )
-    { 
-      backup_image.str >>buf; // realocate buffer if too small
-      if( buf.size == 0 ) // end of data in the stream (or error)
-      {
-        stage= DONE;
-        break;
-      };
-    };
-
-    DBUG_PRINT("backup",("Got %d bytes from stream %d",buf.size,buf.stream_no));
-
-    switch( drv->send_data(buf) )
-    {
-      
-      case backup::OK:
-        stage= NEXT;
-        repeats= 0;
-        break;
-      
-      case backup::ERROR:
-        if( errors > MAX_ERRORS )
-          stage= ERROR;
-        errors++;  
-        break;
-          
-      case backup::WAIT:
-      case backup::NOT_NOW:
-        stage= SENDING;
+    str << img->tables;  // write list of tables stored in the image 
+  }
         
-      default:
-        if( repeats > 7 )
-          stage= ERROR;
-        repeats++;  
-        break;
-          
-    }  
-    
-  }; // data sending loop
-  
-  DBUG_PRINT("backup",("Data sent"));
-  
-  // finalize restore
-  drv->cont();
-  
-  drv->free();
-  be->free();
-  
-  if( stage == ERROR )
-    DBUG_ERROR(backup::ERROR);
-  
-  send_ok(thd);
-  DBUG_RETURN(0);
+  DBUG_PRINT("backup",("catalog written"));
 
+  return TRUE;
 }
-  
-/*
- * restore_table() -- creating empty table from data in Table_data structure.
- * If table with the same name exists, it is removed.
- */ 
-
-
-/*
-  Create list of table's field descriptions as required by
-  mysql_create_table().
-*/
-int setup_fields(THD *thd, const Table_data &td, List<create_field>
&fields);
-
-/* 
-   Create list describing keys of a table as required by
-   mysql_create_table(). (not implemented)
-*/
-int setup_keys(THD *thd, const Table_data &td, List<Key> &keys);
 
-
-void restore_table(THD *thd, const Table_data &td)
+bool read_header(IStream &str, Restore_info &info)
 {
+  DBUG_PRINT("restore",("Reading backup catalog from stram"));
   
-  int err;
-  TABLE_LIST tables;
-  bzero( (char *)&tables, sizeof(TABLE_LIST));
+  str.read2int(info.ver);
   
-  tables.db=    const_cast<char*>("test");
-  tables.alias= tables.table_name = const_cast<char*>(td.name);
+  Restore_subimage *img;
+  stream_result::value   res;
+  uint            ino=0;
   
-  // remove the table if it exists 
-  DBUG_PRINT("backup",("  trying to drop table %s",td.name));
-  err= mysql_rm_table_part2_with_lock(thd, &tables, TRUE, FALSE, TRUE); // = DROP
TABLE IF EXISTS
-  DBUG_PRINT("backup",("  result= %d",err));
+  while( (img= Restore_subimage::create_from_stream(str)) )
+  {
+    DBUG_PRINT("restore",(" got next image")); 
+    ino++;
+    info.images.push_back(img);
+  }
+
+  res= str.next_chunk();  // move to next_chunk
     
-  HA_CREATE_INFO     info;
-  bzero( &info, sizeof(HA_CREATE_INFO) );
-  
-  List<create_field> fields;
-  List<Key>	     keys;
+  if( ino == 0 || res != stream_result::OK )
+    return (res == stream_result::OK);  
   
-  setup_fields(thd,td,fields);
-  setup_keys(thd,td,keys);
+  // read list of tables
+
+  List_iterator<Restore_subimage> it(info.images);
   
-  handlerton  *hton= ha_resolve_by_name(thd,&archive_str);
-	
-  if( !hton ) 
+  for( ino=0 ; (img= it++) ; ino++ )
   {
-    DBUG_PRINT("backup",("Can't find handlerton for SE 'archive'"));
-    return;
+    res= (str >> img->tables); // read list of tables stored in the image
+    TEST_RD_RES(res);
   };
+
+  DBUG_PRINT("restore",("catalog read"));
   
-  info.db_type= hton;
-    
-  DBUG_PRINT("backup",("  calling mysql_create_table"));	  
-  err= mysql_create_table(thd, "test", td.name, &info, fields, keys, FALSE, 0,
FALSE);
-  DBUG_PRINT("backup",("  result= %d",err));	  
-       
+  return TRUE;
 }
 
+// Write/read description of a sub-image. The exact format is determined by specific
+// classes derived from Backup_subimage.
 
-
-/*
- Class Field_create extends the create_field structure used by mysql_create_table
- with a constructor initializing it using information stored in a Field_save object.
-*/
-
-/* TODO/Limitations:
-   - support enum and set type (interval_list prop, string_list: in sql_yacc.yy)
-   - support spatial types,
-   - (re)create tables so that SHOW CREATE displays the same things as in the original,
-   - column flags and options
-*/
-
-
-struct Field_create: public ::create_field
+bool Backup_subimage::write_description(OStream &s)
 {
-  Field_create(THD* thd, const Field_data& data);
+  // TODO: write description length here
+  s.write2int(ver());
+  // TODO: write type id here
+  return do_write_description(s);
+}
 
-  private:
+// TODO: Handle different and unknown types (always skip correct amount of bytes).
 
-    LEX_STRING    comment;
+Restore_subimage* 
+Restore_subimage::create_from_stream(IStream &s)
+{  
+  version_t ver;
   
-    char* itoa(int n);  // convert integer to C-string (dynamically alocated)
-};
-
-
-int setup_fields(THD *thd, const Table_data &td, List<create_field>
&fields)
-{
+  s.read2int(ver);
   
-  for(uint field=0; field < td.fields_no; field++)
-  {
-    Field_create* f= new Field_create(thd,td.fields[field]);  
-    // DBUG_PRINT("backup",("  got field %d: %s",field,f->field_name));
-    fields.push_back(f);
-  };
-    
-  return 0;
+  // assume this is Native engine format
+  return Native_restore_info::create_from_stream(ver,s);
 }
 
-    
-// Not implemented
-int setup_keys(THD *thd, const Table_data &td, List<Key> &keys)
-{
-  return 0;
-}
+//////////////////////////////////////////////////////////////////////////////
 
-  
+TABLE_LIST *build_table_list(const Tables&, thr_lock_type);
 
-// TODO: Use select condition to get only selected rows from a table
-// CHECK: prepare_select_* functions (sql_help.cc)
-  
-TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st)
-{
-  TABLE *t;
-  TABLE_LIST arg;
-  
-  bzero( &arg, sizeof(TABLE_LIST) );
-  
-  /* set context for create_schema_table call */ 
-  arg.schema_table= st;
-  arg.alias=        NULL;
-  arg.select_lex=   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_ASSERT(thd);
   
-  t= create_schema_table(thd,&arg); // Q: who deallocates *t ?
+  List_iterator<Backup_subimage> it(const_cast< List<Backup_subimage>&
>(info.images));
+  Backup_subimage *img;
     
-  if( !t ) return NULL; // error!
+  while ( (img= it++) )
+  {
+    TABLE_LIST *tl= build_table_list(img->tables,TL_READ);
+      
+    DBUG_PRINT("backup", ( "calling open_and_lock_tables" )); 
   
-  /*
-  Temporarily set thd->lex->wild to NULL to keep st->fill_table
-  happy :)
-  */
+    if ( ::open_and_lock_tables(thd, tl) ) 
+    {
+      DBUG_PRINT("backup", ( "error!" )); 
+      return FALSE;
+    }
     
-  LEX local_lex=    *thd->lex;
-  LEX *saved_lex=   thd->lex;
+    DBUG_PRINT("backup", ( "saving table defs" )); 
+    stream_result::value res;
   
-  thd->lex=   &local_lex;
+    for ( TABLE_LIST *ptr= tl; ptr ; ptr= ptr->next_global )
+    {
+      res= prototype::operator<<(s,*(ptr->table));
+      TEST_WR_RES(res);
+    }
   
-  local_lex.wild = NULL;
-  local_lex.sql_command = enum_sql_command(0);
+    DBUG_PRINT("backup",("Table defs written, closing tables"));
+    ::close_thread_tables(thd,FALSE,TRUE);
     
-  // Note: set thd->lex->sql_command to something neutral (see,
make_db_list/get_index_file_values).
+    // TODO: deallocate tl memory
   
-  /* context for fill_table */
-  arg.table= t;
+  } // sub-image loop
   
-  st->fill_table(thd,&arg,NULL);  // NULL = no select condition
-
-  /* restore wild field */
-  thd->lex= saved_lex;
+  DBUG_PRINT("backup",("All table definitions written"));
   
-  return t;
+  return TRUE; 
 }
 
-
-
-// Implementation of Field_create class
-
-
-Field_create::Field_create(THD* thd, const Field_data& data)
+bool create_all_tables(THD *thd, const Restore_info &info, IStream &s)
 {
-  char* ptr=  data.pool;
-  char* name= ptr;
+  DBUG_ASSERT(thd);
   
-  ptr+= strlen(name)+1;
-  comment.str=    ptr;
-  comment.length= strlen(comment.str);
-  
-  init(thd,
-       name, 
-       data.type,
-       itoa(data.length),
-       itoa(data.decimals),
-       0,                   // fld_type_modifiers ?
-       NULL,                // fld_default_value (TBD)
-       NULL,                // fld_on_update_value (ignore)
-       &comment,
-       NULL,                // fld_change (ignore)
-       NULL,                // List<String>* fld_interval_list ?
-       NULL, //&charset,
-       0 );                 // fld_geom_type ?         
-
-}
-
-// CHECK: longlong2str
+  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 next image",tno));
+    
+    TABLE_LIST  *tl= build_table_list(img->tables,TL_READ);
 
-char* Field_create::itoa(int n)
-{
-  char buf[16];
+    // remove tables which exist
   
-  sprintf(buf,"%d",n);
+    DBUG_PRINT("restore",("  trying to drop tables"));
+    err= mysql_rm_table_part2_with_lock(thd, tl, TRUE, FALSE, TRUE); // = DROP TABLE IF
EXISTS
+    DBUG_PRINT("restore",("  result= %d",err));
   
-// char *ptr = my_malloc(strlen(buf)+1,MYF(0));
-  char*   ptr = new char[strlen(buf)+1];
+    DBUG_ASSERT( err == 0 );
 
-  strcpy(ptr,buf);
+    // 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"));
   
-  return ptr;
+  return TRUE;   
 }
 
+//////////////////////////////////////////////////////////////////////////////
 
-// Implementation of Table_map
-
-Table_map::Table_map(): m_head(NULL),m_last(NULL),m_count(0) {};
+// Reading and writing list of tables from/to a stream.
 
-Table_map::~Table_map()
+stream_result::value operator<<(OStream &str, const Tables &tl)
 {
-  for( node *ptr= m_head ; ptr ; )
+  stream_result::value res;
+  
+  res= (str << tl.dbnames); // write the pool of dbnames
+  
+  for( Tables::node *n= tl.m_head ; n ; n= n->next )
   {
-    node *n=ptr;
-    ptr= n->next;
-    delete n;
+    res= str.write2int(n->db);
+    res= str.writestr(n->name);
   };
+  
+  return str.end_chunk();
 }
 
-
-void Table_map::add(const Table_ref &t)
+stream_result::value operator>>(IStream &str, Tables &tl)
 {
-  node *n= new node(t);
-  if( m_head == NULL ) 
-  {
-    m_count=1;
-    m_head= m_last= n;
-  }
-  else
-  { 
-    m_count++;
-    m_last->next= n;
-    m_last= n;
-  };
-}
+  stream_result::value res;
 
+  res= (str >> tl.dbnames);  // read the dbnames pool
 
-const Table_ref &Table_map::operator[](uint pos) const
-{
-  DBUG_ASSERT(pos < m_count);
+  if( tl.dbnames.count() == 0 || res != stream_result::OK )
+    return res;
   
-  node *ptr;
+  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 );
   
-  for( ptr= m_head ; ptr && pos ; ptr= ptr->next )
-   pos--;
+  if( res == stream_result::EOC )
+    res= str.next_chunk();
 
-  if( ptr )
-   return ptr->tbl;
-  else
-   return m_last->tbl;  // can crash if m_last == NULL
+  return res;
 }
 
-// Implementation of Table_ref and Db_ref
-
-
+// Serialization of StringPool
 
-
-Table_ref &Table_ref::operator=(const st_table_list &t)
+stream_result::value operator>>(IStream &str, StringPool &p)
 {
-  m_name.set( STRING_WITH_LEN(t.table_name), table_alias_charset );
-  m_db= t.db;
+  stream_result::value res;
+  String buf;
+  uint  skip, i=0;
   
-  return *this;
-}
-
-const char *Table_ref::path() const
-{
-  static char buf[FN_REFLEN]= "";
+  p.clear();
   
-//  if( buf[0] == '\0' )
-    build_table_filename( buf, sizeof(buf)-1, 
-                           m_db.name().ptr(),
-                           m_name.ptr(),
-                           "", 0 );
-  return buf;
-}
-
-const String &Db_ref::catalog() const 
-{ 
-  return my_null_string; 
-}
-
-
-// In memory stream implementation
-
-static byte image_data[Backup_stream::DATA_LEN];
-
-Backup_image::Backup_image(): str(image_data), howmuch(0) 
-{}
-
-Backup_image::~Backup_image()
-{
-  while( howmuch )
-    meta[--howmuch].free();
+  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;
 }
 
-void Backup_image::add(const Table_data &td)
+stream_result::value operator<<(OStream &str, const StringPool &p)
 {
-  if( howmuch >= 10 ) return;
-  meta[howmuch++]= td;
-}
-
-
-Backup_stream &Backup_stream::operator<<(const Buffer &buf) 
-{ 
-  DBUG_PRINT("backup",( "adding %d bytes to backup image (str %d)",
-                        buf.size, stream ));
-
-  if( buf.size > 0 && pos+sizeof(header)+buf.size < DATA_LEN )
+  stream_result::value res;
+  String buf;
+  uint  skip=0 , i=0;
+  
+  // TODO: error checking
+  
+  for(i=0 ; i < p.count() ; i++ )
   {
-    header hdr(buf);
-            
-    *reinterpret_cast<header*>(data+pos)= hdr;
-    pos += sizeof(header);
-    memcpy(data+pos,buf.data,buf.size);
-    pos += buf.size;
-  };
-         
-  return *this; 
-}
-
-Backup_stream &Backup_stream::operator>>(Buffer &buf) 
-{ 
-  DBUG_PRINT("backup",( "adding %d bytes to backup image (str %d)",
-                        buf.size, stream ));
-  header hdr;
-  
-  uint buflen= buf.size;
-  buf.size= 0;
-
-  if( pos+sizeof(header) >= DATA_LEN )
-    return *this;
-    
-  hdr= *reinterpret_cast<header*>(data+pos);
-  pos+= sizeof(header);
-
-  if( !hdr.size )
-    return *this;
-  if( pos+hdr.size >= DATA_LEN )
-    return *this;
+    if( p.entries[i].el != NULL )
+    {
+      if( skip > 0 )
+        str.writeint(skip);
+      skip= 0;  
+      str.writestr(*p.entries[i].el);
+      continue;
+    };
     
-  if( buflen == 0 || buflen < hdr.size ) // reallocate buffer
-    buf.realloc(hdr.size);
+    if( skip == 0 )
+      str.writenil();
       
-  if( !buf )  // no space for buffer
-      return *this;
-        
-    buf.size= hdr.size;    
-    buf.stream_no= hdr.stream;  
-    memcpy(buf.data,data+pos,hdr.size);
-    pos += hdr.size;
-           
-  return *this; 
+    skip++;  
+      
+  };
+    
+  if( skip > 0 )
+    str.writeint(skip);
+  
+  return str.end_chunk();
 }
+
 
 } // backup namespace
 

--- 1.4/sql/backup.h	2007-02-05 17:09:38 +01:00
+++ 1.5/sql/backup.h	2007-02-05 17:09:38 +01:00
@@ -2,234 +2,543 @@
 #define BACKUP_H
 
 #include "backup0.h"
+extern const String my_null_string;  
+
 
 #ifndef VIEW_ALGORITHM_UNDEFINED
   #error backup.h must be included after table.h  
 #endif
 
-//struct st_table_list;
-//typedef struct st_table_list TABLE_LIST;
 
 namespace backup {
-
-class Db_ref
-{
-  String  m_name;
- 
-  public:
-   
-    Db_ref(const char *db): m_name(db,system_charset_info) {}
-   
-    Db_ref &operator=(const char *db)
-    {
-      m_name.set( STRING_WITH_LEN(db), system_charset_info );
-      return *this;
-    }
-   
-    const String &name() const { return m_name; }
-    const String &catalog() const;
-
-};
-
-
-class Table_ref 
-{
-  String  m_name;
-  Db_ref  m_db;
-      
-  public:
   
-    Table_ref(const char *db, const char *name):
-       m_name(name,table_alias_charset),m_db(db)
-    {};
-   
-    Table_ref(const st_table_list &t):
-       m_name(t.table_name,table_alias_charset),
-       m_db(t.db) 
-    {};
-   
-   Table_ref &operator=(const st_table_list &t);   
-  
-   const Db_ref &db() const { return m_db; }
-   const String &name() const { return m_name; }    
-   
-   const char *path() const;
-   
-};
-
-class Table_map
-{ 
-  public: 
-   
-    Table_map();
-    ~Table_map();
-    
-    void  add(const Table_ref &t);
-    const Table_ref &operator[](uint pos) const;
-    uint  count() const { return m_count; }
-   
-    class iterator;
-   
-  protected:
-   
-    Table_map(const Table_map &) {}
-
-  private: 
-   
-    struct node {
-      node(const Table_ref &t):tbl(t),next(NULL) {};
-      Table_ref  tbl;
-      node *next;
-    };
-    
-    node *m_head,*m_last;
-    uint m_count;
-   
-    friend class iterator;
-};
 
-class Table_map::iterator
-{
-  public:
-    
-    iterator(const Table_map &map):ptr(map.m_head) {};
-    
-    bool operator++() 
-    { 
-      if( ptr ) { ptr= ptr->next; return TRUE; }
-      else  return FALSE; 
-    }
-    
-    operator bool() const { return ptr != NULL; };
-    
-    const Table_ref* operator*() const 
-    {
-      if(ptr) return &(ptr->tbl);
-      else return NULL;
-    }
-  
-  private:
-    Table_map::node *ptr;
-};
+// Class Table_list is used to pass a list of tables to backup engine
+class Table_list;
 
 
+/**
+ * @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:
     
-    class Backup;
-    class Restore;
+    class Backup;   ///< Backup driver class.
+    class Restore;  ///< Restore driver class.
+    
     
+    /// Return version of backup images created by this engine.
     virtual const version_t version() const =0;    
-    virtual result_t get_backup( const Table_map &tables, Backup* &eng ) =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* &eng ) =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_map &tables, Restore* &eng ) =0;
+                                   const Table_list &tables, Restore* &eng ) =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;
 
+
+
+/**
+ * @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) {};
     
-    Driver(const Table_map &tables):m_tables(tables) {}
+    virtual ~Driver() {}; // We want to inherit from this class.
     
-    virtual size_t    buffer_size() =0;
+    //@{
+    /**
+     * 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;
-    virtual void      free() {};
     
+    /**
+     * 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:
     
-    const Table_map &m_tables;
+    /// Refers to the list of tables passed when the driver was created.
+    const Table_list &m_tables;
 };
 
+/**
+ * @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 Engine::Backup
+ * 
+ * @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 Engine::Backup: public Driver
 {
   public:
     
-    Backup(const Table_map &tables):Driver(tables) {}
+    Backup(const Table_list &tables):Driver(tables) {};
     
-    virtual size_t    size() =0;
-    virtual size_t    init_size() =0;
-    virtual result_t  start_backup() =0;
+    virtual ~Backup() {}; // 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  cont() =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 Engine::Restore
+ * 
+ * @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 Engine::Restore: public Driver
 {
   public:
     
-    Restore(const Table_map &tables):Driver(tables) {}
+    Restore(const Table_list &tables):Driver(tables) {};
+    virtual ~Restore() {};    
     
-    virtual result_t  prepare() =0;
+   /** 
+    * @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;
-    virtual result_t  cont() =0;    
     
 };
 
-struct Buffer
+
+//@{
+/// Functions used to implement BACKUP/RESTORE statements. They are called from
sql_parse.cc
+int do_backup(THD*);
+int do_restore(THD*);
+//@}
+
+
+/*
+ *  This class is used to abstract away the way a table is identified inside mysql
instance.
+ *  Right now tables are identified using (table name, database name) pairs. If this
changes
+ *  in the future, implementation of Table_ref is the place to incorporate these changes
+ *  into the backup code.
+ */ 
+class Table_ref;
+
+/**
+ * @class Table_list
+ * 
+ * @brief Objects of this class store list of tables (just table references represented
+ *        by Table_ref objects).
+ * 
+ * Contrary to the List class, this class allocates memory and stores elements of the
+ * list, not just pointers to them.
+ * 
+ * Elements of the list can be accessed by index. E.g.
+ * @code
+ *  Table_list l;
+ *  Table_ref  r = l[1];  // r refers to the second element of the list. 
+ * @endcode
+ * 
+ * @note This is rather ad-hoc implementation and certainly can be improved/optimized.
+ */
+  
+class Table_list
+{ 
+  public: 
+  
+    class Ref;
+   
+    /// Return reference to given list element. Elements are counted from 0.
+    virtual Ref operator[](uint pos) const =0;
+    
+    /// Return number of elements in the list.
+    virtual uint  count() const =0;
+};
+   
+
+/*
+class Opt_val
 {
-  size_t  size;
-  uint    stream_no;
-  bool    last;
-  byte    *data;
-  
-  Buffer():data(NULL),size(0),create_size(0),stream_no(0),last(TRUE) {}
-  
-  Buffer(uint size):size(size),create_size(size),stream_no(0),last(TRUE)
-  {
-    data= my_malloc(size,MYF(0));
-  }
+ protected:
   
-  ~Buffer()
-  {
-    if(data) my_free(data,MYF(0));
-  }
+  bool valid;
   
-  void reset() 
-  { 
-    size= create_size; 
-    stream_no=0; 
-    last= TRUE; 
-  }
+ public:
+ 
+  Opt_val(bool not_null=FALSE): valid(not_null) {}; 
+  virtual operator bool() const { return valid; };
+};
+*/
 
-  void realloc(uint size)
-  {
-    if( data ) my_free(data,MYF(0));
-    if( size ) 
-      data= my_malloc(size,MYF(0));
-    else
-      data= NULL;
-    create_size= data ? size : 0;
-    reset();
-  }
+//@{
 
-  operator bool() const
-  {
-    return data != NULL;
-  }
+/** 
+ * Objects of type Db_ref and Table_ref are used to identify databeses and tables, 
+ * respectively, inside mysql server instance.
+ * 
+ * Current, ad-hoc implementation, uses table name together with a database reference
+ * to identify a table. Database is identified by its name (catalog is empty but can
+ * be introduced in future versions).
+ */  
 
- private:
- 
-  size_t create_size;
-  
+
+struct Db_ref
+{
+  virtual const String &name()    const =0;
+  virtual const String &catalog() const { return my_null_string; };
+};
+
+struct Table_ref
+{
+  virtual const Db_ref &db()   const =0;
+  virtual const String &name() const =0;
 };
+//@}
 
 
-int do_backup(THD*);
+// Table_ref object which doesn't store strings.
+
+class Table_list::Ref: public Table_ref
+{
+  struct Db: public Db_ref
+  { 
+    const String &m_name;
+    const String &name() const { return m_name; };
+    Db(const String &s): m_name(s) {};
+  } m_db;
+  
+ public: 
+  
+  const String &m_name;
+
+  const String &name() const { return m_name; };
+  const Db_ref &db()   const { return m_db; };
+  
+  Ref(const String &db, const String &nm): m_db(db), m_name(nm) {};
+};
+
 
-int do_restore(THD*);
 
 
 } // backup namespace
+
+typedef backup::Engine::Backup   Backup_driver;
+typedef backup::Engine::Restore  Restore_driver;
 
    
 #endif

--- 1.1/sql/backup0.h	2007-02-05 17:09:38 +01:00
+++ 1.2/sql/backup0.h	2007-02-05 17:09:38 +01:00
@@ -2,11 +2,20 @@
 #define BACKUP0_H
 
 namespace backup {
+  
+// Compatibility layer until things in official mySQL code are sane.
+
+typedef unsigned char byte;
+
+extern CHARSET_INFO *default_charset;
+  
+
+// Backup related declarations
 
 typedef uint version_t; 
 typedef int  result_t; 
   
-enum enum_return_codes { OK=0, ERROR, WAIT, NOT_NOW, INIT_DONE };  
+enum enum_return_codes { OK=0, ERROR, NO, WAIT, NOT_NOW, INIT_DONE, READY, END_BLOCK };  
 
 class Engine;
 
--- New file ---
+++ sql/backup_alg.cc	07/02/05 17:09:28
#include "mysql_priv.h"

#include "backup.h"
#include "backup_private.h"
#include "backup_stream.h"

namespace backup {

namespace backup_state {
 
 enum value { INACTIVE, INIT, WAITING, READY, FINISHING, DONE, ERROR };

}

// Stream_multiplexer provides shared access to opened backup::OStream
// for several clients (Block_writer) objects.

struct Stream_multiplexer;

// Block_writer writes data block to an open backup::OStream via a Stream
// multiplexer object. Different writters using the same stream should have
// different numbers.

struct Block_writer
{
  enum result_t { OK, BUSY, ERROR };
  
  byte*     new_buf(size_t len);
  result_t  write_buf(byte *buf, size_t);
  result_t  drop_buf(byte *buf);
   
 private:
  
  Block_writer(uint no, Stream_multiplexer &sm): m_no(no), m_mplx(sm)
  {}
  
  uint  m_no;  
  Stream_multiplexer  &m_mplx;

  friend class Stream_multiplexer;
};

struct Stream_multiplexer
{
  
  Stream_multiplexer(OStream &s): w_no(0), m_stream(s), win(NULL)
  {}
  
  Block_writer  new_writer()
  {
    return Block_writer(++w_no,*this);
  }
    
 private: 
  uint    w_no;
  OStream &m_stream;
  byte    *win;
  // TODO; size_t w_len;

  bool   get_win(uint,size_t);
  bool   write_win(size_t);
  bool   drop_win();
  
  bool    is_taken() const
  { return win != NULL; }
  
  bool    is_owner(uint no) const
  { return is_taken() && w_no == no; }

  friend class Block_writer;
};

// Backup_pump reads data from a backup driver and writes them to 
// a backup stream using Block_writer object. It also maintains current
// state of the backup process and has methods for implementing backup
// protocol (multi-engine synchronization).

struct Backup_pump
{
  backup_state::value       state;
  size_t  bytes_in, bytes_out;
  
  Backup_pump(uint, Backup_subimage&, const Block_writer&);
  
  bool begin()
  {
    state= backup_state::INIT;
    return m_drv.begin() == backup::OK;
  }
  
  bool end()
  { 
    m_drv.end();
    m_drv.free();
    return TRUE;
  }
  
  bool poll();
  
  bool prepare();
  
  bool lock()
  { return m_drv.lock() == backup::OK; }
  
  bool unlock()
  { 
    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;
 
  enum { READING, WRITING } p_state;
  uint          m_drv_no;          
  Backup_driver &m_drv;
  Block_writer  m_bw;
  byte          *m_wptr;  // pointer to a stream window area
  Buffer        m_buf;

  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();
  
  switch (res) {
  
  case READY:
    state= backup_state::READY;
  
  case OK:
    res= OK;
    break;
  
  default:
    break;
  
  }
  
  return res == backup::OK;
}

#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)
{
  Backup_subimage    *img = const_cast<Backup_info&>(info).images.head();
  Stream_multiplexer sm(s);
  
  DBUG_ASSERT(img);
  
  Backup_pump     p(1, *img, sm.new_writer());
  
  p.begin();
  
  CHECK_STATE(p,backup_state::INIT);
  //DBUG_ASSERT( p.state == backup_state::INIT );
  
  while ( p.state == backup_state::INIT )
    p.poll();  // poll the initial data
   
  DBUG_ASSERT( p.state == backup_state::WAITING || p.state == backup_state::READY );
   
  p.prepare(); // ask driver to prepare for VP creation
  
  while ( p.state == backup_state::WAITING )
    p.poll();
   
  CHECK_STATE(p,backup_state::READY);
  //DBUG_ASSERT( p.state == backup_state::READY ); 
   
  // creation of the validity point 
  p.lock();
  p.unlock();
  
  while ( p.state == backup_state::FINISHING )
    p.poll(); // poll the final data
   
  CHECK_STATE(p,backup_state::DONE);
  //DBUG_ASSERT( p.state == backup_state::DONE ); 
  
  p.end();
  
  return TRUE;
}

/**************************************************
 * Implementation of Backup_pump
 **************************************************/

Backup_pump::Backup_pump(uint no, Backup_subimage &img, const Block_writer &bw):
  state(backup_state::INACTIVE), bytes_in(0), bytes_out(0),
  p_state(READING), m_drv_no(no), m_drv(img.driver()), m_bw(bw), m_wptr(NULL)
{
  m_buf.data= NULL;
  bitmap_init(&m_closed_streams,
              NULL,
              1+img.tables.count(),
              FALSE); // not thread safe 
}


bool Backup_pump::poll()
{

  if (state == backup_state::INACTIVE || state == backup_state::ERROR )
    return FALSE;

  if (state == backup_state::DONE)
    return TRUE;
  
  if (p_state == WRITING)
    if (!write_buf())
      return FALSE;
      
  if (p_state == WRITING) // writer was busy -- need to repeat writting
    return TRUE;
  
  get_buf(); // ensure that we have the buffer ready for writing
  
  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 )
      state= backup_state::READY;  
  
  case OK:
  
    if (m_buf.last)
    {
     mark_stream_closed(m_buf.stream_no);
     if (all_streams_closed())
        state= backup_state::DONE;
    }
    
    if ( m_buf.size > 0 )
    { 
      p_state= WRITING;
      // TODO: use OStream interface for consistency
      int2store(m_wptr,m_drv_no);
      int2store(m_wptr+2,m_buf.stream_no);
      return write_buf();
    }
    else
      drop_buf();
    
    m_buf.data= NULL;
    
  case WAIT:
  
    break;
    
  case ERROR:
    state= backup_state::ERROR;
    
  case NOT_NOW:
    
    drop_buf();
    
  default:
    break;
    
  }

  return state != backup_state::ERROR;  
}

bool Backup_pump::get_buf()
{
  if (!m_buf.data)
  {
    m_wptr = m_bw.new_buf(4+m_buf_size);
    DBUG_ASSERT(m_wptr); // FIXME: deal with temporary lack of resources
    m_buf.data= m_wptr+4;
    m_buf.reset(m_buf_size);
  }
  
  return TRUE;
}

bool Backup_pump::drop_buf()
{
  m_buf.data= NULL;
  m_bw.drop_buf(m_wptr);
  m_wptr= NULL;
  
  return TRUE;
}

bool Backup_pump::write_buf()
{
   Block_writer::result_t res= m_bw.write_buf(m_wptr,4+m_buf.size);
   
   if ( res == Block_writer::OK )
   {
      p_state= READING;
      m_buf.data= m_wptr= NULL;
   }
     
   return res != Block_writer::ERROR;
}

/**************************************************
 * Implementation of Block_writer
 **************************************************/

byte* Block_writer::new_buf(size_t len)
{
  if (m_mplx.is_taken())
    return NULL;
  
  if (m_mplx.get_win(m_no,len))
    return m_mplx.win;
  else
    return NULL;  
}
 
Block_writer::result_t 
Block_writer::write_buf(byte *buf, size_t len)
{
  if (!m_mplx.is_owner(m_no) || buf != m_mplx.win)
    return ERROR;
    
  return m_mplx.write_win(len) ? OK : ERROR; 
}

Block_writer::result_t  
Block_writer::drop_buf(byte *buf)
{
  if ( buf == m_mplx.win && m_mplx.is_owner(m_no) )
    return m_mplx.drop_win() ? OK : ERROR;
  else
    return OK;
}

/**************************************************
 * Implementation of Stream_multiplexer
 **************************************************/

bool Stream_multiplexer::get_win(uint no, size_t len)
{
  if ( win && w_no != no ) // someone else allocated a window
    return FALSE;
  
  w_no= no;
  win= m_stream.get_window(len);
  
  return win != NULL;
}

bool  Stream_multiplexer::write_win(size_t len)
{
  DBUG_PRINT("backup",("writing %u bytes to the stream",len));
  m_stream.move(len);     // this sends bytes to the stream
  m_stream.drop_window(); // truncate the window
  m_stream.end_chunk();
  win= NULL;
  return TRUE; // TODO: error handling
}

bool  Stream_multiplexer::drop_win()
{
  // note: we don't need to drop window in the stream since the following
  //       m_stream.get_window() will overwrite the current window.
  win= NULL;
  return TRUE;
}

}


/********
 Notes for extending the code to handle several backup drivers

 Read state for single driver:
 
 - how much data has been read,
 - state {INACTIVE,INIT,WAIT,READY,FINAL,DONE,ERROR}

{
  // Polling init data
  while( <there are active drivers sending init data> )
  {
    <pick next driver and poll it for data>
    
    if( <driver finished init phase> )
      <update info about drivers in init phase>;
    
    if( <data has been read> )
    {  
      <update progress count>
      if( <init size of some inactive driver reached> )
      {
        <initialize the driver>;
        <add it to active list>;
      }
    }
    
    if( <no more drivers sending init data>
        && <there are inactive drivers> )
    {
      <activate one with biggest init size>;
      <add it to active list>;
    }
  }
  
  // Prepare for VP creation
  
  <call prepare() for all active drivers and record which are ready>;
  <put not ready drivers in front of active list>;
  
  while( <there are not ready drivers> )
  {
    <poll each not ready driver and move to ready if done>;
    <poll each ready driver>;
  }

  // Create VP
  
  <call lock for each driver>;
  <call unlock for each driver>;
  
  // Final phase
  
  while( <there are drivers sending data> )
  {
    <pick next driver and poll it for data>
    if( <last data block> )
    {
      <close the driver>
      <remove from queue>
    }
  }
}


  Queue interface:
  
  is_empty()
  get_next()
  remove()
  add()  

  unknown_queue= <drivers with unknown init image size>;
  inactive_list= <all other dirvers>;
  known_queue= <empty queue>;
  
  bool  queue= <unknown_queue is empty> ? known_queue : unknown_queue;

  while( <queue not empty> )
  {
    if( queue == unknown_queue )
      drv= <get next from queue>;
    else
      drv= <the driver in queue with least bytes sent>;
      
    drv.poll();
    
    <update read average>;
    if( <drv leaved INIT state> )
      <insert drv in waiting_queue>;
    else
      <insert drv in queue>;
    
    if( <known_queue is empty> && <there are inactive drivers> )
      <insert inactive drv with biggest init size into known_queue>;
    
    <switch queue>
  }

*/


--- New file ---
+++ sql/backup_private.h	07/02/05 17:09:28
#ifndef BACKUP_INT_H
#define BACKUP_INT_H

#include "backup_util.h"
#include "backup_stream.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
{
  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* &eng )
   { return ERROR; }

   result_t get_restore( version_t version, 
                         const Table_list &tables, Restore* &eng )
   { 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 prototype::operator<<(OStream&,TABLE&);
int create_table_from_str(THD*,IStream&,const Table_ref&);
  
} // prototype namespace

} // backup namespace

/************************************************************
   
   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.
    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
{
   Tables    tables;
   
   virtual version_t ver() const =0;

   virtual ~Subimage_info()
   {}
};

struct Backup_subimage: public Subimage_info
{
   bool write_description(OStream &s);
   
   virtual bool add(const Table_ref &tbl, const ::handlerton *hton)
   { return FALSE; } 
   
   virtual bool do_write_description(OStream&) =0;
   
   virtual Backup_driver& driver() =0;
};

struct Restore_subimage: public Subimage_info
{
  version_t m_ver;
  
  Restore_subimage(version_t ver): m_ver(ver) 
  {}
  
  version_t ver() const
  { return m_ver; }
  
  static Restore_subimage* create_from_stream(IStream &s);
  
  virtual Restore_driver* driver() =0;
};

    
// Info about (contents of) a global backup image.

struct Backup_info
{
   version_t     ver;
   // date
   List<Backup_subimage> images;
   
   Backup_info(): ver(7) {};

   // Add table stored in a given storage engine 
   bool add(const Table_ref &tbl, const ::handlerton *hton);
};

struct Restore_info
{
   version_t     ver;
   // date
   List<Restore_subimage> images;
   
   Restore_info(): ver(7) {};
};
  
/************************************************************
   
   Definitions of subimage types used
   
 ************************************************************/   

// Native backup image: created by backup-capable storage engine

class Native_backup_info: public Backup_subimage
{
  const ::handlerton  *m_hton;
  Engine   *m_be;
  Backup_driver *m_drv;
 
 public: 
  Native_backup_info(const ::handlerton *hton): m_hton(hton), m_drv(NULL) 
  {
    DBUG_ASSERT(hton);
    DBUG_ASSERT(hton->get_backup_engine);
    hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
    DBUG_ASSERT(m_be);
  }
  
  version_t ver() const
  { return m_be->version(); }
  
  Backup_driver&  driver() 
  { 
    result_t res= OK;
    
    if (!m_drv)
      res= m_be->get_backup(tables,m_drv);
      
    DBUG_ASSERT(res == OK);  
    DBUG_ASSERT(m_drv);  
    return *m_drv;
  }
  
  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;
  Restore_driver      *m_drv;
 
 public: 
  Native_restore_info(const ::handlerton *hton, version_t ver):
    Restore_subimage(ver), m_hton(hton), m_drv(NULL)
  {
    DBUG_ASSERT(hton);
    DBUG_ASSERT(hton->get_backup_engine);
    hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
    DBUG_ASSERT(m_be);
  }
  
  Restore_driver*  driver() 
  { 
    result_t res= OK;
    
    if (!m_drv)
      res= m_be->get_restore(m_ver,tables,m_drv);
      
    DBUG_ASSERT(res == OK);  
     
    return m_drv;
  }

  static Native_restore_info* create_from_stream(version_t, IStream&);
};
  
} // backup namespace

#endif

--- New file ---
+++ sql/backup_prototype.cc	07/02/05 17:09:28
#include "mysql_priv.h"

#include "backup_stream.h"
#include "backup.h"
#include "backup_private.h"


/// Static storage for the backup image.

backup::prototype::Chunk_storage  
backup::prototype::Chunk_storage::m_instance;

/// Trivial engine instance.

backup::prototype::Trivial_engine
backup::prototype::Trivial_engine::instance;

Backup_result_t backup::prototype::get_trivial_engine(::handlerton*, Backup_engine*
&eng)
{
  eng= &backup::prototype::Trivial_engine::instance;
  return OK;
}

namespace backup {
  
// 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));  
}
  
}


// 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 subimage",tbl.name().ptr()));  
    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(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(hton,ver);
  }
  else
    return NULL;
}

} // 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() };

    snprintf(len_buf,sizeof(len_buf),"%d",length);
    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 )
  {
    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;
  
  DBUG_PRINT("restore",(" reading colums"));
  
  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);
  DBUG_PRINT("restore",("result= %d",err));    
  
  // TODO: free memory
  
  return err;
}


} // prototype namespace

// 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 Tables &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));

    Tables::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;  
  }

  return tl;    
}

} // backup namespace


--- New file ---
+++ sql/backup_stream.h	07/02/05 17:09:28
#ifndef BACKUP_STREAM_H_
#define BACKUP_STREAM_H_

#include "backup_util.h"
#include "backup.h"

// TODO: replace it by standard type (ptr_off_t?)
typedef int  offset_t;

/************************************************************
   
  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 {
  
namespace 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;
  
  public:
    
    Window(Chunk_storage &st): 
      Stream_win(st.data_buf), m_storage(st)
    {};

    virtual Result  set_length(size_t len) =0;
    Result  move(offset_t off);
    
    byte  *get_window(size_t len)
    {
      Result res= set_length(len);
      return res == stream_result::OK ? m_head : NULL;
    }
    
    void  drop_window()
    {
      set_length(0);
    }
          
  private:
  
    Chunk_storage &m_storage;
    
  friend class IWindow;
  friend class OWindow;  
};

inline 
stream_result::value 
Chunk_storage::Window::move(offset_t off)
{ 
  Result  result= stream_result::OK;
  
  if ( off > m_length )
   result= set_length(off);
  
  if ( result == stream_result::OK )
  { 
    m_head+= off;
    m_length-= off;
  }
   
  return result; 
}

class Chunk_storage::IWindow: public Chunk_storage::Window
{
  uint  chunk; ///< chunk currently read
 
 public:
  IWindow(Chunk_storage &st): Window(st), chunk(0)
  {}

  Result  set_length(size_t len);
  Result  get_chunk();
  Result  next_chunk();
  void    close() {};
  void    rewind();
};

class Chunk_storage::OWindow: public Chunk_storage::Window
{
 public:
 
  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);
  Result  end_chunk();
  void    close();
  void    rewind();
};

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()
{
  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;
}

// 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


/****************************************************
   
   Definitions of input and output backup streams
   
 ****************************************************/

typedef prototype::Chunk_storage::IWindow IWindow;
typedef prototype::Chunk_storage::OWindow OWindow;

template class util::IStream< IWindow >;
template class util::OStream< OWindow >;

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 IStream& open_for_read();
};


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 OStream& open_for_write(); 
};

// Return instances of IStream/OStream which use the in-memory storage.

inline
IStream &open_for_read()
{
  static IStream stream(prototype::Chunk_storage::m_instance);
  stream.rewind();
  return stream;
}

inline
OStream &open_for_write()
{
  static OStream stream(prototype::Chunk_storage::m_instance);
  stream.rewind();
  return stream;
}

// Function for debugging backup stream implementation.
void dump_stream(IStream &);


} // backup namespace

#endif /*BACKUP_STREAM_H_*/

--- New file ---
+++ sql/backup_util.cc	07/02/05 17:09:28
#include "mysql_priv.h"
#include "sql_show.h"

// General utilities

#include "backup_util.h"

namespace util {

String *StringDom::create(const Element &s) 
{
  String *ns= new (current_thd->mem_root) String(); 
  ns->copy(s);
  return ns;
}

const String &StringDom::null= my_null_string;

}

// Backup related utilities 

#include "backup.h"
#include "backup_private.h"

namespace backup {

// TODO: Use select condition to get only selected rows from a table
// CHECK: prepare_select_* functions (sql_help.cc)
  
TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st)
{
  TABLE *t;
  TABLE_LIST arg;
  
  bzero( &arg, sizeof(TABLE_LIST) );
  
  /* set context for create_schema_table call */ 
  arg.schema_table= st;
  arg.alias=        NULL;
  arg.select_lex=   NULL;
  
  t= create_schema_table(thd,&arg); // Q: who deallocates *t ?
    
  if( !t ) return NULL; // error!
  
  /*
  Temporarily set thd->lex->wild to NULL to keep st->fill_table
  happy :)
  */
    
  LEX local_lex=    *thd->lex;
  LEX *saved_lex=   thd->lex;
  
  thd->lex=   &local_lex;
  
  local_lex.wild = NULL;
  local_lex.sql_command = enum_sql_command(0);
    
  // Note: set thd->lex->sql_command to something neutral (see,
make_db_list/get_index_file_values).
  
  /* context for fill_table */
  arg.table= t;
  
  st->fill_table(thd,&arg,NULL);  // NULL = no select condition

  /* restore wild field */
  thd->lex= saved_lex;
  
  return t;
}

  
// Implementation of Table_list

int Tables::add(const Table_ref &t)
{
  backup::StringPool::Key k= dbnames.add(t.db().name());
  
  if( !k ) return -1;

  return add(k,t.name());  
}

int  Tables::add(const backup::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_list::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 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;
  };
}


}      

--- New file ---
+++ sql/backup_util.h	07/02/05 17:09:28
#ifndef UTIL_H
#define UTIL_H

/*
  Contents:
  
  - String convenience functions.
  
  - StringPool class.

  - stream interface.  
  
 */ 

inline
bool operator==(const String &s1, const String &s2)
{
  return stringcmp(&s1,&s2) == 0;
}

/*
operator String(const char *str)
{
  return String(str,default_charset);
}
*/

namespace util 
{
  
typedef unsigned char byte;  


/*******************************************************
  
  Streams for serializing basic types (ints, strings etc)

 *******************************************************/

namespace 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(offset_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 parametrizet by a class implementing stream window.
   Instance of this 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;  
  size_t  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;
  size_t  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);  
}


/*******************************************************
  
  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:
 
  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( k1 )  
    return find_el(k1,e);
    
  Key k2;  
  if( (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; };
  operator bool() const { return val != 0xFF; };
  static bool valid_key(const Key &k) { return (bool)k; };

  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

/************************************************************
   
   StringPool class
   String pool = map from key8 -> Strings
   
 ************************************************************/   

struct StringDom
{
  typedef String Element;
  static const 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;

}

#endif

--- New file ---
+++ sql/restore_alg.cc	07/02/05 17:09:28
#include "mysql_priv.h"

#include "backup.h"
#include "backup_private.h"
#include "backup_stream.h"

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)
{
  enum { READING, SENDING, DONE, ERROR } state= READING;
  
  Restore_subimage  *img= const_cast<Restore_info&>(info).images.head();
  DBUG_ASSERT(img);
  
  Restore_driver  *drv= img->driver();
  DBUG_ASSERT(drv);

  Buffer  buf;
  uint    img_no;
  uint    repeats=0, errors= 0;
  
  static const uint MAX_ERRORS= 3;
  static const uint MAX_REPEATS= 7;

  drv->begin();
  
  // 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 )
      {
        DBUG_PRINT("restore",("Skipping data for subimage %d",img_no));
        state= READING;
        break;
      }

      switch( drv->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));

  drv->end();
  drv->free();  
  
  return state != ERROR;
}


} // backup namespace



--- 1.4/storage/archive/abackup.cc	2007-02-05 17:09:38 +01:00
+++ 1.5/storage/archive/abackup.cc	2007-02-05 17:09:38 +01:00
@@ -1,14 +1,15 @@
-#include <mysql_priv.h>
+#include "mysql_priv.h"
 #include "ha_archive.h"
 #include "backup.h"
 
 namespace archive_backup {
 
-typedef backup::result_t  result_t;
-typedef backup::version_t version_t;
-typedef backup::Table_map Table_map;
-typedef backup::Table_ref Table_ref;
-typedef backup::Buffer    Buffer;
+using backup::byte;
+using backup::result_t;
+using backup::version_t;
+using backup::Table_list;
+using backup::Table_ref;
+using backup::Buffer;
 
 
 // instances of Table represent tables stored in the engine
@@ -16,53 +17,55 @@
 struct Table
 {
   Table(const Table_ref &tbl);
-  const char *path() const { return m_path ? m_path : ""; };
   int open_meta_file(int mode);
   int open_data_file(int mode);
-  ha_archive *open();
+  ha_archive *open(int);
 
- private:
+ protected:
   
-  const char      *m_path;
-  const Table_ref &tbl;
+  String m_name;
   static TABLE_SHARE ts;  // needed for creating table handler
 };
 
-Table::Table(const Table_ref &tbl):tbl(tbl),m_path(NULL) 
+Table::Table(const Table_ref &tbl)
 {
-  uint len= strlen(tbl.path());
-  m_path= sql_alloc(len+1);
-  if( m_path )
-    strncpy(const_cast<char*>(m_path),tbl.path(),len+1);
+  // TODO: find a better way to ensure that the table is correctly identified by its name
+  //       inside archive engine (perhaps use share?)
+  m_name.append("./");
+  m_name.append(tbl.db().name());
+  m_name.append("/");
+  m_name.append(tbl.name());
 }
 
  
 int Table::open_data_file(int options)
 {
-  String file_path;
-  file_path.set_ascii(path(),strlen(path()));
-  file_path.append(".ARZ");
+  char   path[FN_REFLEN];
   
-  return my_open(file_path.ptr(),options,MYF(0));  
+  fn_format(path, m_name.ptr(), "",
+              ARZ,MY_REPLACE_EXT|MY_UNPACK_FILENAME);
+
+  return my_open(path,options,MYF(0));  
 }
 
 int Table::open_meta_file(int options)
 {
-  String file_path;
-  file_path.set_ascii(path(),strlen(path()));
-  file_path.append(".ARM");
+  char   path[FN_REFLEN];
   
-  return my_open(file_path.ptr(),options,MYF(0));
+  fn_format(path, m_name.ptr(), "", ARM,
+            MY_REPLACE_EXT|MY_UNPACK_FILENAME);
+    
+  return my_open(path,options,MYF(0));
 }
 
 TABLE_SHARE Table::ts;
 
-ha_archive *Table::open()
+ha_archive *Table::open(int options= 0)
 {
   bzero(&ts,sizeof(TABLE_SHARE));
   ha_archive *ha= new ha_archive(&ts); // is it ok to use one table share
                                        // for all handlers?
-  ha->open(path(),0,0);   
+  ha->open(m_name.ptr(),0,options);   
   
   return ha;                                          
 }
@@ -77,8 +80,8 @@
     
     const version_t version() const { return 0; };
     
-    result_t get_backup(const Table_map &tables, Backup_engine::Backup* &eng);
-    result_t get_restore(version_t ver, const Table_map &tables,
Backup_engine::Restore* &eng);
+    result_t get_backup(const Table_list &tables, Backup_driver* &drv);
+    result_t get_restore(version_t ver, const Table_list &tables,Restore_driver*
&drv);
 
     void free() { delete this; };
     
@@ -92,22 +95,23 @@
 
 class Table_backup;
 
-class Backup: public Backup_engine::Backup
+class Backup: public Backup_driver
 {
   public:
   
     enum has_data_info { YES, WAIT, EOD };
     
-    Backup(const backup::Table_map &tables);
+    Backup(const Table_list &tables);
     virtual ~Backup();
         
-    size_t    buffer_size() { return 640; };
+    //size_t    buffer_size() { return 640; };
     size_t    size()  { return UNKNOWN_SIZE; };
     size_t    init_size() { return 0; };
-    result_t  start_backup();
+    result_t  begin();
+    result_t  end();
     result_t  get_data(Buffer &buf);
     result_t  lock();
-    result_t  cont();
+    result_t  unlock();
     result_t  cancel() { return backup::OK; };
         
     void free() { delete this; };
@@ -142,20 +146,20 @@
 };
 
 
-result_t Engine::get_backup(const Table_map &tables, Backup_engine::Backup* &eng)
+result_t Engine::get_backup(const Table_list &tables, Backup_driver* &drv)
 {
   Backup *ptr= new archive_backup::Backup(tables);
   
   if( !ptr ) return backup::ERROR;
   
-  eng= ptr;
+  drv= ptr;
   
   return backup::OK;
 }
 
 
-Backup::Backup(const backup::Table_map &tables):
-  Backup_engine::Backup(tables), state(START), images(NULL), stream(0)
+Backup::Backup(const Table_list &tables):
+  Backup_driver(tables), state(START), images(NULL), stream(0)
 {
   // show info from handler share for each table
   DBUG_PRINT("archive/backup",("Creating backup driver"));
@@ -169,7 +173,7 @@
       delete images[n];
 }
 
-result_t Backup::start_backup()
+result_t Backup::begin()
 {
   DBUG_ENTER("archive/backup/start_backup");
   
@@ -178,6 +182,12 @@
   DBUG_RETURN(backup::OK);
 }
 
+result_t Backup::end()
+{
+  return state == DONE ? backup::OK : backup::ERROR;
+}
+
+
 result_t Backup::get_data(Buffer &buf)
 {
   // TODO: assert buf.data != NULL
@@ -254,7 +264,7 @@
 }
 
 
-result_t Backup::cont() 
+result_t Backup::unlock() 
 { 
   return backup::OK; 
 }
@@ -266,7 +276,8 @@
 Table_backup::Table_backup(const backup::Table_ref &tbl):
   Table(tbl), handler(NULL), fd(-1), pos(0), read(0), state(START)
 {
-  DBUG_PRINT("archive/backup",("Initializing backup image for table %s",path()));
+  DBUG_PRINT("archive/backup",("Initializing backup image for table %s/%s",
+                                tbl.db().name().ptr(), tbl.name().ptr()));
   
   fd= open_data_file(O_RDONLY);
   if( fd < 0 )
@@ -299,7 +310,7 @@
   };
   
   // get handler and flush pending data
-  ha_archive *handler =open();
+  ha_archive *handler= open(HA_OPEN_FOR_REPAIR);
 
   if ( !handler ) 
   {
@@ -309,8 +320,9 @@
   };
 
   handler->flush();
-  delete handler;
     
+  delete handler; 
+
   // save current pos
   my_off_t curr= my_tell(fd,MYF(0));
   // find final position
@@ -322,15 +334,17 @@
 
   // save copy of metafile
 
-  String metafile(path(),system_charset_info);
-  metafile.append(".ARM");
-
-  metafile_backup.copy(metafile);
+  char meta_path[FN_REFLEN];
+  
+  fn_format(meta_path, m_name.ptr(), "", ARM,
+            MY_REPLACE_EXT|MY_UNPACK_FILENAME);
+            
+  metafile_backup.set_ascii(meta_path,strlen(meta_path));
   metafile_backup.append(".bak");
 
-  DBUG_PRINT("archive/backup",(" copying %s -> %s",metafile.ptr(),
+  DBUG_PRINT("archive/backup",(" copying %s -> %s",meta_path,
                                                    metafile_backup.ptr()));
-  res= my_copy(metafile.ptr(),metafile_backup.ptr(),MYF(0));
+  res= my_copy(meta_path,metafile_backup.ptr(),MYF(0));
   if( res ) DBUG_PRINT("archive/backup",("  error %d",res));    
   
   state= META;
@@ -365,13 +379,15 @@
       {
         int fd;
         
+        DBUG_ASSERT(howmuch >= 640);
+        
         state= ERROR;
         
         fd= my_open(metafile_backup.c_ptr(),O_RDONLY,MYF(0));
         if( fd < 0 )
           DBUG_RETURN(backup::ERROR);
         
-        res= my_read(fd,buf.data,howmuch,MYF(0));
+        res= my_read(fd,reinterpret_cast< ::byte* >(buf.data),howmuch,MYF(0));
         if( res <= 0 )
           DBUG_RETURN(backup::ERROR);
         
@@ -403,7 +419,7 @@
           DBUG_RETURN(backup::OK);
         };
         
-        res= my_read(fd,buf.data,howmuch,MYF(0));
+        res= my_read(fd,reinterpret_cast< ::byte* >(buf.data),howmuch,MYF(0));
         if( res < 0 )
           DBUG_RETURN(backup::ERROR);
         if( res == 0 ) // end of file
@@ -443,19 +459,20 @@
 
 class Table_restore;
 
-class Restore: public Backup_engine::Restore
+class Restore: public Restore_driver
 {
   public:
   
     enum has_data_info { YES, WAIT, EOD };
     
-    Restore(const backup::Table_map &tables);
+    Restore(const Table_list &tables);
     virtual ~Restore();
         
-    size_t    buffer_size() { return 0; };
-    result_t  prepare();
+    //size_t    buffer_size() { return 0; };
+    result_t  begin();
+    result_t  end();
     result_t  send_data(Buffer &buf);
-    result_t  cont();
+    //result_t  cont();
     result_t  cancel() { return backup::OK; };
         
     void free() { delete this; };
@@ -484,20 +501,20 @@
   enum { META, DATA, DONE, ERROR } stage;
 };
 
-result_t Engine::get_restore(version_t ver, const Table_map &tables,
Backup_engine::Restore* &eng)
+result_t Engine::get_restore(version_t ver, const Table_list &tables, Restore_driver*
&drv)
 {
   Restore *ptr= new archive_backup::Restore(tables);
   
   if( !ptr ) return backup::ERROR;
   
-  eng= ptr;
+  drv= ptr;
   
   return backup::OK;
 }
 
 
-Restore::Restore(const backup::Table_map &tables):
-  Backup_engine::Restore(tables),stage(START),tables(0),images(NULL)
+Restore::Restore(const Table_list &tables):
+  Restore_driver(tables),stage(START),tables(0),images(NULL)
 {
   DBUG_PRINT("archive/restore",("Creating restore driver"));
 }
@@ -510,7 +527,7 @@
       delete images[n];
 }
 
-result_t Restore::prepare()
+result_t Restore::begin()
 {
   tables= m_tables.count();
   
@@ -523,6 +540,11 @@
   return backup::OK;
 }
 
+result_t Restore::end()
+{
+  return backup::OK;
+}
+
 result_t Restore::send_data(Buffer &buf)
 {
   DBUG_PRINT("archive/restore",("Got packet with %d bytes from stream %d",
@@ -560,10 +582,6 @@
 
 }
 
-result_t Restore::cont()
-{
-  return backup::OK;
-}
 
 /*****
  * Restoring single table
@@ -576,57 +594,62 @@
 
 result_t Table_restore::send_data(const Buffer &buf)
 {
-  switch( stage )
+  switch( stage ) {
+  case META: // save first block in the meta file
   {
-    case META: // save first block in the meta file
+    int mfd= open_meta_file(O_WRONLY);
+    
+    if( mfd < 0 )
     {
-      int mfd= open_meta_file(O_WRONLY);
-      if( mfd < 0 )
-      {
-        DBUG_PRINT("archive/restore",("cant open meta file"));
-        stage= ERROR;
-        return backup::ERROR;
-      };
-      uint howmuch= my_write(mfd,buf.data,buf.size,MYF(0));
-      my_close(mfd,MYF(0));
-      if( howmuch != buf.size )
-      {
-        DBUG_PRINT("archive/restore",("Error writing meta file (size=%d,written=%d)",
-                                      buf.size,howmuch));
-        stage= ERROR;
-        return backup::ERROR;
-      };
-      
-      // open data file for DATA phase
-      fd= open_data_file(O_WRONLY);
-      if( mfd < 0 )
-      {
-        DBUG_PRINT("archive/restore",("cant open data file"));
-        stage= ERROR;
-        return backup::ERROR;
-      };
-      stage= DATA;
-      return backup::OK;
-    };
+      DBUG_PRINT("archive/restore",("cant open meta file"));
+      stage= ERROR;
+      return backup::ERROR;
+    }
+    
+    uint howmuch= my_write(mfd,reinterpret_cast< ::byte*
>(buf.data),buf.size,MYF(0));
+    my_close(mfd,MYF(0));
+    
+    if( howmuch != buf.size )
+    {
+      DBUG_PRINT("archive/restore",("Error writing meta file (size=%d,written=%d)",
+                                    buf.size,howmuch));
+      stage= ERROR;
+      return backup::ERROR;
+    }
+    
+    // open data file for DATA phase
+    fd= open_data_file(O_WRONLY);
+    
+    if( fd < 0 )
+    {
+      DBUG_PRINT("archive/restore",("cant open data file"));
+      stage= ERROR;
+      return backup::ERROR;
+    }
+    
+    stage= DATA;
+    return backup::OK;
+  }
 
-    case DATA: // write to data file
+  case DATA: // write to data file
+  {
+    uint howmuch= my_write(fd,reinterpret_cast< ::byte*
>(buf.data),buf.size,MYF(0));
+    
+    if( howmuch != buf.size )
     {
-      uint howmuch= my_write(fd,buf.data,buf.size,MYF(0));
-      if( howmuch != buf.size )
-      {
-        DBUG_PRINT("archive/restore",("Error writing to data file
(packet=%d,written=%d)",
-                                      buf.size,howmuch));
-        my_close(fd,MYF(0));                         
-        fd= -1;               
-        stage= ERROR;
-        return backup::ERROR;
-      };      
-    };
+      DBUG_PRINT("archive/restore",("Error writing to data file (packet=%d,written=%d)",
+                                    buf.size,howmuch));
+      my_close(fd,MYF(0));                         
+      fd= -1;               
+      stage= ERROR;
+      return backup::ERROR;
+    }      
+  }
+  
+  default: // ignore packet
+    return backup::OK;
     
-    default: // ignore packet
-      return backup::OK;
-      
-  };
+  }
 }
 
 result_t Table_restore::finish()
@@ -656,8 +679,14 @@
 
 Backup_result_t archive_backup_engine(handlerton *self, Backup_engine* &be) 
 { 
+  //static archive_backup::Engine engine_instance;
+  //be= &engine_instance;
+    
   be= new archive_backup::Engine();
-  if( !be )  return backup::ERROR;
+  
+  if( !be )  
+    return backup::ERROR;
+  
   return backup::OK;
   
   /*
Thread
bk commit into 5.1 tree (rafal:1.2357)rsomla16 Feb