Commit 910bf380 authored by unknown's avatar unknown
Browse files

WL 2826: Error handling for ALTER TABLE for partitioning

Most of the code for handling the table log is in place now, except
the action part at recovery and proper error handling in some places.


sql/mysql_priv.h:
  Removed internal methods from external table log interface
  Added and changed interface
sql/mysqld.cc:
  Added call to execute table log recovery
sql/sql_table.cc:
  Most of the code for handling the table log is in place now, except
  the action part at recovery and proper error handling in some places.
parent e09fb5ce
Loading
Loading
Loading
Loading
+11 −8
Original line number Diff line number Diff line
@@ -1168,21 +1168,24 @@ typedef struct st_table_log_entry
  char entry_type;
} TABLE_LOG_ENTRY;

typedef struct st_table_log_memory_entry
{
  uint entry_pos;
  TABLE_LOG_MEMORY *next_log_entry;
  TABLE_LOG_MEMORY *prev_log_entry;
  TABLE_LOG_MEMORY *next_active_log_entry;
} TABLE_LOG_MEMORY_ENTRY;

bool write_table_log_entry(TABLE_LOG_ENTRY *table_log_entry,
                           uint next_entry,
                           uint *entry_written);
bool write_execute_table_log_entry(uint first_entry, uint *exec_entry);
uint read_table_log_header();
bool read_table_log_entry(uint read_entry, TABLE_LOG_ENTRY *table_log_entry);
bool init_table_log();
                           TABLE_LOG_MEMORY_ENTRY **active_entry);
bool write_execute_table_log_entry(uint first_entry,
                                   TABLE_LOG_MEMORY_ENTRY **active_entry);
void release_table_log_memory_entry(TABLE_LOG_MEMORY_ENTRY *log_entry);
void release_table_log();
void execute_table_log_recovery();
bool execute_table_log_entry(uint first_entry);
bool execute_table_log_action(TABLE_LOG_ENTRY *table_log_entry);
void lock_global_table_log();
void unlock_global_table_log();
bool sync_table_log();

bool write_log_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt, bool install_flag);
bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt);
+1 −1
Original line number Diff line number Diff line
@@ -3676,7 +3676,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
      unireg_abort(1);
    }
  }

  execute_table_log_recovery();
  init_events();

  create_shutdown_thread();
+332 −151
Original line number Diff line number Diff line
@@ -265,22 +265,19 @@ static int mysql_copy_key_list(List<Key> *orig_key,
   and there is a global struct that contains information about its current
   state.

   History:
   First version written in 2006 by Mikael Ronstrom
--------------------------------------------------------------------------
*/


typedef struct st_table_log_memory_entry
{
  uint entry_pos;
} TABLE_LOG_MEMORY_ENTRY;

typedef struct st_global_table_log
{
  char file_entry[IO_SIZE];
  char file_name_str[FN_REFLEN];
  char *file_name;
  List<TABLE_LOG_MEMORY_ENTRY> free_entries;
  List<TABLE_LOG_MEMORY_ENTRY> log_entries;
  TABLE_LOG_MEMORY_ENTRY *first_free;
  TABLE_LOG_MEMORY_ENTRY *first_used;
  uint no_entries;
  File file_id;
  uint name_len;
@@ -301,6 +298,7 @@ pthread_mutex_t LOCK_gtl;
    FALSE                     Success
*/

static
bool
sync_table_log()
{
@@ -308,7 +306,10 @@ sync_table_log()
  DBUG_ENTER("sync_table_log");

  if (my_sync(global_table_log.file_id, MYF(0)))
  {
    /* Write to error log */
    error= TRUE;
  }
  DBUG_RETURN(error);
}

@@ -317,8 +318,6 @@ sync_table_log()
  Write one entry from table log file
  SYNOPSIS
    write_table_log_file_entry()
    file_id                      File identifier
    file_entry                   Memory area to read entry into
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
@@ -327,10 +326,12 @@ sync_table_log()

static
bool
write_table_log_file_entry(File file_id, byte *file_entry, uint entry_no)
write_table_log_file_entry(uint entry_no)
{
  bool error= FALSE;
  DBUG_ENTER("read_table_log_file_entry");
  File file_id= global_table_log.file_id;
  char *file_entry= (char*)global_table_log.file_entry;
  DBUG_ENTER("write_table_log_file_entry");

  if (my_pwrite(file_id, file_entry, IO_SIZE, IO_SIZE * entry_no, MYF(0)))
    error= TRUE;
@@ -338,64 +339,6 @@ write_table_log_file_entry(File file_id, byte *file_entry, uint entry_no)
}


/*
  SYNOPSIS
    write_table_log_entry()
    table_log_entry         Information about log entry
    out:entry_written       Entry information written into   

  RETURN VALUES
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
    A careful write of the table log is performed to ensure that we can
    handle crashes occurring during CREATE and ALTER TABLE processing.
*/

bool
write_table_log_entry(TABLE_LOG_ENTRY *table_log_entry,
                      uint *entry_written)
{
  bool write_header, error;
  DBUG_ENTER("write_table_log_entry");

  global_table_log.file_entry[0]= 'i';
  global_table_log.file_entry[1]= table_log_entry->action_type;
  int4store(&global_table_log.file_entry[2],
            table_log_entry->next_entry);
  strcpy(&global_table_log.file_entry[6], table_log_entry->name);
  if (table_log_entry.action_type == 'r')
    global_table_log.file_entry[6 + NAMELEN]= 0;
  else
    strcpy(&global_table_log.file_entry[6 + NAMELEN],
          table_log_entry->from_name);
  strcpy(&global_table_log.file_entry[6 + (2*NAMELEN)],
         table_log_entry->handler_type);
  if (global_table_log.free_entries.is_empty())
  {
    global_table_log.no_entries++;
    entry_no= global_table_log.no_entries;
    write_header= TRUE;
  }
  else
  {
    TABLE_LOG_MEMORY *tmp= global_table_log.free_entries.pop();
    global_table_log.log_entries.push_back(tmp);
    entry_no= tmp->entry_pos;
    write_header= FALSE;
  }
  error= FALSE;
  if (write_table_log_entry(global_table_log.file_id,
                            global_table_log.file_entry,
                            entry_no))
    error= TRUE;
  else if (write_header || !(write_table_log_header()))
    error= TRUE;
  DBUG_RETURN(error);
}


/*
  Write table log header
  SYNOPSIS
@@ -405,10 +348,12 @@ write_table_log_entry(TABLE_LOG_ENTRY *table_log_entry,
    FALSE                     Success
*/

static
bool
write_table_log_header()
{
  uint16 const_var;
  bool error= FALSE;
  DBUG_ENTER("write_table_log_header");

  int4store(&global_table_log.file_entry[0], global_table_log.no_entries);
@@ -416,36 +361,9 @@ write_table_log_header()
  int2store(&global_table_log.file_entry[4], const_var);
  const_var= 32;
  int2store(&global_table_log.file_entry[6], const_var);
  DBUG_RETURN(FALSE);
}


/*
  Write final entry in the table log
  SYNOPSIS
    write_execute_table_log_entry()
    first_entry                    First entry in linked list of entries
                                   to execute, if 0 = NULL it means that
                                   the entry is removed and the entries
                                   are put into the free list.
    in:out:exec_entry              Entry to execute, 0 = NULL if the entry
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
  RETURN VALUES
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
    This is the last write in the table log. The previous log entries have
    already been written but not yet synched to disk.
*/ 

bool
write_execute_table_log_entry(uint first_entry, uint *exec_entry)
{
  DBUG_ENTER("write_execute_table_log_entry");
  DBUG_RETURN(FALSE);
  if (write_table_log_file_entry(0UL))
    error= TRUE;
  DBUG_RETURN(error);
}


@@ -453,8 +371,6 @@ write_execute_table_log_entry(uint first_entry, uint *exec_entry)
  Read one entry from table log file
  SYNOPSIS
    read_table_log_file_entry()
    file_id                      File identifier
    file_entry                   Memory area to read entry into
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
@@ -463,9 +379,11 @@ write_execute_table_log_entry(uint first_entry, uint *exec_entry)

static
bool
read_table_log_file_entry(File file_id, byte *file_entry, uint entry_no)
read_table_log_file_entry(uint entry_no)
{
  bool error= FALSE;
  File file_id= global_table_log.file_id;
  char *file_entry= (char*)global_table_log.file_entry;
  DBUG_ENTER("read_table_log_file_entry");

  if (my_pread(file_id, file_entry, IO_SIZE, IO_SIZE * entry_no, MYF(0)))
@@ -474,6 +392,23 @@ read_table_log_file_entry(File file_id, byte *file_entry, uint entry_no)
}


/*
  Create table log file name
  SYNOPSIS
    create_table_log_file_name()
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

static
void
create_table_log_file_name(char *file_name)
{
  strxmov(file_name, mysql_data_home, "/", "table_log.log", NullS);
}


/*
  Read header of table log file
  SYNOPSIS
@@ -487,22 +422,28 @@ read_table_log_file_entry(File file_id, byte *file_entry, uint entry_no)
    of entries in the table log.
*/

static
uint
read_table_log_header()
{
  char *file_entry= (char*)&global_table_log.file_entry;
  char *file_entry= (char*)global_table_log.file_entry;
  char file_name[FN_REFLEN];
  DBUG_ENTER("read_table_log_header");

  if (read_table_log_file_entry(global_table_log.file_id,
                                (char*)&file_entry, 0UL))
  bzero(file_entry, sizeof(global_table_log.file_entry));
  create_table_log_file_name(file_name);
  if (!(my_open(file_name, O_RDWR |O_TRUNC, MYF(0))))
  {
    DBUG_RETURN(0);
    if (read_table_log_file_entry(0UL))
    {
      /* Write message into error log */
    }
  }
  entry_no= uint4korr(&file_entry[0]);
  global_table_log.name_len= uint2korr(&file_entry[4]);
  global_table_log.handler_type_len= uint2korr(&file_entry[6]);
  global_table_log.free_entries.clear();
  global_table_log.log_entries.clear();
  global_table_log.first_free= NULL;
  global_table_log.first_used= NULL;
  global_table_log.no_entries= 0;
  VOID(pthread_mutex_init(&LOCK_gtl, MY_MUTEX_INIT_FAST));
  DBUG_RETURN(entry_no);
@@ -525,7 +466,23 @@ read_table_log_header()
bool
read_table_log_entry(uint read_entry, TABLE_LOG_ENTRY *table_log_entry)
{
  char *file_entry= (char*)&global_table_log.file_entry;
  DBUG_ENTER("read_table_log_entry");

  if (read_table_log_file_entry(global_table_log.file_id,
                                (char*)&file_entry, read_entry))
  {
    /* Error handling */
    DBUG_RETURN(TRUE);
  }
  table_log_entry->entry_type= file_entry[0];
  table_log_entry->action_type= file_entry[1];
  table_log_entry->next_entry= uint4korr(&file_entry[2]);
  table_log_entry->name= &file_entry[6];
  index= 6 + global_table_log->name_len;
  table_log_entry->from_name= &file_entry[index];
  index+= global_table_log->name_len;
  table_log_entry->handler_type= &file_entry[index];
  DBUG_RETURN(FALSE);
}

@@ -542,93 +499,234 @@ read_table_log_entry(uint read_entry, TABLE_LOG_ENTRY *table_log_entry)
    number of entries to zero.
*/

static
bool
init_table_log()
{
  bool error= FALSE;
  char file_name[FN_REFLEN];
  DBUG_ENTER("init_table_log");

  VOID(my_delete(global_table_log.file_name));
  global_table_log.file_id= my_open(global_table_log.file_name,
                                    0, 0, MYF(0));
  create_table_log_file_name(file_name);
  VOID(my_delete(file_name));
  if ((global_table_log.file_id= my_create(file_name,
                                           CREATE_MODE,
                                           create_flags, MYF(0))) < 0)
  {
    /* Couldn't create table log file, this is serious error */
    abort();
  }
  if (write_table_log_header())
  {
    /* Write to error log */
    error= TRUE;
  }
  DBUG_RETURN(error);
}


/*
  Release all memory allocated to the table log
  Execute one action in a table log entry
  SYNOPSIS
    release_table_log()
    execute_table_log_action()
    table_log_entry              Information in action entry to execute
  RETURN VALUES
    NONE
    TRUE                       Error
    FALSE                      Success
*/

void
release_table_log()
static
bool
execute_table_log_action(TABLE_LOG_ENTRY *table_log_entry)
{
  DBUG_ENTER("release_table_log");

  VOID(pthread_mutex_destroy(&LOCK_gtl));
  DBUG_RETURN_VOID;
  DBUG_ENTER("execute_table_log_action");
  DBUG_RETURN(FALSE);
}


/*
  Lock mutex for global table log
  Get a free entry in the table log
  SYNOPSIS
    lock_global_table_log()
    get_free_table_log_entry()
    out:active_entry                A table log memory entry returned
  RETURN VALUES
    NONE
    TRUE                       Error
    FALSE                      Success
*/

void
lock_global_table_log()
static
bool
get_free_table_log_entry(TABLE_LOG_MEMORY_ENTRY **active_entry)
{
  DBUG_ENTER("lock_global_table_log");
  bool write_header;
  TABLE_LOG_MEMORY_ENTRY *used_entry;
  TABLE_LOG_MEMORY_ENTRY *first_used= global_table_log.first_used;
  if (global_table_log.first_free == NULL)
  {
    if (!(used_entry= my_malloc(sizeof(TABLE_LOG_MEMORY_ENTRY))))
    {
      DBUG_RETURN(TRUE);
    }
    global_table_log.no_entries++;
    used_entry->entry_no= entry_no= global_table_log.no_entries;
    write_header= TRUE;
  }
  else
  {
    used_entry= global_table_log.first_free;
    global_table_log.first_free= used_entry->next_log_entry;
    entry_no= first_free->entry_pos;
    used_entry= first_free;
    write_header= FALSE;
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
  global_table_log.first_used= used_entry;
  if (first_used)
    first_used->prev_log_entry= used_entry;

  VOID(pthread_mutex_lock(&LOCK_gtl));
  DBUG_RETURN_VOID;
  *active_entry= used_entry;
}


/*
  Unlock mutex for global table log
  External interface methods for the Table log Module
  ---------------------------------------------------
*/

/*
  SYNOPSIS
    unlock_global_table_log()
    write_table_log_entry()
    table_log_entry         Information about log entry
    out:entry_written       Entry information written into   

  RETURN VALUES
    NONE
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
    A careful write of the table log is performed to ensure that we can
    handle crashes occurring during CREATE and ALTER TABLE processing.
*/

void
unlock_global_table_log()
bool
write_table_log_entry(TABLE_LOG_ENTRY *table_log_entry,
                      TABLE_LOG_MEMORY_ENTRY **active_entry)
{
  DBUG_ENTER("unlock_global_table_log");
  bool error;
  DBUG_ENTER("write_table_log_entry");

  VOID(pthread_mutex_unlock(&LOCK_gtl));
  DBUG_RETURN_VOID;
  global_table_log.file_entry[0]= 'i';
  global_table_log.file_entry[1]= table_log_entry->action_type;
  int4store(&global_table_log.file_entry[2],
            table_log_entry->next_entry);
  strcpy(&global_table_log.file_entry[6], table_log_entry->name);
  if (table_log_entry.action_type == 'r')
    global_table_log.file_entry[6 + NAMELEN]= 0;
  else
    strcpy(&global_table_log.file_entry[6 + NAMELEN],
          table_log_entry->from_name);
  strcpy(&global_table_log.file_entry[6 + (2*NAMELEN)],
         table_log_entry->handler_type);
  if (get_free_table_log_entry(active_entry))
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
  if (write_table_log_file_entry(global_table_log.file_id,
                                 global_table_log.file_entry,
                                 (*active_entry)->entry_pos))
    error= TRUE;
  else if (write_header || !(write_table_log_header()))
    error= TRUE;
  if (error)
    release_table_log_memory_entry(*active_entry);
  DBUG_RETURN(error);
}


/*
  Execute one action in a table log entry
  Write final entry in the table log
  SYNOPSIS
    execute_table_log_action()
    table_log_entry              Information in action entry to execute
    write_execute_table_log_entry()
    first_entry                    First entry in linked list of entries
                                   to execute, if 0 = NULL it means that
                                   the entry is removed and the entries
                                   are put into the free list.
    in:out:exec_entry              Entry to execute, 0 = NULL if the entry
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
  RETURN VALUES
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
    This is the last write in the table log. The previous log entries have
    already been written but not yet synched to disk.
*/ 

bool
execute_table_log_action(TABLE_LOG_ENTRY *table_log_entry)
write_execute_table_log_entry(uint first_entry,
                              TABLE_LOG_MEMORY_ENTRY **active_entry)
{
  DBUG_ENTER("execute_table_log_action");
  char *file_entry= (char*)global_table_log.file_entry;
  DBUG_ENTER("write_execute_table_log_entry");

  VOID(sync_table_log());
  file_entry[0]= 'e';
  file_entry[1]= 0; /* Ignored for execute entries */
  int4store(&file_entry[2], first_entry);
  file_entry[6]= 0;
  file_entry[6 + NAMELEN]= 0;
  file_entry[6 + 2*NAMELEN]= 0;
  if (get_free_table_log_entry(active_entry))
  {
    DBUG_RETURN(TRUE);
  }
  if (write_table_log_file_entry((*active_entry)->entry_pos))
  {
    release_table_log_memory_entry(*active_entry);
    DBUG_RETURN(TRUE);
  }
  VOID(sync_table_log());
  DBUG_RETURN(FALSE);
}


/*
  Release a log memory entry
  SYNOPSIS
    release_table_log_memory_entry()
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

void
release_table_log_memory_entry(TABLE_LOG_MEMORY_ENTRY *log_entry)
{
  TABLE_LOG_MEMORY_ENTRY *first_free= global_table_log.first_free;
  TABLE_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
  TABLE_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
  DBUG_ENTER("release_table_log_memory_entry");

  global_table_log.first_free= log_entry;
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
    global_table_log.first_used= next_log_entry;
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
  DBUG_RETURN_VOID;
}


/*
  Execute one entry in the table log. Executing an entry means executing
  a linked list of actions.
@@ -640,6 +738,7 @@ execute_table_log_action(TABLE_LOG_ENTRY *table_log_entry)
    FALSE                      Success
*/

static
bool
execute_table_log_entry(uint first_entry)
{
@@ -649,19 +748,24 @@ execute_table_log_entry(uint first_entry)

  do
  {
    read_table_log_entry(read_entry, &table_log_entry);
    if (read_table_log_entry(read_entry, &table_log_entry))
    {
      DBUG_ASSERT(0);
      /* Write to error log and continue with next log entry */
      break;
    }
    DBUG_ASSERT(table_log_entry.entry_type == 'i');
    if (execute_table_log_action(&table_log_entry))
    {
      /* error handling */
      DBUG_RETURN(TRUE);
      DBUG_ASSERT(0);
      /* Write to error log and continue with next log entry */
      break;
    }
    read_entry= table_log_entry.next_entry;
  } while (read_entry);
  DBUG_RETURN(FALSE);
}


/*
  Execute the table log at recovery of MySQL Server
  SYNOPSIS
@@ -680,17 +784,94 @@ execute_table_log_recovery()
  no_entries= read_log_header();
  for (i= 0; i < no_entries; i++)
  {
    read_table_log_entry(i, &table_log_entry);
    if (read_table_log_entry(i, &table_log_entry))
    {
      DBUG_ASSERT(0);
      /* Write to error log */
      break;
    }
    if (table_log_entry.entry_type == 'e')
    {
      if (execute_table_log_entry(table_log_entry.next_entry))
      {
        /* error handling */
        /*
           Currently errors are either crashing or ignored so we should
           never end up here
        */
        abort();
        DBUG_RETURN_VOID;
      }
    }
  }
  init_table_log();
  VOID(init_table_log());
  DBUG_RETURN_VOID;
}


/*
  Release all memory allocated to the table log
  SYNOPSIS
    release_table_log()
  RETURN VALUES
    NONE
*/

void
release_table_log()
{
  TABLE_LOG_MEMORY_ENTRY *free_list= global_table_log.first_free;
  TABLE_LOG_MEMORY_ENTRY *used_list= global_table_log.first_used;
  DBUG_ENTER("release_table_log");

  VOID(pthread_mutex_destroy(&LOCK_gtl));
  while (used_list)
  {
    TABLE_LOG_MEMORY_ENTRY tmp= used_list;
    my_free(used_list, MYF(0));
    used_list= tmp->next_log_entry;
  }
  while (free_list)
  {
    TABLE_LOG_MEMORY_ENTRY tmp= free_list;
    my_free(free_list, MYF(0));
    free_list= tmp->next_log_entry;
  }
  DBUG_RETURN_VOID;
}


/*
  Lock mutex for global table log
  SYNOPSIS
    lock_global_table_log()
  RETURN VALUES
    NONE
*/

void
lock_global_table_log()
{
  DBUG_ENTER("lock_global_table_log");

  VOID(pthread_mutex_lock(&LOCK_gtl));
  DBUG_RETURN_VOID;
}


/*
  Unlock mutex for global table log
  SYNOPSIS
    unlock_global_table_log()
  RETURN VALUES
    NONE
*/

void
unlock_global_table_log()
{
  DBUG_ENTER("unlock_global_table_log");

  VOID(pthread_mutex_unlock(&LOCK_gtl));
  DBUG_RETURN_VOID;
}