1
0
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:
Aleksey Midenkov
2021-09-28 12:40:06 +03:00
committed by Sergei Golubchik
parent 7da721be31
commit 69724805bc
17 changed files with 2410 additions and 325 deletions

View File

@ -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.