Commit 910bf380 authored by unknown's avatar unknown

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
......@@ -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);
......
......@@ -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();
......
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment