mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
MDEV-22165 CONVERT TABLE: move in partition from existing table
Syntax for CONVERT TABLE ALTER TABLE tbl_name CONVERT TABLE tbl_name TO PARTITION partition_name partition_spec Examples: ALTER TABLE t1 CONVERT TABLE tp2 TO PARTITION p2 VALUES LESS THAN MAX_VALUE(); New ALTER_PARTITION_CONVERT_IN command for fast_alter_partition_table() is done in alter_partition_convert_in() function which basically does ha_rename_table(). Table structure and data check is basically the same as in EXCHANGE PARTITION command. And these are done by compare_table_with_partition() and check_table_data(). Atomic DDL is done by the scheme from MDEV-22166 (see the corresponding commit message). The only differnce is that it also has to drop source table frm and that is done by WFRM_DROP_CONVERTED_FROM. Initial patch was done by Dmitry Shulga <dmitry.shulga@mariadb.com>
This commit is contained in:
committed by
Sergei Golubchik
parent
7da721be31
commit
69724805bc
@ -4870,7 +4870,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_CONVERT_IN))
|
||||
{
|
||||
/*
|
||||
You can't add column when we are doing alter related to partition
|
||||
@ -5076,6 +5077,13 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
|
||||
}
|
||||
if (alter_info->partition_flags & ALTER_PARTITION_ADD)
|
||||
{
|
||||
if ((alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN) &&
|
||||
!(tab_part_info->part_type == RANGE_PARTITION ||
|
||||
tab_part_info->part_type == LIST_PARTITION))
|
||||
{
|
||||
my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "CONVERT TABLE TO");
|
||||
goto err;
|
||||
}
|
||||
if (*fast_alter_table && thd->locked_tables_mode)
|
||||
{
|
||||
MEM_ROOT *old_root= thd->mem_root;
|
||||
@ -6387,12 +6395,20 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
FALSE Success
|
||||
*/
|
||||
|
||||
enum log_action_enum
|
||||
{
|
||||
ACT_DROP = 0,
|
||||
ACT_CONVERT_IN,
|
||||
ACT_CONVERT_OUT
|
||||
};
|
||||
|
||||
static bool log_drop_or_convert_action(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
uint *next_entry, const char *path,
|
||||
const char *from_name, bool temp_list)
|
||||
const char *from_name, bool temp_list,
|
||||
const log_action_enum convert_action)
|
||||
{
|
||||
DDL_LOG_ENTRY ddl_log_entry;
|
||||
const bool convert_action= (from_name != NULL);
|
||||
DBUG_ASSERT(convert_action == ACT_DROP || (from_name != NULL));
|
||||
partition_info *part_info= lpt->part_info;
|
||||
DDL_LOG_MEMORY_ENTRY *log_entry;
|
||||
char tmp_path[FN_REFLEN + 1];
|
||||
@ -6427,7 +6443,10 @@ static bool log_drop_or_convert_action(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
name_variant= TEMP_PART_NAME;
|
||||
else
|
||||
name_variant= NORMAL_PART_NAME;
|
||||
DBUG_ASSERT(!convert_action || part_elem->part_state == PART_TO_BE_DROPPED);
|
||||
DBUG_ASSERT(convert_action != ACT_CONVERT_IN ||
|
||||
part_elem->part_state == PART_TO_BE_ADDED);
|
||||
DBUG_ASSERT(convert_action != ACT_CONVERT_OUT ||
|
||||
part_elem->part_state == PART_TO_BE_DROPPED);
|
||||
if (part_info->is_sub_partitioned())
|
||||
{
|
||||
DBUG_ASSERT(!convert_action);
|
||||
@ -6464,9 +6483,18 @@ static bool log_drop_or_convert_action(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
part_elem->partition_name, name_variant,
|
||||
TRUE))
|
||||
DBUG_RETURN(TRUE);
|
||||
ddl_log_entry.name= { tmp_path, strlen(tmp_path) };
|
||||
if (convert_action)
|
||||
ddl_log_entry.from_name= { from_name, strlen(from_name) };
|
||||
switch (convert_action)
|
||||
{
|
||||
case ACT_CONVERT_OUT:
|
||||
ddl_log_entry.from_name= { from_name, strlen(from_name) };
|
||||
/* fall through */
|
||||
case ACT_DROP:
|
||||
ddl_log_entry.name= { tmp_path, strlen(tmp_path) };
|
||||
break;
|
||||
case ACT_CONVERT_IN:
|
||||
ddl_log_entry.name= { from_name, strlen(from_name) };
|
||||
ddl_log_entry.from_name= { tmp_path, strlen(tmp_path) };
|
||||
}
|
||||
if (ddl_log_write_entry(&ddl_log_entry, &log_entry))
|
||||
{
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -6486,18 +6514,24 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
uint *next_entry, const char *path,
|
||||
bool temp_list)
|
||||
{
|
||||
return log_drop_or_convert_action(lpt, next_entry, path, NULL, temp_list);
|
||||
return log_drop_or_convert_action(lpt, next_entry, path, NULL, temp_list,
|
||||
ACT_DROP);
|
||||
}
|
||||
|
||||
inline
|
||||
static bool write_log_convert_out_partition(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
uint *next_entry, const char *path)
|
||||
static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
uint *next_entry, const char *path)
|
||||
{
|
||||
char from_name[FN_REFLEN + 1];
|
||||
build_table_filename(from_name, sizeof(from_name) - 1, lpt->alter_ctx->new_db.str,
|
||||
char other_table[FN_REFLEN + 1];
|
||||
const ulong f= lpt->alter_info->partition_flags;
|
||||
DBUG_ASSERT((f & ALTER_PARTITION_CONVERT_IN) || (f & ALTER_PARTITION_CONVERT_OUT));
|
||||
const log_action_enum convert_action= (f & ALTER_PARTITION_CONVERT_IN)
|
||||
? ACT_CONVERT_IN : ACT_CONVERT_OUT;
|
||||
build_table_filename(other_table, sizeof(other_table) - 1, lpt->alter_ctx->new_db.str,
|
||||
lpt->alter_ctx->new_name.str, "", 0);
|
||||
DDL_LOG_MEMORY_ENTRY *main_entry= lpt->part_info->main_entry;
|
||||
bool res= log_drop_or_convert_action(lpt, next_entry, path, from_name, false);
|
||||
bool res= log_drop_or_convert_action(lpt, next_entry, path, other_table,
|
||||
false, convert_action);
|
||||
/*
|
||||
NOTE: main_entry is "drop shadow frm", we have to keep it like this
|
||||
because partitioning crash-safety disables it at install shadow FRM phase.
|
||||
@ -6537,6 +6571,16 @@ static bool write_log_drop_frm(ALTER_PARTITION_PARAM_TYPE *lpt,
|
||||
if (ddl_log_delete_frm(drop_chain, (const char*)path))
|
||||
goto error;
|
||||
|
||||
if (drop_backup && (lpt->alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN))
|
||||
{
|
||||
TABLE_LIST *table_from= lpt->table_list->next_local;
|
||||
build_table_filename(path, sizeof(path) - 1, table_from->db.str,
|
||||
table_from->table_name.str, "", 0);
|
||||
|
||||
if (ddl_log_delete_frm(drop_chain, (const char*) path))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ddl_log_write_execute_entry(drop_chain->list->entry_pos,
|
||||
drop_backup ?
|
||||
main_chain->execute_entry->entry_pos : 0,
|
||||
@ -6664,7 +6708,7 @@ error:
|
||||
}
|
||||
|
||||
|
||||
static bool write_log_convert_out_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||
static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||
{
|
||||
partition_info *part_info= lpt->part_info;
|
||||
char tmp_path[FN_REFLEN + 1];
|
||||
@ -6676,7 +6720,7 @@ static bool write_log_convert_out_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||
|
||||
mysql_mutex_lock(&LOCK_gdl);
|
||||
|
||||
if (write_log_convert_out_partition(lpt, &next_entry, (const char*)path))
|
||||
if (write_log_convert_partition(lpt, &next_entry, (const char*)path))
|
||||
goto error;
|
||||
DBUG_ASSERT(next_entry == part_info->list->entry_pos);
|
||||
if (ddl_log_write_execute_entry(part_info->list->entry_pos,
|
||||
@ -7162,6 +7206,64 @@ bool log_partition_alter_to_ddl_log(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||
}
|
||||
|
||||
|
||||
extern bool alter_partition_convert_in(ALTER_PARTITION_PARAM_TYPE *lpt);
|
||||
|
||||
/**
|
||||
Check that definition of source table fits definition of partition being
|
||||
added and every row stored in the table conforms partition's expression.
|
||||
|
||||
@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(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||
{
|
||||
/*
|
||||
TODO: if destination is partitioned by range(X) and source is indexed by X
|
||||
then just get min(X) and max(X) from index.
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Actually perform the change requested by ALTER TABLE of partitions
|
||||
@ -7390,7 +7492,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
||||
ERROR_INJECT("convert_partition_3") ||
|
||||
wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
|
||||
ERROR_INJECT("convert_partition_4") ||
|
||||
write_log_convert_out_partition(lpt) ||
|
||||
write_log_convert_partition(lpt) ||
|
||||
ERROR_INJECT("convert_partition_5") ||
|
||||
alter_close_table(lpt) ||
|
||||
ERROR_INJECT("convert_partition_6") ||
|
||||
@ -7419,10 +7521,60 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
||||
ERROR_INJECT("convert_partition_11"))
|
||||
goto err;
|
||||
}
|
||||
else if ((alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN))
|
||||
{
|
||||
DDL_LOG_STATE chain_drop_backup;
|
||||
bzero(&chain_drop_backup, sizeof(chain_drop_backup));
|
||||
TABLE *table_from= table_list->next_local->table;
|
||||
|
||||
if (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("convert_partition_1") ||
|
||||
compare_table_with_partition(thd, table_from, table, NULL, 0) ||
|
||||
ERROR_INJECT("convert_partition_2") ||
|
||||
check_table_data(lpt))
|
||||
goto err;
|
||||
|
||||
if (write_log_drop_shadow_frm(lpt) ||
|
||||
ERROR_INJECT("convert_partition_3") ||
|
||||
mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
|
||||
ERROR_INJECT("convert_partition_4") ||
|
||||
alter_close_table(lpt) ||
|
||||
ERROR_INJECT("convert_partition_5") ||
|
||||
write_log_convert_partition(lpt) ||
|
||||
ERROR_INJECT("convert_partition_6") ||
|
||||
alter_partition_convert_in(lpt) ||
|
||||
ERROR_INJECT("convert_partition_7") ||
|
||||
(frm_install= true, false) ||
|
||||
write_log_drop_frm(lpt, &chain_drop_backup) ||
|
||||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) ||
|
||||
log_partition_alter_to_ddl_log(lpt) ||
|
||||
(frm_install= false, false) ||
|
||||
ERROR_INJECT("convert_partition_8") ||
|
||||
((!thd->lex->no_write_to_binlog) &&
|
||||
((thd->binlog_xid= thd->query_id),
|
||||
ddl_log_update_xid(lpt->part_info, thd->binlog_xid),
|
||||
write_bin_log(thd, false, thd->query(), thd->query_length()),
|
||||
(thd->binlog_xid= 0))) ||
|
||||
ERROR_INJECT("convert_partition_9"))
|
||||
{
|
||||
ddl_log_complete(&chain_drop_backup);
|
||||
(void) ddl_log_revert(thd, lpt->part_info);
|
||||
handle_alter_part_error(lpt, true, true, false);
|
||||
goto err;
|
||||
}
|
||||
ddl_log_complete(lpt->part_info);
|
||||
ERROR_INJECT("convert_partition_10");
|
||||
(void) ddl_log_revert(thd, &chain_drop_backup);
|
||||
if (alter_partition_lock_handling(lpt) ||
|
||||
ERROR_INJECT("convert_partition_11"))
|
||||
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_CONVERT_IN));
|
||||
/*
|
||||
ADD RANGE/LIST PARTITIONS
|
||||
In this case there are no tuples removed and no tuples are added.
|
||||
|
Reference in New Issue
Block a user