Commit dc2a6e22 authored by unknown's avatar unknown
Browse files

WL#2985 "Partition Pruning":

- post-...-post review fixes
- Added "integer range walking" that allows to do partition pruning for "a <=? t.field <=? b"
  by finding used partitions for a, a+1, a+2, ..., b-1, b. 


mysql-test/r/partition_pruning.result:
  WL#2985 "Partition Pruning": tests for "integer range walking"
mysql-test/t/partition.test:
  WL#2985 "Partition Pruning": post-review fixes
mysql-test/t/partition_pruning.test:
  WL#2985 "Partition Pruning": tests for "integer range walking"
sql/handler.h:
  WL#2985 "Partition Pruning": "integer range walking": 
  - class partition_info now has pointers to "partitioning interval analysis" functions
  - added "partition set iterator" definitions.
sql/opt_range.cc:
  WL#2985 "Partition Pruning": "integer range walking":
  - Switched to use "partitioning interval analysis" functions
  - Fixed two problems in find_used_partitions() that occur on complicated WHERE clauses.
sql/sql_partition.cc:
  WL#2985 "Partition Pruning": "integer range walking": 
  - Added "partitioning interval analysis" functions: get_part_iter_for_interval_via_mapping, 
    get_part_iter_for_interval_via_walking, 
  - Added appropriate partition-set-iterator implementations
  - Added a function to set up Partitioning Interval Analysis-related fields in partition_info.
sql/sql_select.cc:
  WL#2985 "Partition pruning": added comments.
parent 78d1abba
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -274,3 +274,33 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
1	SIMPLE	X	p1,p2	ALL	a	NULL	NULL	NULL	4	Using where
1	SIMPLE	Y	p1,p2	ref	a	a	4	test.X.a	2	
drop table t1;
create table t1 (a int) partition by hash(a) partitions 20;
insert into t1 values (1),(2),(3);
explain partitions select * from t1 where a >  1 and a < 3;
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	p2	ALL	NULL	NULL	NULL	NULL	3	Using where
explain partitions select * from t1 where a >= 1 and a < 3;
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	p1,p2	ALL	NULL	NULL	NULL	NULL	3	Using where
explain partitions select * from t1 where a >  1 and a <= 3;
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	p2,p3	ALL	NULL	NULL	NULL	NULL	3	Using where
explain partitions select * from t1 where a >= 1 and a <= 3;
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	p1,p2,p3	ALL	NULL	NULL	NULL	NULL	3	Using where
drop table t1;
create table t1 (a int, b int) 
partition by list(a) subpartition by hash(b) subpartitions 20 
(
partition p0 values in (0),
partition p1 values in (1),
partition p2 values in (2),
partition p3 values in (3)
);
insert into t1 values (1,1),(2,2),(3,3);
explain partitions select * from t1 where b >  1 and b < 3;
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	p0_sp2,p1_sp2,p2_sp2,p3_sp2	ALL	NULL	NULL	NULL	NULL	3	Using where
explain partitions select * from t1 where b >  1 and b < 3 and (a =1 or a =2);
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	p1_sp2,p2_sp2	ALL	NULL	NULL	NULL	NULL	3	Using where
+1 −1
Original line number Diff line number Diff line
@@ -209,7 +209,7 @@ create table t1 (a int not null, b int not null) partition by LIST (a+b) (
  partition p0 values in (12),
  partition p1 values in (14)
);
--error 1500
--error ER_NO_PARTITION_FOR_GIVEN_VALUE
insert into t1 values (10,1);

drop table t1;
+23 −0
Original line number Diff line number Diff line
@@ -247,5 +247,28 @@ explain partitions
select * from t1 X, t1 Y where X.a = Y.a and (X.a=1 or X.a=2);

drop table t1;

# Tests for "short ranges"
create table t1 (a int) partition by hash(a) partitions 20;
insert into t1 values (1),(2),(3);
explain partitions select * from t1 where a >  1 and a < 3;
explain partitions select * from t1 where a >= 1 and a < 3;
explain partitions select * from t1 where a >  1 and a <= 3;
explain partitions select * from t1 where a >= 1 and a <= 3;
drop table t1;

create table t1 (a int, b int) 
 partition by list(a) subpartition by hash(b) subpartitions 20 
(
  partition p0 values in (0),
  partition p1 values in (1),
  partition p2 values in (2),
  partition p3 values in (3)
);
insert into t1 values (1,1),(2,2),(3,3);

explain partitions select * from t1 where b >  1 and b < 3;
explain partitions select * from t1 where b >  1 and b < 3 and (a =1 or a =2);

# No tests for NULLs in RANGE(monotonic_expr()) - they depend on BUG#15447
# being fixed.
+158 −2
Original line number Diff line number Diff line
@@ -474,6 +474,8 @@ typedef struct {
  uint32 end_part;
  bool use_bit_array;
} part_id_range;


/**
 * An enum and a struct to handle partitioning and subpartitioning.
 */
@@ -537,7 +539,109 @@ typedef bool (*get_part_id_func)(partition_info *part_info,
                                 uint32 *part_id);
typedef uint32 (*get_subpart_id_func)(partition_info *part_info);

class partition_info :public Sql_alloc {

struct st_partition_iter;
#define NOT_A_PARTITION_ID ((uint32)-1)

/*
  A "Get next" function for partition iterator.
  SYNOPSIS
    partition_iter_func()
      part_iter  Partition iterator, you call only "iter.get_next(&iter)"

  RETURN 
    NOT_A_PARTITION_ID if there are no more partitions.
    [sub]partition_id  of the next partition
*/

typedef uint32 (*partition_iter_func)(st_partition_iter* part_iter);


/*
  Partition set iterator. Used to enumerate a set of [sub]partitions
  obtained in partition interval analysis (see get_partitions_in_range_iter).

  For the user, the only meaningful field is get_next, which may be used as
  follows:
             part_iterator.get_next(&part_iterator);
  
  Initialization is done by any of the following calls:
    - get_partitions_in_range_iter-type function call
    - init_single_partition_iterator()
    - init_all_partitions_iterator()
  Cleanup is not needed.
*/

typedef struct st_partition_iter
{
  partition_iter_func get_next;

  union {
    struct {
      uint32 start_part_num;
      uint32 end_part_num;
    };
    struct {
      longlong start_val;
      longlong end_val;
    };
    bool null_returned;
  };
  partition_info *part_info;
} PARTITION_ITERATOR;


/*
  Get an iterator for set of partitions that match given field-space interval

  SYNOPSIS
    get_partitions_in_range_iter()
      part_info   Partitioning info
      is_subpart  
      min_val     Left edge,  field value in opt_range_key format.
      max_val     Right edge, 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
    Functions with this signature are used to perform "Partitioning Interval
    Analysis". This analysis is applicable for any type of [sub]partitioning 
    by some function of a single fieldX. The idea is as follows:
    Given an interval "const1 <=? fieldX <=? const2", find a set of partitions
    that may contain records with value of fieldX within the given interval.

    The min_val, max_val and flags parameters specify the interval.
    The set of partitions is returned by initializing an iterator in *part_iter

  NOTES
    There are currently two functions of this type:
     - get_part_iter_for_interval_via_walking
     - get_part_iter_for_interval_via_mapping

  RETURN 
    0 - No matching partitions, iterator not initialized
    1 - Some partitions would match, iterator intialized for traversing them
   -1 - All partitions would match, iterator not initialized
*/

typedef int (*get_partitions_in_range_iter)(partition_info *part_info,
                                            bool is_subpart,
                                            byte *min_val, byte *max_val,
                                            uint flags,
                                            PARTITION_ITERATOR *part_iter);


/* Initialize the iterator to return a single partition with given part_id */
inline void init_single_partition_iterator(uint32 part_id,
                                           PARTITION_ITERATOR *part_iter);

/* Initialize the iterator to enumerate all partitions */
inline void init_all_partitions_iterator(partition_info *part_info,
                                         PARTITION_ITERATOR *part_iter);

class partition_info : public Sql_alloc
{
public:
  /*
   * Here comes a set of definitions needed for partitioned table handlers.
@@ -598,6 +702,39 @@ class partition_info :public Sql_alloc {
    longlong *range_int_array;
    LIST_PART_ENTRY *list_array;
  };
  
  /********************************************
   * INTERVAL ANALYSIS
   ********************************************/
  /*
    Partitioning interval analysis function for partitioning, or NULL if 
    interval analysis is not supported for this kind of partitioning.
  */
  get_partitions_in_range_iter get_part_iter_for_interval;
  /*
    Partitioning interval analysis function for subpartitioning, or NULL if
    interval analysis is not supported for this kind of partitioning.
  */
  get_partitions_in_range_iter get_subpart_iter_for_interval;
  
  /*
    Valid iff
    get_part_iter_for_interval=get_part_iter_for_interval_via_walking:
      controls how we'll process "field < C" and "field > C" intervals.
      If the partitioning function F is strictly increasing, then for any x, y
      "x < y" => "F(x) < F(y)" (*), i.e. when we get interval "field < C" 
      we can perform partition pruning on the equivalent "F(field) < F(C)".

      If the partitioning function not strictly increasing (it is simply
      increasing), then instead of (*) we get "x < y" => "F(x) <= F(y)"
      i.e. for interval "field < C" we can perform partition pruning for
      "F(field) <= F(C)".
  */
  bool range_analysis_include_bounds;
  /********************************************
   * INTERVAL ANALYSIS ENDS 
   ********************************************/
  
  char* part_info_string;

  char *part_func_string;
@@ -681,6 +818,25 @@ class partition_info :public Sql_alloc {


#ifdef WITH_PARTITION_STORAGE_ENGINE
uint32 get_next_partition_id_range(struct st_partition_iter* part_iter);

inline void init_single_partition_iterator(uint32 part_id,
                                           PARTITION_ITERATOR *part_iter)
{
  part_iter->start_part_num= part_id;
  part_iter->end_part_num= part_id+1;
  part_iter->get_next= get_next_partition_id_range;
}

inline 
void init_all_partitions_iterator(partition_info *part_info,
                                  PARTITION_ITERATOR *part_iter)
{
  part_iter->start_part_num= 0;
  part_iter->end_part_num= part_info->no_parts;
  part_iter->get_next= get_next_partition_id_range;
}

/*
  Answers the question if subpartitioning is used for a certain table
  SYNOPSIS
+96 −177
Original line number Diff line number Diff line
@@ -2070,7 +2070,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}

/****************************************************************************
 * Partition pruning starts
 * Partition pruning module
 ****************************************************************************/
#ifdef WITH_PARTITION_STORAGE_ENGINE

@@ -2159,10 +2159,6 @@ struct st_part_prune_param;
struct st_part_opt_info;

typedef void (*mark_full_part_func)(partition_info*, uint32);
typedef uint32 (*part_num_to_partition_id_func)(struct st_part_prune_param*,
                                                uint32);
typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint,
                                    bool include_endpoint);

/*
  Partition pruning operation context
@@ -2199,32 +2195,6 @@ typedef struct st_part_prune_param
  int last_part_partno;
  int last_subpart_partno; /* Same as above for supartitioning */

  /*
    Function to be used to analyze non-singlepoint intervals (Can be pointer
    to one of two functions - for RANGE and for LIST types).  NULL means 
    partitioning type and/or expression doesn't allow non-singlepoint interval
    analysis.
    See get_list_array_idx_for_endpoint (or get_range_...) for description of
    what the function does.
  */
  get_endpoint_func   get_endpoint;

  /* Maximum possible value that can be returned by get_endpoint function */
  uint32  max_endpoint_val;

  /* 
    For RANGE partitioning, part_num_to_part_id_range, for LIST partitioning,
    part_num_to_part_id_list. Just to avoid the if-else clutter.
  */
  part_num_to_partition_id_func endpoints_walk_func;

  /*
    If true, process "key < const" as "part_func(key) < part_func(const)",
    otherwise as "part_func(key) <= part_func(const)". Same for '>' and '>='.
    This is defined iff get_endpoint != NULL.
  */
  bool force_include_bounds;
 
  /*
    is_part_keypart[i] == test(keypart #i in partitioning index is a member
                               used in partitioning)
@@ -2244,24 +2214,11 @@ typedef struct st_part_prune_param
  /* Same as cur_part_fields, but for subpartitioning */
  uint cur_subpart_fields;

  /***************************************************************
   Following fields are used to store an 'iterator' that can be 
   used to obtain a set of used partitions.
   **************************************************************/
  /* 
    Start and end+1 partition "numbers". They can have two meanings depending
    of the value of part_num_to_part_id:
    part_num_to_part_id_range - numbers are partition ids
    part_num_to_part_id_list  - numbers are indexes in part_info->list_array
  */
  uint32 start_part_num;
  uint32 end_part_num;
  /* Iterator to be used to obtain the "current" set of used partitions */
  PARTITION_ITERATOR part_iter;

  /* 
    A function that should be used to convert two above "partition numbers"
    to partition_ids.
  */
  part_num_to_partition_id_func part_num_to_part_id;
  /* Initialized bitmap of no_subparts size */
  MY_BITMAP subparts_bitmap;
} PART_PRUNE_PARAM;

static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par);
@@ -2377,9 +2334,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
    prune_param.arg_stack_end= prune_param.arg_stack;
    prune_param.cur_part_fields= 0;
    prune_param.cur_subpart_fields= 0;
    prune_param.part_num_to_part_id= part_num_to_part_id_range;
    prune_param.start_part_num= 0;
    prune_param.end_part_num= prune_param.part_info->no_parts;
    init_all_partitions_iterator(part_info, &prune_param.part_iter);
    if (!tree->keys[0] || (-1 == (res= find_used_partitions(&prune_param,
                                                            tree->keys[0]))))
      goto all_used;
@@ -2451,7 +2406,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
    format.
*/

static void store_key_image_to_rec(Field *field, char *ptr, uint len)
void store_key_image_to_rec(Field *field, char *ptr, uint len)
{
  /* Do the same as print_key() does */ 
  if (field->real_maybe_null())
@@ -2512,19 +2467,6 @@ static void mark_full_partition_used_with_parts(partition_info *part_info,
    bitmap_set_bit(&part_info->used_partitions, start);
}

/* See comment in PART_PRUNE_PARAM::part_num_to_part_id about what this is */
static uint32 part_num_to_part_id_range(PART_PRUNE_PARAM* ppar, uint32 num)
{
  return num; 
}

/* See comment in PART_PRUNE_PARAM::part_num_to_part_id about what this is */
static uint32 part_num_to_part_id_list(PART_PRUNE_PARAM* ppar, uint32 num)
{
  return ppar->part_info->list_array[num].partition_id;
}


/*
  Find the set of used partitions for List<SEL_IMERGE>
  SYNOPSIS
@@ -2612,9 +2554,7 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
    ppar->arg_stack_end= ppar->arg_stack;
    ppar->cur_part_fields= 0;
    ppar->cur_subpart_fields= 0;
    ppar->part_num_to_part_id= part_num_to_part_id_range;
    ppar->start_part_num= 0;
    ppar->end_part_num= ppar->part_info->no_parts;
    init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
    if (-1 == (res |= find_used_partitions(ppar, (*ptree)->keys[0])))
      return -1;
  }
@@ -2683,58 +2623,29 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)

  if (key_tree->type == SEL_ARG::KEY_RANGE)
  {
    if (partno == 0 && (NULL != ppar->get_endpoint))
    if (partno == 0 && (NULL != ppar->part_info->get_part_iter_for_interval))
    {
      /* 
        Partitioning is done by RANGE|INTERVAL(monotonic_expr(fieldX)), and
        we got "const1 CMP fieldX CMP const2" interval
        we got "const1 CMP fieldX CMP const2" interval <-- psergey-todo: change
      */
      DBUG_EXECUTE("info", dbug_print_segment_range(key_tree,
                                                    ppar->range_param.
                                                    key_parts););
      /* Find minimum */
      if (key_tree->min_flag & NO_MIN_RANGE)
        ppar->start_part_num= 0;
      else
      {
        /*
          Store the interval edge in the record buffer, and call the
          function that maps the edge in table-field space to an edge
          in ordered-set-of-partitions (for RANGE partitioning) or 
          indexes-in-ordered-array-of-list-constants (for LIST) space.
        */
        store_key_image_to_rec(key_tree->field, key_tree->min_value, 
                               ppar->range_param.key_parts[0].length);
        bool include_endp= ppar->force_include_bounds ||
                           !test(key_tree->min_flag & NEAR_MIN);
        ppar->start_part_num= ppar->get_endpoint(ppar->part_info, 1,
                                                 include_endp);
        if (ppar->start_part_num == ppar->max_endpoint_val)
        {
          res= 0; /* No satisfying partitions */
          goto pop_and_go_right;
        }
      }

      /* Find maximum, do the same as above but for right interval bound */
      if (key_tree->max_flag & NO_MAX_RANGE)
        ppar->end_part_num= ppar->max_endpoint_val;
      else
      {
        store_key_image_to_rec(key_tree->field, key_tree->max_value,
                               ppar->range_param.key_parts[0].length);
        bool include_endp= ppar->force_include_bounds ||
                           !test(key_tree->max_flag & NEAR_MAX);
        ppar->end_part_num= ppar->get_endpoint(ppar->part_info, 0,
                                               include_endp);
        if (ppar->start_part_num == ppar->end_part_num)
      res= ppar->part_info->
           get_part_iter_for_interval(ppar->part_info,
                                      FALSE,
                                      key_tree->min_value, 
                                      key_tree->max_value,
                                      key_tree->min_flag | key_tree->max_flag,
                                      &ppar->part_iter);
      if (!res)
        goto go_right; /* res=0 --> no satisfying partitions */
      if (res == -1)
      {
          res= 0; /* No satisfying partitions */
          goto pop_and_go_right;
        }
        //get a full range iterator
        init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
      }
      ppar->part_num_to_part_id= ppar->endpoints_walk_func;

      /* 
        Save our intent to mark full partition as used if we will not be able
        to obtain further limits on subpartitions
@@ -2743,6 +2654,42 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
      goto process_next_key_part;
    }

    if (partno == ppar->last_subpart_partno && 
        (NULL != ppar->part_info->get_subpart_iter_for_interval))
    {
      PARTITION_ITERATOR subpart_iter;
      DBUG_EXECUTE("info", dbug_print_segment_range(key_tree,
                                                    ppar->range_param.
                                                    key_parts););
      res= ppar->part_info->
           get_subpart_iter_for_interval(ppar->part_info,
                                         TRUE,
                                         key_tree->min_value, 
                                         key_tree->max_value,
                                         key_tree->min_flag | key_tree->max_flag,
                                         &subpart_iter);
      DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */
      if (res == -1)
        return -1; /* all subpartitions satisfy */
        
      uint32 subpart_id;
      bitmap_clear_all(&ppar->subparts_bitmap);
      while ((subpart_id= subpart_iter.get_next(&subpart_iter)) != NOT_A_PARTITION_ID)
        bitmap_set_bit(&ppar->subparts_bitmap, subpart_id);

      /* Mark each partition as used in each subpartition.  */
      uint32 part_id;
      while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
              NOT_A_PARTITION_ID)
      {
        for (uint i= 0; i < ppar->part_info->no_subparts; i++)
          if (bitmap_is_set(&ppar->subparts_bitmap, i))
            bitmap_set_bit(&ppar->part_info->used_partitions,
                           part_id * ppar->part_info->no_subparts + i);
      }
      goto go_right;
    }

    if (key_tree->is_singlepoint())
    {
      pushed= TRUE;
@@ -2768,9 +2715,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
          goto pop_and_go_right;
        }
        /* Rembember the limit we got - single partition #part_id */
        ppar->part_num_to_part_id= part_num_to_part_id_range;
        ppar->start_part_num= part_id;
        ppar->end_part_num=   part_id + 1;
        init_single_partition_iterator(part_id, &ppar->part_iter);
        
        /*
          If there are no subpartitions/we fail to get any limit for them, 
@@ -2780,7 +2725,8 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
        goto process_next_key_part;
      }

      if (partno == ppar->last_subpart_partno)
      if (partno == ppar->last_subpart_partno &&
          ppar->cur_subpart_fields == ppar->subpart_fields)
      {
        /* 
          Ok, we've got "fieldN<=>constN"-type SEL_ARGs for all subpartitioning
@@ -2796,12 +2742,12 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
        uint32 subpart_id= part_info->get_subpartition_id(part_info);
        
        /* Mark this partition as used in each subpartition. */
        for (uint32 num= ppar->start_part_num; num != ppar->end_part_num; 
             num++)
        uint32 part_id;
        while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
                NOT_A_PARTITION_ID)
        {
          bitmap_set_bit(&part_info->used_partitions,
                         ppar->part_num_to_part_id(ppar, num) * 
                         part_info->no_subparts + subpart_id);
                         part_id * part_info->no_subparts + subpart_id);
        }
        res= 1; /* Some partitions were marked as used */
        goto pop_and_go_right;
@@ -2825,31 +2771,25 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
  else
    res= -1;
 
  if (res == -1) /* Got "full range" for key_tree->next_key_part call */
  {
  if (set_full_part_if_bad_ret)
  {
      for (uint32 num= ppar->start_part_num; num != ppar->end_part_num; 
           num++)
    if (res == -1)
    {
        ppar->mark_full_partition_used(ppar->part_info,
                                       ppar->part_num_to_part_id(ppar, num));
      }
      res= 1;
      /* Got "full range" for subpartitioning fields */
      uint32 part_id;
      bool found= FALSE;
      while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) != NOT_A_PARTITION_ID)
      {
        ppar->mark_full_partition_used(ppar->part_info, part_id);
        found= TRUE;
      }
    else
      return -1;
      res= test(found);
    }

  if (set_full_part_if_bad_ret)
  {
    /*
      Restore the "used partitions iterator" to the default setting that
      specifies iteration over all partitions.
    */
    ppar->part_num_to_part_id= part_num_to_part_id_range;
    ppar->start_part_num= 0;
    ppar->end_part_num= ppar->part_info->no_parts;
    init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
  }

  if (pushed)
@@ -2861,6 +2801,9 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
    ppar->cur_subpart_fields-= ppar->is_subpart_keypart[partno];
  }

  if (res == -1)
    return -1;
go_right:
  if (key_tree->right != &null_element)
  {
    if (-1 == (right_res= find_used_partitions(ppar,key_tree->right)))
@@ -2967,38 +2910,6 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
    ppar->get_top_partition_id_func= part_info->get_partition_id;
  }

  enum_monotonicity_info minfo;
  ppar->get_endpoint= NULL;
  if (part_info->part_expr && 
      (minfo= part_info->part_expr->get_monotonicity_info()) != NON_MONOTONIC)
  {
    /*
      ppar->force_include_bounds controls how we'll process "field < C" and 
      "field > C" intervals.
      If the partitioning function F is strictly increasing, then for any x, y
      "x < y" => "F(x) < F(y)" (*), i.e. when we get interval "field < C" 
      we can perform partition pruning on the equivalent "F(field) < F(C)".

      If the partitioning function not strictly increasing (it is simply
      increasing), then instead of (*) we get "x < y" => "F(x) <= F(y)"
      i.e. for interval "field < C" we can perform partition pruning for
      "F(field) <= F(C)".
    */
    ppar->force_include_bounds= test(minfo == MONOTONIC_INCREASING);
    if (part_info->part_type == RANGE_PARTITION)
    {
      ppar->get_endpoint=        get_partition_id_range_for_endpoint;
      ppar->endpoints_walk_func= part_num_to_part_id_range;
      ppar->max_endpoint_val=    part_info->no_parts;
    }
    else if (part_info->part_type == LIST_PARTITION)
    {
      ppar->get_endpoint=        get_list_array_idx_for_endpoint;
      ppar->endpoints_walk_func= part_num_to_part_id_list;
      ppar->max_endpoint_val=    part_info->no_list_values;
    }
  }

  KEY_PART *key_part;
  MEM_ROOT *alloc= range_par->mem_root;
  if (!total_parts || 
@@ -3012,10 +2923,18 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
                                                           total_parts)))
    return TRUE;
 
  if (ppar->subpart_fields)
  {
    uint32 *buf;
    uint32 bufsize= bitmap_buffer_size(ppar->part_info->no_subparts);
    if (!(buf= (uint32*)alloc_root(alloc, bufsize)))
      return TRUE;
    bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts, FALSE);
  }
  range_par->key_parts= key_part;
  Field **field= (ppar->part_fields)? part_info->part_field_array :
                                           part_info->subpart_field_array;
  bool subpart_fields= FALSE;
  bool in_subpart_fields= FALSE;
  for (uint part= 0; part < total_parts; part++, key_part++)
  {
    key_part->key=          0;
@@ -3036,13 +2955,13 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
    key_part->image_type =  Field::itRAW;
    /* We don't set key_parts->null_bit as it will not be used */

    ppar->is_part_keypart[part]= !subpart_fields;
    ppar->is_subpart_keypart[part]= subpart_fields;
    ppar->is_part_keypart[part]= !in_subpart_fields;
    ppar->is_subpart_keypart[part]= in_subpart_fields;
 
    if (!*(++field))
    {
      field= part_info->subpart_field_array;
      subpart_fields= TRUE;
      in_subpart_fields= TRUE;
    }
  }
  range_par->key_parts_end= key_part;
Loading