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

ChangeSet@stripped, 2007-06-25 11:01:05+02:00, rafal@quant.(none) +5 -0
  WL#3473 (Online backup: Backup engine API):
  
  This patch creates sql/backup directory and adds it to the autotools
  build system. The directory contains basic declarations for the backup
  code including the backup engine API defined in the worklog.

  configure.in@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +2 -1
    Add sql/backup/Makefile.

  sql/backup/Makefile.am@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +39 -0
    Makefile source.

  sql/backup/Makefile.am@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +0 -0

  sql/backup/api_types.h@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +285 -0
    Datatypes used in backup APIs.

  sql/backup/api_types.h@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +0 -0

  sql/backup/backup_engine.h@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +537 -0
    Declarations of backup driver API.

  sql/backup/backup_engine.h@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +0 -0

  sql/backup/debug.h@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +81 -0
    Macros for debugging backup code.

  sql/backup/debug.h@stripped, 2007-06-25 11:01:02+02:00, rafal@quant.(none) +0 -0

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

--- 1.447/configure.in	2007-06-25 11:01:10 +02:00
+++ 1.448/configure.in	2007-06-25 11:01:10 +02:00
@@ -2619,7 +2619,8 @@
  strings/Makefile regex/Makefile storage/Makefile dnl
  man/Makefile BUILD/Makefile vio/Makefile dnl
  libmysql/Makefile client/Makefile dnl
- pstack/Makefile pstack/aout/Makefile sql/Makefile sql/share/Makefile dnl
+ pstack/Makefile pstack/aout/Makefile dnl
+ sql/Makefile sql/share/Makefile sql/backup/Makefile dnl
  sql/sql_builtin.cc sql-common/Makefile dnl
  dbug/Makefile scripts/Makefile include/Makefile dnl
  tests/Makefile Docs/Makefile support-files/Makefile dnl
--- New file ---
+++ sql/backup/Makefile.am	07/06/25 11:01:02
# Copyright (C) 2000 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

## Process this file with automake to create Makefile.in

INCLUDES = \
	-I.. \
	-I$(top_builddir)/include \
	-I$(top_srcdir)/include \
	-I$(top_srcdir)/regex

noinst_HEADERS = \
	api_types.h \
	backup_engine.h \
    debug.h

DEFS = \
	-DMYSQL_SERVER \
	-DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
	-DDATADIR="\"$(MYSQLDATAdir)\"" \
	-DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
	-DLIBDIR="\"$(MYSQLLIBdir)\"" \
	@DEFS@

	
# Don't update the files from bitkeeper
%::SCCS/s.%

--- New file ---
+++ sql/backup/api_types.h	07/06/25 11:01:02
#ifndef _BACKUP_API_TYPES_H
#define _BACKUP_API_TYPES_H

/**
  @file

  Declarations of common data types used in the backup API's
 */

/*
 Note: Structures defined in this file use String class which introduces
 dependency on its implementation defined in sql/ directory. Thus to correctly
 compile any code using the backup API one needs to:
 1) include sql/mysql_priv.h header
 2) link with library ...

 This seems to be not a problem for storage engine plugins as they use String
 class anyway.
 */

extern const String my_null_string;

namespace backup {

typedef unsigned char byte;

/**
  Values returned by backup/restore driver methods and other backup functions.

  @see <code>Backup_driver::get_data</code> and
  <code>Restore_driver::send_data</code>
 */

enum result_t { OK=0, READY, PROCESSING, BUSY, DONE, ERROR };

typedef uint  version_t;

//@{

/**
 * Classes Db_ref and Table_ref are used to identify databases and tables
 * inside mysql server instance.
 *
 * These classes abstract the way a table or database is identified inside mysqld,
 * so that when this changes (introduction of global db/table ids, introduction
 * of catalogues) it is easy to adapt backup code to the new identification schema.
 *
 * Regardless of the internal representation, classes provide methods returning
 * db/table name as a String object. Also, each table belongs to some database
 * and a method returning Db_ref object identifying this database is present. For
 * Db_ref objects there is catalog() method returning name of the catalogue, but
 * currently it always returns null string.
 *
 * Classes are implemented so that the memory for storing names can be allocated
 * outside an instance. This is used in the Tables class implementing
 * Table_list interface to share space used for storing database names among
 * several Table_ref instances.
 *
 * Instances of Table_ref and Db_ref should be considered cheap to use, equivalent
 * to using pointers or other base types. Currently, single instance of each class
 * uses as much memory as a single pointer (+some external memory to store names
 * which can be shared among different instances). The methods are inlined to avoid
 * function call costs.
 */

class Db_ref
{
  const String *m_name;

 public:

  // Construct invalid reference
  Db_ref(): m_name(NULL)
  {}

  const bool is_valid() const
  { return m_name != NULL; }

  const String& name() const
  { return *m_name; }

  const String& catalog() const
  { return my_null_string; }

  bool operator==(const Db_ref &db) const
  { return stringcmp(m_name,&db.name())==0; }

  bool operator!=(const Db_ref &db) const
  { return ! this->operator==(db); }

 protected:

  // Constructors are made protected as clients of this class are
  // not supposed to create instances (see comment inside Table_ref)

  Db_ref(const String &name): m_name(&name)
  {}

  friend class Table_ref;
};


class Table_ref
{
  const Db_ref  m_db;
  const String  *m_name;

 public:

  // Construct invalid reference
  Table_ref(): m_name(NULL)
  {}

  const bool is_valid() const
  { return m_name != NULL; }

  const Db_ref& db() const
  { return m_db; }

  const String& name() const
  { return *m_name; }

  bool operator==(const Table_ref &t) const
  {
    return m_db == t.db() &&
           stringcmp(m_name,&t.name()) == 0;
  }

  bool operator!=(const Table_ref &db) const
  { return ! this->operator==(db); }

  typedef char describe_buf[512];

  /// Produce string identifying the table (e.g. for error reporting)
  const char* describe(char *buf, size_t len) const
  {
    my_snprintf(buf,len,"%s.%s",db().name().ptr(),name().ptr());
    return buf;
  }

 protected:

  /*
    Constructor is made protected as it should not be used by
    clients of this class -- they obtain already constructed
    instances from the backup kernel via Table_list object passed
    when creating backup/restore driver.
  */

  Table_ref(const String &db, const String &name):
    m_db(db), m_name(&name)
  {}
};


//@}


/**
 * @class Table_list
 *
 * @brief This abstract class defines interface used to access a list of
 * tables (e.g. when such a list is passed to a backup/restore driver).
 *
 * Elements of the list can be accessed by index, counting from 0. E.g.
 * @code
 *  Table_list &tables;
 *  Table_ref  t2 = tables[1];  // t2 refers to the second element of the list.
 * @endcode
 *
 * Interface is made abstract, so that different implementations can be
 * used in the backup code. For example it is possible to create a class which
 * adds this interface to a list of tables represented by a linked list of
 * TABLE_LIST structures as used elsewhere in the code. On the other hand, much
 * more space efficient implementations are possible, as for each table we need
 * to store only table's identity (db/table name). In any case, the interface
 * to the list remains the same, as defined by this class.
 *
 * TODO: add iterators.
 */

class Table_list
{
  public:

    virtual ~Table_list() {}

    /// Return reference to given list element. Elements are counted from 0.
    virtual Table_ref operator[](uint pos) const =0;

    /// Return number of elements in the list.
    virtual uint  count() const =0;
};


/**
  @class Buffer

  @brief Used for data transfers between backup kernel and backup/restore
  drivers.

  Apart from allocated memory a Buffer structure contains fields informing about
  its size and holding other information about contained data. Buffers are
  created and memory is allocated by backup kernel. It is also kernel's
  responsibility to write contents of buffers to a backup stream.

  Data created by a backup driver is opaque to the kernel. However, to support
  selective restores, each block of data can be assigned to one of the tables
  being backed-up. This is done by setting <code>table_no</code> member of the
  buffer structure to the number of the table to which this data belongs. Tables
  are numbered from 1 according to their position in the list passed when driver
  is created (<code>m_tables</code> member of <code>Driver</code> class). If
  some of the data doesn't correspond to any particular table, then
  <code>table_no</code> should be set to 0.

  This way, driver can create several "streams" of data blocks. For each table
  there is a stream corresponding to that table and there is one "shared stream"
  consisting of blocks with <code>table_no</code> set to 0. Upon restore, kernel
  sends to a restore driver only blocks corresponding to the tables being
  restored plus all the blocks from the shared stream.

  For example, consider backing-up three tables t1, t2 and t3. Data blocks
  produced by a backup driver are divided into four streams:
  <pre>
  #0: shared data
  #1: data for table t1
  #2: data for table t2
  #3: data for table t3
  </pre>
  When a user restores tables t1 and t3, only blocks from streams #0, #1 and #3
  will be sent to a restore driver, but not the ones from stream #2.

  Using this approach, backup engine can arrange its backup image data in the
  way which best suits its internal data representation. If needed, all data can
  be put in the shared stream #0, so that all of it will be sent back to
  a restore driver. On the other hand, if possible, backup data can be
  distributed into per table streams to reduce the amount of data transferred
  upon a selective restore.

  Backup driver signals end of data in a given stream by setting
  <code>buf.last</code> flag to TRUE when get_data(buf) fills the last block of
  data from that stream (otherwise <code>buf.last</code> should be FALSE). This
  should be done for each stream used by the driver. Upon restore, kernel sets
  <code>buf.last</code> to TRUE when sending to a restore driver the last block
  of data from a stream.

  A driver learns about the size of a buffer provided by the kernel from its
  <code>size</code> member. It does not have to fill the buffer completely.
  It should update the <code>size</code> member to reflect the actual size
  of the data in the buffer. It is possible to return no data in which case
  <code>size</code> should be zero. Such empty buffers are ignored by the
  kernel (no data is written to the archive).
 */

struct Buffer
{
  size_t  size;       ///< size of the buffer (of memory block pointed by data).
  uint    table_no;   ///< Number of the table 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),table_no(0),last(FALSE)
  {}

  void reset(size_t len)
  {
    size= len;
    table_no= 0;
    last= FALSE;
  }
};

// forward declaration
class Engine;
class Backup_driver;
class Restore_driver;

} // backup namespace

typedef backup::result_t Backup_result_t;
typedef backup::Engine   Backup_engine;
typedef Backup_result_t backup_factory(::handlerton *,Backup_engine*&);

#endif


--- New file ---
+++ sql/backup/backup_engine.h	07/06/25 11:01:02
#ifndef MYSQL_PRIV_H
  #error backup_engine.h must be included after mysql_priv.h
#endif

#ifndef _BACKUP_ENGINE_API_H
#define _BACKUP_ENGINE_API_H

/**
 @file backup_engine.h
 @brief Backup engine and backup/restore driver API

 The interface between online backup kernel and a backup solution has form of
 two abstract classes: <code>Backup_driver</code> implementing backup
 functionality and <code>Restore_driver</code> for restore functionality.
 Instances of these two classes are created by a factory class
 <code>Backup_engine</code> which encapsulates and represents the backup
 solution as a whole.
 */

#include <backup/api_types.h>

namespace backup {

// Forward declarations
class Backup_driver;
class Restore_driver;

/**
 @class Engine
 @brief Encapsulates online backup/restore functionality.

 Any backup solution is represented in the online backup kernel by an instance
 of this class, so called <em>backup engine</em>. This object is used to find
 out general information about the solution (e.g. version number). It also
 constructs backup and restore drivers (instances of <code>Backup_driver</code>
 and <code>Restore_driver</code> classes) to perform backup/restore operations
 for a given list of tables.

 @note The backup engine instance is created using get_backup function of
 handlerton structure. It should be cheap to create instances of backup::Engine
 class as they might be used by the kernel to query backup capabilities of an
 engine. On the other hand, kernel will take care to create driver instances
 only when they are really needed.
 */

class Engine
{
 public:

  virtual ~Engine() {}

  /// Return version of backup images created by this engine.
  virtual const version_t version() const =0;

  /**
   Create a backup driver.

   Given a list of tables to be backed-up, create instance of backup
   driver which will create backup image of these tables.

   The <code>flags</code> parameter gives additional information about
   the backup process to be performed by the driver. Currently, we only set
   <code>Driver::FULL</code> flag if the driver is supposed to backup all the
   tables stored in a given storage engine.

   @param  flags  (in) additional info about backup operation.
   @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.
  */
  virtual result_t get_backup(const uint32      flags,
                              const Table_list  &tables,
                              Backup_driver*    &drv) =0;

  /**
   Create a restore driver.

   Given a list of tables to be restored, create instance of restore
   driver which will restore these tables from a backup image.

   The <code>flags</code> parameter gives additional information about
   the restore process to be performed by the driver. Currently, we only set
   <code>Driver::FULL</code> flag if the driver is supposed to replace all the
   tables stored in a given storage engine with the restored ones.

   @param  version  (in) version of the backup image.
   @param  flags    (in) additional info about restore operation.
   @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.
  */
  virtual result_t get_restore(const version_t  version,
                               const uint32     flags,
                               const Table_list &tables,
                               Restore_driver*  &drv) =0;

  /**
   Free any resources allocated by the backup engine.

   It is possible to delete the instance here since backup kernel
   will never use an instance after a call to <code>free()</code> method.

   Backup kernel does not assume that backup engine is allocated
   dynamically and therefore will never delete an instance it has obtained.
   However, it will call <code>free()</code> method when done with the instance.
   If the instance is dynamically allocated it should be deleted in this method.
  */
  virtual void free() {};

}; // Engine class


/**
 @class Driver

 @brief  This class contains methods which are common to both types of drivers.

 A driver is created to backup or restore given list of tables. This list
 is passed as an argument when constructing a driver. A reference to
 the list is stored in <code>m_tables</code> member for future use (the memory
 to store the list is allocated/deallocated by the kernel).

 Driver methods return value of type result_t to inform backup kernel about the
 result of each operation. If ERROR is returned, it means that the driver is
 not able to proceed. The kernel will shutdown the driver by calling
 <code>free()</code> method. No other methods will be called after signalling
 error by a driver.
*/

class Driver
{
 public:

  /// Types of backup/restore operations.
  enum enum_flags { FULL    =0x1,  ///< concerns all tables from given storage engine
                    PARTIAL =0     ///< backup/restore only selected tables
                  };

  /// Construct from list of tables. The list is stored for future use.
  Driver(const Table_list &tables):m_tables(tables) {};

  virtual ~Driver() {}; // We want to inherit from this class.

  /**
   @brief Initialize backup/restore process.

   After return from <code>begin()</code> call, driver should be ready to
   serve requests for sending/receiving image data.

   @param buf_size (in)  this is the minimal size of buffers backup kernel
                         will provide in <code>get_data()</code>,
                         <code>send_data()</code> methods. The buffer can be
                         actually bigger (and its real size will be stored in
                         buffers size member) but it will never be smaller.

   @return  Error code or <code>backup::OK</code> on success.
  */

  virtual result_t  begin(const size_t buf_size) =0;


  /**
   @brief Finalize backup/restore process.

   This method 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 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>.

   @return  Error code or <code>backup::OK</code> on success.
  */

  virtual result_t  end()   =0;

  /// Cancel ongoing backup/restore process.
  virtual result_t  cancel() =0;

  /**
   @brief Free resources allocated by the driver.

   Driver can be deleted here. @see Engine::free()
  */
  virtual void  free() {};

  /// Unknown size constant used for backup image size estimates.
  static const size_t UNKNOWN_SIZE= static_cast<size_t>(-1);

 protected:

  /// Refers to the list of tables passed when the driver was created.
  const Table_list &m_tables;

}; // Driver class


/**
 @class Backup_driver

 @brief Represents backup driver for backing-up a given list of tables.

 This class provides all the methods used to implement the backup protocol
 for communication between backup kernel and the driver. The most important
 method is <code>get_data()</code> which is used by the kernel to poll the
 backup image data and at the same time learn about state of the backup
 process.

 Backup process consists of the following phases (not all
 phases are meaningful for all backup methods).

 -# <b>Idle</b>, after creation of the driver instance and before
    <code>begin()</code> call from kernel. Note that any resources should be
    allocated inside <code>begin()</code> method, not upon driver
    creation.
 -# <b>Initial transfer</b>, when initial data is sent before driver can create
    a <em>validity point</em>. "At end" backup drivers will send majority of
    their data in this phase.
 -# <b>Waiting for prelock</b>, when driver waits for other drivers to finish
    sending their initial transfer phase. This phase is ended by a call to
    <code>prelock()</code> method.
 -# <b>Preparing for lock</b>, when driver does necessary preparations (if any)
    to be able to instantly crate a validity point upon request from kernel.
 -# <b>Waiting for lock</b>, when driver waits for other drivers to finish their
    preparations. Phase is finished by a call to <code>lock()</code> method.
 -# <b>Synchronization</b>, when the validity point is created inside
    <code>lock()</code> method. For synchronization reasons, data in all tables
    being backed-up should be frozen during that phase. Phase is ended by a call
    to <code>unlock()</code> method.
 -# <b>Final transfer</b>, when final backup image data (if any) is sent to the
    kernel. "At begin" will send all their data in this phase. This phase is
    ended by a call to <code>end()</code> method.

 In each phase, except for the synchronization phase (6),  kernel is polling
 driver using <code>get_data()</code> method. Thus a driver has a chance to send
 data in each phase of the backup process. For example, when waiting in phase 3
 or 5, driver can send log recording changes which happen during that time.

 A driver informs the kernel about finishing the initial transfer phase (2) or
 the lock preparation phase (4) by the value returned from the
 <code>get_data()</code> method (see description of the method).

 Not all drivers will need all the phases to perform backup but they should
 still follow the protocol and give correct replies from <code>get_data()</code>
 method.

 @note The list of tables being backed-up is accessible via <code>m_tables</code>
 member inherited from <code>backup::Driver</code> class

 @see Methods <code>begin()</code>, <code>end()</code>, <code>get_data()</code>,
 <code>prelock()</code>, <code>lock()</code> and <code>unlock()</code> and
 <code>backup::Driver</code> class.
*/

class Backup_driver: public Driver
{
 public:

  Backup_driver(const Table_list &tables):Driver(tables) {};

  virtual ~Backup_driver() {}; // Each specific implementation will derive from this class.

 /**
   @fn result_t get_data(Buffer &buf)

   @brief Accept a request for filling a buffer with next chunk of backup data
   or check status of a previously accepted request.

   Backup driver can implement its own policy of handling these requests. It can
   return immediately from the call and use a separate thread to fill the buffer
   or the calling thread can be used to do the job. It is also possible that
   a driver accepts new requests while processing old ones, implementing
   internal queue of requests.

   The kernel learns about what happened to the request from the value returned
   by the method (see below). The returned value is also used to inform the
   kernel that the driver has finished the initial transfer phase (2) or the
   prepare to lock phase (4) (see description of the class).

   When a request is completed, members of <code>buf</code> should be filled
   as described in the documentation of Buffer class. It is possible to complete
   a request without putting any data in the buffer. In that case
   <code>buf.size</code> should be set to zero. The return value (OK or READY)
   and the <code>buf.table_no</code> and <code>buf.last</code> members are
   interpreted as usual. However, no data is written to backup archive and such
   empty buffers are not sent back to restore driver.


   @param  buf   (in/out) buffer to be filled with backup data. Its members are
                 initialized by backup kernel: <code>buf.data</code> points to
                 a memory area where the data should be placed and
                 <code>buf.size</code> is the size of the area. Upon completion
                 of the request (<code>OK</code> or <code>READY</code>
                 returned), members of <code>buf</code> should be filled as
                 described in the documentation of Buffer class.

   @retval OK  The request is completed - new data is in the buffer and
               <code>size</code>, <code>table_no</code> and <code>last</code>
               members of the buffer structure are set accordingly.

   @retval READY Same as OK and additionally informs that the initial transfer
               phase (2) or the prepare to lock phase (4) are finished for that
               driver.

   @retval DONE Same as OK but also indicates that the backup process is
               completed. This result can be returned only in the final transfer
               phase (7).

   @retval PROCESSING The request was accepted but is not completed yet. Further
               calls to get_data() are needed to complete it (until it returns
               OK or READY). Kernel will not reuse the buffer before it knows
               that it is completely filled with data.

   @retval BUSY The request can not be accepted now. Kernel can try to place a
               request later. The buffer is not blocked and can be used for
               other transfers.

   @retval ERROR An error has happened while processing the request.

   @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> and <code>buf.table_no</code> to 0,
   -# set <code>buf.last</code> to TRUE,
   -# return <code>DONE</code>.

   @see <code>Buffer</code> class.
  */

  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> and driver can signal that it has finished
   the preparations by returning <code>READY</code> result.

   @retval READY The driver is ready for synchronization, i.e. it can accept the
              following <code>lock()</code> call.

   @retval OK The driver is preparing for synchronization. Kernel should call
              <code>get_data()</code> and wait until driver is ready.

   @retval ERROR The driver can not prepare for synchronization.
  */

  virtual result_t  prelock()
  {  return READY; };

  /**
   @brief Create validity point and freeze all backed-up data.

   After sending <code>prelock()</code> requests to all backup drivers and
   receiving <code>READY</code> confirmations, backup kernel 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 backed-up tables 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 since it blocks other drivers. Ideally, only fast memory
   access and/or (non-blocking) mutex manipulations should happen but no disk
   operations. The backup kernel expects that this call will return in at most
   few seconds.

   @returns Error code or <code>backup::OK</code> upon success.
  */

  virtual result_t  lock() =0;

  /**
   @brief Unlock data after the <code>lock()</code> call.

   After call to <code>unlock()</code> driver enters the final data transfer
   phase. Any remaining data should be sent in the following
   <code>get_data()</code> calls and all data streams should be closed. The
   process is ended by returning <code>backup::DONE</code> from the last
   <code>get_data()</code> call.

   @note <b>Important!</b>. Similar as with <code>lock()</code>, a call to
   <code>unlock()</code> should return as quickly as possible to not block other
   drivers.
  */

  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 drivers of
   different types at correct times. Roughly, drivers with biggest
   <code>init_size()</code> will be initialized and polled first. Drivers whose
   <code>init_size()</code> is zero, will be initialized and polled last in the
   process.

   Thus "at begin" drivers which send all backup data in the final phase
   of backup should return 0 here. Drivers of "at end" type should return
   estimate for the size of data to be sent before they are ready for validity
   point creation. If estimating this size is impossible or very expensive, the
   driver can return UNKNOWN_SIZE. In that case  the driver will be initialized
   and polled before any other drivers.
  */

  virtual size_t    init_size() =0;

}; // Backup_driver class

/**
 @class Restore_driver

 @brief Represents restore driver used for restoring a given list of tables.

 This class provides all the methods used to implement the restore protocol
 for communication between backup kernel and the driver. Apart from the common
 driver methods of <code>backup::Driver</code> class it provides
 <code>send_data()</code> method which is used by the kernel to send
 backup image data to the driver.

 It is assumed that all tables are blocked during restore process.

 @note The list of tables being restored is accessible via <code>m_tables</code>
 member inherited from <code>backup::Driver</code> class

 @see <code>backup::Driver</code> class.
*/

class Restore_driver: public Driver
{
 public:

  Restore_driver(const Table_list &tables):Driver(tables) {};
  virtual ~Restore_driver() {};

  /**
   @fn result_t send_data(Buffer &buf)

   @brief Request processing of next block of backup image data or check
   status of a previously accepted request.

   Upon restore, backup kernel calls this method periodically sending
   consecutive blocks of data from the backup image. The <code>table_no</code>
   field in the buffer is set to indicate from which stream the data comes.
   Also, <code>buf.last</code> is TRUE if this is the last block
   in the stream.

   Blocks are sent to restore driver in the same order in which they were
   created by a backup driver. This is true also when only selected blocks are
   sent.

   Restore driver can implement its own policy of handling data processing
   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 calling thread is used to do the job.

   Returning OK means that the data has been successfully processed
   and the buffer can be re-used for further transfers. If method returns
   PROCESSING, it means that the request was accepted but is not
   completed yet. The buffer will not be used for other purposes until a further
   call to <code>get_data()</code> with the same buffer as argument returns OK.

   @param  buf   (in) buffer filled with backup data. Fields <code>size</code>,
                 <code>table_no</code> and <code>last</code> are set
                 accordingly.

   @retval OK    The data has been successfully processed - the buffer can be
                 used for other transfers.

   @retval DONE  Same as OK but also indicates that the restore process is
                 completed.

   @retval PROCESSING  The request was accepted but data is not processed yet -
                 further calls to <code>send_data()</code> are needed to
                 complete it. The buffer is blocked and can't be used for other
                 transfers.

   @retval BUSY  The request can not be processed right now. A call to
                 <code>send_data()</code> should be repeated later.

   @retval ERROR An error has happened. The request is cancelled and the buffer
                 can be used for other transfers.

   @see <code>Buffer</code> class.
  */

  virtual result_t  send_data(Buffer &buf) =0;

}; // Restore_driver


} // backup namespace

// export Backup/Restore_driver classes to global namespace

using backup::Backup_driver;
using backup::Restore_driver;


#endif

--- New file ---
+++ sql/backup/debug.h	07/06/25 11:01:02
#ifndef _BACKUP_DEBUG_H
#define _BACKUP_DEBUG_H

#define BACKUP_SYNC_TIMEOUT 300

/*
  TODO
  - decide how to configure DEBUG_BACKUP
 */

#ifndef DBUG_OFF
# define DBUG_BACKUP
#endif

#ifdef DBUG_BACKUP

/*
  Macros for debugging error (or other) conditions. Usage:

  TEST_ERROR_IF(<condition deciding if TEST_ERROR should be true>);

  if (<other conditions> || TEST_ERROR)
  {
    <report error>
  }

  The additional TEST_ERROR condition will be set only if "backup_error_test"
  error injection is set in the server.

  Notes:
   - Whenever TEST_ERROR is used in a condition, TEST_ERROR_IF() should
     be called before - otherwise TEST_ERROR might be unintentionally TRUE.
   - This mechanism is not thread safe.
 */

namespace backup {
 extern bool test_error_flag;
}

#define TEST_ERROR  backup::test_error_flag
// FIXME: DBUG_EXECUTE_IF below doesn't work
#define TEST_ERROR_IF(X) \
 do { \
   backup::test_error_flag= FALSE; \
   DBUG_EXECUTE_IF("backup_error_test", backup::test_error_flag= (X);); \
 } while(0)

/*
  Macros for creating synchronization points in tests.

  Usage

  In the backup code:
    BACKUP_SYNC("<synchronization point name>");

  In a client:
    SELECT get_lock("<synchronization point name>",<timeout>);
    ...
    SELECT release_lock("<synchronization point name>");

  If the lock is kept by a client, server code will wait on the corresponding
  BACKUP_SYNC() until it is released.

  Consider: set thd->proc_info when waiting on lock
 */

#define BACKUP_SYNC(S) \
 do { \
  DBUG_PRINT("backup",("== synchronization on '%s' ==",(S))); \
  debug_sync_point((S),BACKUP_SYNC_TIMEOUT); \
 } while (0)

#else

#define BACKUP_SYNC(S)
#define TEST_ERROR  FALSE
#define TEST_ERROR_IF(X)

#endif

#endif

Thread
bk commit into 5.2 tree (rafal:1.2521)rsomla25 Jun
  • RE: bk commit into 5.2 tree (rafal:1.2521)Chuck Bell27 Jun