Commit 23c5840d authored by Mattias Jonsson's avatar Mattias Jonsson

Bug#16447483: PARTITION PRUNING IS NOT CORRECT FOR RANGE COLUMNS

The problem was in get_partition_id_cols_range_for_endpoint
and cmp_rec_and_tuple_prune, which stepped one partition too long.

Solution was to move a small portion of logic to cmp_rec_and_tuple_prune,
to simplify both get_partition_id_cols_range_for_endpoint and
get_partition_id_cols_list_for_endpoint.
parent 2ee012b2
......@@ -173,7 +173,8 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec);
static int cmp_rec_and_tuple_prune(part_column_list_val *val,
uint32 n_vals_in_rec,
bool tail_is_min);
bool is_left_endpoint,
bool include_endpoint);
/*
Convert constants in VALUES definition to the character set the
......@@ -3293,17 +3294,63 @@ notfound:
}
/*
Find the sub-array part_info->list_array that corresponds to given interval
uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info,
bool left_endpoint,
bool include_endpoint,
uint32 nparts)
{
part_column_list_val *list_col_array= part_info->list_col_array;
uint num_columns= part_info->part_field_list.elements;
uint list_index;
uint min_list_index= 0;
uint max_list_index= part_info->num_list_values;
DBUG_ENTER("get_partition_id_cols_list_for_endpoint");
SYNOPSIS
get_list_array_idx_for_endpoint()
part_info Partitioning info (partitioning type must be LIST)
left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
/* Find the matching partition (including taking endpoint into account). */
do
{
/* Midpoint, adjusted down, so it can never be > last index. */
list_index= (max_list_index + min_list_index) >> 1;
if (cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns,
nparts, left_endpoint, include_endpoint) > 0)
min_list_index= list_index + 1;
else
max_list_index= list_index;
} while (max_list_index > min_list_index);
list_index= max_list_index;
/* Given value must be LESS THAN or EQUAL to the found partition. */
DBUG_ASSERT(list_index == part_info->num_list_values ||
(0 >= cmp_rec_and_tuple_prune(list_col_array +
list_index*num_columns,
nparts, left_endpoint,
include_endpoint)));
/* Given value must be GREATER THAN the previous partition. */
DBUG_ASSERT(list_index == 0 ||
(0 < cmp_rec_and_tuple_prune(list_col_array +
(list_index - 1)*num_columns,
nparts, left_endpoint,
include_endpoint)));
if (!left_endpoint)
{
/* Set the end after this list tuple if not already after the last. */
if (list_index < part_info->num_parts)
list_index++;
}
DBUG_RETURN(list_index);
}
/**
Find the sub-array part_info->list_array that corresponds to given interval.
@param part_info Partitioning info (partitioning type must be LIST)
@param left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
FALSE - the interval is (-inf; a] or (-inf; a)
include_endpoint TRUE iff the interval includes the endpoint
@param include_endpoint TRUE iff the interval includes the endpoint
DESCRIPTION
This function finds the sub-array of part_info->list_array where values of
list_array[idx].list_value are contained within the specifed interval.
list_array is ordered by list_value, so
......@@ -3320,55 +3367,15 @@ notfound:
If all array elements are contained, part_info->num_list_values is
returned.
NOTE
The caller will call this function and then will run along the sub-array of
list_array to collect partition ids. If the number of list values is
significantly higher then number of partitions, this could be slow and
we could invent some other approach. The "run over list array" part is
@note The caller will call this function and then will run along the
sub-array of list_array to collect partition ids. If the number of list
values is significantly higher then number of partitions, this could be slow
and we could invent some other approach. The "run over list array" part is
already wrapped in a get_next()-like function.
RETURN
The edge of corresponding sub-array of part_info->list_array
@return The index of corresponding sub-array of part_info->list_array.
*/
uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info,
bool left_endpoint,
bool include_endpoint,
uint32 nparts)
{
part_column_list_val *list_col_array= part_info->list_col_array;
uint num_columns= part_info->part_field_list.elements;
int list_index, cmp;
uint min_list_index= 0;
uint max_list_index= part_info->num_list_values - 1;
bool tailf= !(left_endpoint ^ include_endpoint);
DBUG_ENTER("get_partition_id_cols_list_for_endpoint");
do
{
list_index= (max_list_index + min_list_index) >> 1;
cmp= cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns,
nparts, tailf);
if (cmp > 0)
min_list_index= list_index + 1;
else if (cmp < 0)
{
if (!list_index)
goto notfound;
max_list_index= list_index - 1;
}
else
{
DBUG_RETURN(list_index + test(!tailf));
}
} while (max_list_index >= min_list_index);
if (cmp > 0)
list_index++;
notfound:
DBUG_RETURN(list_index);
}
uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info,
bool left_endpoint,
bool include_endpoint)
......@@ -7414,15 +7421,17 @@ uint32 store_tuple_to_record(Field **pfield,
return nparts;
}
/*
RANGE(columns) partitioning: compare value bound and probe tuple.
/**
RANGE(columns) partitioning: compare partition value bound and probe tuple.
@param val Partition column values.
@param nvals_in_rec Number of (prefix) fields to compare.
The value bound always is a full tuple (but may include the MAXVALUE
special value).
@return Less than/Equal to/Greater than 0 if the record is L/E/G than val.
The probe tuple may be a prefix of partitioning tuple. The tail_is_min
parameter specifies whether the suffix components should be assumed to
hold MAXVALUE
@note The partition value bound is always a full tuple (but may include the
MAXVALUE special value). The probe tuple may be a prefix of partitioning
tuple.
*/
static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec)
......@@ -7452,25 +7461,73 @@ static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec)
}
/**
Compare record and columns partition tuple including endpoint handling.
@param val Columns partition tuple
@param n_vals_in_rec Number of columns to compare
@param is_left_endpoint True if left endpoint (part_tuple < rec or
part_tuple <= rec)
@param include_endpoint If endpoint is included (part_tuple <= rec or
rec <= part_tuple)
@return Less than/Equal to/Greater than 0 if the record is L/E/G than
the partition tuple.
@see get_list_array_idx_for_endpoint() and
get_partition_id_range_for_endpoint().
*/
static int cmp_rec_and_tuple_prune(part_column_list_val *val,
uint32 n_vals_in_rec,
bool tail_is_min)
bool is_left_endpoint,
bool include_endpoint)
{
int cmp;
Field **field;
partition_info *part_info;
if ((cmp= cmp_rec_and_tuple(val, n_vals_in_rec)))
return cmp;
part_info= val->part_info;
field= part_info->part_field_array + n_vals_in_rec;
for (; *field; field++, val++)
field= val->part_info->part_field_array + n_vals_in_rec;
if (!(*field))
{
if (tail_is_min)
return -1;
if (!tail_is_min && !val->max_value)
return +1;
}
/*
Full match, if right endpoint and not including the endpoint,
(rec < part) return lesser.
*/
if (!is_left_endpoint && !include_endpoint)
return -4;
/* Otherwise they are equal! */
return 0;
}
/*
The prefix is equal and there are more partition columns to compare.
If including left endpoint or not including right endpoint
then the record is considered lesser compared to the partition.
i.e:
part(10, x) <= rec(10, unknown) and rec(10, unknown) < part(10, x)
part <= rec -> lesser (i.e. this or previous partitions)
rec < part -> lesser (i.e. this or previous partitions)
*/
if (is_left_endpoint == include_endpoint)
return -2;
/*
If right endpoint and the first additional partition value
is MAXVALUE, then the record is lesser.
*/
if (!is_left_endpoint && (val + n_vals_in_rec)->max_value)
return -3;
/*
Otherwise the record is considered greater.
rec <= part -> greater (i.e. does not match this partition, seek higher).
part < rec -> greater (i.e. does not match this partition, seek higher).
*/
return 2;
}
......@@ -7481,89 +7538,63 @@ typedef uint32 (*get_col_endpoint_func)(partition_info*, bool left_endpoint,
bool include_endpoint,
uint32 num_parts);
/*
Partitioning Interval Analysis: Initialize the iterator for "mapping" case
SYNOPSIS
get_part_iter_for_interval_via_mapping()
part_info Partition info
is_subpart TRUE - act for subpartitioning
FALSE - act for partitioning
min_value minimum field value, in opt_range key format.
max_value minimum field value, in opt_range key format.
flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
NO_MAX_RANGE.
part_iter Iterator structure to be initialized
DESCRIPTION
Initialize partition set iterator to walk over the interval in
ordered-array-of-partitions (for RANGE partitioning) or
ordered-array-of-list-constants (for LIST partitioning) space.
/**
Get partition for RANGE COLUMNS endpoint.
IMPLEMENTATION
This function is used when partitioning is done by
<RANGE|LIST>(ascending_func(t.field)), and we can map an interval in
t.field space into a sub-array of partition_info::range_int_array or
partition_info::list_array (see get_partition_id_range_for_endpoint,
get_list_array_idx_for_endpoint for details).
@param part_info Partitioning metadata.
@param is_left_endpoint True if left endpoint (const <=/< cols)
@param include_endpoint True if range includes the endpoint (<=/>=)
@param nparts Total number of partitions
The function performs this interval mapping, and sets the iterator to
traverse the sub-array and return appropriate partitions.
@return Partition id of matching partition.
RETURN
0 - No matching partitions (iterator not initialized)
1 - Ok, iterator intialized for traversal of matching partitions.
-1 - All partitions would match (iterator not initialized)
@see get_partition_id_cols_list_for_endpoint and
get_partition_id_range_for_endpoint.
*/
uint32 get_partition_id_cols_range_for_endpoint(partition_info *part_info,
bool left_endpoint,
bool is_left_endpoint,
bool include_endpoint,
uint32 nparts)
{
uint max_partition= part_info->num_parts - 1;
uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
uint min_part_id= 0, max_part_id= part_info->num_parts, loc_part_id;
part_column_list_val *range_col_array= part_info->range_col_array;
uint num_columns= part_info->part_field_list.elements;
bool tailf= !(left_endpoint ^ include_endpoint);
DBUG_ENTER("get_partition_id_cols_range_for_endpoint");
/* Get the partitioning function value for the endpoint */
while (max_part_id > min_part_id)
/* Find the matching partition (including taking endpoint into account). */
do
{
loc_part_id= (max_part_id + min_part_id + 1) >> 1;
if (cmp_rec_and_tuple_prune(range_col_array + loc_part_id*num_columns,
nparts, tailf) >= 0)
/* Midpoint, adjusted down, so it can never be > last partition. */
loc_part_id= (max_part_id + min_part_id) >> 1;
if (0 <= cmp_rec_and_tuple_prune(range_col_array +
loc_part_id * num_columns,
nparts,
is_left_endpoint,
include_endpoint))
min_part_id= loc_part_id + 1;
else
max_part_id= loc_part_id - 1;
}
max_part_id= loc_part_id;
} while (max_part_id > min_part_id);
loc_part_id= max_part_id;
if (loc_part_id < max_partition &&
cmp_rec_and_tuple_prune(range_col_array + (loc_part_id+1)*num_columns,
nparts, tailf) >= 0
)
{
loc_part_id++;
}
if (left_endpoint)
{
if (cmp_rec_and_tuple_prune(range_col_array + loc_part_id*num_columns,
nparts, tailf) >= 0)
loc_part_id++;
}
else
{
if (loc_part_id < max_partition)
{
int res= cmp_rec_and_tuple_prune(range_col_array +
/* Given value must be LESS THAN the found partition. */
DBUG_ASSERT(loc_part_id == part_info->num_parts ||
(0 > cmp_rec_and_tuple_prune(range_col_array +
loc_part_id * num_columns,
nparts, tailf);
if (!res)
loc_part_id += test(include_endpoint);
else if (res > 0)
loc_part_id++;
}
nparts, is_left_endpoint,
include_endpoint)));
/* Given value must be GREATER THAN or EQUAL to the previous partition. */
DBUG_ASSERT(loc_part_id == 0 ||
(0 <= cmp_rec_and_tuple_prune(range_col_array +
(loc_part_id - 1) * num_columns,
nparts, is_left_endpoint,
include_endpoint)));
if (!is_left_endpoint)
{
/* Set the end after this partition if not already after the last. */
if (loc_part_id < part_info->num_parts)
loc_part_id++;
}
DBUG_RETURN(loc_part_id);
......@@ -7637,6 +7668,40 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
}
/**
Partitioning Interval Analysis: Initialize the iterator for "mapping" case
@param part_info Partition info
@param is_subpart TRUE - act for subpartitioning
FALSE - act for partitioning
@param store_length_array Ignored.
@param min_value minimum field value, in opt_range key format.
@param max_value minimum field value, in opt_range key format.
@param min_len Ignored.
@param max_len Ignored.
@param flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
NO_MAX_RANGE.
@param part_iter Iterator structure to be initialized
@details Initialize partition set iterator to walk over the interval in
ordered-array-of-partitions (for RANGE partitioning) or
ordered-array-of-list-constants (for LIST partitioning) space.
This function is used when partitioning is done by
<RANGE|LIST>(ascending_func(t.field)), and we can map an interval in
t.field space into a sub-array of partition_info::range_int_array or
partition_info::list_array (see get_partition_id_range_for_endpoint,
get_list_array_idx_for_endpoint for details).
The function performs this interval mapping, and sets the iterator to
traverse the sub-array and return appropriate partitions.
@return Status of iterator
@retval 0 No matching partitions (iterator not initialized)
@retval 1 Ok, iterator intialized for traversal of matching partitions.
@retval -1 All partitions would match (iterator not initialized)
*/
int get_part_iter_for_interval_via_mapping(partition_info *part_info,
bool is_subpart,
uint32 *store_length_array, /* ignored */
......
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