summaryrefslogtreecommitdiff
path: root/sql/sql_partition.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_partition.cc')
-rw-r--r--sql/sql_partition.cc1429
1 files changed, 1212 insertions, 217 deletions
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 6b111f8406d..f3bbb888eb9 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 MySQL AB
+/* Copyright (C) 2005, 2006 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
@@ -237,8 +237,8 @@ bool partition_default_handling(TABLE *table, partition_info *part_info,
check_reorganise_list()
new_part_info New partition info
old_part_info Old partition info
- list_part_names The list of partition names that will go away and can be reused in the
- new table.
+ list_part_names The list of partition names that will go away and
+ can be reused in the new table.
RETURN VALUES
TRUE Inacceptable name conflict detected.
@@ -914,7 +914,7 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
context->table_list= &tables;
context->first_name_resolution_table= &tables;
context->last_name_resolution_table= NULL;
- func_expr->walk(&Item::change_context_processor, (byte*) context);
+ func_expr->walk(&Item::change_context_processor, 0, (byte*) context);
save_where= thd->where;
thd->where= "partition function";
error= func_expr->fix_fields(thd, (Item**)0);
@@ -1401,7 +1401,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
{
bool result= TRUE;
partition_info *part_info= table->part_info;
- ulong save_set_query_id= thd->set_query_id;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
Item *thd_free_list= thd->free_list;
DBUG_ENTER("fix_partition_func");
@@ -1409,8 +1409,8 @@ bool fix_partition_func(THD *thd, TABLE *table,
{
DBUG_RETURN(FALSE);
}
- thd->set_query_id= 0;
- DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id));
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
if (!is_create_table_ind ||
thd->lex->sql_command != SQLCOM_CREATE_TABLE)
@@ -1531,8 +1531,8 @@ bool fix_partition_func(THD *thd, TABLE *table,
result= FALSE;
end:
thd->free_list= thd_free_list;
- thd->set_query_id= save_set_query_id;
- DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id));
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
DBUG_RETURN(result);
}
@@ -1722,8 +1722,8 @@ static int add_keyword_int(File fptr, const char *keyword, longlong num)
static int add_engine(File fptr, handlerton *engine_type)
{
- const char *engine_str= engine_type->name;
- DBUG_PRINT("info", ("ENGINE = %s", engine_str));
+ const char *engine_str= hton2plugin[engine_type->slot]->name.str;
+ DBUG_PRINT("info", ("ENGINE: %s", engine_str));
int err= add_string(fptr, "ENGINE = ");
return err + add_string(fptr, engine_str);
}
@@ -1732,8 +1732,9 @@ static int add_partition_options(File fptr, partition_element *p_elem)
{
int err= 0;
+ err+= add_space(fptr);
if (p_elem->tablespace_name)
- err+= add_keyword_string(fptr,"TABLESPACE", FALSE,
+ err+= add_keyword_string(fptr,"TABLESPACE", FALSE,
p_elem->tablespace_name);
if (p_elem->nodegroup_id != UNDEF_NODEGROUP)
err+= add_keyword_int(fptr,"NODEGROUP",(longlong)p_elem->nodegroup_id);
@@ -1758,7 +1759,7 @@ static int add_partition_values(File fptr, partition_info *part_info, partition_
if (part_info->part_type == RANGE_PARTITION)
{
- err+= add_string(fptr, "VALUES LESS THAN ");
+ err+= add_string(fptr, " VALUES LESS THAN ");
if (!p_elem->max_value)
{
err+= add_begin_parenthesis(fptr);
@@ -1775,7 +1776,7 @@ static int add_partition_values(File fptr, partition_info *part_info, partition_
{
uint i;
List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
- err+= add_string(fptr, "VALUES IN ");
+ err+= add_string(fptr, " VALUES IN ");
uint no_items= p_elem->list_val_list.elements;
err+= add_begin_parenthesis(fptr);
@@ -1804,7 +1805,7 @@ static int add_partition_values(File fptr, partition_info *part_info, partition_
err+= add_end_parenthesis(fptr);
}
end:
- return err + add_space(fptr);
+ return err;
}
/*
@@ -1818,7 +1819,7 @@ end:
buf_length A pointer to the returned buffer length
use_sql_alloc Allocate buffer from sql_alloc if true
otherwise use my_malloc
- write_all Write everything, also default values
+ show_partition_options Should we display partition options
RETURN VALUES
NULL error
@@ -1847,7 +1848,7 @@ end:
char *generate_partition_syntax(partition_info *part_info,
uint *buf_length,
bool use_sql_alloc,
- bool write_all)
+ bool show_partition_options)
{
uint i,j, tot_no_parts, no_subparts, no_parts;
partition_element *part_elem;
@@ -1856,15 +1857,10 @@ char *generate_partition_syntax(partition_info *part_info,
char path[FN_REFLEN];
int err= 0;
List_iterator<partition_element> part_it(part_info->partitions);
- List_iterator<partition_element> temp_it(part_info->temp_partitions);
File fptr;
char *buf= NULL; //Return buffer
- uint use_temp= 0;
- uint no_temp_parts= part_info->temp_partitions.elements;
- bool write_part_state;
DBUG_ENTER("generate_partition_syntax");
- write_part_state= (part_info->part_state && !part_info->part_state_len);
if (unlikely(((fptr= create_temp_file(path,mysql_tmpdir,"psy",
O_RDWR | O_BINARY | O_TRUNC |
O_TEMPORARY, MYF(MY_WME)))) < 0))
@@ -1912,6 +1908,8 @@ char *generate_partition_syntax(partition_info *part_info,
{
err+= add_subpartition_by(fptr);
/* Must be hash partitioning for subpartitioning */
+ if (part_info->linear_hash_ind)
+ err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_subpart_fields)
err+= add_key_partition(fptr, part_info->subpart_field_list);
else
@@ -1929,75 +1927,36 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_space(fptr);
}
}
- no_parts= part_info->no_parts;
- tot_no_parts= no_parts + no_temp_parts;
+ tot_no_parts= part_info->partitions.elements;
no_subparts= part_info->no_subparts;
- if (write_all || (!part_info->use_default_partitions))
+ if (!part_info->use_default_partitions)
{
+ bool first= TRUE;
err+= add_begin_parenthesis(fptr);
i= 0;
do
{
- /*
- We need to do some clever list manipulation here since we have two
- different needs for our list processing and here we take some of the
- cost of using a simpler list processing for the other parts of the
- code.
-
- ALTER TABLE REORGANIZE PARTITIONS has the list of partitions to be
- the final list as the main list and the reorganised partitions is in
- the temporary partition list. Thus when finding the first part added
- we insert the temporary list if there is such a list. If there is no
- temporary list we are performing an ADD PARTITION.
- */
- if (use_temp && use_temp <= no_temp_parts)
- {
- part_elem= temp_it++;
- DBUG_ASSERT(no_temp_parts);
- no_temp_parts--;
- }
- else if (use_temp)
- {
- DBUG_ASSERT(no_parts);
- part_elem= save_part_elem;
- use_temp= 0;
- no_parts--;
- }
- else
- {
- part_elem= part_it++;
- if ((part_elem->part_state == PART_TO_BE_ADDED ||
- part_elem->part_state == PART_IS_ADDED) && no_temp_parts)
- {
- save_part_elem= part_elem;
- part_elem= temp_it++;
- no_temp_parts--;
- use_temp= 1;
- }
- else
- {
- DBUG_ASSERT(no_parts);
- no_parts--;
- }
- }
-
- if (part_elem->part_state != PART_IS_DROPPED)
+ part_elem= part_it++;
+ if (part_elem->part_state != PART_TO_BE_DROPPED &&
+ part_elem->part_state != PART_REORGED_DROPPED)
{
- if (write_part_state)
+ if (!first)
{
- uint32 part_state_id= part_info->part_state_len;
- part_info->part_state[part_state_id]= (uchar)part_elem->part_state;
- part_info->part_state_len= part_state_id+1;
+ err+= add_comma(fptr);
+ err+= add_space(fptr);
}
+ first= FALSE;
err+= add_partition(fptr);
err+= add_name_string(fptr, part_elem->partition_name);
- err+= add_space(fptr);
err+= add_partition_values(fptr, part_info, part_elem);
- if (!part_info->is_sub_partitioned())
- err+= add_partition_options(fptr, part_elem);
- if (part_info->is_sub_partitioned() &&
- (write_all || (!part_info->use_default_subpartitions)))
+ if (!part_info->is_sub_partitioned() ||
+ part_info->use_default_subpartitions)
+ {
+ if (show_partition_options)
+ err+= add_partition_options(fptr, part_elem);
+ }
+ else
{
err+= add_space(fptr);
err+= add_begin_parenthesis(fptr);
@@ -2008,8 +1967,8 @@ char *generate_partition_syntax(partition_info *part_info,
part_elem= sub_it++;
err+= add_subpartition(fptr);
err+= add_name_string(fptr, part_elem->partition_name);
- err+= add_space(fptr);
- err+= add_partition_options(fptr, part_elem);
+ if (show_partition_options)
+ err+= add_partition_options(fptr, part_elem);
if (j != (no_subparts-1))
{
err+= add_comma(fptr);
@@ -2019,16 +1978,10 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_end_parenthesis(fptr);
} while (++j < no_subparts);
}
- if (i != (tot_no_parts-1))
- {
- err+= add_comma(fptr);
- err+= add_space(fptr);
- }
}
if (i == (tot_no_parts-1))
err+= add_end_parenthesis(fptr);
} while (++i < tot_no_parts);
- DBUG_ASSERT(!no_parts && !no_temp_parts);
}
if (err)
goto close_file;
@@ -2106,10 +2059,7 @@ bool partition_key_modified(TABLE *table, List<Item> &fields)
in function
*/
-static
-inline
-longlong
-part_val_int(Item *item_expr)
+static inline longlong part_val_int(Item *item_expr)
{
longlong value= item_expr->val_int();
if (item_expr->null_value)
@@ -2313,9 +2263,11 @@ static uint32 get_part_id_linear_key(partition_info *part_info,
out:part_id The partition id is returned through this pointer
RETURN VALUE
- part_id
- return TRUE means that the fields of the partition function didn't fit
- into any partition and thus the values of the PF-fields are not allowed.
+ part_id Partition id of partition that would contain
+ row with given values of PF-fields
+ HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't
+ fit into any partition and thus the values of
+ the PF-fields are not allowed.
DESCRIPTION
A routine used from write_row, update_row and delete_row from any
@@ -2354,9 +2306,11 @@ static uint32 get_part_id_linear_key(partition_info *part_info,
out:part_id The partition id is returned through this pointer
RETURN VALUE
- part_id
- return TRUE means that the fields of the partition function didn't fit
- into any partition and thus the values of the PF-fields are not allowed.
+ part_id Partition id of partition that would contain
+ row with given values of PF-fields
+ HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't
+ fit into any partition and thus the values of
+ the PF-fields are not allowed.
DESCRIPTION
@@ -2536,6 +2490,8 @@ int get_partition_id_range(partition_info *part_info,
range_array[loc_part_id] != LONGLONG_MAX &&
part_func_value >= range_array[loc_part_id])
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ DBUG_PRINT("exit",("partition: %d", *part_id));
DBUG_RETURN(0);
}
@@ -3559,6 +3515,7 @@ bool mysql_unpack_partition(THD *thd, const uchar *part_buf,
result= FALSE;
end:
+ lex_end(thd->lex);
thd->free_list= thd_free_list;
thd->lex= old_lex;
thd->variables.character_set_client= old_character_set_client;
@@ -3604,27 +3561,6 @@ set_engine_all_partitions(partition_info *part_info,
}
/*
SYNOPSIS
- fast_alter_partition_error_handler()
- lpt Container for parameters
-
- RETURN VALUES
- None
-
- DESCRIPTION
- Support routine to clean up after failures of on-line ALTER TABLE
- for partition management.
-*/
-
-static void fast_alter_partition_error_handler(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- DBUG_ENTER("fast_alter_partition_error_handler");
- /* TODO: WL 2826 Error handling */
- DBUG_VOID_RETURN;
-}
-
-
-/*
- SYNOPSIS
fast_end_partition()
thd Thread object
out:copied Number of records copied
@@ -3644,6 +3580,7 @@ static void fast_alter_partition_error_handler(ALTER_PARTITION_PARAM_TYPE *lpt)
static int fast_end_partition(THD *thd, ulonglong copied,
ulonglong deleted,
+ TABLE *table,
TABLE_LIST *table_list, bool is_empty,
ALTER_PARTITION_PARAM_TYPE *lpt,
bool written_bin_log)
@@ -3671,7 +3608,7 @@ static int fast_end_partition(THD *thd, ulonglong copied,
send_ok(thd,copied+deleted,0L,tmp_name);
DBUG_RETURN(FALSE);
}
- fast_alter_partition_error_handler(lpt);
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(TRUE);
}
@@ -3707,17 +3644,9 @@ static bool check_engine_condition(partition_element *p_elem,
DBUG_ENTER("check_engine_condition");
DBUG_PRINT("enter", ("def_eng = %u, first = %u", default_engine, *first));
- if (*engine_type)
- DBUG_PRINT("info", ("engine_type = %s", (*engine_type)->name));
- else
- DBUG_PRINT("info", ("engine_type = NULL"));
if (*first && default_engine)
{
*engine_type= p_elem->engine_type;
- if (*engine_type)
- DBUG_PRINT("info", ("engine_type changed to = %s", (*engine_type)->name));
- else
- DBUG_PRINT("info", ("engine_type changed to = NULL"));
}
*first= FALSE;
if ((!default_engine &&
@@ -3920,7 +3849,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info,
after the change as before. Thus we can reply ok immediately
without any changes at all.
*/
- DBUG_RETURN(fast_end_partition(thd, ULL(0), ULL(0), NULL,
+ DBUG_RETURN(fast_end_partition(thd, ULL(0), ULL(0),
+ table, NULL,
TRUE, NULL, FALSE));
}
else if (new_part_no > curr_part_no)
@@ -4026,6 +3956,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info,
DBUG_RETURN(TRUE);
}
alt_part_info->part_type= tab_part_info->part_type;
+ alt_part_info->subpart_type= tab_part_info->subpart_type;
if (alt_part_info->set_up_defaults_for_partitioning(table->file,
ULL(0),
tab_part_info->no_parts))
@@ -4223,6 +4154,7 @@ that are reorganised.
tab_part_info->use_default_partitions= FALSE;
}
tab_part_info->use_default_no_partitions= FALSE;
+ tab_part_info->is_auto_partitioned= FALSE;
}
}
else if (alter_info->flags == ALTER_DROP_PARTITION)
@@ -4238,6 +4170,8 @@ that are reorganised.
uint no_parts_dropped= alter_info->partition_names.elements;
uint no_parts_found= 0;
List_iterator<partition_element> part_it(tab_part_info->partitions);
+
+ tab_part_info->is_auto_partitioned= FALSE;
if (!(tab_part_info->part_type == RANGE_PARTITION ||
tab_part_info->part_type == LIST_PARTITION))
{
@@ -4272,6 +4206,7 @@ that are reorganised.
my_error(ER_ROW_IS_REFERENCED, MYF(0));
DBUG_RETURN(TRUE);
}
+ tab_part_info->no_parts-= no_parts_dropped;
}
else if ((alter_info->flags & ALTER_OPTIMIZE_PARTITION) ||
(alter_info->flags & ALTER_ANALYZE_PARTITION) ||
@@ -4316,6 +4251,11 @@ that are reorganised.
my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), ptr);
DBUG_RETURN(TRUE);
}
+ if (!(*fast_alter_partition))
+ {
+ table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
}
else if (alter_info->flags & ALTER_COALESCE_PARTITION)
{
@@ -4416,7 +4356,10 @@ state of p1.
tab_part_info->no_parts= no_parts_remain;
}
if (!(alter_info->flags & ALTER_TABLE_REORG))
+ {
tab_part_info->use_default_no_partitions= FALSE;
+ tab_part_info->is_auto_partitioned= FALSE;
+ }
}
else if (alter_info->flags == ALTER_REORGANIZE_PARTITION)
{
@@ -4435,6 +4378,8 @@ state of p1.
uint no_parts_new= thd->work_part_info->partitions.elements;
partition_info *alt_part_info= thd->work_part_info;
uint check_total_partitions;
+
+ tab_part_info->is_auto_partitioned= FALSE;
if (no_parts_reorged > tab_part_info->no_parts)
{
my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0));
@@ -4454,6 +4399,15 @@ state of p1.
my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
+ alt_part_info->part_type= tab_part_info->part_type;
+ alt_part_info->subpart_type= tab_part_info->subpart_type;
+ DBUG_ASSERT(!alt_part_info->use_default_partitions);
+ if (alt_part_info->set_up_defaults_for_partitioning(table->file,
+ ULL(0),
+ 0))
+ {
+ DBUG_RETURN(TRUE);
+ }
/*
Online handling:
REORGANIZE PARTITION:
@@ -4651,13 +4605,13 @@ the generated partition syntax in a correct manner.
if (alter_info->flags & ALTER_REMOVE_PARTITIONING)
{
DBUG_PRINT("info", ("Remove partitioning"));
- if (!(thd->lex->create_info.used_fields & HA_CREATE_USED_ENGINE))
+ if (!(create_info->used_fields & HA_CREATE_USED_ENGINE))
{
DBUG_PRINT("info", ("No explicit engine used"));
create_info->db_type= table->part_info->default_engine_type;
}
- DBUG_PRINT("info", ("New engine type = %s",
- create_info->db_type->name));
+ DBUG_PRINT("info", ("New engine type: %s",
+ hton2plugin[create_info->db_type->slot]->name.str));
thd->work_part_info= NULL;
*partition_changed= TRUE;
}
@@ -4668,13 +4622,29 @@ the generated partition syntax in a correct manner.
beneath.
*/
thd->work_part_info= table->part_info;
- if (thd->lex->create_info.used_fields & HA_CREATE_USED_ENGINE &&
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE &&
create_info->db_type != table->part_info->default_engine_type)
{
/*
Make sure change of engine happens to all partitions.
*/
- set_engine_all_partitions(thd->work_part_info, create_info->db_type);
+ DBUG_PRINT("info", ("partition changed"));
+ if (table->part_info->is_auto_partitioned)
+ {
+ /*
+ If the user originally didn't specify partitioning to be
+ used we can remove it now.
+ */
+ thd->work_part_info= NULL;
+ }
+ else
+ {
+ /*
+ Ensure that all partitions have the proper engine set-up
+ */
+ set_engine_all_partitions(thd->work_part_info,
+ create_info->db_type);
+ }
*partition_changed= TRUE;
}
}
@@ -4688,7 +4658,10 @@ the generated partition syntax in a correct manner.
using the partition handler.
*/
if (thd->work_part_info != table->part_info)
+ {
+ DBUG_PRINT("info", ("partition changed"));
*partition_changed= TRUE;
+ }
if (create_info->db_type == &partition_hton)
part_info->default_engine_type= table->part_info->default_engine_type;
else
@@ -4700,11 +4673,9 @@ the generated partition syntax in a correct manner.
}
if (!is_native_partitioned)
{
- DBUG_ASSERT(create_info->db_type != &default_hton);
+ DBUG_ASSERT(create_info->db_type);
create_info->db_type= &partition_hton;
}
- DBUG_PRINT("info", ("default_engine_type = %s",
- thd->work_part_info->default_engine_type->name));
}
}
DBUG_RETURN(FALSE);
@@ -4740,14 +4711,22 @@ the generated partition syntax in a correct manner.
static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
{
char path[FN_REFLEN+1];
+ int error;
+ handler *file= lpt->table->file;
DBUG_ENTER("mysql_change_partitions");
build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, "");
- DBUG_RETURN(lpt->table->file->change_partitions(lpt->create_info, path,
- &lpt->copied,
- &lpt->deleted,
- lpt->pack_frm_data,
- lpt->pack_frm_len));
+ if ((error= file->change_partitions(lpt->create_info, path, &lpt->copied,
+ &lpt->deleted, lpt->pack_frm_data,
+ lpt->pack_frm_len)))
+ {
+ if (error != ER_OUTOFMEMORY)
+ file->print_error(error, MYF(0));
+ else
+ lpt->thd->fatal_error();
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
}
@@ -4773,10 +4752,17 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
{
char path[FN_REFLEN+1];
+ int error;
DBUG_ENTER("mysql_rename_partitions");
build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, "");
- DBUG_RETURN(lpt->table->file->rename_partitions(path));
+ if ((error= lpt->table->file->rename_partitions(path)))
+ {
+ if (error != 1)
+ lpt->table->file->print_error(error, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
}
@@ -4807,11 +4793,13 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
List_iterator<partition_element> part_it(part_info->partitions);
uint i= 0;
uint remove_count= 0;
+ int error;
DBUG_ENTER("mysql_drop_partitions");
build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, "");
- if (lpt->table->file->drop_partitions(path))
+ if ((error= lpt->table->file->drop_partitions(path)))
{
+ lpt->table->file->print_error(error, MYF(0));
DBUG_RETURN(TRUE);
}
do
@@ -4829,6 +4817,838 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
/*
+ Insert log entry into list
+ SYNOPSIS
+ insert_part_info_log_entry_list()
+ log_entry
+ RETURN VALUES
+ NONE
+*/
+
+static void insert_part_info_log_entry_list(partition_info *part_info,
+ DDL_LOG_MEMORY_ENTRY *log_entry)
+{
+ log_entry->next_active_log_entry= part_info->first_log_entry;
+ part_info->first_log_entry= log_entry;
+}
+
+
+/*
+ Release all log entries for this partition info struct
+ SYNOPSIS
+ release_part_info_log_entries()
+ first_log_entry First log entry in list to release
+ RETURN VALUES
+ NONE
+*/
+
+static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry)
+{
+ DBUG_ENTER("release_part_info_log_entries");
+
+ while (log_entry)
+ {
+ release_ddl_log_memory_entry(log_entry);
+ log_entry= log_entry->next_active_log_entry;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Log an delete/rename frm file
+ SYNOPSIS
+ write_log_replace_delete_frm()
+ lpt Struct for parameters
+ next_entry Next reference to use in log record
+ from_path Name to rename from
+ to_path Name to rename to
+ replace_flag TRUE if replace, else delete
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Support routine that writes a replace or delete of an frm file into the
+ ddl log. It also inserts an entry that keeps track of used space into
+ the partition info object
+*/
+
+static bool write_log_replace_delete_frm(ALTER_PARTITION_PARAM_TYPE *lpt,
+ uint next_entry,
+ const char *from_path,
+ const char *to_path,
+ bool replace_flag)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DBUG_ENTER("write_log_replace_delete_frm");
+
+ if (replace_flag)
+ ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
+ else
+ ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
+ ddl_log_entry.next_entry= next_entry;
+ ddl_log_entry.handler_name= reg_ext;
+ ddl_log_entry.name= to_path;
+ if (replace_flag)
+ ddl_log_entry.from_name= from_path;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ insert_part_info_log_entry_list(lpt->part_info, log_entry);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Log final partition changes in change partition
+ SYNOPSIS
+ write_log_changed_partitions()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ This code is used to perform safe ADD PARTITION for HASH partitions
+ and COALESCE for HASH partitions and REORGANIZE for any type of
+ partitions.
+ We prepare entries for all partitions except the reorganised partitions
+ in REORGANIZE partition, those are handled by
+ write_log_dropped_partitions. For those partitions that are replaced
+ special care is needed to ensure that this is performed correctly and
+ this requires a two-phased approach with this log as a helper for this.
+
+ This code is closely intertwined with the code in rename_partitions in
+ the partition handler.
+*/
+
+static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
+ uint *next_entry, const char *path)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ char tmp_path[FN_LEN];
+ char normal_path[FN_LEN];
+ List_iterator<partition_element> part_it(part_info->partitions);
+ uint temp_partitions= part_info->temp_partitions.elements;
+ uint no_elements= part_info->partitions.elements;
+ uint i= 0;
+ DBUG_ENTER("write_log_changed_partitions");
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_IS_CHANGED ||
+ (part_elem->part_state == PART_IS_ADDED && temp_partitions))
+ {
+ if (part_info->is_sub_partitioned())
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint no_subparts= part_info->no_subparts;
+ uint j= 0;
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(sub_elem->engine_type);
+ create_subpartition_name(tmp_path, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ TEMP_PART_NAME);
+ create_subpartition_name(normal_path, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ NORMAL_PART_NAME);
+ ddl_log_entry.name= normal_path;
+ ddl_log_entry.from_name= tmp_path;
+ if (part_elem->part_state == PART_IS_CHANGED)
+ ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
+ else
+ ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *next_entry= log_entry->entry_pos;
+ sub_elem->log_entry= log_entry;
+ insert_part_info_log_entry_list(part_info, log_entry);
+ } while (++j < no_subparts);
+ }
+ else
+ {
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(part_elem->engine_type);
+ create_partition_name(tmp_path, path,
+ part_elem->partition_name,
+ TEMP_PART_NAME, TRUE);
+ create_partition_name(normal_path, path,
+ part_elem->partition_name,
+ NORMAL_PART_NAME, TRUE);
+ ddl_log_entry.name= normal_path;
+ ddl_log_entry.from_name= tmp_path;
+ if (part_elem->part_state == PART_IS_CHANGED)
+ ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
+ else
+ ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *next_entry= log_entry->entry_pos;
+ part_elem->log_entry= log_entry;
+ insert_part_info_log_entry_list(part_info, log_entry);
+ }
+ }
+ } while (++i < no_elements);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Log dropped partitions
+ SYNOPSIS
+ write_log_dropped_partitions()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
+ uint *next_entry,
+ const char *path,
+ bool temp_list)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ char tmp_path[FN_LEN];
+ List_iterator<partition_element> part_it(part_info->partitions);
+ List_iterator<partition_element> temp_it(part_info->temp_partitions);
+ uint no_temp_partitions= part_info->temp_partitions.elements;
+ uint no_elements= part_info->partitions.elements;
+ uint i= 0;
+ DBUG_ENTER("write_log_dropped_partitions");
+
+ ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
+ if (temp_list)
+ no_elements= no_temp_partitions;
+ while (no_elements--)
+ {
+ partition_element *part_elem;
+ if (temp_list)
+ part_elem= temp_it++;
+ else
+ part_elem= part_it++;
+ if (part_elem->part_state == PART_TO_BE_DROPPED ||
+ part_elem->part_state == PART_TO_BE_ADDED ||
+ part_elem->part_state == PART_CHANGED)
+ {
+ uint name_variant;
+ if (part_elem->part_state == PART_CHANGED ||
+ (part_elem->part_state == PART_TO_BE_ADDED &&
+ no_temp_partitions))
+ name_variant= TEMP_PART_NAME;
+ else
+ name_variant= NORMAL_PART_NAME;
+ if (part_info->is_sub_partitioned())
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint no_subparts= part_info->no_subparts;
+ uint j= 0;
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(sub_elem->engine_type);
+ create_subpartition_name(tmp_path, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ name_variant);
+ ddl_log_entry.name= tmp_path;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *next_entry= log_entry->entry_pos;
+ sub_elem->log_entry= log_entry;
+ insert_part_info_log_entry_list(part_info, log_entry);
+ } while (++j < no_subparts);
+ }
+ else
+ {
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(part_elem->engine_type);
+ create_partition_name(tmp_path, path,
+ part_elem->partition_name,
+ name_variant, TRUE);
+ ddl_log_entry.name= tmp_path;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *next_entry= log_entry->entry_pos;
+ part_elem->log_entry= log_entry;
+ insert_part_info_log_entry_list(part_info, log_entry);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Set execute log entry in ddl log for this partitioned table
+ SYNOPSIS
+ set_part_info_exec_log_entry()
+ part_info Partition info object
+ exec_log_entry Log entry
+ RETURN VALUES
+ NONE
+*/
+
+static void set_part_info_exec_log_entry(partition_info *part_info,
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry)
+{
+ part_info->exec_log_entry= exec_log_entry;
+ exec_log_entry->next_active_log_entry= NULL;
+}
+
+
+/*
+ Write the log entry to ensure that the shadow frm file is removed at
+ crash.
+ SYNOPSIS
+ write_log_drop_shadow_frm()
+ lpt Struct containing parameters
+ install_frm Should we log action to install shadow frm or should
+ the action be to remove the shadow frm file.
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare an entry to the ddl log indicating a drop/install of the shadow frm
+ file and its corresponding handler file.
+*/
+
+static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
+ char shadow_path[FN_LEN];
+ DBUG_ENTER("write_log_drop_shadow_frm");
+
+ build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
+ lpt->table_name, "#");
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_replace_delete_frm(lpt, 0UL, NULL,
+ (const char*)shadow_path, FALSE))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ if (write_execute_ddl_log_entry(log_entry->entry_pos,
+ FALSE, &exec_log_entry))
+ goto error;
+ pthread_mutex_unlock(&LOCK_gdl);
+ set_part_info_exec_log_entry(part_info, exec_log_entry);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= NULL;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Log renaming of shadow frm to real frm name and dropping of old frm
+ SYNOPSIS
+ write_log_rename_frm()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare an entry to ensure that we complete the renaming of the frm
+ file if failure occurs in the middle of the rename process.
+*/
+
+static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
+ char path[FN_LEN];
+ char shadow_path[FN_LEN];
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ DBUG_ENTER("write_log_rename_frm");
+
+ part_info->first_log_entry= NULL;
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "");
+ build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
+ lpt->table_name, "#");
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ part_info->frm_log_entry= log_entry;
+ if (write_execute_ddl_log_entry(log_entry->entry_pos,
+ FALSE, &exec_log_entry))
+ goto error;
+ release_part_info_log_entries(old_first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= old_first_log_entry;
+ part_info->frm_log_entry= NULL;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Write the log entries to ensure that the drop partition command is completed
+ even in the presence of a crash.
+
+ SYNOPSIS
+ write_log_drop_partition()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare entries to the ddl log indicating all partitions to drop and to
+ install the shadow frm file and remove the old frm file.
+*/
+
+static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
+ char tmp_path[FN_LEN];
+ char path[FN_LEN];
+ uint next_entry= 0;
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ DBUG_ENTER("write_log_drop_partition");
+
+ part_info->first_log_entry= NULL;
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "");
+ build_table_filename(tmp_path, sizeof(tmp_path), lpt->db,
+ lpt->table_name, "#");
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
+ FALSE))
+ goto error;
+ if (write_log_replace_delete_frm(lpt, next_entry, (const char*)tmp_path,
+ (const char*)path, TRUE))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ part_info->frm_log_entry= log_entry;
+ if (write_execute_ddl_log_entry(log_entry->entry_pos,
+ FALSE, &exec_log_entry))
+ goto error;
+ release_part_info_log_entries(old_first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= old_first_log_entry;
+ part_info->frm_log_entry= NULL;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Write the log entries to ensure that the add partition command is not
+ executed at all if a crash before it has completed
+
+ SYNOPSIS
+ write_log_add_change_partition()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare entries to the ddl log indicating all partitions to drop and to
+ remove the shadow frm file.
+ We always inject entries backwards in the list in the ddl log since we
+ don't know the entry position until we have written it.
+*/
+
+static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
+ char tmp_path[FN_LEN];
+ char path[FN_LEN];
+ uint next_entry= 0;
+ DBUG_ENTER("write_log_add_change_partition");
+
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "");
+ build_table_filename(tmp_path, sizeof(tmp_path), lpt->db,
+ lpt->table_name, "#");
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
+ FALSE))
+ goto error;
+ if (write_log_replace_delete_frm(lpt, next_entry, NULL, tmp_path,
+ FALSE))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ if (write_execute_ddl_log_entry(log_entry->entry_pos,
+ FALSE, &exec_log_entry))
+ goto error;
+ pthread_mutex_unlock(&LOCK_gdl);
+ set_part_info_exec_log_entry(part_info, exec_log_entry);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= NULL;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Write description of how to complete the operation after first phase of
+ change partitions.
+
+ SYNOPSIS
+ write_log_final_change_partition()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ We will write log entries that specify to remove all partitions reorganised,
+ to rename others to reflect the new naming scheme and to install the shadow
+ frm file.
+*/
+
+static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
+ char path[FN_LEN];
+ char shadow_path[FN_LEN];
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ uint next_entry= 0;
+ DBUG_ENTER("write_log_final_change_partition");
+
+ part_info->first_log_entry= NULL;
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "");
+ build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
+ lpt->table_name, "#");
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
+ lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION))
+ goto error;
+ if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
+ goto error;
+ if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ part_info->frm_log_entry= log_entry;
+ if (write_execute_ddl_log_entry(log_entry->entry_pos,
+ FALSE, &exec_log_entry))
+ goto error;
+ release_part_info_log_entries(old_first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= old_first_log_entry;
+ part_info->frm_log_entry= NULL;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Remove entry from ddl log and release resources for others to use
+
+ SYNOPSIS
+ write_log_completed()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
+ bool dont_crash)
+{
+ partition_info *part_info= lpt->part_info;
+ uint count_loop= 0;
+ bool not_success;
+ DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry;
+ DBUG_ENTER("write_log_completed");
+
+ DBUG_ASSERT(log_entry);
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_execute_ddl_log_entry(0UL, TRUE, &log_entry))
+ {
+ /*
+ Failed to write, Bad...
+ We have completed the operation but have log records to REMOVE
+ stuff that shouldn't be removed. What clever things could one do
+ here? An error output was written to the error output by the
+ above method so we don't do anything here.
+ */
+ ;
+ }
+ release_part_info_log_entries(part_info->first_log_entry);
+ release_part_info_log_entries(part_info->exec_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->exec_log_entry= NULL;
+ part_info->first_log_entry= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Release all log entries
+ SYNOPSIS
+ release_log_entries()
+ part_info Partition info struct
+ RETURN VALUES
+ NONE
+*/
+
+static void release_log_entries(partition_info *part_info)
+{
+ pthread_mutex_lock(&LOCK_gdl);
+ release_part_info_log_entries(part_info->first_log_entry);
+ release_part_info_log_entries(part_info->exec_log_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= NULL;
+ part_info->exec_log_entry= NULL;
+}
+
+
+/*
+ Get a lock on table name to avoid that anyone can open the table in
+ a critical part of the ALTER TABLE.
+ SYNOPSIS
+ get_name_lock()
+ lpt Struct carrying parameters
+ RETURN VALUES
+ FALSE Success
+ TRUE Failure
+*/
+
+static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ int error= 0;
+ DBUG_ENTER("get_name_lock");
+
+ bzero(&lpt->table_list, sizeof(lpt->table_list));
+ lpt->table_list.db= (char*)lpt->db;
+ lpt->table_list.table= lpt->table;
+ lpt->table_list.table_name= (char*)lpt->table_name;
+ pthread_mutex_lock(&LOCK_open);
+ error= lock_table_name(lpt->thd, &lpt->table_list, FALSE);
+ pthread_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Unlock and close table before renaming and dropping partitions
+ SYNOPSIS
+ alter_close_tables()
+ lpt Struct carrying parameters
+ RETURN VALUES
+ 0
+*/
+
+static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ THD *thd= lpt->thd;
+ TABLE *table= lpt->table;
+ DBUG_ENTER("alter_close_tables");
+ /*
+ We need to also unlock tables and close all handlers.
+ We set lock to zero to ensure we don't do this twice
+ and we set db_stat to zero to ensure we don't close twice.
+ */
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ table->file->close();
+ table->db_stat= 0;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Release a lock name
+ SYNOPSIS
+ release_name_lock()
+ lpt
+ RETURN VALUES
+ 0
+*/
+
+static int release_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ DBUG_ENTER("release_name_lock");
+ pthread_mutex_lock(&LOCK_open);
+ unlock_table_name(lpt->thd, &lpt->table_list);
+ pthread_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Handle errors for ALTER TABLE for partitioning
+ SYNOPSIS
+ handle_alter_part_error()
+ lpt Struct carrying parameters
+ not_completed Was request in complete phase when error occurred
+ RETURN VALUES
+ NONE
+*/
+
+void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
+ bool not_completed,
+ bool drop_partition,
+ bool frm_install)
+{
+ partition_info *part_info= lpt->part_info;
+ DBUG_ENTER("handle_alter_part_error");
+
+ if (!part_info->first_log_entry &&
+ execute_ddl_log_entry(current_thd,
+ part_info->first_log_entry->entry_pos))
+ {
+ /*
+ We couldn't recover from error, most likely manual interaction
+ is required.
+ */
+ write_log_completed(lpt, FALSE);
+ release_log_entries(part_info);
+ if (not_completed)
+ {
+ if (drop_partition)
+ {
+ /* Table is still ok, but we left a shadow frm file behind. */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s",
+ "Operation was unsuccessful, table is still intact,",
+ "but it is possible that a shadow frm file was left behind");
+ }
+ else
+ {
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s %s %s",
+ "Operation was unsuccessful, table is still intact,",
+ "but it is possible that a shadow frm file was left behind.",
+ "It is also possible that temporary partitions are left behind,",
+ "these could be empty or more or less filled with records");
+ }
+ }
+ else
+ {
+ if (frm_install)
+ {
+ /*
+ Failed during install of shadow frm file, table isn't intact
+ and dropped partitions are still there
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s %s",
+ "Failed during alter of partitions, table is no longer intact.",
+ "The frm file is in an unknown state, and a backup",
+ "is required.");
+ }
+ else if (drop_partition)
+ {
+ /*
+ Table is ok, we have switched to new table but left dropped
+ partitions still in their places. We remove the log records and
+ ask the user to perform the action manually. We remove the log
+ records and ask the user to perform the action manually.
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s",
+ "Failed during drop of partitions, table is intact.",
+ "Manual drop of remaining partitions is required");
+ }
+ else
+ {
+ /*
+ We failed during renaming of partitions. The table is most
+ certainly in a very bad state so we give user warning and disable
+ the table by writing an ancient frm version into it.
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s %s",
+ "Failed during renaming of partitions. We are now in a position",
+ "where table is not reusable",
+ "Table is disabled by writing ancient frm file version into it");
+ }
+ }
+ }
+ else
+ {
+ release_log_entries(part_info);
+ if (not_completed)
+ {
+ /*
+ We hit an error before things were completed but managed
+ to recover from the error. An error occurred and we have
+ restored things to original so no need for further action.
+ */
+ ;
+ }
+ else
+ {
+ /*
+ We hit an error after we had completed most of the operation
+ and were successful in a second attempt so the operation
+ actually is successful now. We need to issue a warning that
+ even though we reported an error the operation was successfully
+ completed.
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s",
+ "Operation was successfully completed by failure handling,",
+ "after failure of normal operation");
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
Actually perform the change requested by ALTER TABLE of partitions
previously prepared.
@@ -4858,7 +5678,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
List<create_field> *create_list,
- List<Key> *key_list, const char *db,
+ List<Key> *key_list, char *db,
const char *table_name,
uint fast_alter_partition)
{
@@ -4869,9 +5689,13 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ALTER_PARTITION_PARAM_TYPE lpt_obj;
ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj;
bool written_bin_log= TRUE;
+ bool not_completed= TRUE;
+ bool frm_install= FALSE;
DBUG_ENTER("fast_alter_partition_table");
lpt->thd= thd;
+ lpt->part_info= part_info;
+ lpt->alter_info= alter_info;
lpt->create_info= create_info;
lpt->create_list= create_list;
lpt->key_list= key_list;
@@ -4903,17 +5727,18 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
In this case it is enough to call optimise_partitions, there is no
need to change frm files or anything else.
*/
+ int error;
written_bin_log= FALSE;
if (((alter_info->flags & ALTER_OPTIMIZE_PARTITION) &&
- (table->file->optimize_partitions(thd))) ||
+ (error= table->file->optimize_partitions(thd))) ||
((alter_info->flags & ALTER_ANALYZE_PARTITION) &&
- (table->file->analyze_partitions(thd))) ||
+ (error= table->file->analyze_partitions(thd))) ||
((alter_info->flags & ALTER_CHECK_PARTITION) &&
- (table->file->check_partitions(thd))) ||
+ (error= table->file->check_partitions(thd))) ||
((alter_info->flags & ALTER_REPAIR_PARTITION) &&
- (table->file->repair_partitions(thd))))
+ (error= table->file->repair_partitions(thd))))
{
- fast_alter_partition_error_handler(lpt);
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(TRUE);
}
}
@@ -4958,10 +5783,9 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
1) Write the new frm, pack it and then delete it
2) Perform the change within the handler
*/
- if ((mysql_write_frm(lpt, WFRM_INITIAL_WRITE | WFRM_PACK_FRM)) ||
- (mysql_change_partitions(lpt)))
+ if (mysql_write_frm(lpt, WFRM_WRITE_SHADOW | WFRM_PACK_FRM) ||
+ mysql_change_partitions(lpt))
{
- fast_alter_partition_error_handler(lpt);
DBUG_RETURN(TRUE);
}
}
@@ -4989,32 +5813,72 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
after a DROP PARTITION) if one ensured that failed accesses to the
dropped partitions was aborted for sure (thus only possible for
transactional engines).
-
- 1) Lock the table in TL_WRITE_ONLY to ensure all other accesses to
- the table have completed
- 2) Write the new frm file where the partitions have changed but are
- still remaining with the state PART_TO_BE_DROPPED
- 3) Write the bin log
- 4) Prepare MyISAM handlers for drop of partitions
- 5) Ensure that any users that has opened the table but not yet
- reached the abort lock do that before downgrading the lock.
- 6) Drop the partitions
- 7) Write the frm file that the partition has been dropped
- 8) Wait until all accesses using the old frm file has completed
- 9) Complete query
+
+ 0) Write an entry that removes the shadow frm file if crash occurs
+ 1) Write the new frm file as a shadow frm
+ 2) Write the ddl log to ensure that the operation is completed
+ even in the presence of a MySQL Server crash
+ 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to
+ the table have completed. This ensures that other threads can not
+ execute on the table in parallel.
+ 4) Get a name lock on the table. This ensures that we can release all
+ locks on the table and since no one can open the table, there can
+ be no new threads accessing the table. They will be hanging on the
+ name lock.
+ 5) Close all tables that have already been opened but didn't stumble on
+ the abort locked previously. This is done as part of the
+ get_name_lock call.
+ 6) We are now ready to release all locks we got in this thread.
+ 7) Write the bin log
+ Unfortunately the writing of the binlog is not synchronised with
+ other logging activities. So no matter in which order the binlog
+ is written compared to other activities there will always be cases
+ where crashes make strange things occur. In this placement it can
+ happen that the ALTER TABLE DROP PARTITION gets performed in the
+ master but not in the slaves if we have a crash, after writing the
+ ddl log but before writing the binlog. A solution to this would
+ require writing the statement first in the ddl log and then
+ when recovering from the crash read the binlog and insert it into
+ the binlog if not written already.
+ 8) Install the previously written shadow frm file
+ 9) Prepare handlers for drop of partitions
+ 10) Drop the partitions
+ 11) Remove entries from ddl log
+ 12) Release name lock so that all other threads can access the table
+ again.
+ 13) Complete query
+
+ We insert Error injections at all places where it could be interesting
+ to test if recovery is properly done.
*/
- if ((abort_and_upgrade_lock(lpt)) ||
- (mysql_write_frm(lpt, WFRM_INITIAL_WRITE)) ||
+ if (write_log_drop_shadow_frm(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_1") ||
+ mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_2") ||
+ write_log_drop_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_3") ||
+ (not_completed= FALSE) ||
+ abort_and_upgrade_lock(lpt) || /* Always returns 0 */
+ ERROR_INJECT_CRASH("crash_drop_partition_4") ||
+ get_name_lock(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_5") ||
+ alter_close_tables(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_6") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
- thd->query, thd->query_length), FALSE)) ||
- (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE)) ||
- (close_open_tables_and_downgrade(lpt), FALSE) ||
- (mysql_drop_partitions(lpt)) ||
- (mysql_write_frm(lpt, WFRM_CREATE_HANDLER_FILES)) ||
- (mysql_wait_completed_table(lpt, table), FALSE))
+ thd->query, thd->query_length), FALSE)) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_7") ||
+ ((frm_install= TRUE), FALSE) ||
+ mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ ((frm_install= FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_8") ||
+ mysql_drop_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_9") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_10") ||
+ (release_name_lock(lpt), FALSE))
{
- fast_alter_partition_error_handler(lpt);
+ handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
DBUG_RETURN(TRUE);
}
}
@@ -5031,28 +5895,59 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
miss updates made by a transaction serialised before it that are
inserted into the new partition.
- 1) Write the new frm file where state of added partitions is
- changed to PART_TO_BE_ADDED
+ 0) Write an entry that removes the shadow frm file if crash occurs
+ 1) Write the new frm file as a shadow frm file
+ 2) Log the changes to happen in ddl log
2) Add the new partitions
3) Lock all partitions in TL_WRITE_ONLY to ensure that no users
are still using the old partitioning scheme. Wait until all
ongoing users have completed before progressing.
- 4) Write a new frm file of the table where the partitions are added
- to the table.
- 5) Write binlog
- 6) Wait until all accesses using the old frm file has completed
- 7) Complete query
+ 4) Get a name lock on the table. This ensures that we can release all
+ locks on the table and since no one can open the table, there can
+ be no new threads accessing the table. They will be hanging on the
+ name lock.
+ 5) Close all tables that have already been opened but didn't stumble on
+ the abort locked previously. This is done as part of the
+ get_name_lock call.
+ 6) Close all table handlers and unlock all handlers but retain name lock
+ 7) Write binlog
+ 8) Now the change is completed except for the installation of the
+ new frm file. We thus write an action in the log to change to
+ the shadow frm file
+ 9) Install the new frm file of the table where the partitions are
+ added to the table.
+ 10)Wait until all accesses using the old frm file has completed
+ 11)Remove entries from ddl log
+ 12)Release name lock
+ 13)Complete query
*/
- if ((mysql_write_frm(lpt, WFRM_INITIAL_WRITE)) ||
- (mysql_change_partitions(lpt)) ||
- (abort_and_upgrade_lock(lpt)) ||
- (mysql_write_frm(lpt, WFRM_CREATE_HANDLER_FILES)) ||
+ if (write_log_add_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_1") ||
+ mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_add_partition_2") ||
+ mysql_change_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_3") ||
+ abort_and_upgrade_lock(lpt) || /* Always returns 0 */
+ ERROR_INJECT_CRASH("crash_add_partition_3") ||
+ get_name_lock(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_4") ||
+ alter_close_tables(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query, thd->query_length), FALSE)) ||
- (close_open_tables_and_downgrade(lpt), FALSE))
+ ERROR_INJECT_CRASH("crash_add_partition_6") ||
+ write_log_rename_frm(lpt) ||
+ (not_completed= FALSE) ||
+ ERROR_INJECT_CRASH("crash_add_partition_7") ||
+ ((frm_install= TRUE), FALSE) ||
+ mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_add_partition_8") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_add_partition_9") ||
+ (release_name_lock(lpt), FALSE))
{
- fast_alter_partition_error_handler(lpt);
+ handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
DBUG_RETURN(TRUE);
}
}
@@ -5089,44 +5984,64 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
use a lower lock level. This can be handled inside store_lock in the
respective handler.
- 1) Write the new frm file where state of added partitions is
- changed to PART_TO_BE_ADDED and the reorganised partitions
- are set in state PART_TO_BE_REORGED.
- 2) Add the new partitions
+ 0) Write an entry that removes the shadow frm file if crash occurs
+ 1) Write the shadow frm file of new partitioning
+ 2) Log such that temporary partitions added in change phase are
+ removed in a crash situation
+ 3) Add the new partitions
Copy from the reorganised partitions to the new partitions
- 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users
+ 4) Log that operation is completed and log all complete actions
+ needed to complete operation from here
+ 5) Lock all partitions in TL_WRITE_ONLY to ensure that no users
are still using the old partitioning scheme. Wait until all
ongoing users have completed before progressing.
- 4) Prepare MyISAM handlers for rename and delete of partitions
- 5) Write a new frm file of the table where the partitions are
- reorganised.
- 6) Rename the reorged partitions such that they are no longer
- used and rename those added to their real new names.
- 7) Write bin log
- 8) Wait until all accesses using the old frm file has completed
- 9) Drop the reorganised partitions
- 10)Write a new frm file of the table where the partitions are
- reorganised.
- 11)Wait until all accesses using the old frm file has completed
- 12)Complete query
+ 6) Get a name lock of the table
+ 7) Close all tables opened but not yet locked, after this call we are
+ certain that no other thread is in the lock wait queue or has
+ opened the table. The name lock will ensure that they are blocked
+ on the open call. This is achieved also by get_name_lock call.
+ 8) Close all partitions opened by this thread, but retain name lock.
+ 9) Write bin log
+ 10) Prepare handlers for rename and delete of partitions
+ 11) Rename and drop the reorged partitions such that they are no
+ longer used and rename those added to their real new names.
+ 12) Install the shadow frm file
+ 13) Release the name lock to enable other threads to start using the
+ table again.
+ 14) Complete query
*/
-
- if ((mysql_write_frm(lpt, WFRM_INITIAL_WRITE)) ||
- (mysql_change_partitions(lpt)) ||
- (abort_and_upgrade_lock(lpt)) ||
- (mysql_write_frm(lpt, WFRM_CREATE_HANDLER_FILES)) ||
- (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE)) ||
- (mysql_rename_partitions(lpt)) ||
+ if (write_log_add_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_1") ||
+ mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_change_partition_2") ||
+ mysql_change_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_3") ||
+ write_log_final_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_4") ||
+ (not_completed= FALSE) ||
+ abort_and_upgrade_lock(lpt) || /* Always returns 0 */
+ ERROR_INJECT_CRASH("crash_change_partition_5") ||
+ get_name_lock(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_6") ||
+ alter_close_tables(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_7") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query, thd->query_length), FALSE)) ||
- (close_open_tables_and_downgrade(lpt), FALSE) ||
- (mysql_drop_partitions(lpt)) ||
- (mysql_write_frm(lpt, 0UL)) ||
- (mysql_wait_completed_table(lpt, table), FALSE))
+ ERROR_INJECT_CRASH("crash_change_partition_8") ||
+ mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_change_partition_9") ||
+ mysql_drop_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_10") ||
+ mysql_rename_partitions(lpt) ||
+ ((frm_install= TRUE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_change_partition_11") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_change_partition_12") ||
+ (release_name_lock(lpt), FALSE))
{
- fast_alter_partition_error_handler(lpt);
- DBUG_RETURN(TRUE);
+ handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
+ DBUG_RETURN(TRUE);
}
}
/*
@@ -5134,7 +6049,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
user
*/
DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted,
- table_list, FALSE, lpt,
+ table, table_list, FALSE, lpt,
written_bin_log));
}
#endif
@@ -5786,5 +6701,85 @@ static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter)
field->store(part_iter->field_vals.cur++, FALSE);
return part_iter->part_info->get_subpartition_id(part_iter->part_info);
}
+
+
+/*
+ Create partition names
+
+ SYNOPSIS
+ create_partition_name()
+ out:out Created partition name string
+ in1 First part
+ in2 Second part
+ name_variant Normal, temporary or renamed partition name
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ This method is used to calculate the partition name, service routine to
+ the del_ren_cre_table method.
+*/
+
+void create_partition_name(char *out, const char *in1,
+ const char *in2, uint name_variant,
+ bool translate)
+{
+ char transl_part_name[FN_REFLEN];
+ const char *transl_part;
+
+ if (translate)
+ {
+ tablename_to_filename(in2, transl_part_name, FN_REFLEN);
+ transl_part= transl_part_name;
+ }
+ else
+ transl_part= in2;
+ if (name_variant == NORMAL_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part, NullS);
+ else if (name_variant == TEMP_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part, "#TMP#", NullS);
+ else if (name_variant == RENAMED_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part, "#REN#", NullS);
+}
+
+
+/*
+ Create subpartition name
+
+ SYNOPSIS
+ create_subpartition_name()
+ out:out Created partition name string
+ in1 First part
+ in2 Second part
+ in3 Third part
+ name_variant Normal, temporary or renamed partition name
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ This method is used to calculate the subpartition name, service routine to
+ the del_ren_cre_table method.
+*/
+
+void create_subpartition_name(char *out, const char *in1,
+ const char *in2, const char *in3,
+ uint name_variant)
+{
+ char transl_part_name[FN_REFLEN], transl_subpart_name[FN_REFLEN];
+
+ tablename_to_filename(in2, transl_part_name, FN_REFLEN);
+ tablename_to_filename(in3, transl_subpart_name, FN_REFLEN);
+ if (name_variant == NORMAL_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part_name,
+ "#SP#", transl_subpart_name, NullS);
+ else if (name_variant == TEMP_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part_name,
+ "#SP#", transl_subpart_name, "#TMP#", NullS);
+ else if (name_variant == RENAMED_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part_name,
+ "#SP#", transl_subpart_name, "#REN#", NullS);
+}
#endif