diff options
author | Michael Widenius <monty@askmonty.org> | 2013-06-15 18:32:08 +0300 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2013-06-15 18:32:08 +0300 |
commit | 5f1f2fc0e443f098af24d21f7d1ec1a8166a4030 (patch) | |
tree | 7b870d0c390c05d6629f4813966e740ea073fcef /sql/sql_partition.cc | |
parent | 3143ad589a24ac7581e2195ba0dc13576cb3c9da (diff) | |
download | mariadb-git-5f1f2fc0e443f098af24d21f7d1ec1a8166a4030.tar.gz |
Applied all changes from Igor and Sanja
Diffstat (limited to 'sql/sql_partition.cc')
-rw-r--r-- | sql/sql_partition.cc | 558 |
1 files changed, 367 insertions, 191 deletions
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index f5f72fcdd9c..bb94e2392c5 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -59,6 +59,7 @@ #include <m_ctype.h> #include "my_md5.h" #include "transaction.h" +#include "debug_sync.h" #include "sql_base.h" // close_all_tables_for_name #include "sql_table.h" // build_table_filename, @@ -67,6 +68,8 @@ // mysql_*_alter_copy_data #include "opt_range.h" // store_key_image_to_rec #include "sql_analyse.h" // append_escaped +#include "sql_alter.h" // Alter_table_ctx + #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -575,7 +578,13 @@ static bool set_up_field_array(TABLE *table, } while (++inx < num_fields); if (inx == num_fields) { - mem_alloc_error(1); + /* + Should not occur since it should already been checked in either + add_column_list_values, handle_list_of_fields, + check_partition_info etc. + */ + DBUG_ASSERT(0); + my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0)); result= TRUE; continue; } @@ -879,7 +888,8 @@ static bool handle_list_of_fields(List_iterator<char> it, uint primary_key= table->s->primary_key; if (primary_key != MAX_KEY) { - uint num_key_parts= table->key_info[primary_key].key_parts, i; + uint num_key_parts= table->key_info[primary_key].user_defined_key_parts; + uint i; /* In the case of an empty list we use primary key as partition key. */ @@ -1072,7 +1082,7 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, goto end; } else - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR)); } @@ -1241,20 +1251,17 @@ void check_range_capable_PF(TABLE *table) } -/* - Set up partition bitmap +/** + Set up partition bitmaps - SYNOPSIS - set_up_partition_bitmap() - thd Thread object - part_info Reference to partitioning data structure + @param thd Thread object + @param part_info Reference to partitioning data structure - RETURN VALUE - TRUE Memory allocation failure - FALSE Success + @return Operation status + @retval TRUE Memory allocation failure + @retval FALSE Success - DESCRIPTION - Allocate memory for bitmap of the partitioned table + Allocate memory for bitmaps of the partitioned table and initialise it. */ @@ -1265,15 +1272,23 @@ static bool set_up_partition_bitmap(THD *thd, partition_info *part_info) (part_info->num_subparts* part_info->num_parts): part_info->num_parts; uint bitmap_bytes= bitmap_buffer_size(bitmap_bits); - DBUG_ENTER("set_up_partition_bitmap"); + DBUG_ENTER("set_up_partition_bitmaps"); - if (!(bitmap_buf= (uint32*)thd->alloc(bitmap_bytes))) + DBUG_ASSERT(!part_info->bitmaps_are_initialized); + + /* Allocate for both read and lock_partitions */ + if (!(bitmap_buf= (uint32*) alloc_root(&part_info->table->mem_root, + bitmap_bytes * 2))) { - mem_alloc_error(bitmap_bytes); + mem_alloc_error(bitmap_bytes * 2); DBUG_RETURN(TRUE); } - bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE); - bitmap_set_all(&part_info->used_partitions); + bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE); + /* Use the second half of the allocated buffer for lock_partitions */ + bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4), + bitmap_bits, FALSE); + part_info->bitmaps_are_initialized= TRUE; + part_info->set_partition_bitmaps(NULL); DBUG_RETURN(FALSE); } @@ -3824,6 +3839,92 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf, DBUG_VOID_RETURN; } + +/** + @brief Verify that all rows in a table is in the given partition + + @param table Table which contains the data that will be checked if + it is matching the partition definition. + @param part_table Partitioned table containing the partition to check. + @param part_id Which partition to match with. + + @return Operation status + @retval TRUE Not all rows match the given partition + @retval FALSE OK +*/ +bool verify_data_with_partition(TABLE *table, TABLE *part_table, + uint32 part_id) +{ + uint32 found_part_id; + longlong func_value; /* Unused */ + handler *file; + int error; + uchar *old_rec; + partition_info *part_info; + DBUG_ENTER("verify_data_with_partition"); + DBUG_ASSERT(table && table->file && part_table && part_table->part_info && + part_table->file); + + /* + Verify all table rows. + First implementation uses full scan + evaluates partition functions for + every row. TODO: add optimization to use index if possible, see WL#5397. + + 1) Open both tables (already done) and set the row buffers to use + the same buffer (to avoid copy). + 2) Init rnd on table. + 3) loop over all rows. + 3.1) verify that partition_id on the row is correct. Break if error. + */ + file= table->file; + part_info= part_table->part_info; + bitmap_union(table->read_set, &part_info->full_part_field_set); + old_rec= part_table->record[0]; + part_table->record[0]= table->record[0]; + set_field_ptr(part_info->full_part_field_array, table->record[0], old_rec); + if ((error= file->ha_rnd_init(TRUE))) + { + file->print_error(error, MYF(0)); + goto err; + } + + do + { + if ((error= file->ha_rnd_next(table->record[0]))) + { + if (error == HA_ERR_RECORD_DELETED) + continue; + if (error == HA_ERR_END_OF_FILE) + error= 0; + else + file->print_error(error, MYF(0)); + break; + } + if ((error= part_info->get_partition_id(part_info, &found_part_id, + &func_value))) + { + part_table->file->print_error(error, MYF(0)); + break; + } + DEBUG_SYNC(current_thd, "swap_partition_first_row_read"); + if (found_part_id != part_id) + { + my_error(ER_ROW_DOES_NOT_MATCH_PARTITION, MYF(0)); + error= 1; + break; + } + } while (TRUE); + (void) file->ha_rnd_end(); +err: + set_field_ptr(part_info->full_part_field_array, old_rec, + table->record[0]); + part_table->record[0]= old_rec; + if (error) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + + /* Prune the set of partitions to use in query @@ -3834,7 +3935,7 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf, DESCRIPTION This function is called to prune the range of partitions to scan by - checking the used_partitions bitmap. + checking the read_partitions bitmap. If start_part > end_part at return it means no partition needs to be scanned. If start_part == end_part it always means a single partition needs to be scanned. @@ -3851,7 +3952,7 @@ void prune_partition_set(const TABLE *table, part_id_range *part_spec) DBUG_ENTER("prune_partition_set"); for (i= part_spec->start_part; i <= part_spec->end_part; i++) { - if (bitmap_is_set(&(part_info->used_partitions), i)) + if (bitmap_is_set(&(part_info->read_partitions), i)) { DBUG_PRINT("info", ("Partition %d is set", i)); if (last_partition == -1) @@ -3933,7 +4034,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, */ get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec); /* - Check if range can be adjusted by looking in used_partitions + Check if range can be adjusted by looking in read_partitions */ prune_partition_set(table, part_spec); DBUG_VOID_RETURN; @@ -3985,7 +4086,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec); clear_indicator_in_key_fields(key_info); /* - Check if range can be adjusted by looking in used_partitions + Check if range can be adjusted by looking in read_partitions */ prune_partition_set(table, part_spec); DBUG_VOID_RETURN; @@ -4055,7 +4156,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, if (found_part_field) clear_indicator_in_key_fields(key_info); /* - Check if range can be adjusted by looking in used_partitions + Check if range can be adjusted by looking in read_partitions */ prune_partition_set(table, part_spec); DBUG_VOID_RETURN; @@ -4203,6 +4304,7 @@ bool mysql_unpack_partition(THD *thd, *work_part_info_used= true; } table->part_info= part_info; + part_info->table= table; table->file->set_part_info(part_info); if (!part_info->default_engine_type) part_info->default_engine_type= default_db_type; @@ -4420,7 +4522,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, do { partition_element *part_elem= part_it++; - if ((alter_info->flags & ALTER_ALL_PARTITION) || + if ((alter_info->flags & Alter_info::ALTER_ALL_PARTITION) || (is_name_in_list(part_elem->partition_name, alter_info->partition_names))) { @@ -4439,7 +4541,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, } while (++part_count < tab_part_info->num_parts); if (num_parts_found != alter_info->partition_names.elements && - !(alter_info->flags & ALTER_ALL_PARTITION)) + !(alter_info->flags & Alter_info::ALTER_ALL_PARTITION)) { /* Not all given partitions found, revert and return failure */ part_it.rewind(); @@ -4456,16 +4558,60 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, /** + @brief Check if partition is exchangable with table by checking table options + + @param table_create_info Table options from table. + @param part_elem All the info of the partition. + + @retval FALSE if they are equal, otherwise TRUE. + + @note Any differens that would cause a change in the frm file is prohibited. + Such options as data_file_name, index_file_name, min_rows, max_rows etc. are + not allowed to differ. But comment is allowed to differ. +*/ +bool compare_partition_options(HA_CREATE_INFO *table_create_info, + partition_element *part_elem) +{ +#define MAX_COMPARE_PARTITION_OPTION_ERRORS 5 + const char *option_diffs[MAX_COMPARE_PARTITION_OPTION_ERRORS + 1]; + int i, errors= 0; + DBUG_ENTER("compare_partition_options"); + DBUG_ASSERT(!part_elem->tablespace_name && + !table_create_info->tablespace); + + /* + Note that there are not yet any engine supporting tablespace together + with partitioning. TODO: when there are, add compare. + */ + if (part_elem->tablespace_name || table_create_info->tablespace) + option_diffs[errors++]= "TABLESPACE"; + if (part_elem->part_max_rows != table_create_info->max_rows) + option_diffs[errors++]= "MAX_ROWS"; + if (part_elem->part_min_rows != table_create_info->min_rows) + option_diffs[errors++]= "MIN_ROWS"; + if (part_elem->data_file_name || table_create_info->data_file_name) + option_diffs[errors++]= "DATA DIRECTORY"; + if (part_elem->index_file_name || table_create_info->index_file_name) + option_diffs[errors++]= "INDEX DIRECTORY"; + + for (i= 0; i < errors; i++) + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + option_diffs[i]); + DBUG_RETURN(errors != 0); +} + + +/** Prepare for ALTER TABLE of partition structure @param[in] thd Thread object @param[in] table Table object @param[in,out] alter_info Alter information @param[in,out] create_info Create info for CREATE TABLE - @param[in] old_db_type Old engine type + @param[in] alter_ctx ALTER TABLE runtime context @param[out] partition_changed Boolean indicating whether partition changed - @param[out] fast_alter_table Internal temporary table allowing fast - partition change or NULL if not possible + @param[out] fast_alter_table Boolean indicating if fast partition alter is + possible. @return Operation status @retval TRUE Error @@ -4483,22 +4629,27 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, - handlerton *old_db_type, + Alter_table_ctx *alter_ctx, bool *partition_changed, - char *db, - const char *table_name, - const char *path, - TABLE **fast_alter_table) + bool *fast_alter_table) { TABLE *new_table= NULL; DBUG_ENTER("prep_alter_part_table"); /* Foreign keys on partitioned tables are not supported, waits for WL#148 */ - if (table->part_info && (alter_info->flags & ALTER_FOREIGN_KEY)) + if (table->part_info && (alter_info->flags & Alter_info::ADD_FOREIGN_KEY || + alter_info->flags & Alter_info::DROP_FOREIGN_KEY)) { my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0)); DBUG_RETURN(TRUE); } + /* Remove partitioning on a not partitioned table is not possible */ + if (!table->part_info && (alter_info->flags & + Alter_info::ALTER_REMOVE_PARTITIONING)) + { + my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(TRUE); + } thd->work_part_info= thd->lex->part_info; @@ -4507,12 +4658,15 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, DBUG_RETURN(TRUE); /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */ - DBUG_ASSERT(!(alter_info->flags & ALTER_ADMIN_PARTITION)); + DBUG_ASSERT(!(alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)); if (alter_info->flags & - (ALTER_ADD_PARTITION | ALTER_DROP_PARTITION | - ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION | - ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION)) + (Alter_info::ALTER_ADD_PARTITION | + Alter_info::ALTER_DROP_PARTITION | + Alter_info::ALTER_COALESCE_PARTITION | + Alter_info::ALTER_REORGANIZE_PARTITION | + Alter_info::ALTER_TABLE_REORG | + Alter_info::ALTER_REBUILD_PARTITION)) { partition_info *tab_part_info; partition_info *alt_part_info= thd->work_part_info; @@ -4534,21 +4688,16 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, Open it as a copy of the original table, and modify its partition_info object to allow fast_alter_partition_table to perform the changes. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + alter_ctx->db, + alter_ctx->table_name, MDL_INTENTION_EXCLUSIVE)); - new_table= open_table_uncached(thd, path, db, table_name, 0); - if (!new_table) - DBUG_RETURN(TRUE); - /* - This table may be used for copy rows between partitions - and also read/write columns when fixing the partition_info struct. - */ new_table->use_all_columns(); tab_part_info= new_table->part_info; - if (alter_info->flags & ALTER_TABLE_REORG) + if (alter_info->flags & Alter_info::ALTER_TABLE_REORG) { uint new_part_no, curr_part_no; if (tab_part_info->part_type != HASH_PARTITION || @@ -4557,7 +4706,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, my_error(ER_REORG_NO_PARAM_ERROR, MYF(0)); goto err; } - new_part_no= new_table->file->get_default_no_partitions(create_info); + new_part_no= table->file->get_default_no_partitions(create_info); curr_part_no= tab_part_info->num_parts; if (new_part_no == curr_part_no) { @@ -4566,7 +4715,7 @@ 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. */ - *fast_alter_table= new_table; + *fast_alter_table= true; thd->work_part_info= tab_part_info; DBUG_RETURN(FALSE); } @@ -4576,7 +4725,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, We will add more partitions, we use the ADD PARTITION without setting the flag for no default number of partitions */ - alter_info->flags|= ALTER_ADD_PARTITION; + alter_info->flags|= Alter_info::ALTER_ADD_PARTITION; thd->work_part_info->num_parts= new_part_no - curr_part_no; } else @@ -4585,21 +4734,17 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, We will remove hash partitions, we use the COALESCE PARTITION without setting the flag for no default number of partitions */ - alter_info->flags|= ALTER_COALESCE_PARTITION; + alter_info->flags|= Alter_info::ALTER_COALESCE_PARTITION; alter_info->num_parts= curr_part_no - new_part_no; } } - if (!(flags= new_table->file->alter_table_flags(alter_info->flags))) + if (!(flags= table->file->alter_table_flags(alter_info->flags))) { my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0)); goto err; } - if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0) - *fast_alter_table= new_table; - DBUG_PRINT("info", ("*fast_alter_table: %p flags: 0x%x", - *fast_alter_table, flags)); - if ((alter_info->flags & ALTER_ADD_PARTITION) || - (alter_info->flags & ALTER_REORGANIZE_PARTITION)) + if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) || + (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)) { if (thd->work_part_info->part_type != tab_part_info->part_type) { @@ -4666,7 +4811,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, goto err; } } - if (alter_info->flags & ALTER_ADD_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION) { /* We start by moving the new partitions to the list of temporary @@ -4717,8 +4862,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, } 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(new_table->file, - ULL(0), + if (alt_part_info->set_up_defaults_for_partitioning(table->file, + ULL(0), tab_part_info->num_parts)) { goto err; @@ -4906,7 +5051,7 @@ that are reorganised. of partitions anymore. We use this code also for Table reorganisations and here we don't set any default flags to FALSE. */ - if (!(alter_info->flags & ALTER_TABLE_REORG)) + if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG)) { if (!alt_part_info->use_default_partitions) { @@ -4917,7 +5062,7 @@ that are reorganised. tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->flags & ALTER_DROP_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION) { /* Drop a partition from a range partition and list partitioning is @@ -4961,14 +5106,14 @@ that are reorganised. my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP"); goto err; } - if (new_table->file->is_fk_defined_on_table_or_index(MAX_KEY)) + if (table->file->is_fk_defined_on_table_or_index(MAX_KEY)) { my_error(ER_ROW_IS_REFERENCED, MYF(0)); goto err; } tab_part_info->num_parts-= num_parts_dropped; } - else if (alter_info->flags & ALTER_REBUILD_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_REBUILD_PARTITION) { if (set_part_state(alter_info, tab_part_info, PART_CHANGED)) { @@ -4977,11 +5122,11 @@ that are reorganised. } if (!(*fast_alter_table)) { - new_table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0)); + table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0)); goto err; } } - else if (alter_info->flags & ALTER_COALESCE_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION) { uint num_parts_coalesced= alter_info->num_parts; uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced; @@ -5079,13 +5224,13 @@ state of p1. } while (part_count < tab_part_info->num_parts); tab_part_info->num_parts= num_parts_remain; } - if (!(alter_info->flags & ALTER_TABLE_REORG)) + if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG)) { tab_part_info->use_default_num_partitions= FALSE; tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->flags & ALTER_REORGANIZE_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION) { /* Reorganise partitions takes a number of partitions that are next @@ -5133,8 +5278,8 @@ state of p1. alt_part_info->subpart_type= tab_part_info->subpart_type; alt_part_info->num_subparts= tab_part_info->num_subparts; DBUG_ASSERT(!alt_part_info->use_default_partitions); - if (alt_part_info->set_up_defaults_for_partitioning(new_table->file, - ULL(0), + if (alt_part_info->set_up_defaults_for_partitioning(table->file, + ULL(0), 0)) { goto err; @@ -5258,8 +5403,8 @@ the generated partition syntax in a correct manner. } *partition_changed= TRUE; thd->work_part_info= tab_part_info; - if (alter_info->flags & ALTER_ADD_PARTITION || - alter_info->flags & ALTER_REORGANIZE_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION || + alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION) { if (tab_part_info->use_default_subpartitions && !alt_part_info->use_default_subpartitions) @@ -5268,7 +5413,7 @@ the generated partition syntax in a correct manner. tab_part_info->use_default_num_subpartitions= FALSE; } if (tab_part_info->check_partition_info(thd, (handlerton**)NULL, - new_table->file, ULL(0), TRUE)) + table->file, ULL(0), TRUE)) { goto err; } @@ -5277,7 +5422,7 @@ the generated partition syntax in a correct manner. since this function "fixes" the item trees of the new partitions to reorganize into */ - if (alter_info->flags == ALTER_REORGANIZE_PARTITION && + if (alter_info->flags == Alter_info::ALTER_REORGANIZE_PARTITION && tab_part_info->part_type == RANGE_PARTITION && ((is_last_partition_reorged && (tab_part_info->column_list ? @@ -5358,7 +5503,7 @@ the generated partition syntax in a correct manner. */ if (table->part_info) { - if (alter_info->flags & ALTER_REMOVE_PARTITIONING) + if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING) { DBUG_PRINT("info", ("Remove partitioning")); if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) @@ -5451,15 +5596,7 @@ the generated partition syntax in a correct manner. } DBUG_RETURN(FALSE); err: - if (new_table) - { - /* - Only remove the intermediate table object and its share object, - do not remove the .frm file, since it is the original one. - */ - close_temporary(new_table, 1, 0); - } - *fast_alter_table= NULL; + *fast_alter_table= false; DBUG_RETURN(TRUE); } @@ -5500,9 +5637,6 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); - /* First lock the original tables */ - if (file->ha_external_lock(thd, F_WRLCK)) - DBUG_RETURN(TRUE); /* Disable transactions for all new tables */ if (mysql_trans_prepare_alter_copy_data(thd)) @@ -5520,8 +5654,6 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) if (mysql_trans_commit_alter_copy_data(thd)) error= 1; /* The error has been reported */ - if (file->ha_external_lock(thd, F_UNLCK)) - error= 1; DBUG_RETURN(test(error)); } @@ -5593,6 +5725,11 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) int error; DBUG_ENTER("mysql_drop_partitions"); + DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE, + lpt->table->s->db.str, + lpt->table->s->table_name.str, + MDL_EXCLUSIVE)); + build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); if ((error= lpt->table->file->ha_drop_partitions(path))) { @@ -6174,7 +6311,7 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) if (write_log_changed_partitions(lpt, &next_entry, (const char*)path)) goto error; if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, - lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION)) + lpt->alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)) goto error; if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE)) goto error; @@ -6271,47 +6408,54 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) { THD *thd= lpt->thd; - if (lpt->old_table) - close_all_tables_for_name(thd, lpt->old_table->s, HA_EXTRA_NOT_USED); if (lpt->table) { /* - Only remove the intermediate table object and its share object, - do not remove the .frm file, since it is the original one. + Remove all instances of the table and its locks and other resources. */ - close_temporary(lpt->table, 1, 0); + close_all_tables_for_name(thd, lpt->table->s, HA_EXTRA_NOT_USED, NULL); } lpt->table= 0; - lpt->old_table= 0; lpt->table_list->table= 0; - if (thd->locked_tables_list.reopen_tables(thd)) - sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + if (thd->locked_tables_mode) + { + Diagnostics_area *stmt_da= NULL; + Diagnostics_area tmp_stmt_da(true); + + if (thd->is_error()) + { + /* reopen might fail if we have a previous error, use a temporary da. */ + stmt_da= thd->get_stmt_da(); + thd->set_stmt_da(&tmp_stmt_da); + } + + if (thd->locked_tables_list.reopen_tables(thd)) + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + + if (stmt_da) + thd->set_stmt_da(stmt_da); + } } -/* - Unlock and close table before renaming and dropping partitions - SYNOPSIS - alter_close_tables() - lpt Struct carrying parameters - close_old Close original table too - RETURN VALUES - 0 +/** + Unlock and close table before renaming and dropping partitions. + + @param lpt Struct carrying parameters + + @return Always 0. */ -static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt, bool close_old) +static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt) { - DBUG_ENTER("alter_close_tables"); + DBUG_ENTER("alter_close_table"); + if (lpt->table->db_stat) { + mysql_lock_remove(lpt->thd, lpt->thd->lock, lpt->table); lpt->table->file->ha_close(); lpt->table->db_stat= 0; // Mark file closed } - if (close_old && lpt->old_table) - { - close_all_tables_for_name(lpt->thd, lpt->old_table->s, HA_EXTRA_NOT_USED); - lpt->old_table= 0; - } DBUG_RETURN(0); } @@ -6333,23 +6477,55 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, bool close_table) { partition_info *part_info= lpt->part_info; + THD *thd= lpt->thd; + TABLE *table= lpt->table; DBUG_ENTER("handle_alter_part_error"); + DBUG_ASSERT(table->m_needs_reopen); if (close_table) { /* - Since the error handling (ddl_log) needs to drop newly created - partitions they must be closed first to not issue errors. - But we still need some information from the part_info object, - so we clone it first to have a copy. + All instances of this table needs to be closed. + Better to do that here, than leave the cleaning up to others. + Aquire EXCLUSIVE mdl lock if not already aquired. */ + if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db, + lpt->table_name, + MDL_EXCLUSIVE)) + { + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + { + /* At least remove this instance on failure */ + goto err_exclusive_lock; + } + } + /* Ensure the share is destroyed and reopened. */ + part_info= lpt->part_info->get_clone(); + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); + } + + else + { +err_exclusive_lock: + /* + Temporarily remove it from the locked table list, so that it will get + reopened. + */ + thd->locked_tables_list.unlink_from_list(thd, + table->pos_in_locked_tables, + false); + /* + Make sure that the table is unlocked, closed and removed from + the table cache. + */ + mysql_lock_remove(thd, thd->lock, table); part_info= lpt->part_info->get_clone(); - alter_close_tables(lpt, action_completed); + close_thread_table(thd, &thd->open_tables); + lpt->table_list->table= NULL; } if (part_info->first_log_entry && - execute_ddl_log_entry(lpt->thd, - part_info->first_log_entry->entry_pos)) + execute_ddl_log_entry(thd, part_info->first_log_entry->entry_pos)) { /* We couldn't recover from error, most likely manual interaction @@ -6362,14 +6538,14 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, 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, + push_warning_printf(thd, Sql_condition::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, + push_warning_printf(thd, Sql_condition::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.", @@ -6385,7 +6561,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, 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, + push_warning_printf(thd, Sql_condition::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", @@ -6399,7 +6575,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, 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, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s", "Failed during drop of partitions, table is intact.", "Manual drop of remaining partitions is required"); @@ -6411,7 +6587,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, 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, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s %s", "Failed during renaming of partitions. We are now in a position", "where table is not reusable", @@ -6440,11 +6616,31 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, even though we reported an error the operation was successfully completed. */ - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,"%s %s", "Operation was successfully completed by failure handling,", "after failure of normal operation"); } } + + if (thd->locked_tables_mode) + { + Diagnostics_area *stmt_da= NULL; + Diagnostics_area tmp_stmt_da(true); + + if (thd->is_error()) + { + /* reopen might fail if we have a previous error, use a temporary da. */ + stmt_da= thd->get_stmt_da(); + thd->set_stmt_da(&tmp_stmt_da); + } + + if (thd->locked_tables_list.reopen_tables(thd)) + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + + if (stmt_da) + thd->set_stmt_da(stmt_da); + } + DBUG_VOID_RETURN; } @@ -6461,7 +6657,7 @@ static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket, enum_mdl_type type) { if (thd->locked_tables_mode) - ticket->downgrade_exclusive_lock(type); + ticket->downgrade_lock(type); } @@ -6470,14 +6666,12 @@ static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket, previously prepared. @param thd Thread object - @param table Original table object + @param table Original table object with new part_info @param alter_info ALTER TABLE info @param create_info Create info for CREATE TABLE @param table_list List of the table involved @param db Database name of new table @param table_name Table name of new table - @param fast_alter_table Prepared table object - @return Operation status @retval TRUE Error @retval FALSE Success @@ -6492,8 +6686,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, char *db, - const char *table_name, - TABLE *fast_alter_table) + const char *table_name) { /* Set-up struct used to write frm files */ partition_info *part_info; @@ -6503,10 +6696,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, bool close_table_on_failure= FALSE; bool frm_install= FALSE; MDL_ticket *mdl_ticket= table->mdl_ticket; - DBUG_ASSERT(fast_alter_table); DBUG_ENTER("fast_alter_partition_table"); + DBUG_ASSERT(table->m_needs_reopen); - part_info= fast_alter_table->part_info; + part_info= table->part_info; lpt->thd= thd; lpt->table_list= table_list; lpt->part_info= part_info; @@ -6515,8 +6708,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->db_options= create_info->table_options; if (create_info->row_type == ROW_TYPE_DYNAMIC) lpt->db_options|= HA_OPTION_PACK_RECORD; - lpt->table= fast_alter_table; - lpt->old_table= table; + lpt->table= table; lpt->key_info_buffer= 0; lpt->key_count= 0; lpt->db= db; @@ -6574,7 +6766,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, goto err; } } - else if (alter_info->flags & ALTER_DROP_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION) { /* Now after all checks and setting state on dropped partitions we can @@ -6609,9 +6801,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 3) Write the ddl log to ensure that the operation is completed even in the presence of a MySQL Server crash (the log is executed before any other threads are started, so there are no locking issues). - 4) Close all tables that have already been opened but didn't stumble on + 4) Close the table that have already been opened but didn't stumble on + the abort locked previously. This is done as part of the the abort locked previously. This is done as part of the - alter_close_tables call. + alter_close_table call. 5) 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 @@ -6647,7 +6840,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, (action_completed= TRUE, FALSE) || ERROR_INJECT_CRASH("crash_drop_partition_4") || ERROR_INJECT_ERROR("fail_drop_partition_4") || - alter_close_tables(lpt, action_completed) || + alter_close_table(lpt) || (close_table_on_failure= FALSE, FALSE) || ERROR_INJECT_CRASH("crash_drop_partition_5") || ERROR_INJECT_ERROR("fail_drop_partition_5") || @@ -6674,7 +6867,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, goto err; } } - else if ((alter_info->flags & ALTER_ADD_PARTITION) && + else if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) && (part_info->part_type == RANGE_PARTITION || part_info->part_type == LIST_PARTITION)) { @@ -6724,7 +6917,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_add_partition_5") || ERROR_INJECT_ERROR("fail_add_partition_5") || (close_table_on_failure= FALSE, FALSE) || - alter_close_tables(lpt, action_completed) || + alter_close_table(lpt) || ERROR_INJECT_CRASH("crash_add_partition_6") || ERROR_INJECT_ERROR("fail_add_partition_6") || ((!thd->lex->no_write_to_binlog) && @@ -6788,24 +6981,24 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 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) Add the new partitions. + Copy from the reorganised partitions to the new partitions. 4) Get an exclusive metadata lock on the table (waits for all active transactions using this table). This ensures that we can release all other 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 this exclusive lock. - 5) Log that operation is completed and log all complete actions - needed to complete operation from here - 6) Write bin log - 7) Close all instances of the table and remove them from the table cache. - 8) Prepare handlers for rename and delete of partitions + 5) Close the table. + 6) Log that operation is completed and log all complete actions + needed to complete operation from here. + 7) Write bin log. + 8) Prepare handlers for rename and delete of partitions. 9) Rename and drop the reorged partitions such that they are no longer used and rename those added to their real new names. - 10) Install the shadow frm file - 11) Reopen the table if under lock tables - 12) Complete query - */ + 10) Install the shadow frm file. + 11) Reopen the table if under lock tables. + 12) Complete query. + */ if (write_log_drop_shadow_frm(lpt) || ERROR_INJECT_CRASH("crash_change_partition_1") || ERROR_INJECT_ERROR("fail_change_partition_1") || @@ -6822,22 +7015,22 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || ERROR_INJECT_CRASH("crash_change_partition_5") || ERROR_INJECT_ERROR("fail_change_partition_5") || - write_log_final_change_partition(lpt) || - (action_completed= TRUE, FALSE) || + alter_close_table(lpt) || + (close_table_on_failure= FALSE, FALSE) || ERROR_INJECT_CRASH("crash_change_partition_6") || ERROR_INJECT_ERROR("fail_change_partition_6") || + write_log_final_change_partition(lpt) || + (action_completed= TRUE, FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_7") || + ERROR_INJECT_ERROR("fail_change_partition_7") || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT_CRASH("crash_change_partition_7") || - ERROR_INJECT_ERROR("fail_change_partition_7") || + ERROR_INJECT_CRASH("crash_change_partition_8") || + ERROR_INJECT_ERROR("fail_change_partition_8") || ((frm_install= TRUE), FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || (frm_install= FALSE, FALSE) || - ERROR_INJECT_CRASH("crash_change_partition_8") || - ERROR_INJECT_ERROR("fail_change_partition_8") || - alter_close_tables(lpt, action_completed) || - (close_table_on_failure= FALSE, FALSE) || ERROR_INJECT_CRASH("crash_change_partition_9") || ERROR_INJECT_ERROR("fail_change_partition_9") || mysql_drop_partitions(lpt) || @@ -6863,22 +7056,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, */ DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, table_list)); err: - if (action_completed) - { - /* - Although error occurred, the action was forced to retry for completion. - Therefore we must close+reopen all instances of the table. - */ - (void) alter_partition_lock_handling(lpt); - } - else - { - /* - The failed action was reverted, leave the original table as is and - close/destroy the intermediate table object and its share. - */ - close_temporary(lpt->table, 1, 0); - } downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE); DBUG_RETURN(TRUE); } @@ -6976,20 +7153,19 @@ void mem_alloc_error(size_t size) } #ifdef WITH_PARTITION_STORAGE_ENGINE -/* - Return comma-separated list of used partitions in the provided given string +/** + Return comma-separated list of used partitions in the provided given string. - SYNOPSIS - make_used_partitions_str() - part_info IN Partitioning info - parts_str OUT The string to fill + @param part_info Partitioning info + @param[out] parts The resulting list of string to fill - DESCRIPTION - Generate a list of used partitions (from bits in part_info->used_partitions - bitmap), asd store it into the provided String object. + Generate a list of used partitions (from bits in part_info->read_partitions + bitmap), and store it into the provided String object. - NOTE + @note The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN). + In case of UPDATE, only the partitions read is given, not the partitions + that was written or locked. */ void make_used_partitions_str(partition_info *part_info, String *parts_str) @@ -7007,7 +7183,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) List_iterator<partition_element> it2(head_pe->subpartitions); while ((pe= it2++)) { - if (bitmap_is_set(&part_info->used_partitions, partition_id)) + if (bitmap_is_set(&part_info->read_partitions, partition_id)) { if (parts_str->length()) parts_str->append(','); @@ -7027,7 +7203,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) { while ((pe= it++)) { - if (bitmap_is_set(&part_info->used_partitions, partition_id)) + if (bitmap_is_set(&part_info->read_partitions, partition_id)) { if (parts_str->length()) parts_str->append(','); |