List:Commits« Previous MessageNext Message »
From:cbell Date:June 20 2007 8:03pm
Subject:bk commit into 5.1 tree (cbell:1.2539)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of cbell. When cbell does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-06-20 16:03:29-04:00, cbell@stripped +7 -0
  WL#3570 : Default algorithm backup & restore (blocking)
  
  This patch contains enhancements to the default backup algorithm. These
  enhancements include buffering of data during read and write, better
  open and lock methods for backup, and documentation updates.
  
  The patch also contains the Buffer_iterator class and header files. The
  Buffer_iterator class is used to buffer data during read and write that
  is larger than the kernel's stream buffer.
  

  sql/CMakeLists.txt@stripped, 2007-06-20 16:03:22-04:00, cbell@stripped +1 -1
    WL#3570 : Default backup algorithm
    
    This patch adds the buffer_iterator class to the cmake input file.

  sql/backup/Makefile.am@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +2 -0
    WL#3570 : Default backup algorithm
    
    This patch adds the buffer_iterator class to the make input file.

  sql/backup/be_default.cc@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +567 -382
    WL#3570 : Default backup algorithm backup & restore
        
    This patch adds a buffering mechanism to read and write operations. The
    Buffer_iterator class used by the default driver algorithm provides
    a mechanism to store the record buffer and blob fields and split the 
    data into smaller pieces for reading (backup) and to reassemble them 
    for writing (restore).

  sql/backup/be_default.h@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +87 -206
    WL#3570 : Default backup algorithm backup & restore
            
    This patch adds a Buffer_iterator class used by the default driver 
    algorithm. The patch also includes numerous documentation updates for
    doxygen formatting.

  sql/backup/buffer_iterator.cc@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +201 -0
    WL#3570 : Default algorithm backup & restore (blocking)
        
    This patch adds the buffer_iterator class file for the Buffer_iterator
    class. This class can be used to buffer data into smaller chunks during
    the backup operation or recombine them during the restore operation.
    

  sql/backup/buffer_iterator.cc@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +0 -0

  sql/backup/buffer_iterator.h@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +90 -0
    WL#3570 : Default algorithm backup & restore (blocking)
    
    This patch adds the buffer_iterator header file for the Buffer_iterator
    class. This class can be used to buffer data into smaller chunks during
    the backup operation or recombine them during the restore operation.
    

  sql/backup/buffer_iterator.h@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +0 -0

  sql/sql_yacc.yy@stripped, 2007-06-20 16:03:23-04:00, cbell@stripped +2 -2
    WL#3570 : Default backup algorithm
    
    This patch removes the DATABASE keyword from the RESTORE command.

# 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:	cbell
# Host:	suse.vabb.com
# Root:	/home/Chuck/source/mysql-5.1_WL_3570

--- 1.548/sql/sql_yacc.yy	2007-05-10 18:20:24 -04:00
+++ 1.549/sql/sql_yacc.yy	2007-06-20 16:03:23 -04:00
@@ -5582,14 +5582,14 @@
 
 
 restore:
-	RESTORE_SYM DATABASE
+	RESTORE_SYM
 	{
 	   Lex->sql_command = SQLCOM_RESTORE;
      Lex->db_list.empty();
 	}
 	/*database_list*/ FROM TEXT_STRING_sys
   {
-	  Lex->backup_dir = $5; 
+	  Lex->backup_dir = $4; 
   };
 
 backup:

--- 1.16/sql/backup/be_default.cc	2007-05-10 18:20:24 -04:00
+++ 1.17/sql/backup/be_default.cc	2007-06-20 16:03:23 -04:00
@@ -4,7 +4,7 @@
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 2 of the License.
 
-   This program is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
@@ -15,9 +15,9 @@
 */
 
 /**
- * @file
+ * @file 
  *
- * @brief Implements the default backup engine.
+ * @brief Contains the default backup algorithm driver.
  *
  * This file contains the default backup algorithm (also called a "driver"
  * in the online backup terminology. The default backup algorithm may be
@@ -30,38 +30,39 @@
  * backup is a row-level backup and therefore does not backup the indexes
  * or any of the engine-specific files.
  *
- * The classes in this file use the namespace "default_backup" to distinguish
+ * The classes in this file use the namespace @c default_backup to distinguish
  * these classes from other backup drivers. The backup functionality is
  * contained in the backup class shown below. Similarly, the restore
  * functionality is contained in the restore class below.
  *
- * The format of the backup is written as a series of data blocks where the
- * first block contains the record buffer (table->record[0]). This is
- * followed by one or more blocks containing the blob (text) fields one per
- * block. This sequence is repeated for each row in the table.
- * <pre>
- * blknum   contents
- * ------------------------------------------------------
- *   1      (byte)   copy record_buffer read from handler
- * ------------------------------------------------------
- *   2...n  (uint32) size of blob data to read
- *          (byte)   blob data for blob
- * ------------------------------------------------------
- * </pre>
+ * The format of the backup is written as a series of data blocks where each
+ * block contains a flag indicating what kind of data is in the block. The 
+ * flags are:
+ *
+ * <code>
+ *   RCD_ONCE   - Single data block for record data
+ *   RCD_FIRST  - First data block in buffer for record buffer
+ *   RCD_DATA   - Intermediate data block for record buffer
+ *   RCD_LAST   - Last data block in buffer for record buffer
+ *   BLOB_ONCE  - Single data block for blob data
+ *   BLOB_FIRST - First data block in buffer for blob buffer
+ *   BLOB_DATA  - Intermediate data block for blob buffer
+ *   BLOB_LAST  - Last data block in buffer for blob buffer
+ * </code>
+ *
+ * The flag is the first byte in the block. The remaining space in the block
+ * is the data -- either record data or blob fields.
+ *
+ * The block flagged as BLOB_FIRST also contains a 4-byte field which 
+ * contains the total size of the blob field. This is necessary for restore
+ * because the size of the blob field is unknown and the size is needed to 
+ * allocate memory for the buffer_iterator used to buffer large data from
+ * the kernel.
  */
-
 #include "../mysql_priv.h"
-
-#include "backup_aux.h"
+#include "backup_engine.h"
 #include "be_default.h"
-
-/*
-  TODO list:
-    - Change code in send_data to allow random table writes -- the backup
-      algorithm may send blocks of data in a random order from among the
-      tables in the list.
-    - (optional) Change code to use a big switch in get_data and send_data.
-*/
+#include "backup_aux.h"
 
 namespace default_backup {
 
@@ -77,7 +78,20 @@
   m_thd= t_thd;
 }
 
-result_t Engine::get_backup(const uint32, const Table_list &tables, Backup_driver* &drv)
+/**
+  * Create a default backup backup driver.
+  *
+  * Given a list of tables to be backed-up, create instance of backup
+  * driver which will create backup image of these tables.
+  *
+  * @param  tables (in) list of tables to be backed-up.
+  * @param  eng    (out) pointer to backup driver instance.
+  *
+  * @retval  backup::ERROR  if cannot create backup driver class.
+  * @retval  backup::OK     on success.
+  */
+result_t Engine::get_backup(const uint32, const Table_list &tables,
+                            Backup_driver* &drv)
 {
   DBUG_ENTER("Engine::get_backup");
   Backup *ptr= new default_backup::Backup(tables, m_thd);
@@ -91,90 +105,72 @@
 {
   DBUG_PRINT("default_backup",("Creating backup driver"));
   m_thd= t_thd;         /* save current thread */
-  initialized= 0;       /* begin in initialize mode */
-  cur_table= NULL;      /* flag current table a null */
+  cur_table= NULL;      /* flag current table as null */
   tbl_num= 0;           /* set table number to 0 */
-  get_next_table= 1;    /* turn on get next table mode */
-  last_read_res = 0;    /* initialize last table result */
+  mode= INITIALIZE;     /* initialize read */
 
   /*
      Create a TABLE_LIST * list for iterating through the tables.
      Initialize the list for opening the tables in read mode.
   */
-  tables_in_backup= build_table_list(tables, TL_READ);
+  tables_in_backup= build_table_list(tables, TL_READ_NO_INSERT);
   all_tables= tables_in_backup;
 }
 
-Backup::~Backup()
-{
-}
-
-/*
-  Start table read.
-
-  SYNOPSIS
-    start_tbl_read()
-    tbl     Pointer to table
-
-  DESCRIPTION
-    This method saves the handler for the table and initializes the
-    handler for reading.
-
-  RETURNS
-    backup::OK    - handler initialized properly.
-    backup::Error - problem with hander initialization.
-*/
+/**
+  * @brief Start table read.
+  *
+  * This method saves the handler for the table and initializes the
+  * handler for reading.
+  *
+  * @retval backup::OK     handler initialized properly.
+  * @retval backup::ERROR  problem with hander initialization.
+  */
 result_t Backup::start_tbl_read(TABLE *tbl)
 {
+  int last_read_res;  
+
   DBUG_ENTER("Default_backup::start_tbl_read)");
   hdl= tbl->file;
   last_read_res= hdl->ha_rnd_init(1);
-  DBUG_ASSERT(last_read_res == 0);
   if (last_read_res != 0)
     DBUG_RETURN(backup::ERROR);
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  End table read.
-
-  SYNOPSIS
-    end_tbl_read()
-
-  DESCRIPTION
-    This method signals the handler that the reading process is complete.
-
-  RETURNS
-    backup::OK    - handler read stopped properly.
-    backup::Error - problem with hander.
-*/
+/**
+  * @brief End table read.
+  *
+  * This method signals the handler that the reading process is complete.
+  *
+  * @retval backup::OK     handler read stopped properly.
+  * @retval backup::ERROR  problem with hander.
+  */
 result_t Backup::end_tbl_read()
 {
+  int last_read_res;  
+
   DBUG_ENTER("Default_backup::end_tbl_read)");
   last_read_res= hdl->ha_rnd_end();
-  DBUG_ASSERT(last_read_res == 0);
   if (last_read_res != 0)
     DBUG_RETURN(backup::ERROR);
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  Start backup process.
-
-  SYNOPSIS
-    begin()
-
-  DESCRIPTION
-    This method locks all of the tables for reading.
-
-  RETURNS
-    backup::OK    - all tables locked properly.
-    backup::Error - problem with locking tables.
-*/
+/**
+  * @brief Start backup process.
+  *
+  * This method opens all of the tables for reading.
+  *
+  * @retval backup::OK     all tables locked properly.
+  * @retval backup::ERROR  problem with locking tables.
+  */
 result_t Backup::begin(const size_t)
 {
+  uint count;
+
   DBUG_ENTER("Default_backup::start_backup");
-  if (open_and_lock_tables(m_thd, all_tables))
+  if (open_tables(m_thd, &all_tables, &count, 0))
   {
     DBUG_PRINT("backup", ( "error!" ));
     DBUG_RETURN(backup::ERROR);
@@ -182,43 +178,29 @@
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  End backup process.
-
-  SYNOPSIS
-    end()
-
-  DESCRIPTION
-    This method unlocks all of the tables.
-
-  RETURNS
-    backup::OK    - all tables unlocked.
-*/
+/**
+  * @brief End backup process.
+  *
+  * This method unlocks all of the tables.
+  *
+  * @retval backup::OK   all tables closed.
+  */
 result_t Backup::end()
 {
   DBUG_ENTER("Default_backup::end");
-  if (m_thd->lock)
-  {
-    mysql_unlock_tables(m_thd, m_thd->lock);
-    m_thd->lock=0;
-  }
+  close_thread_tables(m_thd);
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  Get next table in the list.
-
-  SYNOPSIS
-    next_table()
-
-  DESCRIPTION
-    This method iterates through the list of tables selecting the
-    next table in the list and starting the read process.
-
-  RETURNS
-    0  - no errors.
-    -1 - no more tables in list.
-*/
+/**
+  * @brief Get next table in the list.
+  *
+  * This method iterates through the list of tables selecting the
+  * next table in the list and starting the read process.
+  *
+  * @retval 0   no errors.
+  * @retval -1  no more tables in list.
+  */
 int Backup::next_table()
 {
   DBUG_ENTER("Backup::next_table()");
@@ -244,50 +226,67 @@
   DBUG_RETURN(0);
 }
 
-/*
-  Get the data for a row in the table.
-
-  SYNOPSIS
-    get_data()
-    buf - the data buffer structure from the backup algorithm
-
-  DESCRIPTION
-    This method is the main method used in the backup operation. It is
-    responsible for reading a row from the table and placing the data in
-    the buffer (buf.data) and setting the correct attributes for processing
-    (e.g., buf.size = size of record data).
-
-    Control of the method is accomplished by using several modes that
-    signal portions of the method to run. These modes are:
-
-    initialized    - Signals (when false) method to initialize variables
-                     and prepare for backup process. When true, indicates
-                     the initialization process is complete.
-    get_next_table - Signals method to move to the next table in the list.
-                     May be used in conjunction with reading and read_blobs.
-    reading        - Signals method to read next row of data.
-    read_blobs     - Signals that row has blobs and to read blob data.
-
-  RETURNS
-    backup::INIT_DODE - initialization phase complete.
-    backup::OK        - data read.
-    backup::Error     - problem with reading data.
-*/
+/**
+  * @brief Get the data for a row in the table.
+  * This method is the main method used in the backup operation. It is
+  * responsible for reading a row from the table and placing the data in
+  * the buffer (buf.data) and setting the correct attributes for processing
+  * (e.g., buf.size = size of record data).
+  *
+  * Control of the method is accomplished by using several modes that
+  * signal portions of the method to run. These modes are:
+  *
+  * <code>
+  * INITIALIZE          Indicates time to initialize read
+  * GET_NEXT_TABLE      Open next table in the list
+  * READ_RCD            Reading rows from table mode
+  * READ_RCD_BUFFER     Buffer records mode
+  * CHECK_BLOBS         See if record has blobs
+  * READ_BLOB           Reading blobs from record mode
+  * READ_BLOB_BUFFER    Buffer blobs mode
+  * READ_COMPLETE       Complete. Return to kernel.
+  * </code>
+  *
+  * @retval backup::READY   initialization phase complete.
+  * @retval backup::OK      data read.
+  * @retval backup::ERROR   problem with reading data.
+  * @retval backup::DONE    driver finished reading from all tables.
+  */
 result_t Backup::get_data(Buffer &buf)
 {
+  int last_read_res;  
+
   DBUG_ENTER("Default_backup::get_data(Buffer &buf)");
 
   buf.table_no= tbl_num;
+  buf.last= FALSE;
+
+  /* 
+    Determine mode of operation and execute mode.
+  */
+  switch (mode) {
+
+  /*
+    Initialize the reading process. Return READY to indicate complete.
+  */
+  case INITIALIZE:
+  {
+    buf.table_no= 0;
+    buf.size= 0;
+    buf.last= TRUE;
+    mode= GET_NEXT_TABLE;
+    DBUG_RETURN(backup::READY);
+  }
+
   /*
     If class has been initialized and we need to read the next table,
     advance the current table pointer and initialize read process.
   */
-  if (initialized && get_next_table)
+  case GET_NEXT_TABLE:
   {
-    int res;
-
-    get_next_table= 0;
-    res= next_table();
+    buf.size= 0;
+    mode= READ_RCD;
+    int res= next_table();
     /*
       If no more tables in list, tell backup algorithm we're done else
       signal reading mode.
@@ -296,45 +295,31 @@
     {
       if (cur_table == NULL)
       {
-        buf.table_no= tbl_num;
-        buf.size= 0;
         buf.last= TRUE;
+        mode= READ_COMPLETE;
       }
-      DBUG_RETURN(backup::READY);
     }
     else
     {
       start_tbl_read(cur_table);
       tbl_num++;
-      reading= 1;
       buf.table_no= tbl_num;
     }
+    break;
   }
-  /*
-    Initialize the reading process. Return READY to indicate complete.
-  */
-  if (!initialized)
-  {
-    buf.size= 0;
-    buf.table_no= 0;
-    buf.last= TRUE;
-    initialized= 1;
-    reading= 1;
-    DBUG_RETURN(backup::OK);
-  }
+
   /*
     Read a row from the table and save the data in the buffer.
   */
-  else if (reading)
+  case READ_RCD:
   {
-    /*
-      If buffer isn't large enough for a record, abort.
-    */
-    if (buf.size < cur_table->s->reclength)
-      DBUG_RETURN(backup::ERROR);
+    uint32 size= cur_table->s->reclength;
+    buf.last= FALSE;
+
+    cur_blob= 0;
     cur_table->use_all_columns();
     last_read_res = hdl->rnd_next(cur_table->record[0]);
-    DBUG_ASSERT(last_read_res == HA_ERR_END_OF_FILE || last_read_res == 0);
+    DBUG_EXECUTE_IF("SLEEP_DRIVER", sleep(4););
     /*
       If we are end of file, stop the read process and signal the
       backup algorithm that we're done. Turn get_next_table mode on.
@@ -344,87 +329,183 @@
       end_tbl_read();
       buf.size= 0;
       buf.last= TRUE;
-      get_next_table= 1;
-      reading= 0;
+      mode= GET_NEXT_TABLE;
     }
+    else if (last_read_res != 0)
+      DBUG_RETURN(backup::ERROR);
     else
     {
       /*
-        Copy the record buffer into the buf.data structure and set
-        the size attribute.
+        Check size of buffer to ensure data fits in the buffer. If it does
+        not fit, create new blob_buffer object.
       */
-      memcpy(buf.data, cur_table->record[0], cur_table->s->reclength);
-      buf.last= FALSE;
-      buf.size= cur_table->s->reclength;
+      if ((size + META_SIZE) <= buf.size)
+      {
+        memcpy((byte *)buf.data, &RCD_ONCE, 1); //only part 1 of 1
+        memcpy((byte *)buf.data + META_SIZE, cur_table->record[0], size);
+        buf.size = size + META_SIZE;
+        mode= CHECK_BLOBS;
+      }
+      else
+      {
+        uint rec_size= 0;
+        byte *rec_ptr= 0;
 
-      /*
-        Check for the existence of blobs. If no blobs, we're finished
-        reading data for this row. If there are blobs, turn read_blob
-        mode on and get the first blob in the list.
-      */
-      read_blobs= (cur_table->s->blob_fields > 0);
-      //read_blobs= 0;
-      if (read_blobs)
+        rec_buffer.initialize(cur_table->record[0], size);
+        rec_size= rec_buffer.get_next((::byte **)&rec_ptr, 
+          (buf.size - META_SIZE));
+        memcpy((byte *)buf.data, &RCD_FIRST, 1); // first part
+        memcpy((byte *)buf.data + META_SIZE, rec_ptr, rec_size);
+        buf.size = rec_size + META_SIZE;
+        mode= READ_RCD_BUFFER;
+      }
+    }
+    break;
+  }
+
+  /*
+    Read data from the record buffer and write to the kernel buffer.
+  */
+  case READ_RCD_BUFFER:
+  {
+    uint rec_size= 0; 
+
+    rec_size= rec_buffer.get_next((::byte **)&ptr, (buf.size - META_SIZE));
+    if (rec_buffer.num_windows(buf.size - META_SIZE) == 0)
+    {
+      memcpy((byte *)buf.data, &RCD_LAST, 1);
+      mode= CHECK_BLOBS;   // Check for blobs.
+      rec_buffer.reset();  // dump the memory 
+    }
+    else
+      memcpy((byte *)buf.data, &RCD_DATA, 1);
+    memcpy((byte *)buf.data + META_SIZE, ptr, rec_size);
+    buf.size = rec_size + META_SIZE;
+    break;
+  }
+
+  /*
+    Check for the existence of blobs. If no blobs, we're finished
+    reading data for this row. If there are blobs, turn read_blob
+    mode on and get the first blob in the list.
+  */
+  case CHECK_BLOBS:
+  {
+    buf.size= 0;
+    if (cur_table->s->blob_fields > 0)
+    {
+      mode= READ_BLOB;
+      if (!cur_blob)
       {
-        reading= 0;
         cur_blob= cur_table->s->blob_field;
         last_blob_ptr = cur_blob + cur_table->s->blob_fields;
       }
+      /*
+        Iterate to the next blob. If no more blobs, we're finished reading
+        the row.
+      */
+      else 
+      {
+        cur_blob++;
+        if (cur_blob == last_blob_ptr)
+          mode= READ_RCD;
+      }
     }
-    DBUG_RETURN(backup::OK);
+    else
+      mode= READ_RCD;
+    break;
   }
+
   /*
-    Read the blobs from the row. Save the size of the blob data first in
-    the buffer (buf.data) then save the data for the blob and set the size
-    accordingly.
+    Get next blob. Use blob buffer if blob field is too large for buffer.data.
   */
-  else if (read_blobs)
+  case READ_BLOB:
   {
-    int meta_size= sizeof(uint32);
-    buf.last= FALSE;
     uint32 size= ((Field_blob*) cur_table->field[*cur_blob])->get_length();
     /*
       Check size of buffer to ensure data fits in the buffer. If it does
-      not fit, exit with an error.
+      not fit, create new blob_buffer object.
     */
-    if ((size + meta_size) <= buf.size)
+    if ((size + META_SIZE) <= buf.size)
     {
-      char *ptr= 0;
-      memcpy((byte *)buf.data, (byte *)&size, sizeof(uint32));
-      ((Field_blob*) cur_table->field[*cur_blob])->get_ptr(&ptr);
-      memcpy((byte *)buf.data + meta_size, ptr, size);
-      buf.size = size + meta_size;
+      memcpy((byte *)buf.data, &BLOB_ONCE, 1);
+      ((Field_blob*) cur_table->field[*cur_blob])->get_ptr((char **)&ptr);
+      memcpy((byte *)buf.data + META_SIZE, ptr, size);
+      buf.size = size + META_SIZE;
+      mode= CHECK_BLOBS;
     }
     else
-      DBUG_RETURN(backup::ERROR);
-    /*
-      Iterate to the next blob. If no more blobs, we're finished reading
-      the row.
-    */
-    cur_blob++;
-    if (cur_blob == last_blob_ptr)
     {
-      reading= 1;
-      read_blobs= 0;
+      uint bb_size= 0;
+      byte *blob_ptr= 0;
+
+      ((Field_blob*) cur_table->field[*cur_blob])->get_ptr((char **)&ptr);
+      blob_buffer.initialize((::byte *)ptr, size);
+      memcpy((byte *)buf.data, &BLOB_FIRST, 1);   //first block
+      uint32 field_size= 
+        ((Field_blob*) cur_table->field[*cur_blob])->get_length();
+      int4store(buf.data + META_SIZE, field_size);     //save max size
+      bb_size= blob_buffer.get_next((::byte **)&blob_ptr, 
+        (buf.size - META_SIZE - 4));
+      memcpy((byte *)buf.data + META_SIZE + 4, blob_ptr, bb_size);
+      buf.size = bb_size + META_SIZE + 4;
+      mode= READ_BLOB_BUFFER;
     }
-    DBUG_RETURN(backup::OK);
+    break;
   }
-  DBUG_RETURN(backup::OK); /* Ignore extraneous calls to get_data() */
-}
 
-result_t Backup::lock()
-{
-  DBUG_ENTER("Default_backup::lock()");
-  DBUG_RETURN(backup::OK);
-}
+/*
+  Read data from the blob buffer.
+*/
+  case READ_BLOB_BUFFER:
+  {
+    uint bb_size= 0;
 
-result_t Backup::unlock()
-{
-  DBUG_ENTER("Default_backup::unlock()");
-  DBUG_RETURN(backup::OK);
+    bb_size= blob_buffer.get_next((::byte **)&ptr, (buf.size - META_SIZE));
+    if (blob_buffer.num_windows(buf.size - META_SIZE) == 0)
+    {
+      memcpy((byte *)buf.data, &BLOB_LAST, 1);
+      mode= CHECK_BLOBS;
+      blob_buffer.reset();     // dump the memory 
+    }
+    else
+      memcpy((byte *)buf.data, &BLOB_DATA, 1);
+    memcpy((byte *)buf.data + META_SIZE, ptr, bb_size);
+    buf.size = bb_size + META_SIZE;
+    break;
+  }
+
+  /* 
+    Reading is complete. Return to kernel.
+  */
+  case READ_COMPLETE:
+  {
+    buf.size= 0;
+    buf.table_no= 0;
+    DBUG_RETURN(backup::DONE);
+  }
+
+  default:
+    DBUG_RETURN(backup::ERROR);
+  }
+  DBUG_RETURN(backup::OK); 
 }
 
-result_t Engine::get_restore(version_t ver, const uint32, const Table_list &tables, Restore_driver* &drv)
+/**
+  * Create a default backup restore driver.
+  *
+  * Given a list of tables to be restored, create instance of restore
+  * driver which will restore these tables from a backup image.
+  *
+  * @param  version  (in) version of the backup image.
+  * @param  tables   (in) list of tables to be restored.
+  * @param  eng      (out) pointer to restore driver instance.
+  *
+  * @retval  backup::ERROR  if cannot create restore driver class.
+  * @retval  backup::OK     on success.
+  */
+result_t Engine::get_restore(version_t ver, const uint32, 
+                             const Table_list &tables, Restore_driver* &drv)
 {
   DBUG_ENTER("Engine::get_restore");
   Restore *ptr= new default_backup::Restore(tables, m_thd);
@@ -438,11 +519,9 @@
 {
   DBUG_PRINT("default_backup",("Creating restore driver"));
   m_thd= t_thd;         /* save current thread */
-  initialized= 0;       /* begin in initialize mode */
-  cur_table= NULL;      /* flag current table a null */
+  cur_table= NULL;      /* flag current table as null */
   tbl_num= 0;           /* set table number to 0 */
-  get_next_table= 1;    /* turn on get next table mode */
-  last_write_res = 0;   /* initialize last table result */
+  mode= INITIALIZE;     /* initialize write */
 
   /*
      Create a TABLE_LIST * list for iterating through the tables.
@@ -452,27 +531,19 @@
   all_tables= tables_in_backup;
 }
 
-Restore::~Restore()
-{
-}
-
-/*
-  Truncate table.
-
-  SYNOPSIS
-    truncate_table()
-    tbl     Pointer to table
-
-  DESCRIPTION
-    This method saves the handler for the table and deletes all rows in
-    the table.
-
-  RETURNS
-    backup::OK    - rows deleted.
-    backup::Error - problem with deleting rows.
-*/
+/**
+  * @brief Truncate table.
+  *
+  * This method saves the handler for the table and deletes all rows in
+  * the table.
+  *
+  * @retval backup::OK     rows deleted.
+  * @retval backup::ERROR  problem with deleting rows.
+  */
 result_t Restore::truncate_table(TABLE *tbl)
 {
+  int last_write_res; 
+
   DBUG_ENTER("Default_backup::truncate_table)");
   hdl= tbl->file;
   last_write_res= cur_table->file->delete_all_rows();
@@ -480,25 +551,19 @@
     Check to see if delete all rows was ok. Ignore if the handler
     doesn't support it.
   */
-  DBUG_ASSERT((last_write_res == 0)||(last_write_res == HA_ERR_WRONG_COMMAND));
   if ((last_write_res != 0) && (last_write_res != HA_ERR_WRONG_COMMAND))
     DBUG_RETURN(backup::ERROR);
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  Start restore process.
-
-  SYNOPSIS
-    begin()
-
-  DESCRIPTION
-    This method locks all of the tables for writing.
-
-  RETURNS
-    backup::OK    - all tables locked properly.
-    backup::Error - problem with locking tables.
-*/
+/**
+  * @brief Start restore process.
+  *
+  * This method locks all of the tables for writing.
+  *
+  * @retval backup::OK     all tables locked properly.
+  * @retval backup::ERROR  problem with locking tables.
+  */
 result_t Restore::begin(const size_t)
 {
   DBUG_ENTER("Restore::begin");
@@ -512,43 +577,29 @@
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  End restore process.
-
-  SYNOPSIS
-    end()
-
-  DESCRIPTION
-    This method unlocks all of the tables.
-
-  RETURNS
-    backup::OK    - all tables unlocked.
-*/
+/**
+  * @brief End restore process.
+  *
+  * This method unlocks and closes all of the tables.
+  *
+  * @retval backup::OK    all tables unlocked.
+  */
 result_t Restore::end()
 {
   DBUG_ENTER("Restore::end");
-  if (m_thd->lock)
-  {
-    mysql_unlock_tables(m_thd, m_thd->lock);
-    m_thd->lock=0;
-  }
+  close_thread_tables(m_thd);
   DBUG_RETURN(backup::OK);
 }
 
-/*
-  Get next table in the list.
-
-  SYNOPSIS
-    next_table()
-
-  DESCRIPTION
-    This method iterates through the list of tables selecting the
-    next table in the list and starting the write process.
-
-  RETURNS
-    0  - no errors.
-    -1 - no more tables in list.
-*/
+/**
+  * @brief Get next table in the list.
+  *
+  * This method iterates through the list of tables selecting the
+  *  next table in the list and starting the write process.
+  *
+  * @retval 0   no errors.
+  * @retval -1  no more tables in list.
+  */
 int Restore::next_table()
 {
   DBUG_ENTER("Restore::next_table()");
@@ -564,52 +615,68 @@
       cur_table= NULL;
       DBUG_RETURN(-1);
     }
-  }
+  } 
   DBUG_RETURN(0);
 }
 
-/*
-  Restore the data for a row in the table.
-
-  SYNOPSIS
-    send_data()
-    buf - the data buffer structure from the backup algorithm
-
-  DESCRIPTION
-    This method is the main method used in the restore operation. It is
-    responsible for writing a row to the table.
-
-    Control of the method is accomplished by using several modes that
-    signal portions of the method to run. These modes are:
-
-    initialized    - Signals (when false) method to initialize variables
-                     and prepare for restore process. When true, indicates
-                     the initialization process is complete.
-    get_next_table - Signals method to move to the next table in the list.
-                     May be used in conjunction with writing and write_blobs.
-    writing        - Signals method to write next row of data.
-    write_blobs    - Signals that row has blobs and to write blob data.
-
-  RETURNS
-    backup::INIT_DODE - initialization phase complete.
-    backup::OK        - data written.
-    backup::Error     - problem with writing data.
-*/
+/**
+  * @brief Restore the data for a row in the table.
+  *
+  * This method is the main method used in the restore operation. It is
+  * responsible for writing a row to the table.
+  *
+  * Control of the method is accomplished by using several modes that
+  * signal portions of the method to run. These modes are:
+  *
+  * <code>
+  * INITIALIZE          Indicates time to initialize read
+  * GET_NEXT_TABLE      Open next table in the list
+  * WRITE_RCD           Writing rows from table mode
+  * CHECK_BLOBS         See if record has blobs
+  * WRITE_BLOB          Writing blobs from record mode
+  * WRITE_BLOB_BUFFER   Buffer blobs mode
+  * WRITE_COMPLETE      Complete. Return to kernel.
+  * </code>
+  *
+  * @retval backup::READY       initialization phase complete.
+  * @retval backup::OK          data written.
+  * @retval backup::ERROR       problem with writing data.
+  * @retval backup::PROCESSING  switching modes -- do not advance stream.
+  * @retval backup::DONE        driver finished writing to all tables.
+  */
 result_t Restore::send_data(Buffer &buf)
 {
+  byte *ptr= 0;
+  int last_write_res; 
+  byte block_type= 0;
+
   DBUG_ENTER("Restore::send_data");
   DBUG_PRINT("default/restore",("Got packet with %lu bytes from stream %u",
                                 (unsigned long)buf.size, buf.table_no));
+  
+  /* 
+    Determine mode of operation and execute mode.
+  */
+  switch (mode) {
+
+  /*
+    Initialize the reading process. Return READY to indicate complete.
+  */
+  case INITIALIZE:
+  {
+    mode= GET_NEXT_TABLE;
+    DBUG_RETURN(backup::PROCESSING);
+  }
 
   /*
-    If class has been initialized and we need to get the next table,
-    advance the current table pointer and initialize write process.
+    If class has been initialized and we need to read the next table,
+    advance the current table pointer and initialize read process.
   */
-  if (initialized && get_next_table)
+  case GET_NEXT_TABLE:
   {
     int res;
 
-    get_next_table= 0;
+    mode= WRITE_RCD;
     /*
       It is possible for the backup system to send data to this
       engine out of sequence from the table list. When a non-sequential
@@ -637,105 +704,223 @@
     {
       if (cur_table == NULL)
       {
-        buf.size= 0;
         buf.last= TRUE;
+        mode= WRITE_COMPLETE;
       }
-      DBUG_RETURN(backup::OK);
     }
     else
     {
       truncate_table(cur_table); /* delete all rows from table */
       tbl_num++;
-      writing= 1;
     }
+    if (mode != WRITE_COMPLETE)
+      DBUG_RETURN(backup::PROCESSING);
+    break;
   }
+
   /*
-    Initialize the writing process. Return INIT_DONE to indicate complete.
-  */
-  if (!initialized)
-  {
-    initialized= 1;
-    writing= 1;
-    DBUG_RETURN(backup::PROCESSING);
-  }
-  /*
-    If the table number is different from the stream number, we're
-    receiving data from the backup for a different table. Set the mode to
-    get the next table in the list.
+    Write a row to the table from the data in the buffer.
   */
-  else if (tbl_num != buf.table_no)
+  case WRITE_RCD:
   {
-    get_next_table= 1;
-    DBUG_RETURN(backup::PROCESSING);
+    cur_blob= 0;
+    max_blob_size= 0;
+
+    /*
+      If the table number is different from the stream number, we're
+      receiving data from the backup for a different table. Set the mode to
+      get the next table in the list.
+    */
+    if (tbl_num != buf.table_no)
+    {
+      mode= GET_NEXT_TABLE;
+      DBUG_RETURN(backup::PROCESSING);
+    }
+    else
+    {
+      uint32 size= buf.size - META_SIZE;
+      memcpy(&block_type, buf.data, 1);
+      cur_table->use_all_columns();
+      /*
+         Now we're reconstructing the rec from multiple parts.
+      */
+      switch (block_type) {
+
+      /*
+        Buffer iterator not needed, just write the data.
+      */
+      case RCD_ONCE:
+      {
+        if (size == cur_table->s->reclength)
+        {
+          ptr= (byte *)my_malloc(size, MYF(MY_WME));
+          memcpy(cur_table->record[0], (byte *)buf.data + META_SIZE, size);
+          mode= CHECK_BLOBS;
+          DBUG_RETURN(backup::PROCESSING);
+        }
+        else 
+          DBUG_RETURN(backup::ERROR);
+      }
+
+      /*
+        This is the first part of several, create new iterator.
+      */
+      case RCD_FIRST:
+      {
+        rec_buffer.initialize(cur_table->s->reclength);
+        rec_buffer.put_next((::byte *)buf.data + META_SIZE, size);
+        mode= WRITE_RCD;
+        break;
+      }
+
+      /*
+        Save the part and keep reading.
+      */
+      case RCD_DATA:
+      {
+        rec_buffer.put_next((::byte *)buf.data + META_SIZE, size);
+        mode= WRITE_RCD;
+        break;
+
+      }
+      /*
+        If this is the last part, assemble and write.
+      */
+      case RCD_LAST:
+      {
+        rec_buffer.put_next((::byte *)buf.data + META_SIZE, size);
+        ptr= (byte *)rec_buffer.get_base_ptr();
+        memcpy(cur_table->record[0], ptr, cur_table->s->reclength);
+        mode= CHECK_BLOBS;
+        DBUG_RETURN(backup::PROCESSING);
+      }
+      default:
+        DBUG_RETURN(backup::ERROR);
+      }
+    }
+    break;
   }
+
   /*
-    Write the row from the buffer to the table.
+    Check for the existence of blobs. If no blobs, we're finished
+    writing data for this row so just run the write_row(). If there
+    are blobs, turn write_blob mode on and get the first blob in
+    the list. The write_row() call will be delayed until after all
+    blobs are written.
   */
-  else if (writing)
+  case CHECK_BLOBS:
   {
-    memcpy(cur_table->record[0], buf.data, cur_table->s->reclength);
-    cur_table->use_all_columns();
-    /*
-      Check for the existence of blobs. If no blobs, we're finished
-      writing data for this row so just run the write_row(). If there
-      are blobs, turn write_blob mode on and get the first blob in
-      the list. The write_row() call will be delayed until after all
-      blobs are written.
-    */
-    write_blobs= (cur_table->s->blob_fields > 0);
-    if (!write_blobs)
+    my_bool write_row= (cur_table->s->blob_fields == 0);
+    if (cur_table->s->blob_fields > 0)
     {
-      last_write_res = hdl->write_row(cur_table->record[0]);
-      DBUG_ASSERT(last_write_res == 0);
-      if (last_write_res == 0)
-        writing= 1;
+      mode= WRITE_BLOB;
+      if (!cur_blob)
+      {
+        cur_blob= cur_table->s->blob_field;
+        last_blob_ptr = cur_blob + cur_table->s->blob_fields;
+      }
+      /*
+        Iterate to the next blob. If no more blobs, we're finished writing 
+        the row.
+      */
+      else 
+      {
+        cur_blob++;
+        if (cur_blob == last_blob_ptr)
+          write_row= TRUE;
+      }
     }
-    else
+    if (write_row)
     {
-      writing= 0;
-      cur_blob= cur_table->s->blob_field;
-      last_blob_ptr = cur_blob + cur_table->s->blob_fields;
+      last_write_res = hdl->write_row(cur_table->record[0]);
+      if (last_write_res == 0)
+        mode= WRITE_RCD;
+      else
+        DBUG_RETURN(backup::ERROR);
     }
-    DBUG_RETURN(backup::OK);
+    break;
   }
+
   /*
     Write the blobs for the row. Get the size from the buffer and write
     the buffer to the blob field for the specified number of bytes.
   */
-  else if (write_blobs)
+  case WRITE_BLOB:
   {
-    uint32 size= 0;
-    byte *ptr;
-    int meta_size= sizeof(uint32);
+    uint32 size= buf.size - META_SIZE;
+
+    memcpy(&block_type, buf.data, 1);
+    switch (block_type) {
 
     /*
-      Check size of buffer to ensure data size matches the buffer. If it does
-      not match, exit with an error.
+      Buffer iterator not needed, just write the data.
     */
-    memcpy((byte *)&size, (byte *)buf.data, sizeof(uint32));
-    if ((size + meta_size) <= buf.size)
+    case BLOB_ONCE:
     {
       ptr= (byte *)my_malloc(size, MYF(MY_WME));
-      memcpy(ptr, (byte *)buf.data + meta_size, size);
+      memcpy(ptr, (byte *)buf.data + META_SIZE, size);
       ((Field_blob*) cur_table->field[*cur_blob])->set_ptr(size, (char *)ptr);
+      mode= CHECK_BLOBS;
+      DBUG_RETURN(backup::PROCESSING);
     }
+
     /*
-      Iterate to the next blob. If no more blobs, we're finished writing
-      the row.
+      This is the first part of several, create new iterator.
     */
-    cur_blob++;
-    if (cur_blob == last_blob_ptr)
+    case BLOB_FIRST:
     {
-      last_write_res = hdl->write_row(cur_table->record[0]);
-      DBUG_ASSERT(last_write_res == 0);
-      if (last_write_res == 0)
-        writing= 1;
-      write_blobs= 0;
+      max_blob_size= uint4korr(buf.data + META_SIZE);
+      blob_buffer.initialize(max_blob_size);
+      size= buf.size - META_SIZE - 4;
+      blob_buffer.put_next((::byte *)buf.data + META_SIZE + 4, size);
+      mode= WRITE_BLOB;
+      break;
+    }
+
+    /*
+      Save the part and keep reading.
+    */
+    case BLOB_DATA:
+    {
+      blob_buffer.put_next((::byte *)buf.data + META_SIZE, size);
+      mode= WRITE_BLOB;
+      break;
     }
-    DBUG_RETURN(backup::OK);
+
+    /*
+      If this is the last part, assemble and write.
+    */
+    case BLOB_LAST:
+    {
+      blob_buffer.put_next((::byte *)buf.data + META_SIZE, size);
+      ptr= (byte *)blob_buffer.get_base_ptr();
+      ((Field_blob*) cur_table->field[*cur_blob])->set_ptr(max_blob_size, 
+        (char *)ptr);
+      mode= CHECK_BLOBS;
+      DBUG_RETURN(backup::PROCESSING);
+    }
+    default:
+      DBUG_RETURN(backup::ERROR);
+    }
+    break;
   }
-  DBUG_RETURN(backup::OK); /* Ignore extraneous calls to send_data() */
+
+  /* 
+    Writing is complete. Return to kernel.
+  */
+  case WRITE_COMPLETE:
+  {
+    buf.size= 0;
+    buf.table_no= 0;
+    DBUG_RETURN(backup::DONE);
+  }
+
+  default:
+    DBUG_RETURN(backup::ERROR);
+  }
+  DBUG_RETURN(backup::OK); 
 }
 
 } /* default_backup namespace */
+
 
--- New file ---
+++ sql/backup/buffer_iterator.cc	07/06/20 16:03:23
/* Copyright (C) 2004-2007 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful, 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

/**
 * @file
 *
 * @brief Contains a buffering class for breaking large data into parts.
 *
 * This file contains a buffering class for buffering large chunks of
 * data. It can be used to store a large chunk of data and iterate
 * through windows of a specified size until all the data is read.
 * It can also be used to recombine the data from smaller windows.
  */

#include "buffer_iterator.h"

/**
 * @brief Create a buffer iterator.
 *
 * Given a pointer to a block of data, its maximum size, and
 * window size, start iterator for reading or writing data.
 *
 * @param  buff_ptr (in) a pointer to a block of memory
 * @param  size     (in) the maximum size of the data
 */
int Buffer_iterator::initialize(byte *buff_ptr, long size)
{
  DBUG_ENTER("buffer_iterator::initialize(buff_ptr, size, window)");
  buffer= buff_ptr;
  max_size= size;
  window_size= 0;
  alloc_used= false;
  cur_bytes_read= 0;
  cur_ptr= buffer;
  DBUG_RETURN(0); 
}

/**
 * @brief Create a buffer iterator.
 *
 * Given the maximum size of a block of data and the
 * window size, start iterator for reading or writing data.
 *
 * @param  size     (in) the maximum size of the data
 */
int Buffer_iterator::initialize(long size)
{
  DBUG_ENTER("buffer_iterator::initialize(size, window)");
  buffer= (byte *)my_malloc(size, MYF(MY_WME));
  max_size= size;
  window_size= 0;
  alloc_used= true;
  cur_bytes_read= 0;
  cur_ptr= buffer;
  DBUG_RETURN(0); 
}

/**
 * @brief Reset buffer iterator.
 *
 * Destroy any memory used.
 */
int Buffer_iterator::reset()
{
  DBUG_ENTER("buffer_iterator::reset()");
  if (alloc_used && buffer)
    my_free((gptr)buffer, MYF(0));
  DBUG_RETURN(0);
}

/**
 * @brief Get the next window of data in the iterator.
 *
 * This method retrieves the next window in the iterator. It
 * returns the number of bytes read (may be less if last
 * window is smaller than the max window size), and updates
 * the pointer passed as an argument.
 *
 * @param  buff_ptr  (in) a pointer to the window to be read
 * @param  window    (in) the size of the window
 * 
 * @retval the size of the window
 */
long Buffer_iterator::get_next(byte **buff_ptr, long window)
{
  long bytes_read;

  DBUG_ENTER("buffer_iterator::get_next()");
  *buff_ptr= cur_ptr;
  if (*buff_ptr)
  {
    if (!window_size)
      window_size= window;
    if ((cur_bytes_read + window_size) < max_size)
    {
      cur_ptr= cur_ptr + window_size;
      bytes_read= window_size;
      cur_bytes_read= cur_bytes_read + window_size;
    }
    else
    { 
      cur_ptr= 0;
      bytes_read= max_size - cur_bytes_read;
      cur_bytes_read= max_size;
    }
  }
  else
    bytes_read= 0;
  DBUG_RETURN(bytes_read);
}

/**
 * @brief Insert the next window of data into the iterator.
 *
 * This method inserts the next window into the iterator. It
 * uses the pointer passed as an argument to copy data from 
 * that location to the internal buffer based on the size of
 * the window passed as an argument.
 *
 * @param  buff_ptr (in/out) a pointer to the window to be written
 * @param  size     (in) the size of the window to be written
 * 
 * @retval 0  success
 * @retval 1  window size exceeds maximum size of the block of data
 */
int Buffer_iterator::put_next(byte *buff_ptr, long size)
{
  DBUG_ENTER("buffer_iterator::put_next()");
  /*
    This needs to be a memory copy. Copy to the cur_ptr.
  */
  if (cur_bytes_read + size > max_size)
    DBUG_RETURN(-1); // error buffer overrun
  memcpy(cur_ptr, buff_ptr, size);
  cur_bytes_read= cur_bytes_read + size;
  cur_ptr= cur_ptr + size;
  DBUG_RETURN(0);
}

/**
 * @brief Determines the number of windows left to read.
 *
 * This method calculates how many windows are left to read in
 * the iterator. Use this method following initialize() to
 * determine the maximum windows you can write to the buffer or
 * use this method to determine how many more windows are 
 * remaining to be read.
 * 
 * @param  size     (in) the size of the window 
 *
 * @retval the number of windows left to read
 */
int Buffer_iterator::num_windows(long size)
{
  int num_windows;
  DBUG_ENTER("buffer_iterator::num_windows()");
  window_size= size;
  num_windows= (max_size - cur_bytes_read) / window_size;
  if ((max_size - cur_bytes_read) % window_size)
    num_windows++;
  DBUG_RETURN(num_windows);
}

/**
 * @brief Retrieve the pointer to the block of data.
 *
 * This method gets the base pointer to the block of data and 
 * returns it to the caller. This method can be used after writing
 * a series of windows to a buffer. When called, the method turns
 * off the free() mechanism for freeing the base memory allocated.
 * This was included to allow callers to reuse the memory. For 
 * example, this method is used in the default algorithm to read
 * and write blob data. On write, the pointer to the blob data (the
 * data in the buffer) is needed to write to the storage engine. Thus,
 * when this method is called the memory is not freed on destruction.
 * 
 * @retval the pointer to the buffer
 */
byte *Buffer_iterator::get_base_ptr()
{
  byte *ptr;
  DBUG_ENTER("buffer_iterator::get_base_ptr()");
  cur_bytes_read= 0;
  ptr= buffer;
  cur_ptr= 0;
  buffer= 0;
  DBUG_RETURN(ptr);
}

--- New file ---
+++ sql/backup/buffer_iterator.h	07/06/20 16:03:23
#ifndef _BUFFER_ITERATOR_H
#define _BUFFER_ITERATOR_H

#include "mysql_priv.h"

/**
 * @class Buffer_iterator
 *
 * @brief Encapsulates data buffering functionality.
 *
 * This class is used in the backup drivers for buffering large blocks
 * of data into smaller windows. This allows the backup drivers to
 * store a large field in multiple blocks of data allocated by the
 * backup kernel.
 *
 * For example, if a driver needs to store a blob field of size 8000 
 * bytes, but the kernel provides a buffer of size 1024 bytes, this 
 * class can be used to break the data into 8 parts. Upon restore, 
 * this class can be used to reassemble the parts into the original
 * size of data and thus write the data as one block to the engine.
 *
 * The class provides two methods for creation. The class can take
 * a pointer to an existing memory block or if omitted will allocate
 * a buffer of the size passed. See the class constructor for more
 * details.
 *
 * To use this class for reading, instantiate the class passing in
 * a pointer to the block you want to read, the total size of the
 * block, and the size of the window you want to read. Then call
 * get_next() for each window you want to read. You can use the
 * num_windows() method to indicate how many windows are left to
 * read. This is best used in a loop-like arrangement like the 
 * following:
 *
 * byte *ptr;
 * byte *outptr;
 * ptr= (byte *)my_malloc(8000, MYF(0));
 * Buffer_iterator *my_buff = new Buffer_iterator(ptr, 8000, 1024);
 * while (my_buff->num_windows())
 * {
 *   bytes_read= my_buff->get_next(&out_ptr);
 *   // do something with the window in out_ptr here 
 * } 
 *
 * Note: If you want to permit the Buffer_iterator class to create
 * it's own buffer, you must use the put_next() method to copy the data
 * from your own buffer into the buffer in the class.
 *
 * To use this class for writing, instantiate the class passing in 
 * the total size of the block, and the size of the window you will be
 * writing. Note: The window size is not used for writing. Then call
 * put_next() to insert a window into the buffer. Once all of the data 
 * has been placed into the buffer, you can use the get_base_ptr() to
 * retrieve the pointer to the buffer. This is best used in a loop-like
 * arrangement like the following:
 *
 * long size; //contains size of window to write
 * byte *ptr; //contains the pointer to the window
 *
 * size= read_my_data(&ptr); //read data here
 * Buffer_iterator *my_buff = new Buffer_iterator(8000, 1024);
 * while (there is data to read)
 * {
 *   my_buff->put_next(&out_ptr, size);
 *   // read more here 
 * } 
 * write_my_reassembled_block(my_buff->get_base_ptr(), total_size);
 *
 */
class Buffer_iterator
{
  public:
    int initialize(byte *buff_ptr, long size);
    int initialize(long size);
    int reset();
    long get_next(byte **buff_ptr, long window);
    int put_next(byte *buff_ptr, long size);
    int num_windows(long size);
    byte *get_base_ptr();

  private: 
    byte *buffer;        ///< The pointer to the block of data to iterate
    byte *cur_ptr;       ///< The current position in the buffer
    long max_size;       ///< The maximum size of the block of data
    long window_size;    ///< The size of the window to read
    long cur_bytes_read; ///< The number of bytes read
    bool alloc_used;     ///< Indicates whether to dealloc memory or not
};

#endif


--- 1.7/sql/backup/Makefile.am	2007-05-30 03:26:19 -04:00
+++ 1.8/sql/backup/Makefile.am	2007-06-20 16:03:23 -04:00
@@ -28,6 +28,7 @@
 	meta_backup.cc \
 	sql_backup.cc \
 	be_default.cc \
+	buffer_iterator.cc \
 	be_nodata.cc \
 	archive.cc \
 	stream.cc \
@@ -40,6 +41,7 @@
 	meta_backup.h \
 	backup_aux.h \
 	be_default.h \
+	buffer_iterator.h \
 	be_nodata.h \
 	archive.h \
 	stream.h \

--- 1.5/sql/backup/be_default.h	2007-05-10 18:20:24 -04:00
+++ 1.6/sql/backup/be_default.h	2007-06-20 16:03:23 -04:00
@@ -1,14 +1,8 @@
 #ifndef _DEFAULT_BACKUP_H
 #define _DEFAULT_BACKUP_H
 
-/**
-  @file
-
-  Declarations for the default backup engine.
- */
-
-#include <backup/backup_engine.h>
-#include <backup/archive.h>   // to define default backup image class
+#include "archive.h"
+#include "buffer_iterator.h"
 
 namespace default_backup {
 
@@ -19,6 +13,22 @@
 using backup::Table_ref;
 using backup::Buffer;
 
+const uint META_SIZE= 1;
+
+/*
+  The following are the flags for the first byte in the data layout for
+  the default and consistent snapshot algorithms. They describe what is 
+  included in the buffer going to the kernel.
+*/
+const byte RCD_ONCE=    1;     // Single data block for record data
+const byte RCD_FIRST=  (1<<1); // First data block in buffer for record buffer
+const byte RCD_DATA=   (1<<2); // Intermediate data block for record buffer
+const byte RCD_LAST=   (1<<3); // Last data block in buffer for record buffer
+const byte BLOB_ONCE=   3;     // Single data block for blob data
+const byte BLOB_FIRST= (3<<1); // First data block in buffer for blob buffer
+const byte BLOB_DATA=  (3<<2); // Intermediate data block for blob buffer
+const byte BLOB_LAST=  (3<<3); // Last data block in buffer for blob buffer
+
 /**
  * @class Engine
  *
@@ -39,47 +49,21 @@
   public:
     Engine(THD *t_thd);
 
-    /// Return version of backup images created by this engine.
+    /*
+      Return version of backup images created by this engine.
+    */
     const version_t version() const { return 0; };
-
-    /**
-     * Create a default backup backup driver.
-     *
-     * Given a list of tables to be backed-up, create instance of backup
-     * driver which will create backup image of these tables.
-     *
-     * @param  flags  (in) additional info about type of backup operation to be
-     *                     performed.
-     * @param  tables (in) list of tables to be backed-up.
-     * @param  drv    (out) pointer to backup driver instance.
-     *
-     * @return  Error code or <code>backup::OK</code> on success.
-     */
-    result_t get_backup(const uint32 flags, const Table_list &tables, Backup_driver* &drv);
-
-    /**
-     * Create a default backup restore driver.
-     *
-     * Given a list of tables to be restored, create instance of restore
-     * driver which will restore these tables from a backup image.
-     *
-     * @param  version  (in) version of the backup image.
-     * @param  flags  (in) additional info about type of restore operation to be
-     *                     performed.
-     * @param  tables   (in) list of tables to be restored.
-     * @param  drv      (out) pointer to restore driver instance.
-     *
-     * @return  Error code or <code>backup::OK</code> on success.
-     */
-    result_t get_restore(version_t version, const uint32 flags, const Table_list &tables,
+    result_t get_backup(const uint32, const Table_list &tables, 
+                        Backup_driver* &drv);
+    result_t get_restore(version_t ver, const uint32, const Table_list &tables,
                          Restore_driver* &drv);
 
-    /**
-     * Free any resources allocated by the default backup engine.
-     */
+    /*
+     Free any resources allocated by the default backup engine.
+    */
     void free() { delete this; }
   private:
-    THD *m_thd; ///Pointer to the current thread.
+    THD *m_thd; ///< Pointer to the current thread.
 };
 
 /**
@@ -91,116 +75,59 @@
  * a table scan on each table reading the rows and saving the data to the
  * buffer from the backup algorithm.
  *
- * @see @<backup driver>
+ * @see <backup driver>
  */
 class Backup: public Backup_driver
 {
   public:
     enum has_data_info { YES, WAIT, EOD };
     Backup(const Table_list &tables, THD *t_thd);
-    virtual ~Backup();
+    virtual ~Backup() {};
     size_t size()  { return UNKNOWN_SIZE; };
     size_t init_size() { return UNKNOWN_SIZE; };
-
-    /**
-     * @brief Start backup process.
-     *
-     * This method locks all of the tables for reading.
-     *
-     * @return
-     * backup::OK    - all tables locked properly.
-     * backup::Error - problem with locking tables.
-     */
     result_t begin(const size_t);
-
-    /**
-     * @brief End backup process.
-     *
-     * This method unlocks all of the tables.
-     *
-     * @return
-     * backup::OK    - all tables unlocked.
-     */
     result_t end();
-
-    /**
-     * @brief Get the data for a row in the table.
-     * This method is the main method used in the backup operation. It is
-     * responsible for reading a row from the table and placing the data in
-     * the buffer (buf.data) and setting the correct attributes for processing
-     * (e.g., buf.size = size of record data).
-     *
-     * Control of the method is accomplished by using several modes that
-     * signal portions of the method to run. These modes are:
-     *
-     * initialized    - Signals (when false) method to initialize variables
-     *                  and prepare for backup process. When true, indicates
-     *                  the initialization process is complete.
-     * get_next_table - Signals method to move to the next table in the list.
-     *                  May be used in conjunction with reading and read_blobs.
-     * reading        - Signals method to read next row of data.
-     * read_blobs     - Signals that row has blobs and to read blob data.
-     *
-     * @return
-     * backup::INIT_DODE - initialization phase complete.
-     * backup::OK        - data read.
-     * backup::Error     - problem with reading data.
-     */
     result_t get_data(Buffer &buf);
-    result_t lock();
-    result_t unlock();
+    result_t lock() { return backup::OK; };
+    result_t unlock() { return backup::OK; };
     result_t cancel() { return backup::OK; };
     void free() { delete this; };
+
+ protected:
+    THD *m_thd;                    ///< Pointer to current thread struct.
+    TABLE_LIST *all_tables;        ///< Reference to list of tables used.
+
   private:
-    /**
-     * @brief Start table read.
-     *
-     * This method saves the handler for the table and initializes the
-     * handler for reading.
-     *
-     * @return
-     * backup::OK    - handler initialized properly.
-     * backup::Error - problem with hander initialization.
-     */
-    result_t start_tbl_read(TABLE *tbl);
+    /*
+      We use an enum to control the flow of the algorithm. Each mode 
+      invokes a different behavior through a large switch. The mode is
+      set in the code as a response to conditions or flow of data.
+    */
+    typedef enum {
+      INITIALIZE,                  ///< Indicates time to initialize read
+      GET_NEXT_TABLE,              ///< Open next table in the list
+      READ_RCD,                    ///< Reading rows from table mode
+      READ_RCD_BUFFER,             ///< Buffer records mode
+      CHECK_BLOBS,                 ///< See if record has blobs
+      READ_BLOB,                   ///< Reading blobs from record mode
+      READ_BLOB_BUFFER,            ///< Buffer blobs mode
+      READ_COMPLETE                ///< Complete. Return to kernel.
+    } BACKUP_MODE;
 
-    /**
-     * @brief End table read.
-     *
-     * This method signals the handler that the reading process is complete.
-     *
-     * @return
-     * backup::OK    - handler read stopped properly.
-     * backup::Error - problem with hander.
-     */
+    result_t start_tbl_read(TABLE *tbl);
     result_t end_tbl_read();
-
-    /**
-     * @brief Get next table in the list.
-     *
-     * This method iterates through the list of tables selecting the
-     * next table in the list and starting the read process.
-     *
-     * @return
-     * 0  - no errors.
-     * -1 - no more tables in list.
-     */
     int next_table();
-
-    int initialized;               ///< Indicates code has been initialized.
-    int reading;                   ///< Indicates mode is read row.
-    int last_read_res;             ///< The last result code from handler.
+    BACKUP_MODE mode;              ///< Indicates which mode the code is in
     int tbl_num;                   ///< The index of the current table.
-    int read_blobs;                ///< Indicates mode is read blob data.
-    int get_next_table;            ///< Indicates get the next table in list.
     TABLE *cur_table;              ///< The table currently being read.
-    THD *m_thd;                    ///< Pointer to current thread struct.
     handler *hdl;                  ///< Pointer to table handler.
     uint *cur_blob;                ///< The current blob field.
     uint *last_blob_ptr;           ///< Position of last blob field.
-	  TABLE_LIST *tables_in_backup;  ///< List of tables used in backup.
-    TABLE_LIST *all_tables;        ///< Reference to list of tables used.
     MY_BITMAP *read_set;           ///< The file read set.
+    Buffer_iterator rec_buffer;    ///< Buffer iterator for windowing records
+    Buffer_iterator blob_buffer;   ///< Buffer iterator for windowing BLOB fields
+    byte *ptr;                     ///< Pointer to blob data from record.
+    TABLE_LIST *tables_in_backup;  ///< List of tables used in backup.
 };
 
 /**
@@ -212,99 +139,53 @@
  * data for each table by writing the data for the rows from the
  * buffer given by the backup algorithm.
  *
- * @see @<restore driver>
+ * @see <restore driver>
  */
 class Restore: public Restore_driver
 {
   public:
     enum has_data_info { YES, WAIT, EOD };
     Restore(const Table_list &tables, THD *t_thd);
-    virtual ~Restore();
-
-    /**
-     * @brief Start restore process.
-     *
-     * This method locks all of the tables for writing.
-     *
-     * @return
-     * backup::OK    - all tables locked properly.
-     * backup::Error - problem with locking tables.
-     */
+    virtual ~Restore() {};
     result_t  begin(const size_t);
-
-    /**
-     * @brief End restore process.
-     *
-     * This method unlocks all of the tables.
-     *
-     * @return
-     * backup::OK    - all tables unlocked.
-     */
     result_t  end();
-
-    /**
-     * @brief Restore the data for a row in the table.
-     *
-     * This method is the main method used in the restore operation. It is
-     * responsible for writing a row to the table.
-     *
-     * Control of the method is accomplished by using several modes that
-     * signal portions of the method to run. These modes are:
-     *
-     * initialized    - Signals (when false) method to initialize variables
-     *                  and prepare for restore process. When true, indicates
-     *                  the initialization process is complete.
-     * get_next_table - Signals method to move to the next table in the list.
-     *                  May be used in conjunction with writing and write_blobs.
-     * writing        - Signals method to write next row of data.
-     * write_blobs    - Signals that row has blobs and to write blob data.
-     *
-     * @return
-     * backup::INIT_DODE - initialization phase complete.
-     * backup::OK        - data written.
-     * backup::Error     - problem with writing data.
-     */
     result_t  send_data(Buffer &buf);
     result_t  cancel() { return backup::OK; };
+    TABLE_LIST *get_table_list() { return all_tables; }
     void free() { delete this; };
+
+ protected:
+    THD *m_thd;                    ///< Pointer to current thread struct.
+    TABLE_LIST *all_tables;        ///< Reference to list of tables used.
+
  private:
-    /**
-     * @brief Truncate table.
-     *
-     * This method saves the handler for the table and deletes all rows in
-     * the table.
-     *
-     * @return
-     * backup::OK    - rows deleted.
-     * backup::Error - problem with deleting rows.
-     */
-    result_t truncate_table(TABLE *tbl);
-
-    /**
-     * @brief Get next table in the list.
-     *
-     * This method iterates through the list of tables selecting the
-     * next table in the list and starting the read process.
-     *
-     * @return
-     * 0  - no errors.
-     * -1 - no more tables in list.
-     */
-    int next_table();
+     /*
+      We use an enum to control the flow of the algorithm. Each mode 
+      invokes a different behavior through a large switch. The mode is
+      set in the code as a response to conditions or flow of data.
+    */
+    typedef enum {
+      INITIALIZE,                  ///< Indicates time to initialize read
+      GET_NEXT_TABLE,              ///< Open next table in the list
+      WRITE_RCD,                   ///< Writing rows from table mode
+      CHECK_BLOBS,                 ///< See if record has blobs
+      WRITE_BLOB,                  ///< Writing blobs from record mode
+      WRITE_BLOB_BUFFER,           ///< Buffer blobs mode
+      WRITE_COMPLETE               ///< Complete. Return to kernel.
+    } RESTORE_MODE;
 
-    int initialized;               ///< Indicates code has been initialized.
-    int writing;                   ///< Indicates mode is write row.
-    int last_write_res;            ///< The last result code from handler.
+   result_t truncate_table(TABLE *tbl);
+    int next_table();
+    RESTORE_MODE mode;             ///< Indicates which mode the code is in
     uint tbl_num;                  ///< The index of the current table.
-    int write_blobs;               ///< Indicates mode is write blob data.
-    int get_next_table;            ///< Indicates get the next table in list.
+    uint32 max_blob_size;          ///< The total size (sum of parts) for the blob.
     TABLE *cur_table;              ///< The table currently being read.
-    THD *m_thd;                    ///< Pointer to current thread struct.
     handler *hdl;                  ///< Pointer to table handler.
     uint *cur_blob;                ///< The current blob field.
     uint *last_blob_ptr;           ///< Position of last blob field.
-	  TABLE_LIST *tables_in_backup;  ///< List of tables used in backup.
-    TABLE_LIST *all_tables;        ///< Reference to list of tables used.
+    Buffer_iterator rec_buffer;    ///< Buffer iterator for windowing records
+    Buffer_iterator blob_buffer;   ///< Buffer iterator for windowing BLOB fields
+    TABLE_LIST *tables_in_backup;  ///< List of tables used in backup.
 };
 } // default_backup namespace
 

--- 1.38/sql/CMakeLists.txt	2007-05-30 03:26:19 -04:00
+++ 1.39/sql/CMakeLists.txt	2007-06-20 16:03:22 -04:00
@@ -73,7 +73,7 @@
 	             sql_connect.cc scheduler.cc 
                backup/archive.cc backup/be_default.cc backup/data_backup.cc 
                backup/meta_backup.cc backup/sql_backup.cc backup/stream.cc 
-               backup/string_pool.cc
+               backup/string_pool.cc backup/buffer_iterator.cc
                      ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
   			   ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
 			   ${PROJECT_SOURCE_DIR}/include/mysqld_error.h
Thread
bk commit into 5.1 tree (cbell:1.2539)cbell20 Jun