Commit 4ec088f2 authored by Oleksandr Byelkin's avatar Oleksandr Byelkin

MDEV-8348: Add catchall to all table partitioning for list partitions

DEFAULT partition support added to LIST and LIST COLUMN partitioning.
Partitions Prunning added for DEFAULT partititon.
parent 95b8dcbd
This diff is collapsed.
This diff is collapsed.
......@@ -65,7 +65,7 @@ typedef struct p_column_list_val
Item* item_expression;
partition_info *part_info;
uint partition_id;
bool max_value;
bool max_value; // MAXVALUE for RANGE type or DEFAULT value for LIST type
bool null_value;
char fixed;
} part_column_list_val;
......
......@@ -1472,6 +1472,8 @@ bool partition_info::check_list_constants(THD *thd)
List_iterator<partition_element> list_func_it(partitions);
DBUG_ENTER("partition_info::check_list_constants");
DBUG_ASSERT(part_type == LIST_PARTITION);
num_list_values= 0;
/*
We begin by calculating the number of list values that have been
......@@ -1503,21 +1505,15 @@ bool partition_info::check_list_constants(THD *thd)
has_null_part_id= i;
found_null= TRUE;
}
List_iterator<part_elem_value> list_val_it1(part_def->list_val_list);
while (list_val_it1++)
num_list_values++;
num_list_values+= part_def->list_val_list.elements;
} while (++i < num_parts);
list_func_it.rewind();
num_column_values= part_field_list.elements;
size_entries= column_list ?
(num_column_values * sizeof(part_column_list_val)) :
sizeof(LIST_PART_ENTRY);
ptr= thd->calloc((num_list_values+1) * size_entries);
if (unlikely(ptr == NULL))
{
mem_alloc_error(num_list_values * size_entries);
if (unlikely(!(ptr= thd->calloc((num_list_values+1) * size_entries))))
goto end;
}
if (column_list)
{
part_column_list_val *loc_list_col_array;
......@@ -1528,6 +1524,13 @@ bool partition_info::check_list_constants(THD *thd)
do
{
part_def= list_func_it++;
if (part_def->max_value)
{
// DEFAULT is not a real value so let's exclude it from sorting.
DBUG_ASSERT(num_list_values);
num_list_values--;
continue;
}
List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
while ((list_value= list_val_it2++))
{
......@@ -1557,6 +1560,13 @@ bool partition_info::check_list_constants(THD *thd)
do
{
part_def= list_func_it++;
if (part_def->max_value && part_type == LIST_PARTITION)
{
// DEFAULT is not a real value so let's exclude it from sorting.
DBUG_ASSERT(num_list_values);
num_list_values--;
continue;
}
List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
while ((list_value= list_val_it2++))
{
......@@ -2287,11 +2297,19 @@ int partition_info::add_max_value(THD *thd)
DBUG_ENTER("partition_info::add_max_value");
part_column_list_val *col_val;
if (!(col_val= add_column_value(thd)))
/*
Makes for LIST COLUMNS 'num_columns' DEFAULT tuples, 1 tuple for RANGEs
*/
uint max_val= (num_columns && part_type == LIST_PARTITION) ?
num_columns : 1;
for (uint i= 0; i < max_val; i++)
{
DBUG_RETURN(TRUE);
if (!(col_val= add_column_value(thd)))
{
DBUG_RETURN(TRUE);
}
col_val->max_value= TRUE;
}
col_val->max_value= TRUE;
DBUG_RETURN(FALSE);
}
......@@ -2566,8 +2584,7 @@ int partition_info::reorganize_into_single_field_col_val(THD *thd)
*/
int partition_info::fix_partition_values(THD *thd,
part_elem_value *val,
partition_element *part_elem,
uint part_id)
partition_element *part_elem)
{
part_column_list_val *col_val= val->col_val_array;
DBUG_ENTER("partition_info::fix_partition_values");
......@@ -2576,59 +2593,31 @@ int partition_info::fix_partition_values(THD *thd,
{
DBUG_RETURN(FALSE);
}
if (val->added_items != 1)
{
my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
if (col_val->max_value)
Item *item_expr= col_val->item_expression;
if ((val->null_value= item_expr->null_value))
{
/* The parser ensures we're not LIST partitioned here */
DBUG_ASSERT(part_type == RANGE_PARTITION);
if (defined_max_value)
{
my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
if (part_id == (num_parts - 1))
if (part_elem->has_null_value)
{
defined_max_value= TRUE;
part_elem->max_value= TRUE;
part_elem->range_value= LONGLONG_MAX;
}
else
{
my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
part_elem->has_null_value= TRUE;
}
else
else if (item_expr->result_type() != INT_RESULT)
{
Item *item_expr= col_val->item_expression;
if ((val->null_value= item_expr->null_value))
{
if (part_elem->has_null_value)
{
my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
part_elem->has_null_value= TRUE;
}
else if (item_expr->result_type() != INT_RESULT)
my_error(ER_VALUES_IS_NOT_INT_TYPE_ERROR, MYF(0),
part_elem->partition_name);
DBUG_RETURN(TRUE);
}
if (part_type == RANGE_PARTITION)
{
if (part_elem->has_null_value)
{
my_error(ER_VALUES_IS_NOT_INT_TYPE_ERROR, MYF(0),
part_elem->partition_name);
my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0));
DBUG_RETURN(TRUE);
}
if (part_type == RANGE_PARTITION)
{
if (part_elem->has_null_value)
{
my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0));
DBUG_RETURN(TRUE);
}
part_elem->range_value= val->value;
}
part_elem->range_value= val->value;
}
col_val->fixed= 2;
DBUG_RETURN(FALSE);
......@@ -2828,6 +2817,7 @@ bool partition_info::fix_parser_data(THD *thd)
key_algorithm == KEY_ALGORITHM_NONE)
key_algorithm= KEY_ALGORITHM_55;
}
defined_max_value= FALSE; // in case it already set (CREATE TABLE LIKE)
do
{
part_elem= it++;
......@@ -2835,16 +2825,60 @@ bool partition_info::fix_parser_data(THD *thd)
num_elements= part_elem->list_val_list.elements;
DBUG_ASSERT(part_type == RANGE_PARTITION ?
num_elements == 1U : TRUE);
for (j= 0; j < num_elements; j++)
{
part_elem_value *val= list_val_it++;
if (column_list)
if (val->added_items != (column_list ? num_columns : 1))
{
my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
/*
Check the last MAX_VALUE for range partitions and DEFAULT value
for LIST partitions.
Both values are marked with defined_max_value and
default_partition_id.
This is a max_value/default is max_value is set and this is
a normal RANGE (no column list) or if it's a LIST partition:
PARTITION p3 VALUES LESS THAN MAXVALUE
or
PARTITION p3 VALUES DEFAULT
*/
if (val->added_items && val->col_val_array[0].max_value &&
(!column_list || part_type == LIST_PARTITION))
{
if (val->added_items != num_columns)
DBUG_ASSERT(part_type == RANGE_PARTITION ||
part_type == LIST_PARTITION);
if (defined_max_value)
{
my_error((part_type == RANGE_PARTITION) ?
ER_PARTITION_MAXVALUE_ERROR :
ER_PARTITION_DEFAULT_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
/* For RANGE PARTITION MAX_VALUE must be last */
if (i != (num_parts - 1) &&
part_type != LIST_PARTITION)
{
my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
defined_max_value= TRUE;
default_partition_id= i;
part_elem->max_value= TRUE;
part_elem->range_value= LONGLONG_MAX;
continue;
}
if (column_list)
{
for (k= 0; k < num_columns; k++)
{
part_column_list_val *col_val= &val->col_val_array[k];
......@@ -2857,10 +2891,8 @@ bool partition_info::fix_parser_data(THD *thd)
}
else
{
if (fix_partition_values(thd, val, part_elem, i))
{
if (fix_partition_values(thd, val, part_elem))
DBUG_RETURN(TRUE);
}
if (val->null_value)
{
/*
......
......@@ -202,6 +202,7 @@ class partition_info : public Sql_alloc
uint num_full_part_fields;
uint has_null_part_id;
uint32 default_partition_id;
/*
This variable is used to calculate the partition id when using
LINEAR KEY/HASH. This functionality is kept in the MySQL Server
......@@ -230,6 +231,10 @@ class partition_info : public Sql_alloc
bool use_default_num_subpartitions;
bool default_partitions_setup;
bool defined_max_value;
inline bool has_default_partititon()
{
return (part_type == LIST_PARTITION && defined_max_value);
}
bool list_of_part_fields; // KEY or COLUMNS PARTITIONING
bool list_of_subpart_fields; // KEY SUBPARTITIONING
bool linear_hash_ind; // LINEAR HASH/KEY
......@@ -323,8 +328,7 @@ class partition_info : public Sql_alloc
Item* get_column_item(Item *item, Field *field);
int fix_partition_values(THD *thd,
part_elem_value *val,
partition_element *part_elem,
uint part_id);
partition_element *part_elem);
bool fix_column_value_functions(THD *thd,
part_elem_value *val,
uint part_id);
......@@ -399,6 +403,7 @@ static inline void init_single_partition_iterator(uint32 part_id,
part_iter->part_nums.start= part_iter->part_nums.cur= part_id;
part_iter->part_nums.end= part_id+1;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
part_iter->get_next= get_next_partition_id_range;
}
......@@ -410,6 +415,7 @@ void init_all_partitions_iterator(partition_info *part_info,
part_iter->part_nums.start= part_iter->part_nums.cur= 0;
part_iter->part_nums.end= part_info->num_parts;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
part_iter->get_next= get_next_partition_id_range;
}
......
......@@ -7219,3 +7219,6 @@ ER_CALCULATING_DEFAULT_VALUE
eng "Got an error when calculating default value for %`s"
ER_EXPRESSION_REFERS_TO_UNINIT_FIELD 01000
eng "Expression for field %`-.64s is refering to uninitialized field %`s"
ER_PARTITION_DEFAULT_ERROR
eng "Only one DEFAULT partition allowed"
ukr "Припустимо мати тільки один DEFAULT розділ"
......@@ -2311,6 +2311,15 @@ static int add_partition_values(File fptr, partition_info *part_info,
{
uint i;
List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
if (p_elem->max_value)
{
DBUG_ASSERT(part_info->defined_max_value ||
current_thd->lex->sql_command == SQLCOM_ALTER_TABLE);
err+= add_string(fptr, " DEFAULT");
return err;
}
err+= add_string(fptr, " VALUES IN ");
uint num_items= p_elem->list_val_list.elements;
......@@ -3070,6 +3079,11 @@ int get_partition_id_list_col(partition_info *part_info,
}
}
notfound:
if (part_info->defined_max_value)
{
*part_id= part_info->default_partition_id;
DBUG_RETURN(0);
}
*part_id= 0;
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
}
......@@ -3123,6 +3137,11 @@ int get_partition_id_list(partition_info *part_info,
}
}
notfound:
if (part_info->defined_max_value)
{
*part_id= part_info->default_partition_id;
DBUG_RETURN(0);
}
*part_id= 0;
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
}
......@@ -4691,9 +4710,26 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
DBUG_RETURN(TRUE);
}
thd->work_part_info= thd->lex->part_info;
if (thd->work_part_info &&
partition_info *alt_part_info= thd->lex->part_info;
/*
This variable is TRUE in very special case when we add only DEFAULT
partition to the existing table
*/
bool only_default_value_added=
(alt_part_info &&
alt_part_info->current_partition &&
alt_part_info->current_partition->list_val_list.elements == 1 &&
alt_part_info->current_partition->list_val_list.head()->
added_items >= 1 &&
alt_part_info->current_partition->list_val_list.head()->
col_val_array[0].max_value) &&
alt_part_info->part_type == LIST_PARTITION &&
(alter_info->flags & Alter_info::ALTER_ADD_PARTITION);
if (only_default_value_added &&
!thd->lex->part_info->num_columns)
thd->lex->part_info->num_columns= 1; // to make correct clone
if ((thd->work_part_info= thd->lex->part_info) &&
!(thd->work_part_info= thd->lex->part_info->get_clone(thd)))
DBUG_RETURN(TRUE);
......@@ -4709,12 +4745,12 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
Alter_info::ALTER_REBUILD_PARTITION))
{
partition_info *tab_part_info;
partition_info *alt_part_info= thd->work_part_info;
uint flags= 0;
bool is_last_partition_reorged= FALSE;
part_elem_value *tab_max_elem_val= NULL;
part_elem_value *alt_max_elem_val= NULL;
longlong tab_max_range= 0, alt_max_range= 0;
alt_part_info= thd->work_part_info;
if (!table->part_info)
{
......@@ -4805,7 +4841,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
goto err;
}
if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0)
if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0 &&
!tab_part_info->has_default_partititon())
{
/*
"Fast" change of partitioning is supported in this case.
......@@ -4879,14 +4916,16 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
}
}
if ((tab_part_info->column_list &&
alt_part_info->num_columns != tab_part_info->num_columns) ||
alt_part_info->num_columns != tab_part_info->num_columns &&
!only_default_value_added) ||
(!tab_part_info->column_list &&
(tab_part_info->part_type == RANGE_PARTITION ||
tab_part_info->part_type == LIST_PARTITION) &&
alt_part_info->num_columns != 1U) ||
alt_part_info->num_columns != 1U &&
!only_default_value_added) ||
(!tab_part_info->column_list &&
tab_part_info->part_type == HASH_PARTITION &&
alt_part_info->num_columns != 0))
(alt_part_info->num_columns != 0)))
{
my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
goto err;
......@@ -4919,9 +4958,13 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_NO_BINLOG_ERROR, MYF(0));
goto err;
}
if (tab_part_info->defined_max_value)
if (tab_part_info->defined_max_value &&
(tab_part_info->part_type == RANGE_PARTITION ||
alt_part_info->defined_max_value))
{
my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
my_error((tab_part_info->part_type == RANGE_PARTITION ?
ER_PARTITION_MAXVALUE_ERROR :
ER_PARTITION_DEFAULT_ERROR), MYF(0));
goto err;
}
if (num_new_partitions == 0)
......@@ -7677,6 +7720,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
uint flags,
PARTITION_ITERATOR *part_iter)
{
bool can_match_multiple_values;
uint32 nparts;
get_col_endpoint_func UNINIT_VAR(get_col_endpoint);
DBUG_ENTER("get_part_iter_for_interval_cols_via_map");
......@@ -7696,6 +7740,14 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
else
assert(0);
can_match_multiple_values= ((flags &
(NO_MIN_RANGE | NO_MAX_RANGE | NEAR_MIN |
NEAR_MAX)) ||
memcmp(min_value, max_value, min_len));
DBUG_ASSERT(can_match_multiple_values || (flags & EQ_RANGE) || flags == 0);
if (can_match_multiple_values && part_info->has_default_partititon())
part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
if (flags & NO_MIN_RANGE)
part_iter->part_nums.start= part_iter->part_nums.cur= 0;
else
......@@ -7731,7 +7783,15 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
nparts);
}
if (part_iter->part_nums.start == part_iter->part_nums.end)
{
// No matching partition found.
if (part_info->has_default_partititon())
{
part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
DBUG_RETURN(1);
}
DBUG_RETURN(0);
}
DBUG_RETURN(1);
}
......@@ -7792,6 +7852,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
(void)min_len;
(void)max_len;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
if (part_info->part_type == RANGE_PARTITION)
{
......@@ -7828,8 +7889,13 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
else
MY_ASSERT_UNREACHABLE();
can_match_multiple_values= (flags || !min_value || !max_value ||
can_match_multiple_values= ((flags &
(NO_MIN_RANGE | NO_MAX_RANGE | NEAR_MIN |
NEAR_MAX)) ||
memcmp(min_value, max_value, field_len));
DBUG_ASSERT(can_match_multiple_values || (flags & EQ_RANGE) || flags == 0);
if (can_match_multiple_values && part_info->has_default_partititon())
part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
if (can_match_multiple_values &&
(part_info->part_type == RANGE_PARTITION ||
part_info->has_null_value))
......@@ -7859,6 +7925,12 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
{
/* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
part_iter->part_nums.end= 0;
/*
It is something like select * from tbl where col IS NULL
and we have partition with NULL to catch it, so we do not need
DEFAULT partition
*/
part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
DBUG_RETURN(1);
}
}
......@@ -7900,7 +7972,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
}
}
if (part_iter->part_nums.start == max_endpoint_val)
DBUG_RETURN(0); /* No partitions */
goto not_found;
}
}
......@@ -7937,9 +8009,17 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
}
if (part_iter->part_nums.start >= part_iter->part_nums.end &&
!part_iter->ret_null_part)
DBUG_RETURN(0); /* No partitions */
goto not_found;
}
DBUG_RETURN(1); /* Ok, iterator initialized */
not_found:
if (part_info->has_default_partititon())
{
part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
DBUG_RETURN(1);
}
DBUG_RETURN(0); /* No partitions */
}
......@@ -8003,6 +8083,8 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
(void)max_len;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
if (is_subpart)
{
field= part_info->subpart_field_array[0];
......@@ -8134,6 +8216,9 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
part_iter->ret_null_part= FALSE;
return 0; /* NULL always in first range partition */
}
// we do not have default partition in RANGE partitioning
DBUG_ASSERT(!part_iter->ret_default_part);
part_iter->part_nums.cur= part_iter->part_nums.start;
part_iter->ret_null_part= part_iter->ret_null_part_orig;
return NOT_A_PARTITION_ID;
......@@ -8171,8 +8256,15 @@ uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
part_iter->ret_null_part= FALSE;
return part_iter->part_info->has_null_part_id;
}
if (part_iter->ret_default_part)
{
part_iter->ret_default_part= FALSE;
return part_iter->part_info->default_partition_id;
}
/* Reset partition for next read */
part_iter->part_nums.cur= part_iter->part_nums.start;
part_iter->ret_null_part= part_iter->ret_null_part_orig;
part_iter->ret_default_part= part_iter->ret_default_part_orig;
return NOT_A_PARTITION_ID;
}
else
......
......@@ -177,6 +177,10 @@ typedef struct st_partition_iter
iterator also produce id of the partition that contains NULL value.
*/
bool ret_null_part, ret_null_part_orig;
/*
We should return DEFAULT partition.
*/
bool ret_default_part, ret_default_part_orig;
struct st_part_num_range
{
uint32 start;
......
......@@ -5217,6 +5217,27 @@ opt_part_values:
part_info->part_type= LIST_PARTITION;
}
part_values_in {}
| DEFAULT
{
LEX *lex= Lex;
partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
if (part_info->part_type != LIST_PARTITION)
my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
"LIST", "DEFAULT"));
}
else
part_info->part_type= LIST_PARTITION;
if (part_info->init_column_part(thd))
{
MYSQL_YYABORT;
}
if (part_info->add_max_value(thd))
{
MYSQL_YYABORT;
}
}
;
part_func_max:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment