summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2021-08-26 18:54:17 +0700
committerAleksey Midenkov <midenok@gmail.com>2021-09-10 18:50:18 +0300
commit90724c78f70160ee042a1f7ef6a12b47fc257cc5 (patch)
tree37a086b2a433575b19b19b6a330503625bf14fea
parent818407474d66956b016c380a59ec3a9d4983aa0a (diff)
downloadmariadb-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.h1
-rw-r--r--sql/share/errmsg-utf8.txt2
-rw-r--r--sql/sql_alter.cc4
-rw-r--r--sql/sql_partition.cc230
-rw-r--r--sql/sql_partition.h3
-rw-r--r--sql/sql_partition_admin.cc195
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 */