diff options
| author | Dmitry Shulga <dmitry.shulga@mariadb.com> | 2021-08-26 18:54:17 +0700 |
|---|---|---|
| committer | Aleksey Midenkov <midenok@gmail.com> | 2021-09-10 18:50:18 +0300 |
| commit | 90724c78f70160ee042a1f7ef6a12b47fc257cc5 (patch) | |
| tree | 37a086b2a433575b19b19b6a330503625bf14fea | |
| parent | 818407474d66956b016c380a59ec3a9d4983aa0a (diff) | |
| download | mariadb-git-90724c78f70160ee042a1f7ef6a12b47fc257cc5.tar.gz | |
MDEV-22165: Starter
This is squashed bb-10.7-MDEV-22165 of Dmitry's work with needless
things reverted.
| -rw-r--r-- | sql/handler.h | 1 | ||||
| -rw-r--r-- | sql/share/errmsg-utf8.txt | 2 | ||||
| -rw-r--r-- | sql/sql_alter.cc | 4 | ||||
| -rw-r--r-- | sql/sql_partition.cc | 230 | ||||
| -rw-r--r-- | sql/sql_partition.h | 3 | ||||
| -rw-r--r-- | sql/sql_partition_admin.cc | 195 |
6 files changed, 433 insertions, 2 deletions
diff --git a/sql/handler.h b/sql/handler.h index c1f49d06bde..657618ab4e7 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -825,6 +825,7 @@ typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); // Set for REORGANIZE PARTITION #define ALTER_PARTITION_TABLE_REORG (1ULL << 12) #define ALTER_PARTITION_CONVERT_OUT (1ULL << 13) +#define ALTER_PARTITION_ADD_FROM_TABLE (1ULL << 14) /* This is master database for most of system tables. However there diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 904c80d9f55..bba9357122d 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7992,3 +7992,5 @@ ER_STORAGE_ENGINE_DISABLED eng "Storage engine %s is disabled" ER_PARTITION_CONVERT_SUBPARTITIONED eng "Convert partition is not supported for subpartitioned table." +ER_PARTITION_METHOD_NOT_COMPATIBLE_WITH_ADD_FROM_TABLE + eng "ADD PARTITION FROM TABLE is not compatible with %s table partition method" diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index a144fe029bf..889eca2f4ba 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -259,6 +259,8 @@ Alter_table_ctx::Alter_table_ctx() tables_opened(0), db(null_clex_str), table_name(null_clex_str), alias(null_clex_str), new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str), + storage_engine_partitioned(false), + tmp_storage_engine_name_partitioned(false), fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_table(NULL), tmp_table(false) @@ -277,6 +279,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, : implicit_default_value_error_field(NULL), error_if_not_empty(false), tables_opened(tables_opened_arg), new_db(*new_db_arg), new_name(*new_name_arg), + tmp_storage_engine_name_partitioned(false), fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_table(NULL), tmp_table(false) @@ -438,6 +441,7 @@ bool Sql_cmd_alter_table::execute(THD *thd) */ if ((alter_info.partition_flags & ALTER_PARTITION_DROP) || (alter_info.partition_flags & ALTER_PARTITION_CONVERT_OUT) || + (alter_info.partition_flags & ALTER_PARTITION_ADD_FROM_TABLE) || (alter_info.flags & ALTER_RENAME)) priv_needed|= DROP_ACL; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index cbdb40472d8..14db4054642 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4875,7 +4875,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, ALTER_PARTITION_COALESCE | ALTER_PARTITION_REORGANIZE | ALTER_PARTITION_TABLE_REORG | - ALTER_PARTITION_REBUILD)) + ALTER_PARTITION_REBUILD | + ALTER_PARTITION_ADD_FROM_TABLE)) { /* You can't add column when we are doing alter related to partition @@ -5079,7 +5080,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, goto err; } } - if (alter_info->partition_flags & ALTER_PARTITION_ADD) + if ((alter_info->partition_flags & ALTER_PARTITION_ADD) || + (alter_info->partition_flags & ALTER_PARTITION_ADD_FROM_TABLE)) { if (*fast_alter_table && thd->locked_tables_mode) { @@ -7205,6 +7207,176 @@ bool log_partition_alter_to_ddl_log(ALTER_PARTITION_PARAM_TYPE *lpt) } +extern bool move_table_to_partition(ALTER_PARTITION_PARAM_TYPE *lpt); + + +/** + Check that definition of a table specified in the clause FROM of + the statement ALTER TABLE <tablename> ADD PARTITION ... FROM <from_table> + fit with definition of a partition being added and every row stored in + the table <from_table> conform with partition's expression. On return from + the function an actual name of a file corresponding to the partition + is stored in the buffer part_file_name_buf. + + @param lpt Structure containing parameters required for checking + @param[in,out] part_file_name_buf Buffer for storing a partition name + @param part_file_name_buf_sz Size of buffer for storing a partition name + @param part_file_name_len Length of partition prefix stored in the buffer + on invocation of function + + @return false on success, true on error +*/ + +static bool check_table_data_fit_new_partition(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + THD *thd= lpt->thd; + TABLE *table_to= lpt->table_list->table; + TABLE *table_from= lpt->table_list->next_local->table; + + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table_to->s->db.str, + table_to->s->table_name.str, + MDL_EXCLUSIVE)); + + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table_from->s->db.str, + table_from->s->table_name.str, + MDL_EXCLUSIVE)); + + uint32 new_part_id; + partition_element *part_elem; + // FIXME: really? + const char* partition_name= + thd->lex->part_info->curr_part_elem->partition_name; + + part_elem= table_to->part_info->get_part_elem(partition_name, + nullptr, 0, &new_part_id); + if (unlikely(!part_elem)) + return true; + + if (unlikely(new_part_id == NOT_A_PARTITION_ID)) + { + DBUG_ASSERT(table_to->part_info->is_sub_partitioned()); + my_error(ER_PARTITION_INSTEAD_OF_SUBPARTITION, MYF(0)); + return true; + } + + if (verify_data_with_partition(table_from, table_to, new_part_id)) + { + return true; + } + + return false; +} + + +/** + Check whether metadata of a partitioned table and a being moved to partition + are equal + + @para[in, out] lpt Struct containing parameters required for handling of + the statement ALTER TABLE + + @return false on ok (tables metadata are equal), + true on error (tables metadata are different) +*/ + +static bool compare_tables_metadata(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + TABLE *part_table= lpt->table_list->table; + TABLE *table= lpt->table_list->next_local->table; + bool metadata_equal; + HA_CREATE_INFO *part_create_info= lpt->create_info; + + handlerton *db_type= part_create_info->db_type; + part_create_info->db_type= part_table->part_info->default_engine_type; + + if (mysql_compare_tables(table, lpt->alter_info, part_create_info, + &metadata_equal)) + + { + part_create_info->db_type= db_type; + my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0)); + return true; + } + + part_create_info->db_type= db_type; + + DEBUG_SYNC(lpt->thd, "swap_partition_after_compare_tables"); + if (!metadata_equal) + { + my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0)); + return true; + } + DBUG_ASSERT(table->s->db_create_options == + part_table->s->db_create_options); + DBUG_ASSERT(table->s->db_options_in_use == + part_table->s->db_options_in_use); + + if (table->s->avg_row_length != part_create_info->avg_row_length) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "AVG_ROW_LENGTH"); + return true; + } + + if (table->s->db_create_options != part_create_info->table_options) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "TABLE OPTION"); + return true; + } + + if (table->s->table_charset != part_table->s->table_charset) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "CHARACTER SET"); + return true; + } + + return false; +} + + +/** + For the statement is ALTER TABLE ... ADD PARTITION... FROM <tbl_name> + check that partition metadata is compatible with table definition and + partition type supported for moving table to partition. + + @param lpt Structure containing parameters required for handling of + the statement ALTER TABLE + + @return false on success, true on failure +*/ + +static bool check_table_and_partition_compatibility( + ALTER_PARTITION_PARAM_TYPE *lpt) +{ + partition_info* tab_part_info= lpt->table->part_info; + DBUG_ASSERT((lpt->alter_info->partition_flags & + ALTER_PARTITION_ADD_FROM_TABLE)); + if (tab_part_info->part_type != RANGE_PARTITION && + tab_part_info->part_type != LIST_PARTITION) + { + /* + ALTER TABLE ... ADD PARTITION ... FROM TABLE is not compatible with + partition methods other RANGE and LIST. + */ + my_error(ER_PARTITION_METHOD_NOT_COMPATIBLE_WITH_ADD_FROM_TABLE, + MYF(0), (tab_part_info->part_type == HASH_PARTITION ? + "HASH": "VERSIONING")); + return true; + } + + if (compare_tables_metadata(lpt)) + return true; + + if (check_table_data_fit_new_partition(lpt)) + return true; + + return false; +} + /** Actually perform the change requested by ALTER TABLE of partitions @@ -7491,10 +7663,64 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, if (alter_partition_lock_handling(lpt)) goto err; } + else if ((alter_info->partition_flags & ALTER_PARTITION_ADD_FROM_TABLE)) + { + TABLE *table_from= table_list->next_local->table; + + if (write_log_drop_shadow_frm(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_from_1") || + ERROR_INJECT_ERROR("fail_add_partition_from_1") || + mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || + ERROR_INJECT_CRASH("crash_add_partition_from_2") || + ERROR_INJECT_ERROR("fail_add_partition_from_2") || + wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || + wait_while_table_is_used(thd, table_from, + HA_EXTRA_PREPARE_FOR_RENAME) || + + ERROR_INJECT_CRASH("crash_add_partition_from_3") || + ERROR_INJECT_ERROR("fail_add_partition_from_3") || + write_log_add_change_partition(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_from_4") || + ERROR_INJECT_ERROR("fail_add_partition_from_4") || + mysql_change_partitions(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_from_5") || + ERROR_INJECT_ERROR("fail_add_partition_from_5") || + alter_close_table(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_from_6") || + ERROR_INJECT_ERROR("fail_add_partition_from_6") || + check_table_and_partition_compatibility(lpt) || + move_table_to_partition(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_from_7") || + ERROR_INJECT_ERROR("fail_add_partition_from_7") || + write_log_rename_frm(lpt) || + (action_completed= TRUE, FALSE) || + ERROR_INJECT_CRASH("crash_add_partition_from_8") || + ERROR_INJECT_ERROR("fail_add_partition_from_8") || + (frm_install= TRUE, FALSE) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || + log_partition_alter_to_ddl_log(lpt) || + (frm_install= FALSE, FALSE) || + ERROR_INJECT_CRASH("crash_add_partition_from_9") || + ERROR_INJECT_ERROR("fail_add_partition_from_9") || + (write_log_completed(lpt, FALSE), FALSE) || + ((!thd->lex->no_write_to_binlog) && + (write_bin_log(thd, FALSE, + thd->query(), thd->query_length()), FALSE)) || + ERROR_INJECT_CRASH("crash_add_partition_from_10") || + ERROR_INJECT_ERROR("fail_add_partition_from_10")) + { + handle_alter_part_error(lpt, action_completed, FALSE, frm_install); + goto err; + } + if (alter_partition_lock_handling(lpt)) + goto err; + + } else if ((alter_info->partition_flags & ALTER_PARTITION_ADD) && (part_info->part_type == RANGE_PARTITION || part_info->part_type == LIST_PARTITION)) { + DBUG_ASSERT(!(alter_info->partition_flags & ALTER_PARTITION_ADD_FROM_TABLE)); /* ADD RANGE/LIST PARTITIONS In this case there are no tuples removed and no tuples are added. diff --git a/sql/sql_partition.h b/sql/sql_partition.h index e2e5fdf4c4b..04db1036c9e 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -284,6 +284,9 @@ bool write_log_replace_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint next_entry, const char *from_path, const char *to_path); + +bool check_table_fit_new_partition(ALTER_PARTITION_PARAM_TYPE *lpt); + #else #define partition_key_modified(X,Y) 0 #endif diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 1056998ef08..b64386eae23 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -15,7 +15,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ +#include <memory> // unique_ptr #include "mariadb.h" +#include "scope.h" // scope_exit #include "sql_parse.h" // check_one_table_access // check_merge_table_access // check_one_table_access @@ -988,4 +990,197 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) DBUG_RETURN(error); } + +// FIXME: replace with ddl_log_complete() +static void finalize_ddl_log_entry(DDL_LOG_MEMORY_ENTRY *log_entry, + DDL_LOG_MEMORY_ENTRY **exec_log_entry) +{ + (void) ddl_log_execute_entry(current_thd, log_entry->entry_pos); + mysql_mutex_lock(&LOCK_gdl); + /* mark the execute log entry done */ + (void) ddl_log_disable_execute_entry(exec_log_entry); + /* release the execute log entry */ + ddl_log_release_memory_entry(*exec_log_entry); + /* release the action log entry */ + ddl_log_release_memory_entry(log_entry); + mysql_mutex_unlock(&LOCK_gdl); +} + + +/** + Move a table specified by the clause FROM <table_name> of the statement + ALTER TABLE ... ADD PARTITION ... FROM <table_name> to the new partition. + + @param lpt A structure containing parameters regarding to the statement + ALTER TABLE ... ADD PARTITION ... + @param part_file_name a file name of the partition being added + + @return false on success, true on error +*/ + +bool move_table_to_partition(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + char part_file_name[2*FN_REFLEN+1]; + THD *thd= lpt->thd; + const char *path= lpt->table_list->table->s->path.str; + TABLE_LIST *table_from= lpt->table_list->next_local; + LEX_CSTRING &path_from= table_from->table->s->path; + char frm_from[FN_REFLEN + 1]; + memcpy(frm_from, LEX_STRING_WITH_LEN(path_from)); + strmov(frm_from + path_from.length, reg_ext); + + const char *partition_name= + thd->lex->part_info->curr_part_elem->partition_name; + + if (create_partition_name(part_file_name, sizeof(part_file_name), + path, partition_name, + NORMAL_PART_NAME, false)) + return true; + + char from_file_name[FN_REFLEN+1]; + + build_table_filename(from_file_name, + sizeof(from_file_name), + table_from->db.str, + table_from->table_name.str, + "", 0); + + handler *file_ptr= + get_new_handler(nullptr, thd->mem_root, + table_from->table->file->ht); + if (unlikely(!file_ptr)) + return true; + + /* + Install the guard object to delete a pointer to the class handler on return + from this function. + */ + std::unique_ptr<handler> file(file_ptr); + + DDL_LOG_ENTRY move_entry; + DDL_LOG_MEMORY_ENTRY *log_entry= nullptr; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= nullptr; + + bzero(&move_entry, sizeof(move_entry)); + move_entry.action_type= DDL_LOG_RENAME_ACTION; + lex_string_set(&move_entry.name, part_file_name); + lex_string_set(&move_entry.from_name, from_file_name); + lex_string_set(&move_entry.handler_name, + ha_resolve_storage_engine_name( + table_from->table->file->ht)); + move_entry.phase= EXCH_PHASE_NAME_TO_TEMP; + + mysql_mutex_lock(&LOCK_gdl); + + /* + Install the guard object to release LOCK_gdl on any return from the current + function that could be happened before this lock explicitly released + */ + auto lock_gdl_unlocker = make_scope_exit( + []() { + mysql_mutex_unlock(&LOCK_gdl); + } + ); + DBUG_EXECUTE_IF("move_partition_fail_1", return true;); + DBUG_EXECUTE_IF("move_partition_abort_1", DBUG_SUICIDE();); + if (unlikely(ddl_log_write_entry(&move_entry, &log_entry))) + { + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true; + } + + DBUG_EXECUTE_IF("move_partition_fail_2", + ddl_log_release_memory_entry(log_entry); return true;); + DBUG_EXECUTE_IF("move_partition_abort_2", DBUG_SUICIDE();); + if (unlikely(ddl_log_write_execute_entry(log_entry->entry_pos, + &exec_log_entry))) + { + my_error(ER_DDL_LOG_ERROR, MYF(0)); + ddl_log_release_memory_entry(log_entry); + return true; + } + + mysql_mutex_unlock(&LOCK_gdl); + /* + Since the global mutex LOCK_gd has just been release, turn off + its guard object to avoid double releasing of the lock. + */ + lock_gdl_unlocker.release(); + + /* + Install the guard object to finalize ddl log entry on return from + the current function + */ + auto ddl_lock_finalizer = make_scope_exit( + [log_entry, &exec_log_entry]() + { + finalize_ddl_log_entry(log_entry, &exec_log_entry); + } + ); + + DBUG_EXECUTE_IF("move_partition_fail_3", + my_error(ER_ERROR_ON_RENAME, MYF(0), + from_file_name, part_file_name, 0); + return true;); + DBUG_EXECUTE_IF("move_partition_abort_3", DBUG_SUICIDE();); + + if (unlikely(file->delete_table(part_file_name))) + { + my_error(ER_ERROR_ON_RENAME, MYF(0), from_file_name, + part_file_name, my_errno); + return true; + } + + DBUG_EXECUTE_IF("move_partition_fail_4", + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true;); + DBUG_EXECUTE_IF("move_partition_abort_4", DBUG_SUICIDE();); + + if (unlikely(ddl_log_increment_phase(log_entry->entry_pos))) + { + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true; + } + + DBUG_EXECUTE_IF("move_partition_fail_5", + my_error(ER_ERROR_ON_RENAME, MYF(0), + from_file_name, part_file_name, 0); + return true;); + DBUG_EXECUTE_IF("move_partition_abort_5", DBUG_SUICIDE();); + + close_all_tables_for_name(thd, table_from->table->s, + HA_EXTRA_PREPARE_FOR_RENAME, nullptr); + + if (unlikely(file->ha_rename_table(from_file_name, part_file_name))) + { + my_error(ER_ERROR_ON_RENAME, MYF(0), from_file_name, + part_file_name, my_errno); + return true; + } + + DBUG_EXECUTE_IF("move_partition_fail_6", + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true;); + if (unlikely(ddl_log_increment_phase(log_entry->entry_pos))) + { + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true; + } + + if (mysql_file_delete(key_file_frm, frm_from, + MYF(MY_WME | MY_IGNORE_ENOENT))) + { + return true; + } + + /* The move is complete and ddl_log is deactivated */ + DBUG_EXECUTE_IF("exchange_partition_fail_9", + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true;); + DBUG_EXECUTE_IF("exchange_partition_abort_9", DBUG_SUICIDE();); + + /* all OK */ + return false; +} + #endif /* WITH_PARTITION_STORAGE_ENGINE */ |
