/* Copyright (C) 2005 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  This file was introduced as a container for general functionality related
  to partitioning introduced in MySQL version 5.1. It contains functionality
  used by all handlers that support partitioning, which in the first version
  is the partitioning handler itself and the NDB handler.

  The first version was written by Mikael Ronstrom.

  This version supports RANGE partitioning, LIST partitioning, HASH
  partitioning and composite partitioning (hereafter called subpartitioning)
  where each RANGE/LIST partitioning is HASH partitioned. The hash function
  can either be supplied by the user or by only a list of fields (also
  called KEY partitioning, where the MySQL server will use an internal
  hash function.
  There are quite a few defaults that can be used as well.
*/

/* Some general useful functions */

#include "mysql_priv.h"
#include <errno.h>
#include <m_ctype.h>
#include "md5.h"

#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
/*
  Partition related functions declarations and some static constants;
*/
static const char *hash_str= "HASH";
static const char *range_str= "RANGE";
static const char *list_str= "LIST";
static const char *part_str= "PARTITION";
static const char *sub_str= "SUB";
static const char *by_str= "BY";
static const char *key_str= "KEY";
static const char *space_str= " ";
static const char *equal_str= "=";
static const char *end_paren_str= ")";
static const char *begin_paren_str= "(";
static const char *comma_str= ",";
static char buff[22];

bool get_partition_id_list(partition_info *part_info,
                           uint32 *part_id);
bool get_partition_id_range(partition_info *part_info,
                            uint32 *part_id);
bool get_partition_id_hash_nosub(partition_info *part_info,
                                 uint32 *part_id);
bool get_partition_id_key_nosub(partition_info *part_info,
                                uint32 *part_id);
bool get_partition_id_linear_hash_nosub(partition_info *part_info,
                                        uint32 *part_id);
bool get_partition_id_linear_key_nosub(partition_info *part_info,
                                       uint32 *part_id);
bool get_partition_id_range_sub_hash(partition_info *part_info,
                                     uint32 *part_id);
bool get_partition_id_range_sub_key(partition_info *part_info,
                                    uint32 *part_id);
bool get_partition_id_range_sub_linear_hash(partition_info *part_info,
                                            uint32 *part_id);
bool get_partition_id_range_sub_linear_key(partition_info *part_info,
                                           uint32 *part_id);
bool get_partition_id_list_sub_hash(partition_info *part_info,
                                    uint32 *part_id);
bool get_partition_id_list_sub_key(partition_info *part_info,
                                   uint32 *part_id);
bool get_partition_id_list_sub_linear_hash(partition_info *part_info,
                                           uint32 *part_id);
bool get_partition_id_list_sub_linear_key(partition_info *part_info,
                                          uint32 *part_id);
uint32 get_partition_id_hash_sub(partition_info *part_info); 
uint32 get_partition_id_key_sub(partition_info *part_info); 
uint32 get_partition_id_linear_hash_sub(partition_info *part_info); 
uint32 get_partition_id_linear_key_sub(partition_info *part_info); 
#endif


/*
  A routine used by the parser to decide whether we are specifying a full
  partitioning or if only partitions to add or to split.
  SYNOPSIS
    is_partition_management()
    lex                    Reference to the lex object
  RETURN VALUE
    TRUE                   Yes, it is part of a management partition command
    FALSE                  No, not a management partition command
  DESCRIPTION
    This needs to be outside of WITH_PARTITION_STORAGE_ENGINE since it is
    used from the sql parser that doesn't have any #ifdef's
*/

my_bool is_partition_management(LEX *lex)
{
  return (lex->sql_command == SQLCOM_ALTER_TABLE &&
          (lex->alter_info.flags == ALTER_ADD_PARTITION ||
           lex->alter_info.flags == ALTER_REORGANISE_PARTITION));
}

#ifdef WITH_PARTITION_STORAGE_ENGINE
/*
  A support function to check if a partition name is in a list of strings
  SYNOPSIS
    is_partition_in_list()
    part_name          String searched for
    list_part_names    A list of names searched in
  RETURN VALUES
    TRUE               String found
    FALSE              String not found
*/

bool is_partition_in_list(char *part_name,
                          List<char> list_part_names)
{
  List_iterator<char> part_names_it(list_part_names);
  uint no_names= list_part_names.elements;
  uint i= 0;
  do
  {
    char *list_name= part_names_it++;
    if (!(my_strcasecmp(system_charset_info, part_name, list_name)))
      return TRUE;
  } while (++i < no_names);
  return FALSE;
}


/*
  A support function to check partition names for duplication in a
  partitioned table
  SYNOPSIS
    is_partitions_in_table()
    new_part_info      New partition info
    old_part_info      Old partition info
  RETURN VALUES
    TRUE               Duplicate names found
    FALSE              Duplicate names not found
  DESCRIPTION
    Can handle that the new and old parts are the same in which case it
    checks that the list of names in the partitions doesn't contain any
    duplicated names.
*/

bool is_partitions_in_table(partition_info *new_part_info,
                            partition_info *old_part_info)
{
  uint no_new_parts= new_part_info->partitions.elements, new_count;
  uint no_old_parts= old_part_info->partitions.elements, old_count;
  List_iterator<partition_element> new_parts_it(new_part_info->partitions);
  bool same_part_info= (new_part_info == old_part_info);
  DBUG_ENTER("is_partitions_in_table");

  new_count= 0;
  do
  {
    List_iterator<partition_element> old_parts_it(old_part_info->partitions);
    char *new_name= (new_parts_it++)->partition_name;
    new_count++;
    old_count= 0;
    do
    {
      char *old_name= (old_parts_it++)->partition_name;
      old_count++;
      if (same_part_info && old_count == new_count)
        break;
      if (!(my_strcasecmp(system_charset_info, old_name, new_name)))
      {
        DBUG_RETURN(TRUE);
      }
    } while (old_count < no_old_parts);
  } while (new_count < no_new_parts);
  DBUG_RETURN(FALSE);
}


/*
  Check that the reorganized table will not have duplicate partitions.

  SYNOPSIS
    check_reorganise_list()
    new_part_info      New partition info
    old_part_info      Old partition info
    list_part_names    The list of partition names that will go away and can be reused in the
                       new table.

  RETURN VALUES
    TRUE               Inacceptable name conflict detected.
    FALSE              New names are OK.

  DESCRIPTION
    Can handle that the 'new_part_info' and 'old_part_info' the same
    in which case it checks that the list of names in the partitions
    doesn't contain any duplicated names.
*/

bool check_reorganise_list(partition_info *new_part_info,
                           partition_info *old_part_info,
                           List<char> list_part_names)
{
  uint new_count, old_count;
  uint no_new_parts= new_part_info->partitions.elements;
  uint no_old_parts= old_part_info->partitions.elements;
  List_iterator<partition_element> new_parts_it(new_part_info->partitions);
  bool same_part_info= (new_part_info == old_part_info);
  DBUG_ENTER("check_reorganise_list");

  new_count= 0;
  do
  {
    List_iterator<partition_element> old_parts_it(old_part_info->partitions);
    char *new_name= (new_parts_it++)->partition_name;
    new_count++;
    old_count= 0;
    do
    {
      char *old_name= (old_parts_it++)->partition_name;
      old_count++;
      if (same_part_info && old_count == new_count)
        break;
      if (!(my_strcasecmp(system_charset_info, old_name, new_name)))
      {
        if (!is_partition_in_list(old_name, list_part_names))
          DBUG_RETURN(TRUE);
      }
    } while (old_count < no_old_parts);
  } while (new_count < no_new_parts);
  DBUG_RETURN(FALSE);
}


/*
  A useful routine used by update_row for partition handlers to calculate
  the partition ids of the old and the new record.
  SYNOPSIS
    get_part_for_update()
    old_data                Buffer of old record
    new_data                Buffer of new record
    rec0                    Reference to table->record[0]
    part_info               Reference to partition information
    part_field_array        A NULL-terminated array of fields for partition
                            function
    old_part_id             The returned partition id of old record 
    new_part_id             The returned partition id of new record 
  RETURN VALUE
    0                       Success
    > 0                     Error code
  DESCRIPTION
    Dependent on whether buf is not record[0] we need to prepare the
    fields. Then we call the function pointer get_partition_id to
    calculate the partition ids.
*/

int get_parts_for_update(const byte *old_data, byte *new_data,
                         const byte *rec0, partition_info *part_info,
                         uint32 *old_part_id, uint32 *new_part_id)
{
  Field **part_field_array= part_info->full_part_field_array;
  int error;
  DBUG_ENTER("get_parts_for_update");
  DBUG_ASSERT(new_data == rec0);

  set_field_ptr(part_field_array, old_data, rec0);
  error= part_info->get_partition_id(part_info, old_part_id);
  set_field_ptr(part_field_array, rec0, old_data);
  if (unlikely(error))                             // Should never happen
  {
    DBUG_ASSERT(0);
    DBUG_RETURN(error);
  }
#ifdef NOT_NEEDED
  if (new_data == rec0)
#endif
  {
    if (unlikely(error= part_info->get_partition_id(part_info,new_part_id)))
    {
      DBUG_RETURN(error);
    }
  }
#ifdef NOT_NEEDED
  else
  {
    /*
      This branch should never execute but it is written anyways for
      future use. It will be tested by ensuring that the above
      condition is false in one test situation before pushing the code.
    */
    set_field_ptr(part_field_array, new_data, rec0);
    error= part_info->get_partition_id(part_info, new_part_id);
    set_field_ptr(part_field_array, rec0, new_data);
    if (unlikely(error))
    {
      DBUG_RETURN(error);
    }
  }
#endif
  DBUG_RETURN(0);
}


/*
  A useful routine used by delete_row for partition handlers to calculate
  the partition id.
  SYNOPSIS
    get_part_for_delete()
    buf                     Buffer of old record
    rec0                    Reference to table->record[0]
    part_info               Reference to partition information
    part_field_array        A NULL-terminated array of fields for partition
                            function
    part_id                 The returned partition id to delete from
  RETURN VALUE
    0                       Success
    > 0                     Error code
  DESCRIPTION
    Dependent on whether buf is not record[0] we need to prepare the
    fields. Then we call the function pointer get_partition_id to
    calculate the partition id.
*/

int get_part_for_delete(const byte *buf, const byte *rec0,
                        partition_info *part_info, uint32 *part_id)
{
  int error;
  DBUG_ENTER("get_part_for_delete");

  if (likely(buf == rec0))
  {
    if (unlikely((error= part_info->get_partition_id(part_info, part_id))))
    {
      DBUG_RETURN(error);
    }
    DBUG_PRINT("info", ("Delete from partition %d", *part_id));
  }
  else
  {
    Field **part_field_array= part_info->full_part_field_array;
    set_field_ptr(part_field_array, buf, rec0);
    error= part_info->get_partition_id(part_info, part_id);
    set_field_ptr(part_field_array, rec0, buf);
    if (unlikely(error))
    {
      DBUG_RETURN(error);
    }
    DBUG_PRINT("info", ("Delete from partition %d (path2)", *part_id));
  }
  DBUG_RETURN(0);
}


/*
  This routine allocates an array for all range constants to achieve a fast
  check what partition a certain value belongs to. At the same time it does
  also check that the range constants are defined in increasing order and
  that the expressions are constant integer expressions.
  SYNOPSIS
    check_range_constants()
      part_info
  RETURN VALUE
    TRUE                An error occurred during creation of range constants
    FALSE               Successful creation of range constant mapping
  DESCRIPTION
    This routine is called from check_partition_info to get a quick error
    before we came too far into the CREATE TABLE process. It is also called
    from fix_partition_func every time we open the .frm file. It is only
    called for RANGE PARTITIONed tables.
*/

static bool check_range_constants(partition_info *part_info)
{
  partition_element* part_def;
  longlong current_largest_int= LONGLONG_MIN, part_range_value_int;
  uint no_parts= part_info->no_parts, i;
  List_iterator<partition_element> it(part_info->partitions);
  bool result= TRUE;
  DBUG_ENTER("check_range_constants");
  DBUG_PRINT("enter", ("INT_RESULT with %d parts", no_parts));

  part_info->part_result_type= INT_RESULT;
  part_info->range_int_array= 
                      (longlong*)sql_alloc(no_parts * sizeof(longlong));
  if (unlikely(part_info->range_int_array == NULL))
  {
    my_error(ER_OUTOFMEMORY, MYF(0), no_parts*sizeof(longlong));
    goto end;
  }
  i= 0;
  do
  {
    part_def= it++;
    if ((i != (no_parts - 1)) || !part_info->defined_max_value)
      part_range_value_int= part_def->range_value; 
    else
      part_range_value_int= LONGLONG_MAX;
    if (likely(current_largest_int < part_range_value_int))
    {
      current_largest_int= part_range_value_int;
      part_info->range_int_array[i]= part_range_value_int;
    }
    else
    {
      my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0));
      goto end;
    }
  } while (++i < no_parts);
  result= FALSE;
end:
  DBUG_RETURN(result);
}


/*
  A support routine for check_list_constants used by qsort to sort the
  constant list expressions.
  SYNOPSIS
    list_part_cmp()
      a                First list constant to compare with
      b                Second list constant to compare with
  RETURN VALUE
    +1                 a > b
    0                  a  == b
    -1                 a < b
*/

static int list_part_cmp(const void* a, const void* b)
{
  longlong a1, b1;
  a1= ((LIST_PART_ENTRY*)a)->list_value;
  b1= ((LIST_PART_ENTRY*)b)->list_value;
  if (a1 < b1)
    return -1;
  else if (a1 > b1)
    return +1;
  else
    return 0;
}


/*
  This routine allocates an array for all list constants to achieve a fast
  check what partition a certain value belongs to. At the same time it does
  also check that there are no duplicates among the list constants and that
  that the list expressions are constant integer expressions.
  SYNOPSIS
    check_list_constants()
      part_info
  RETURN VALUE
    TRUE                  An error occurred during creation of list constants
    FALSE                 Successful creation of list constant mapping
  DESCRIPTION
    This routine is called from check_partition_info to get a quick error
    before we came too far into the CREATE TABLE process. It is also called
    from fix_partition_func every time we open the .frm file. It is only
    called for LIST PARTITIONed tables.
*/

static bool check_list_constants(partition_info *part_info)
{
  uint i, no_list_values= 0, no_parts, list_index= 0;
  longlong *list_value;
  bool not_first, result= TRUE;
  longlong curr_value, prev_value;
  partition_element* part_def;
  List_iterator<partition_element> list_func_it(part_info->partitions);
  DBUG_ENTER("check_list_constants");

  part_info->part_result_type= INT_RESULT;

  /*
    We begin by calculating the number of list values that have been
    defined in the first step.

    We use this number to allocate a properly sized array of structs
    to keep the partition id and the value to use in that partition.
    In the second traversal we assign them values in the struct array.

    Finally we sort the array of structs in order of values to enable
    a quick binary search for the proper value to discover the
    partition id.
    After sorting the array we check that there are no duplicates in the
    list.
  */

  no_parts= part_info->no_parts;
  i= 0;
  do
  {
    part_def= list_func_it++;
    List_iterator<longlong> list_val_it1(part_def->list_val_list);
    while (list_val_it1++)
      no_list_values++;
  } while (++i < no_parts);
  list_func_it.rewind();
  part_info->no_list_values= no_list_values;
  part_info->list_array=
      (LIST_PART_ENTRY*)sql_alloc(no_list_values*sizeof(LIST_PART_ENTRY));
  if (unlikely(part_info->list_array == NULL))
  {
    my_error(ER_OUTOFMEMORY, MYF(0), no_list_values*sizeof(LIST_PART_ENTRY));
    goto end;
  }

  i= 0;
  do
  {
    part_def= list_func_it++;
    List_iterator<longlong> list_val_it2(part_def->list_val_list);
    while ((list_value= list_val_it2++))
    {
      part_info->list_array[list_index].list_value= *list_value;
      part_info->list_array[list_index++].partition_id= i;
    }
  } while (++i < no_parts);

  qsort((void*)part_info->list_array, no_list_values,
        sizeof(LIST_PART_ENTRY), &list_part_cmp);

  not_first= FALSE;
  i= prev_value= 0; //prev_value initialised to quiet compiler
  do
  {
    curr_value= part_info->list_array[i].list_value;
    if (likely(!not_first || prev_value != curr_value))
    {
      prev_value= curr_value;
      not_first= TRUE;
    }
    else
    {
      my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
      goto end;
    }
  } while (++i < no_list_values);
  result= FALSE;
end:
  DBUG_RETURN(result);
}


/*
  Create a memory area where default partition names are stored and fill it
  up with the names.
  SYNOPSIS
    create_default_partition_names()
    no_parts                        Number of partitions
    subpart                         Is it subpartitions
  RETURN VALUE
    A pointer to the memory area of the default partition names
  DESCRIPTION
    A support routine for the partition code where default values are
    generated.
    The external routine needing this code is check_partition_info
*/

#define MAX_PART_NAME_SIZE 8

static char *create_default_partition_names(uint no_parts, uint start_no,
                                            bool subpart)
{
  char *ptr= sql_calloc(no_parts*MAX_PART_NAME_SIZE);
  char *move_ptr= ptr;
  uint i= 0;
  DBUG_ENTER("create_default_partition_names");
  if (likely(ptr != 0))
  {
    do
    {
      if (subpart)
        my_sprintf(move_ptr, (move_ptr,"sp%u", (start_no + i)));
      else
        my_sprintf(move_ptr, (move_ptr,"p%u", (start_no + i)));
      move_ptr+=MAX_PART_NAME_SIZE;
    } while (++i < no_parts);
  }
  else
  {
    my_error(ER_OUTOFMEMORY, MYF(0), no_parts*MAX_PART_NAME_SIZE);
  }
  DBUG_RETURN(ptr);
}


/*
  Set up all the default partitions not set-up by the user in the SQL
  statement. Also perform a number of checks that the user hasn't tried
  to use default values where no defaults exists.
  SYNOPSIS
    set_up_default_partitions()
    part_info           The reference to all partition information
    file                A reference to a handler of the table
    max_rows            Maximum number of rows stored in the table
  RETURN VALUE
    TRUE                Error, attempted default values not possible
    FALSE               Ok, default partitions set-up
  DESCRIPTION
    The routine uses the underlying handler of the partitioning to define
    the default number of partitions. For some handlers this requires
    knowledge of the maximum number of rows to be stored in the table.
    This routine only accepts HASH and KEY partitioning and thus there is
    no subpartitioning if this routine is successful.
    The external routine needing this code is check_partition_info
*/

static bool set_up_default_partitions(partition_info *part_info,
                                      handler *file, ulonglong max_rows,
                                      uint start_no)
{
  uint no_parts, i;
  char *default_name;
  bool result= TRUE;
  DBUG_ENTER("set_up_default_partitions");

  if (part_info->part_type != HASH_PARTITION)
  {
    const char *error_string;
    if (part_info->part_type == RANGE_PARTITION)
      error_string= range_str;
    else
      error_string= list_str;
    my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_string);
    goto end;
  }
  if (part_info->no_parts == 0)
    part_info->no_parts= file->get_default_no_partitions(max_rows);
  no_parts= part_info->no_parts;
  part_info->use_default_partitions= FALSE;
  if (unlikely(no_parts > MAX_PARTITIONS))
  {
    my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
    goto end;
  }
  if (unlikely((!(default_name= create_default_partition_names(no_parts,
                                                               start_no,
                                                               FALSE)))))
    goto end;
  i= 0;
  do
  {
    partition_element *part_elem= new partition_element();
    if (likely(part_elem != 0))
    {
      part_elem->engine_type= NULL;
      part_elem->partition_name= default_name;
      default_name+=MAX_PART_NAME_SIZE;
      part_info->partitions.push_back(part_elem);
    }
    else
    {
      my_error(ER_OUTOFMEMORY, MYF(0), sizeof(partition_element));
      goto end;
    }
  } while (++i < no_parts);
  result= FALSE;
end:
  DBUG_RETURN(result);
}


/*
  Set up all the default subpartitions not set-up by the user in the SQL
  statement. Also perform a number of checks that the default partitioning
  becomes an allowed partitioning scheme.
  SYNOPSIS
    set_up_default_subpartitions()
    part_info           The reference to all partition information
    file                A reference to a handler of the table
    max_rows            Maximum number of rows stored in the table
  RETURN VALUE
    TRUE                Error, attempted default values not possible
    FALSE               Ok, default partitions set-up
  DESCRIPTION
    The routine uses the underlying handler of the partitioning to define
    the default number of partitions. For some handlers this requires
    knowledge of the maximum number of rows to be stored in the table.
    This routine is only called for RANGE or LIST partitioning and those
    need to be specified so only subpartitions are specified.
    The external routine needing this code is check_partition_info
*/

static bool set_up_default_subpartitions(partition_info *part_info,
                                         handler *file, ulonglong max_rows)
{
  uint i, j, no_parts, no_subparts;
  char *default_name, *name_ptr;
  bool result= TRUE;
  partition_element *part_elem;
  List_iterator<partition_element> part_it(part_info->partitions);
  DBUG_ENTER("set_up_default_subpartitions");

  if (part_info->no_subparts == 0)
    part_info->no_subparts= file->get_default_no_partitions(max_rows);
  no_parts= part_info->no_parts;
  no_subparts= part_info->no_subparts;
  part_info->use_default_subpartitions= FALSE;
  if (unlikely((no_parts * no_subparts) > MAX_PARTITIONS))
  {
    my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
    goto end;
  }
  if (unlikely((!(default_name=
             create_default_partition_names(no_subparts, (uint)0, TRUE)))))
    goto end;
  i= 0;
  do
  {
    part_elem= part_it++;
    j= 0;
    name_ptr= default_name;
    do
    {
      partition_element *subpart_elem= new partition_element();
      if (likely(subpart_elem != 0))
      {
        subpart_elem->engine_type= NULL;
        subpart_elem->partition_name= name_ptr;
        name_ptr+= MAX_PART_NAME_SIZE;
        part_elem->subpartitions.push_back(subpart_elem);
      }
      else
      {
        my_error(ER_OUTOFMEMORY, MYF(0), sizeof(partition_element));
        goto end;
      }
    } while (++j < no_subparts);
  } while (++i < no_parts);
  result= FALSE;
end:
  DBUG_RETURN(result);
}


/*
  Set up defaults for partition or subpartition (cannot set-up for both,
  this will return an error.
  SYNOPSIS
    set_up_defaults_for_partitioning()
    part_info           The reference to all partition information
    file                A reference to a handler of the table
    max_rows            Maximum number of rows stored in the table
  RETURN VALUE
    TRUE                Error, attempted default values not possible
    FALSE               Ok, default partitions set-up
  DESCRIPTION
    Support routine for check_partition_info
*/

bool set_up_defaults_for_partitioning(partition_info *part_info,
                                      handler *file,
                                      ulonglong max_rows, uint start_no)
{
  DBUG_ENTER("set_up_defaults_for_partitioning");

  if (part_info->use_default_partitions)
    DBUG_RETURN(set_up_default_partitions(part_info, file, max_rows,
                                          start_no));
  if (is_sub_partitioned(part_info) && part_info->use_default_subpartitions)
    DBUG_RETURN(set_up_default_subpartitions(part_info, file, max_rows));
  DBUG_RETURN(FALSE);
}


/*
  Check that all partitions use the same storage engine.
  This is currently a limitation in this version.
  SYNOPSIS
    check_engine_mix()
    engine_array           An array of engine identifiers
    no_parts               Total number of partitions
  RETURN VALUE
    TRUE                   Error, mixed engines
    FALSE                  Ok, no mixed engines
*/

static bool check_engine_mix(handlerton **engine_array, uint no_parts)
{
  /*
    Current check verifies only that all handlers are the same.
    Later this check will be more sophisticated.
  */
  uint i= 0;
  bool result= FALSE;
  DBUG_ENTER("check_engine_mix");

  do
  {
    if (engine_array[i] != engine_array[0])
    {
      result= TRUE;
      break;
    }
  } while (++i < no_parts);
  DBUG_RETURN(result);
}


/*
  We will check that the partition info requested is possible to set-up in
  this version. This routine is an extension of the parser one could say.
  If defaults were used we will generate default data structures for all
  partitions.
  SYNOPSIS
    check_partition_info()
    part_info           The reference to all partition information
    db_type             Default storage engine if no engine specified per
                        partition.
    file                A reference to a handler of the table
    max_rows            Maximum number of rows stored in the table
  RETURN VALUE
    TRUE                 Error, something went wrong
    FALSE                Ok, full partition data structures are now generated
  DESCRIPTION
    This code is used early in the CREATE TABLE and ALTER TABLE process.
*/

bool check_partition_info(partition_info *part_info,handlerton *eng_type,
                          handler *file, ulonglong max_rows)
{
  handlerton **engine_array= NULL;
  uint part_count= 0, i, no_parts, tot_partitions;
  bool result= TRUE;
  List_iterator<partition_element> part_it(part_info->partitions);
  DBUG_ENTER("check_partition_info");

  if (unlikely(is_sub_partitioned(part_info) &&
              (!(part_info->part_type == RANGE_PARTITION ||
                 part_info->part_type == LIST_PARTITION))))
  {
    /* Only RANGE and LIST partitioning can be subpartitioned */
    my_error(ER_SUBPARTITION_ERROR, MYF(0));
    goto end;
  }
  if (unlikely(set_up_defaults_for_partitioning(part_info, file,
                                                max_rows, (uint)0)))
    goto end;
  tot_partitions= get_tot_partitions(part_info);
  if (unlikely(tot_partitions > MAX_PARTITIONS))
  {
    my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
    goto end;
  }
  if (unlikely(is_partitions_in_table(part_info, part_info)))
  {
    my_error(ER_SAME_NAME_PARTITION, MYF(0));
    goto end;
  }
  engine_array= (handlerton**)my_malloc(tot_partitions * sizeof(handlerton *), 
                                        MYF(MY_WME));
  if (unlikely(!engine_array))
    goto end;
  i= 0;
  no_parts= part_info->no_parts;
  do
  {
    partition_element *part_elem= part_it++;
    if (!is_sub_partitioned(part_info))
    {
      if (part_elem->engine_type == NULL)
        part_elem->engine_type= eng_type;
      DBUG_PRINT("info", ("engine = %s", part_elem->engine_type->name));
      engine_array[part_count++]= part_elem->engine_type;
    }
    else
    {
      uint j= 0, no_subparts= part_info->no_subparts;;
      List_iterator<partition_element> sub_it(part_elem->subpartitions);
      do
      {
        part_elem= sub_it++;
        if (part_elem->engine_type == NULL)
          part_elem->engine_type= eng_type;
        DBUG_PRINT("info", ("engine = %s", part_elem->engine_type->name));
        engine_array[part_count++]= part_elem->engine_type;
      } while (++j < no_subparts);
    }
  } while (++i < part_info->no_parts);
  if (unlikely(check_engine_mix(engine_array, part_count)))
  {
    my_error(ER_MIX_HANDLER_ERROR, MYF(0));
    goto end;
  }

  /*
    We need to check all constant expressions that they are of the correct
    type and that they are increasing for ranges and not overlapping for
    list constants.
  */

  if (unlikely((part_info->part_type == RANGE_PARTITION &&
                check_range_constants(part_info)) ||
               (part_info->part_type == LIST_PARTITION &&
                check_list_constants(part_info))))
    goto end;
  result= FALSE;
end:
  my_free((char*)engine_array,MYF(MY_ALLOW_ZERO_PTR));
  DBUG_RETURN(result);
}


/*
  A great number of functions below here is part of the fix_partition_func
  method. It is used to set up the partition structures for execution from
  openfrm. It is called at the end of the openfrm when the table struct has
  been set-up apart from the partition information.
  It involves:
  1) Setting arrays of fields for the partition functions.
  2) Setting up binary search array for LIST partitioning
  3) Setting up array for binary search for RANGE partitioning
  4) Setting up key_map's to assist in quick evaluation whether one
     can deduce anything from a given index of what partition to use
  5) Checking whether a set of partitions can be derived from a range on
     a field in the partition function.
  As part of doing this there is also a great number of error controls.
  This is actually the place where most of the things are checked for
  partition information when creating a table.
  Things that are checked includes
  1) No NULLable fields in partition function
  2) All fields of partition function in Primary keys and unique indexes
     (if not supported)
  3) No fields in partition function that are BLOB's or VARCHAR with a
     collation other than the binary collation.



  Create an array of partition fields (NULL terminated). Before this method
  is called fix_fields or find_table_in_sef has been called to set
  GET_FIXED_FIELDS_FLAG on all fields that are part of the partition
  function.
  SYNOPSIS
    set_up_field_array()
    table                TABLE object for which partition fields are set-up
    sub_part             Is the table subpartitioned as well
  RETURN VALUE
    TRUE                 Error, some field didn't meet requirements
    FALSE                Ok, partition field array set-up
  DESCRIPTION
    This method is used to set-up both partition and subpartitioning
    field array and used for all types of partitioning.
    It is part of the logic around fix_partition_func.
*/
static bool set_up_field_array(TABLE *table,
                              bool sub_part)
{
  Field **ptr, *field, **field_array;
  uint no_fields= 0, size_field_array, i= 0;
  partition_info *part_info= table->part_info;
  int result= FALSE;
  DBUG_ENTER("set_up_field_array");

  ptr= table->field;
  while ((field= *(ptr++))) 
  {
    if (field->flags & GET_FIXED_FIELDS_FLAG)
      no_fields++;
  }
  size_field_array= (no_fields+1)*sizeof(Field*);
  field_array= (Field**)sql_alloc(size_field_array);
  if (unlikely(!field_array))
  {
    my_error(ER_OUTOFMEMORY, MYF(0), size_field_array);
    result= TRUE;
  }
  ptr= table->field;
  while ((field= *(ptr++))) 
  {
    if (field->flags & GET_FIXED_FIELDS_FLAG)
    {
      field->flags&= ~GET_FIXED_FIELDS_FLAG;
      field->flags|= FIELD_IN_PART_FUNC_FLAG;
      if (likely(!result))
      {
        field_array[i++]= field;

        /*
          We check that the fields are proper. It is required for each
          field in a partition function to:
          1) Not be a BLOB of any type
            A BLOB takes too long time to evaluate so we don't want it for
            performance reasons.
          2) Not be a VARCHAR other than VARCHAR with a binary collation
            A VARCHAR with character sets can have several values being
            equal with different number of spaces or NULL's. This is not a
            good ground for a safe and exact partition function. Thus it is
            not allowed in partition functions.
        */

        if (unlikely(field->flags & BLOB_FLAG))
        {
          my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
          result= TRUE;
        }
        else if (unlikely((!field->flags & BINARY_FLAG) &&
                          field->real_type() == MYSQL_TYPE_VARCHAR))
        {
          my_error(ER_CHAR_SET_IN_PART_FIELD_ERROR, MYF(0));
          result= TRUE;
        }
      }
    }
  }
  field_array[no_fields]= 0;
  if (!sub_part)
  {
    part_info->part_field_array= field_array;
    part_info->no_part_fields= no_fields;
  }
  else
  {
    part_info->subpart_field_array= field_array;
    part_info->no_subpart_fields= no_fields;
  }
  DBUG_RETURN(result);
}


/*
  Create a field array including all fields of both the partitioning and the
  subpartitioning functions.
  SYNOPSIS
    create_full_part_field_array()
    table                TABLE object for which partition fields are set-up
    part_info            Reference to partitioning data structure
  RETURN VALUE
    TRUE                 Memory allocation of field array failed
    FALSE                Ok
  DESCRIPTION
    If there is no subpartitioning then the same array is used as for the
    partitioning. Otherwise a new array is built up using the flag
    FIELD_IN_PART_FUNC in the field object.
    This function is called from fix_partition_func
*/

static bool create_full_part_field_array(TABLE *table,
                                         partition_info *part_info)
{
  bool result= FALSE;
  DBUG_ENTER("create_full_part_field_array");

  if (!is_sub_partitioned(part_info))
  {
    part_info->full_part_field_array= part_info->part_field_array;
    part_info->no_full_part_fields= part_info->no_part_fields;
  }
  else
  {
    Field **ptr, *field, **field_array;
    uint no_part_fields=0, size_field_array;
    ptr= table->field;
    while ((field= *(ptr++)))
    {
      if (field->flags & FIELD_IN_PART_FUNC_FLAG)
        no_part_fields++;
    }
    size_field_array= (no_part_fields+1)*sizeof(Field*);
    field_array= (Field**)sql_alloc(size_field_array);
    if (unlikely(!field_array))
    {
      my_error(ER_OUTOFMEMORY, MYF(0), size_field_array);
      result= TRUE;
      goto end;
    }
    no_part_fields= 0;
    ptr= table->field;
    while ((field= *(ptr++)))
    {
      if (field->flags & FIELD_IN_PART_FUNC_FLAG)
        field_array[no_part_fields++]= field;
    }
    field_array[no_part_fields]=0;
    part_info->full_part_field_array= field_array;
    part_info->no_full_part_fields= no_part_fields;
  }
end:
  DBUG_RETURN(result);
}


/*
  These support routines is used to set/reset an indicator of all fields
  in a certain key. It is used in conjunction with another support routine
  that traverse all fields in the PF to find if all or some fields in the
  PF is part of the key. This is used to check primary keys and unique
  keys involve all fields in PF (unless supported) and to derive the
  key_map's used to quickly decide whether the index can be used to
  derive which partitions are needed to scan.



  Clear flag GET_FIXED_FIELDS_FLAG in all fields of a key previously set by
  set_indicator_in_key_fields (always used in pairs).
  SYNOPSIS
    clear_indicator_in_key_fields()
    key_info                  Reference to find the key fields
*/

static void clear_indicator_in_key_fields(KEY *key_info)
{
  KEY_PART_INFO *key_part;
  uint key_parts= key_info->key_parts, i;
  for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
    key_part->field->flags&= (~GET_FIXED_FIELDS_FLAG);
}


/*
  Set flag GET_FIXED_FIELDS_FLAG in all fields of a key.
  SYNOPSIS
    set_indicator_in_key_fields
    key_info                  Reference to find the key fields
*/

static void set_indicator_in_key_fields(KEY *key_info)
{
  KEY_PART_INFO *key_part;
  uint key_parts= key_info->key_parts, i;
  for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
    key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
}


/*
  Check if all or some fields in partition field array is part of a key
  previously used to tag key fields.
  SYNOPSIS
    check_fields_in_PF()
    ptr                  Partition field array
    all_fields           Is all fields of partition field array used in key
    some_fields          Is some fields of partition field array used in key
  RETURN VALUE
    all_fields, some_fields
*/

static void check_fields_in_PF(Field **ptr, bool *all_fields,
                               bool *some_fields)
{
  DBUG_ENTER("check_fields_in_PF");
  *all_fields= TRUE;
  *some_fields= FALSE;
  do
  {
  /* Check if the field of the PF is part of the current key investigated */
    if ((*ptr)->flags & GET_FIXED_FIELDS_FLAG)
      *some_fields= TRUE; 
    else
      *all_fields= FALSE;
  } while (*(++ptr));
  DBUG_VOID_RETURN;
}


/*
  Clear flag GET_FIXED_FIELDS_FLAG in all fields of the table.
  This routine is used for error handling purposes.
  SYNOPSIS
    clear_field_flag()
    table                TABLE object for which partition fields are set-up
*/

static void clear_field_flag(TABLE *table)
{
  Field **ptr;
  DBUG_ENTER("clear_field_flag");

  for (ptr= table->field; *ptr; ptr++)
    (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG);
  DBUG_VOID_RETURN;
}


/*
  This routine sets-up the partition field array for KEY partitioning, it
  also verifies that all fields in the list of fields is actually a part of
  the table.
  SYNOPSIS
    handle_list_of_fields()
    it                   A list of field names for the partition function
    table                TABLE object for which partition fields are set-up
    part_info            Reference to partitioning data structure
    sub_part             Is the table subpartitioned as well
  RETURN VALUE
    TRUE                 Fields in list of fields not part of table
    FALSE                All fields ok and array created
  DESCRIPTION
    find_field_in_table_sef finds the field given its name. All fields get
    GET_FIXED_FIELDS_FLAG set.
*/

static bool handle_list_of_fields(List_iterator<char> it,
                                  TABLE *table,
                                  partition_info *part_info,
                                  bool sub_part)
{
  Field *field;
  bool result;
  char *field_name;
  DBUG_ENTER("handle_list_of_fields");

  while ((field_name= it++))
  {
    field= find_field_in_table_sef(table, field_name);
    if (likely(field != 0))
      field->flags|= GET_FIXED_FIELDS_FLAG;
    else
    {
      my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
      clear_field_flag(table);
      result= TRUE;
      goto end;
    }
  }
  result= set_up_field_array(table, sub_part);
end:
  DBUG_RETURN(result);
}


/*
  This function is used to build an array of partition fields for the
  partitioning function and subpartitioning function. The partitioning
  function is an item tree that must reference at least one field in the
  table. This is checked first in the parser that the function doesn't
  contain non-cacheable parts (like a random function) and by checking
  here that the function isn't a constant function.
  SYNOPSIS
    fix_fields_part_func()
    thd                  The thread object
    tables               A list of one table, the partitioned table
    func_expr            The item tree reference of the partition function
    part_info            Reference to partitioning data structure
    sub_part             Is the table subpartitioned as well
  RETURN VALUE
    TRUE                 An error occurred, something was wrong with the
                         partition function.
    FALSE                Ok, a partition field array was created
  DESCRIPTION
    The function uses a new feature in fix_fields where the flag 
    GET_FIXED_FIELDS_FLAG is set for all fields in the item tree.
    This field must always be reset before returning from the function
    since it is used for other purposes as well.
*/

static bool fix_fields_part_func(THD *thd, TABLE_LIST *tables,
                                 Item* func_expr, partition_info *part_info,
                                 bool sub_part)
{
  /*
    Calculate the number of fields in the partition function.
    Use it allocate memory for array of Field pointers.
    Initialise array of field pointers. Use information set when
    calling fix_fields and reset it immediately after.
    The get_fields_in_item_tree activates setting of bit in flags
    on the field object.
  */

  bool result= TRUE;
  TABLE *table= tables->table;
  TABLE_LIST *save_table_list, *save_first_table, *save_last_table;
  int error;
  Name_resolution_context *context;
  DBUG_ENTER("fix_fields_part_func");

  context= thd->lex->current_context();
  table->map= 1; //To ensure correct calculation of const item
  table->get_fields_in_item_tree= TRUE;
  save_table_list= context->table_list;
  save_first_table= context->first_name_resolution_table;
  save_last_table= context->last_name_resolution_table;
  context->table_list= tables;
  context->first_name_resolution_table= tables;
  context->last_name_resolution_table= NULL;
  func_expr->walk(&Item::change_context_processor, (byte*) context);
  thd->where= "partition function";
  error= func_expr->fix_fields(thd, (Item**)0);
  context->table_list= save_table_list;
  context->first_name_resolution_table= save_first_table;
  context->last_name_resolution_table= save_last_table;
  if (unlikely(error))
  {
    DBUG_PRINT("info", ("Field in partition function not part of table"));
    clear_field_flag(table);
    goto end;
  }
  if (unlikely(func_expr->const_item()))
  {
    my_error(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
    clear_field_flag(table);
    goto end;
  }
  result= set_up_field_array(table, sub_part);
end:
  table->get_fields_in_item_tree= FALSE;
  table->map= 0; //Restore old value
  DBUG_RETURN(result);
}


/*
  This function verifies that if there is a primary key that it contains
  all the fields of the partition function.
  This is a temporary limitation that will hopefully be removed after a
  while.
  SYNOPSIS
    check_primary_key()
    table                TABLE object for which partition fields are set-up
  RETURN VALUES
    TRUE                 Not all fields in partitioning function was part
                         of primary key
    FALSE                Ok, all fields of partitioning function were part
                         of primary key
*/

static bool check_primary_key(TABLE *table)
{
  uint primary_key= table->s->primary_key;
  bool all_fields, some_fields, result= FALSE;
  DBUG_ENTER("check_primary_key");

  if (primary_key < MAX_KEY)
  {
    set_indicator_in_key_fields(table->key_info+primary_key);
    check_fields_in_PF(table->part_info->full_part_field_array,
                        &all_fields, &some_fields);
    clear_indicator_in_key_fields(table->key_info+primary_key);
    if (unlikely(!all_fields))
    {
      my_error(ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF,MYF(0),"PRIMARY KEY");
      result= TRUE;
    }
  }
  DBUG_RETURN(result);
}


/*
  This function verifies that if there is a unique index that it contains
  all the fields of the partition function.
  This is a temporary limitation that will hopefully be removed after a
  while.
  SYNOPSIS
    check_unique_keys()
    table                TABLE object for which partition fields are set-up
  RETURN VALUES
    TRUE                 Not all fields in partitioning function was part
                         of all unique keys
    FALSE                Ok, all fields of partitioning function were part
                         of unique keys
*/

static bool check_unique_keys(TABLE *table)
{
  bool all_fields, some_fields, result= FALSE;
  uint keys= table->s->keys, i;
  DBUG_ENTER("check_unique_keys");
  for (i= 0; i < keys; i++)
  {
    if (table->key_info[i].flags & HA_NOSAME) //Unique index
    {
      set_indicator_in_key_fields(table->key_info+i);
      check_fields_in_PF(table->part_info->full_part_field_array,
                         &all_fields, &some_fields);
      clear_indicator_in_key_fields(table->key_info+i);
      if (unlikely(!all_fields))
      {
        my_error(ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF,MYF(0),"UNIQUE INDEX");
        result= TRUE;
        break;
      }
    }
  }
  DBUG_RETURN(result);
}


/*
  An important optimisation is whether a range on a field can select a subset
  of the partitions.
  A prerequisite for this to happen is that the PF is a growing function OR
  a shrinking function.
  This can never happen for a multi-dimensional PF. Thus this can only happen
  with PF with at most one field involved in the PF.
  The idea is that if the function is a growing function and you know that
  the field of the PF is 4 <= A <= 6 then we can convert this to a range
  in the PF instead by setting the range to PF(4) <= PF(A) <= PF(6). In the
  case of RANGE PARTITIONING and LIST PARTITIONING this can be used to
  calculate a set of partitions rather than scanning all of them.
  Thus the following prerequisites are there to check if sets of partitions
  can be found.
  1) Only possible for RANGE and LIST partitioning (not for subpartitioning)
  2) Only possible if PF only contains 1 field
  3) Possible if PF is a growing function of the field
  4) Possible if PF is a shrinking function of the field
  OBSERVATION:
  1) IF f1(A) is a growing function AND f2(A) is a growing function THEN
     f1(A) + f2(A) is a growing function
     f1(A) * f2(A) is a growing function if f1(A) >= 0 and f2(A) >= 0
  2) IF f1(A) is a growing function and f2(A) is a shrinking function THEN
     f1(A) / f2(A) is a growing function if f1(A) >= 0 and f2(A) > 0
  3) IF A is a growing function then a function f(A) that removes the
     least significant portion of A is a growing function
     E.g. DATE(datetime) is a growing function
     MONTH(datetime) is not a growing/shrinking function
  4) IF f1(A) is a growing function and f2(A) is a growing function THEN
     f1(f2(A)) and f2(f1(A)) are also growing functions
  5) IF f1(A) is a shrinking function and f2(A) is a growing function THEN
     f1(f2(A)) is a shrinking function and f2(f1(A)) is a shrinking function
  6) f1(A) = A is a growing function
  7) f1(A) = A*a + b (where a and b are constants) is a growing function

  By analysing the item tree of the PF we can use these deducements and
  derive whether the PF is a growing function or a shrinking function or
  neither of it.

  If the PF is range capable then a flag is set on the table object
  indicating this to notify that we can use also ranges on the field
  of the PF to deduce a set of partitions if the fields of the PF were
  not all fully bound.
  SYNOPSIS
    check_range_capable_PF()
    table                TABLE object for which partition fields are set-up
  DESCRIPTION
    Support for this is not implemented yet.
*/

void check_range_capable_PF(TABLE *table)
{
  DBUG_ENTER("check_range_capable_PF");
  DBUG_VOID_RETURN;
}


/*
  Set up partition key maps
  SYNOPSIS
    set_up_partition_key_maps()
    table                TABLE object for which partition fields are set-up
    part_info            Reference to partitioning data structure
  RETURN VALUES
    None
  DESCRIPTION
  This function sets up a couple of key maps to be able to quickly check
  if an index ever can be used to deduce the partition fields or even
  a part of the fields of the  partition function.
  We set up the following key_map's.
  PF = Partition Function
  1) All fields of the PF is set even by equal on the first fields in the
     key
  2) All fields of the PF is set if all fields of the key is set
  3) At least one field in the PF is set if all fields is set
  4) At least one field in the PF is part of the key
*/

static void set_up_partition_key_maps(TABLE *table,
                                      partition_info *part_info)
{
  uint keys= table->s->keys, i;
  bool all_fields, some_fields;
  DBUG_ENTER("set_up_partition_key_maps");

  part_info->all_fields_in_PF.clear_all();
  part_info->all_fields_in_PPF.clear_all();
  part_info->all_fields_in_SPF.clear_all();
  part_info->some_fields_in_PF.clear_all();
  for (i= 0; i < keys; i++)
  {
    set_indicator_in_key_fields(table->key_info+i);
    check_fields_in_PF(part_info->full_part_field_array,
                       &all_fields, &some_fields);
    if (all_fields)
      part_info->all_fields_in_PF.set_bit(i);
    if (some_fields)
      part_info->some_fields_in_PF.set_bit(i);
    if (is_sub_partitioned(part_info))
    {
      check_fields_in_PF(part_info->part_field_array,
                         &all_fields, &some_fields);
      if (all_fields)
        part_info->all_fields_in_PPF.set_bit(i);
      check_fields_in_PF(part_info->subpart_field_array,
                         &all_fields, &some_fields);
      if (all_fields)
        part_info->all_fields_in_SPF.set_bit(i);
    }
    clear_indicator_in_key_fields(table->key_info+i);
  }
  DBUG_VOID_RETURN;
}


/*
  Set-up all function pointers for calculation of partition id,
  subpartition id and the upper part in subpartitioning. This is to speed up
  execution of get_partition_id which is executed once every record to be
  written and deleted and twice for updates.
  SYNOPSIS
    set_up_partition_function_pointers()
    part_info            Reference to partitioning data structure
*/

static void set_up_partition_func_pointers(partition_info *part_info)
{
  if (is_sub_partitioned(part_info))
  {
    if (part_info->part_type == RANGE_PARTITION)
    {
      part_info->get_part_partition_id= get_partition_id_range;
      if (part_info->list_of_subpart_fields)
      {
        if (part_info->linear_hash_ind)
        {
          part_info->get_partition_id= get_partition_id_range_sub_linear_key;
          part_info->get_subpartition_id= get_partition_id_linear_key_sub;
        }
        else
        {
          part_info->get_partition_id= get_partition_id_range_sub_key;
          part_info->get_subpartition_id= get_partition_id_key_sub;
        }
      }
      else
      {
        if (part_info->linear_hash_ind)
        {
          part_info->get_partition_id= get_partition_id_range_sub_linear_hash;
          part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
        }
        else
        {
          part_info->get_partition_id= get_partition_id_range_sub_hash;
          part_info->get_subpartition_id= get_partition_id_hash_sub;
        }
      }
    }
    else //LIST Partitioning
    {
      part_info->get_part_partition_id= get_partition_id_list;
      if (part_info->list_of_subpart_fields)
      {
        if (part_info->linear_hash_ind)
        {
          part_info->get_partition_id= get_partition_id_list_sub_linear_key;
          part_info->get_subpartition_id= get_partition_id_linear_key_sub;
        }
        else
        {
          part_info->get_partition_id= get_partition_id_list_sub_key;
          part_info->get_subpartition_id= get_partition_id_key_sub;
        }
      }
      else
      {
        if (part_info->linear_hash_ind)
        {
          part_info->get_partition_id= get_partition_id_list_sub_linear_hash;
          part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
        }
        else
        {
          part_info->get_partition_id= get_partition_id_list_sub_hash;
          part_info->get_subpartition_id= get_partition_id_hash_sub;
        }
      }
    }
  }
  else //No subpartitioning
  {
    part_info->get_part_partition_id= NULL;
    part_info->get_subpartition_id= NULL;
    if (part_info->part_type == RANGE_PARTITION)
      part_info->get_partition_id= get_partition_id_range;
    else if (part_info->part_type == LIST_PARTITION)
      part_info->get_partition_id= get_partition_id_list;
    else //HASH partitioning
    {
      if (part_info->list_of_part_fields)
      {
        if (part_info->linear_hash_ind)
          part_info->get_partition_id= get_partition_id_linear_key_nosub;
        else
          part_info->get_partition_id= get_partition_id_key_nosub;
      }
      else
      {
        if (part_info->linear_hash_ind)
          part_info->get_partition_id= get_partition_id_linear_hash_nosub;
        else
          part_info->get_partition_id= get_partition_id_hash_nosub;
      }
    }
  }
}
          
        
/*
  For linear hashing we need a mask which is on the form 2**n - 1 where
  2**n >= no_parts. Thus if no_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7.
  SYNOPSIS
    set_linear_hash_mask()
    part_info            Reference to partitioning data structure
    no_parts             Number of parts in linear hash partitioning
*/

static void set_linear_hash_mask(partition_info *part_info, uint no_parts)
{
  uint mask;
  for (mask= 1; mask < no_parts; mask<<=1)
    ;
  part_info->linear_hash_mask= mask - 1;
}


/*
  This function calculates the partition id provided the result of the hash
  function using linear hashing parameters, mask and number of partitions.
  SYNOPSIS
    get_part_id_from_linear_hash()
    hash_value          Hash value calculated by HASH function or KEY function
    mask                Mask calculated previously by set_linear_hash_mask
    no_parts            Number of partitions in HASH partitioned part
  RETURN VALUE
    part_id             The calculated partition identity (starting at 0)
  DESCRIPTION
    The partition is calculated according to the theory of linear hashing.
    See e.g. Linear hashing: a new tool for file and table addressing,
    Reprinted from VLDB-80 in Readings Database Systems, 2nd ed, M. Stonebraker
    (ed.), Morgan Kaufmann 1994.
*/

static uint32 get_part_id_from_linear_hash(longlong hash_value, uint mask,
                                           uint no_parts)
{
  uint32 part_id= (uint32)(hash_value & mask);
  if (part_id >= no_parts)
  {
    uint new_mask= ((mask + 1) >> 1) - 1;
    part_id= hash_value & new_mask;
  }
  return part_id;
}

/*
  fix partition functions

  SYNOPSIS
    fix_partition_func()
    thd                  The thread object
    name                 The name of the partitioned table
    table                TABLE object for which partition fields are set-up

  RETURN VALUE
    TRUE
    FALSE

  DESCRIPTION
    The name parameter contains the full table name and is used to get the
    database name of the table which is used to set-up a correct
    TABLE_LIST object for use in fix_fields.

NOTES
    This function is called as part of opening the table by opening the .frm
    file. It is a part of CREATE TABLE to do this so it is quite permissible
    that errors due to erroneus syntax isn't found until we come here.
    If the user has used a non-existing field in the table is one such example
    of an error that is not discovered until here.
*/

bool fix_partition_func(THD *thd, const char *name, TABLE *table)
{
  bool result= TRUE;
  uint dir_length, home_dir_length;
  TABLE_LIST tables;
  TABLE_SHARE *share= table->s;
  char db_name_string[FN_REFLEN];
  char* db_name;
  partition_info *part_info= table->part_info;
  ulong save_set_query_id= thd->set_query_id;
  DBUG_ENTER("fix_partition_func");

  thd->set_query_id= 0;
  /*
    Set-up the TABLE_LIST object to be a list with a single table
    Set the object to zero to create NULL pointers and set alias
    and real name to table name and get database name from file name.
  */

  bzero((void*)&tables, sizeof(TABLE_LIST));
  tables.alias= tables.table_name= (char*) share->table_name.str;
  tables.table= table;
  tables.next_local= 0;
  tables.next_name_resolution_table= 0;
  strmov(db_name_string, name);
  dir_length= dirname_length(db_name_string);
  db_name_string[dir_length - 1]= 0;
  home_dir_length= dirname_length(db_name_string);
  db_name= &db_name_string[home_dir_length];
  tables.db= db_name;

  if (is_sub_partitioned(part_info))
  {
    DBUG_ASSERT(part_info->subpart_type == HASH_PARTITION);
    /*
      Subpartition is defined. We need to verify that subpartitioning
      function is correct.
    */
    if (part_info->linear_hash_ind)
      set_linear_hash_mask(part_info, part_info->no_subparts);
    if (part_info->list_of_subpart_fields)
    {
      List_iterator<char> it(part_info->subpart_field_list);
      if (unlikely(handle_list_of_fields(it, table, part_info, TRUE)))
        goto end;
    }
    else
    {
      if (unlikely(fix_fields_part_func(thd, &tables,
                                        part_info->subpart_expr, part_info,
                                        TRUE)))
        goto end;
      if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT))
      {
        my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0),
                 "SUBPARTITION");
        goto end;
      }
    }
  }
  DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION);
  /*
    Partition is defined. We need to verify that partitioning
    function is correct.
  */
  if (part_info->part_type == HASH_PARTITION)
  {
    if (part_info->linear_hash_ind)
      set_linear_hash_mask(part_info, part_info->no_parts);
    if (part_info->list_of_part_fields)
    {
      List_iterator<char> it(part_info->part_field_list);
      if (unlikely(handle_list_of_fields(it, table, part_info, FALSE)))
        goto end;
    }
    else
    {
      if (unlikely(fix_fields_part_func(thd, &tables, part_info->part_expr,
                                        part_info, FALSE)))
        goto end;
      if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
      {
        my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
        goto end;
      }
      part_info->part_result_type= INT_RESULT;
    }
  }
  else
  {
    const char *error_str;
    if (part_info->part_type == RANGE_PARTITION)
    {
      error_str= range_str; 
      if (unlikely(check_range_constants(part_info)))
        goto end;
    }
    else if (part_info->part_type == LIST_PARTITION)
    {
      error_str= list_str; 
      if (unlikely(check_list_constants(part_info)))
        goto end;
    }
    else
    {
      DBUG_ASSERT(0);
      my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0));
      goto end;
    }
    if (unlikely(part_info->no_parts < 1))
    {
      my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_str);
      goto end;
    }
    if (unlikely(fix_fields_part_func(thd, &tables, part_info->part_expr,
                                      part_info, FALSE)))
      goto end;
    if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
    {
      my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
      goto end;
    }
  }
  if (unlikely(create_full_part_field_array(table, part_info)))
    goto end;
  if (unlikely(check_primary_key(table)))
    goto end;
  if (unlikely((!table->file->partition_flags() & HA_CAN_PARTITION_UNIQUE) &&
               check_unique_keys(table)))
    goto end;
  check_range_capable_PF(table);
  set_up_partition_key_maps(table, part_info);
  set_up_partition_func_pointers(part_info);
  result= FALSE;
end:
  thd->set_query_id= save_set_query_id;
  DBUG_RETURN(result);
}


/*
  The code below is support routines for the reverse parsing of the 
  partitioning syntax. This feature is very useful to generate syntax for
  all default values to avoid all default checking when opening the frm
  file. It is also used when altering the partitioning by use of various
  ALTER TABLE commands. Finally it is used for SHOW CREATE TABLES.
*/

static int add_write(File fptr, const char *buf, uint len)
{
  uint len_written= my_write(fptr, (const byte*)buf, len, MYF(0));
  if (likely(len == len_written))
    return 0;
  else
    return 1;
}

static int add_string(File fptr, const char *string)
{
  return add_write(fptr, string, strlen(string));
}

static int add_string_len(File fptr, const char *string, uint len)
{
  return add_write(fptr, string, len);
}

static int add_space(File fptr)
{
  return add_string(fptr, space_str);
}

static int add_comma(File fptr)
{
  return add_string(fptr, comma_str);
}

static int add_equal(File fptr)
{
  return add_string(fptr, equal_str);
}

static int add_end_parenthesis(File fptr)
{
  return add_string(fptr, end_paren_str);
}

static int add_begin_parenthesis(File fptr)
{
  return add_string(fptr, begin_paren_str);
}

static int add_part_key_word(File fptr, const char *key_string)
{
  int err= add_string(fptr, key_string);
  err+= add_space(fptr);
  return err + add_begin_parenthesis(fptr);
}

static int add_hash(File fptr)
{
  return add_part_key_word(fptr, hash_str);
}

static int add_partition(File fptr)
{
  strxmov(buff, part_str, space_str, NullS);
  return add_string(fptr, buff);
}

static int add_subpartition(File fptr)
{
  int err= add_string(fptr, sub_str);
  return err + add_partition(fptr);
}

static int add_partition_by(File fptr)
{
  strxmov(buff, part_str, space_str, by_str, space_str, NullS);
  return add_string(fptr, buff);
}

static int add_subpartition_by(File fptr)
{
  int err= add_string(fptr, sub_str);
  return err + add_partition_by(fptr);
}

static int add_key_partition(File fptr, List<char> field_list)
{
  uint i, no_fields;
  int err;
  List_iterator<char> part_it(field_list);
  err= add_part_key_word(fptr, key_str);
  no_fields= field_list.elements;
  i= 0;
  do
  {
    const char *field_str= part_it++;
    err+= add_string(fptr, field_str);
    if (i != (no_fields-1))
      err+= add_comma(fptr);
  } while (++i < no_fields);
  return err;
}

static int add_int(File fptr, longlong number)
{
  llstr(number, buff);
  return add_string(fptr, buff);
}

static int add_keyword_string(File fptr, const char *keyword,
                              const char *keystr)
{
  int err= add_string(fptr, keyword);
  err+= add_space(fptr);
  err+= add_equal(fptr);
  err+= add_space(fptr);
  err+= add_string(fptr, keystr);
  return err + add_space(fptr);
}

static int add_keyword_int(File fptr, const char *keyword, longlong num)
{
  int err= add_string(fptr, keyword);
  err+= add_space(fptr);
  err+= add_equal(fptr);
  err+= add_space(fptr);
  err+= add_int(fptr, num);
  return err + add_space(fptr);
}

static int add_engine(File fptr, handlerton *engine_type)
{
  const char *engine_str= engine_type->name;
  int err= add_string(fptr, "ENGINE = ");
  return err + add_string(fptr, engine_str);
  return err;
}

static int add_partition_options(File fptr, partition_element *p_elem)
{
  int err= 0;
  if (p_elem->tablespace_name)
    err+= add_keyword_string(fptr,"TABLESPACE",p_elem->tablespace_name);
  if (p_elem->nodegroup_id != UNDEF_NODEGROUP)
    err+= add_keyword_int(fptr,"NODEGROUP",(longlong)p_elem->nodegroup_id);
  if (p_elem->part_max_rows)
    err+= add_keyword_int(fptr,"MAX_ROWS",(longlong)p_elem->part_max_rows);
  if (p_elem->part_min_rows)
    err+= add_keyword_int(fptr,"MIN_ROWS",(longlong)p_elem->part_min_rows);
  if (p_elem->data_file_name)
    err+= add_keyword_string(fptr,"DATA DIRECTORY",p_elem->data_file_name);
  if (p_elem->index_file_name)
    err+= add_keyword_string(fptr,"INDEX DIRECTORY",p_elem->index_file_name);
  if (p_elem->part_comment)
    err+= add_keyword_string(fptr, "COMMENT",p_elem->part_comment);
  return err + add_engine(fptr,p_elem->engine_type);
}

static int add_partition_values(File fptr, partition_info *part_info,
                         partition_element *p_elem)
{
  int err= 0;
  if (part_info->part_type == RANGE_PARTITION)
  {
    err+= add_string(fptr, "VALUES LESS THAN ");
    if (p_elem->range_value != LONGLONG_MAX)
    {
      err+= add_begin_parenthesis(fptr);
      err+= add_int(fptr, p_elem->range_value);
      err+= add_end_parenthesis(fptr);
    }
    else
      err+= add_string(fptr, "MAXVALUE");
  }
  else if (part_info->part_type == LIST_PARTITION)
  {
    uint i;
    List_iterator<longlong> list_val_it(p_elem->list_val_list);
    err+= add_string(fptr, "VALUES IN ");
    uint no_items= p_elem->list_val_list.elements;
    err+= add_begin_parenthesis(fptr);
    i= 0;
    do
    {
      longlong *list_value= list_val_it++;
      err+= add_int(fptr, *list_value);
      if (i != (no_items-1))
        err+= add_comma(fptr);
    } while (++i < no_items);
    err+= add_end_parenthesis(fptr);
  }
  return err + add_space(fptr);
}

/*
  Generate the partition syntax from the partition data structure.
  Useful for support of generating defaults, SHOW CREATE TABLES
  and easy partition management.
  SYNOPSIS
    generate_partition_syntax()
    part_info                  The partitioning data structure
    buf_length                 A pointer to the returned buffer length
    use_sql_alloc              Allocate buffer from sql_alloc if true
                               otherwise use my_malloc
    add_default_info           Add info generated by default
  RETURN VALUES
    NULL error
    buf, buf_length            Buffer and its length
  DESCRIPTION
  Here we will generate the full syntax for the given command where all
  defaults have been expanded. By so doing the it is also possible to
  make lots of checks of correctness while at it.
  This could will also be reused for SHOW CREATE TABLES and also for all
  type ALTER TABLE commands focusing on changing the PARTITION structure
  in any fashion.

  The implementation writes the syntax to a temporary file (essentially
  an abstraction of a dynamic array) and if all writes goes well it
  allocates a buffer and writes the syntax into this one and returns it.

  As a security precaution the file is deleted before writing into it. This
  means that no other processes on the machine can open and read the file
  while this processing is ongoing.

  The code is optimised for minimal code size since it is not used in any
  common queries.
*/

char *generate_partition_syntax(partition_info *part_info,
                                uint *buf_length,
                                bool use_sql_alloc,
				bool add_default_info)
{
  uint i,j, no_parts, no_subparts;
  partition_element *part_elem;
  ulonglong buffer_length;
  char path[FN_REFLEN];
  int err= 0;
  DBUG_ENTER("generate_partition_syntax");
  File fptr;
  char *buf= NULL; //Return buffer
  const char *file_name;

  sprintf(path, "%s_%lx_%lx", "part_syntax", current_pid,
          current_thd->thread_id);
  fn_format(path,path,mysql_tmpdir,".psy", MY_REPLACE_EXT);
  file_name= &path[0];
  DBUG_PRINT("info", ("File name = %s", file_name));
  if (unlikely(((fptr= my_open(file_name,O_CREAT|O_RDWR, MYF(MY_WME))) == -1)))
    DBUG_RETURN(NULL);
#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
#else
  my_delete(file_name, MYF(0));
#endif
  err+= add_space(fptr);
  err+= add_partition_by(fptr);
  switch (part_info->part_type)
  {
    case RANGE_PARTITION:
      add_default_info= TRUE;
      err+= add_part_key_word(fptr, range_str);
      break;
    case LIST_PARTITION:
      add_default_info= TRUE;
      err+= add_part_key_word(fptr, list_str);
      break;
    case HASH_PARTITION:
      if (part_info->linear_hash_ind)
        err+= add_string(fptr, "LINEAR ");
      if (part_info->list_of_part_fields)
        err+= add_key_partition(fptr, part_info->part_field_list);
      else
        err+= add_hash(fptr);
      break;
    default:
      DBUG_ASSERT(0);
      /* We really shouldn't get here, no use in continuing from here */
      current_thd->fatal_error();
      DBUG_RETURN(NULL);
  }
  if (part_info->part_expr)
    err+= add_string_len(fptr, part_info->part_func_string,
                         part_info->part_func_len);
  err+= add_end_parenthesis(fptr);
  err+= add_space(fptr);
  if (is_sub_partitioned(part_info))
  {
    err+= add_subpartition_by(fptr);
    /* Must be hash partitioning for subpartitioning */
    if (part_info->list_of_subpart_fields)
      err+= add_key_partition(fptr, part_info->subpart_field_list);
    else
      err+= add_hash(fptr);
    if (part_info->subpart_expr)
      err+= add_string_len(fptr, part_info->subpart_func_string,
                           part_info->subpart_func_len);
    err+= add_end_parenthesis(fptr);
    err+= add_space(fptr);
  }
  if (add_default_info)
  {
  err+= add_begin_parenthesis(fptr);
  List_iterator<partition_element> part_it(part_info->partitions);
  no_parts= part_info->no_parts;
  no_subparts= part_info->no_subparts;
  i= 0;
  do
  {
    part_elem= part_it++;
    err+= add_partition(fptr);
    err+= add_string(fptr, part_elem->partition_name);
    err+= add_space(fptr);
    err+= add_partition_values(fptr, part_info, part_elem);
    if (!is_sub_partitioned(part_info))
      err+= add_partition_options(fptr, part_elem);
    if (is_sub_partitioned(part_info))
    {
      err+= add_space(fptr);
      err+= add_begin_parenthesis(fptr);
      List_iterator<partition_element> sub_it(part_elem->subpartitions);
      j= 0;
      do
      {
        part_elem= sub_it++;
        err+= add_subpartition(fptr);
        err+= add_string(fptr, part_elem->partition_name);
        err+= add_space(fptr);
        err+= add_partition_options(fptr, part_elem);
        if (j != (no_subparts-1))
        {
          err+= add_comma(fptr);
          err+= add_space(fptr);
        }
        else
          err+= add_end_parenthesis(fptr);
      } while (++j < no_subparts);
    }
    if (i != (no_parts-1))
    {
      err+= add_comma(fptr);
      err+= add_space(fptr);
    }
    else
      err+= add_end_parenthesis(fptr);
  } while (++i < no_parts);
  }
  if (err)
    goto close_file;
  buffer_length= my_seek(fptr, 0L,MY_SEEK_END,MYF(0));
  if (unlikely(buffer_length == MY_FILEPOS_ERROR))
    goto close_file;
  if (unlikely(my_seek(fptr, 0L, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR))
    goto close_file;
  *buf_length= (uint)buffer_length;
  if (use_sql_alloc)
    buf= sql_alloc(*buf_length+1);
  else
    buf= my_malloc(*buf_length+1, MYF(MY_WME));
  if (!buf)
    goto close_file;

  if (unlikely(my_read(fptr, (byte*)buf, *buf_length, MYF(MY_FNABP))))
  {
    if (!use_sql_alloc)
      my_free(buf, MYF(0));
    else
      buf= NULL;
  }
  else
    buf[*buf_length]= 0;

close_file:
  /*
    Delete the file before closing to ensure the file doesn't get synched
    to disk unnecessary. We only used the file system as a dynamic array
    implementation so we are not really interested in getting the file
    present on disk.
    This is not possible on Windows so here it has to be done after closing
    the file. Also on Unix we delete immediately after opening to ensure no
    other process can read the information written into the file.
  */
  my_close(fptr, MYF(0));
#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
  my_delete(file_name, MYF(0));
#endif
  DBUG_RETURN(buf);
}


/*
  Check if partition key fields are modified and if it can be handled by the
  underlying storage engine.
  SYNOPSIS
    partition_key_modified
    table                TABLE object for which partition fields are set-up
    fields               A list of the to be modifed
  RETURN VALUES
    TRUE                 Need special handling of UPDATE
    FALSE                Normal UPDATE handling is ok
*/

bool partition_key_modified(TABLE *table, List<Item> &fields)
{
  List_iterator_fast<Item> f(fields);
  partition_info *part_info= table->part_info;
  Item_field *item_field;
  DBUG_ENTER("partition_key_modified");
  if (!part_info)
    DBUG_RETURN(FALSE);
  if (table->file->partition_flags() & HA_CAN_UPDATE_PARTITION_KEY)
    DBUG_RETURN(FALSE);
  f.rewind();
  while ((item_field=(Item_field*) f++))
    if (item_field->field->flags & FIELD_IN_PART_FUNC_FLAG)
      DBUG_RETURN(TRUE);
  DBUG_RETURN(FALSE);
}


/*
  The next set of functions are used to calculate the partition identity.
  A handler sets up a variable that corresponds to one of these functions
  to be able to quickly call it whenever the partition id needs to calculated
  based on the record in table->record[0] (or set up to fake that).
  There are 4 functions for hash partitioning and 2 for RANGE/LIST partitions.
  In addition there are 4 variants for RANGE subpartitioning and 4 variants
  for LIST subpartitioning thus in total there are 14 variants of this
  function.

  We have a set of support functions for these 14 variants. There are 4
  variants of hash functions and there is a function for each. The KEY
  partitioning uses the function calculate_key_value to calculate the hash
  value based on an array of fields. The linear hash variants uses the
  method get_part_id_from_linear_hash to get the partition id using the
  hash value and some parameters calculated from the number of partitions.
*/

/*
  Calculate hash value for KEY partitioning using an array of fields.
  SYNOPSIS
    calculate_key_value()
    field_array             An array of the fields in KEY partitioning
  RETURN VALUE
    hash_value calculated
  DESCRIPTION
    Uses the hash function on the character set of the field. Integer and
    floating point fields use the binary character set by default.
*/

static uint32 calculate_key_value(Field **field_array)
{
  uint32 hashnr= 0;
  ulong nr2= 4;
  do
  {
    Field *field= *field_array;
    if (field->is_null())
    {
      hashnr^= (hashnr << 1) | 1;
    }
    else
    {
      uint len= field->pack_length();
      ulong nr1= 1;
      CHARSET_INFO *cs= field->charset();
      cs->coll->hash_sort(cs, (uchar*)field->ptr, len, &nr1, &nr2);
      hashnr^= (uint32)nr1;
    }
  } while (*(++field_array));
  return hashnr;
}


/*
  A simple support function to calculate part_id given local part and
  sub part.
  SYNOPSIS
    get_part_id_for_sub()
    loc_part_id             Local partition id
    sub_part_id             Subpartition id
    no_subparts             Number of subparts
*/

inline
static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
                                  uint no_subparts)
{
  return (uint32)((loc_part_id * no_subparts) + sub_part_id);
}


/*
  Calculate part_id for (SUB)PARTITION BY HASH
  SYNOPSIS
    get_part_id_hash()
    no_parts                 Number of hash partitions
    part_expr                Item tree of hash function
  RETURN VALUE
    Calculated partition id
*/

inline
static uint32 get_part_id_hash(uint no_parts,
                               Item *part_expr)
{
  DBUG_ENTER("get_part_id_hash");
  DBUG_RETURN((uint32)(part_expr->val_int() % no_parts));
}


/*
  Calculate part_id for (SUB)PARTITION BY LINEAR HASH
  SYNOPSIS
    get_part_id_linear_hash()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
    no_parts            Number of hash partitions
    part_expr           Item tree of hash function
  RETURN VALUE
    Calculated partition id
*/

inline
static uint32 get_part_id_linear_hash(partition_info *part_info,
                                      uint no_parts,
                                      Item *part_expr)
{
  DBUG_ENTER("get_part_id_linear_hash");
  DBUG_RETURN(get_part_id_from_linear_hash(part_expr->val_int(),
                                           part_info->linear_hash_mask,
                                           no_parts));
}


/*
  Calculate part_id for (SUB)PARTITION BY KEY
  SYNOPSIS
    get_part_id_key()
    field_array         Array of fields for PARTTION KEY
    no_parts            Number of KEY partitions
  RETURN VALUE
    Calculated partition id
*/

inline
static uint32 get_part_id_key(Field **field_array,
                              uint no_parts)
{
  DBUG_ENTER("get_part_id_key");
  DBUG_RETURN(calculate_key_value(field_array) % no_parts);
}


/*
  Calculate part_id for (SUB)PARTITION BY LINEAR KEY
  SYNOPSIS
    get_part_id_linear_key()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
    field_array         Array of fields for PARTTION KEY
    no_parts            Number of KEY partitions
  RETURN VALUE
    Calculated partition id
*/

inline
static uint32 get_part_id_linear_key(partition_info *part_info,
                                     Field **field_array,
                                     uint no_parts)
{
  DBUG_ENTER("get_partition_id_linear_key");
  DBUG_RETURN(get_part_id_from_linear_hash(calculate_key_value(field_array),
                                           part_info->linear_hash_mask,
                                           no_parts));
}

/*
  This function is used to calculate the partition id where all partition
  fields have been prepared to point to a record where the partition field
  values are bound.
  SYNOPSIS
    get_partition_id()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
    part_id             The partition id is returned through this pointer
  RETURN VALUE
    part_id
    return TRUE means that the fields of the partition function didn't fit
    into any partition and thus the values of the PF-fields are not allowed.
  DESCRIPTION
    A routine used from write_row, update_row and delete_row from any
    handler supporting partitioning. It is also a support routine for
    get_partition_set used to find the set of partitions needed to scan
    for a certain index scan or full table scan.
    
    It is actually 14 different variants of this function which are called
    through a function pointer.

    get_partition_id_list
    get_partition_id_range
    get_partition_id_hash_nosub
    get_partition_id_key_nosub
    get_partition_id_linear_hash_nosub
    get_partition_id_linear_key_nosub
    get_partition_id_range_sub_hash
    get_partition_id_range_sub_key
    get_partition_id_range_sub_linear_hash
    get_partition_id_range_sub_linear_key
    get_partition_id_list_sub_hash
    get_partition_id_list_sub_key
    get_partition_id_list_sub_linear_hash
    get_partition_id_list_sub_linear_key
*/

/*
  This function is used to calculate the main partition to use in the case of
  subpartitioning and we don't know enough to get the partition identity in
  total.
  SYNOPSIS
    get_part_partition_id()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
    part_id             The partition id is returned through this pointer
  RETURN VALUE
    part_id
    return TRUE means that the fields of the partition function didn't fit
    into any partition and thus the values of the PF-fields are not allowed.
  DESCRIPTION
    
    It is actually 6 different variants of this function which are called
    through a function pointer.

    get_partition_id_list
    get_partition_id_range
    get_partition_id_hash_nosub
    get_partition_id_key_nosub
    get_partition_id_linear_hash_nosub
    get_partition_id_linear_key_nosub
*/


bool get_partition_id_list(partition_info *part_info,
                           uint32 *part_id)
{
  DBUG_ENTER("get_partition_id_list");
  LIST_PART_ENTRY *list_array= part_info->list_array;
  uint list_index;
  longlong list_value;
  uint min_list_index= 0, max_list_index= part_info->no_list_values - 1;
  longlong part_func_value= part_info->part_expr->val_int();
  while (max_list_index >= min_list_index)
  {
    list_index= (max_list_index + min_list_index) >> 1;
    list_value= list_array[list_index].list_value;
    if (list_value < part_func_value)
      min_list_index= list_index + 1;
    else if (list_value > part_func_value)
    {
      if (!list_index)
        goto notfound;
      max_list_index= list_index - 1;
    }
    else
    {
      *part_id= (uint32)list_array[list_index].partition_id;
      DBUG_RETURN(FALSE);
    }
  }
notfound:
  *part_id= 0;
  DBUG_RETURN(TRUE);
}


/*
  Find the sub-array part_info->list_array that corresponds to given interval

  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)
                        FALSE - the interval is (-inf; a] or (-inf; a)
      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
    1. For [a; +inf) or (a; +inf)-type intervals (left_endpoint==TRUE), the 
       sought sub-array starts at some index idx and continues till array end.
       The function returns first number idx, such that 
       list_array[idx].list_value is contained within the passed interval.
       
    2. For (-inf; a] or (-inf; a)-type intervals (left_endpoint==FALSE), the
       sought sub-array starts at array start and continues till some last 
       index idx.
       The function returns first number idx, such that 
       list_array[idx].list_value is NOT contained within the passed interval.
       If all array elements are contained, part_info->no_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
    already wrapped in a get_next()-like function.

  RETURN
    The edge of corresponding sub-array of part_info->list_array
*/

uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
                                       bool left_endpoint,
                                       bool include_endpoint)
{
  DBUG_ENTER("get_list_array_idx_for_endpoint");
  LIST_PART_ENTRY *list_array= part_info->list_array;
  uint list_index;
  longlong list_value;
  uint min_list_index= 0, max_list_index= part_info->no_list_values - 1;
  /* Get the partitioning function value for the endpoint */
  longlong part_func_value= part_info->part_expr->val_int();
  while (max_list_index >= min_list_index)
  {
    list_index= (max_list_index + min_list_index) >> 1;
    list_value= list_array[list_index].list_value;
    if (list_value < part_func_value)
      min_list_index= list_index + 1;
    else if (list_value > part_func_value)
    {
      if (!list_index)
        goto notfound;
      max_list_index= list_index - 1;
    }
    else 
    {
      DBUG_RETURN(list_index + test(left_endpoint ^ include_endpoint));
    }
  }
notfound:
  if (list_value < part_func_value)
    list_index++;
  DBUG_RETURN(list_index);
}


bool get_partition_id_range(partition_info *part_info,
                            uint32 *part_id)
{
  DBUG_ENTER("get_partition_id_int_range");
  longlong *range_array= part_info->range_int_array;
  uint max_partition= part_info->no_parts - 1;
  uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
  longlong part_func_value= part_info->part_expr->val_int();
  while (max_part_id > min_part_id)
  {
    loc_part_id= (max_part_id + min_part_id + 1) >> 1;
    if (range_array[loc_part_id] < part_func_value)
      min_part_id= loc_part_id + 1;
    else
      max_part_id= loc_part_id - 1;
  }
  loc_part_id= max_part_id;
  if (part_func_value >= range_array[loc_part_id])
    if (loc_part_id != max_partition)
      loc_part_id++;
  *part_id= (uint32)loc_part_id;
  if (loc_part_id == max_partition)
    if (range_array[loc_part_id] != LONGLONG_MAX)
      if (part_func_value >= range_array[loc_part_id])
        DBUG_RETURN(TRUE);
  DBUG_RETURN(FALSE);
}


/*
  Find the sub-array of part_info->range_int_array that covers given interval
 
  SYNOPSIS 
    get_partition_id_range_for_endpoint()
      part_info         Partitioning info (partitioning type must be RANGE)
      left_endpoint     TRUE  - the interval is [a; +inf) or (a; +inf)
                        FALSE - the interval is (-inf; a] or (-inf; a).
      include_endpoint  TRUE <=> the endpoint itself is included in the
                        interval

  DESCRIPTION
    This function finds the sub-array of part_info->range_int_array where the
    elements have non-empty intersections with the given interval.
 
    A range_int_array element at index idx represents the interval
      
      [range_int_array[idx-1], range_int_array[idx]),

    intervals are disjoint and ordered by their right bound, so
    
    1. For [a; +inf) or (a; +inf)-type intervals (left_endpoint==TRUE), the
       sought sub-array starts at some index idx and continues till array end.
       The function returns first number idx, such that the interval
       represented by range_int_array[idx] has non empty intersection with 
       the passed interval.
       
    2. For (-inf; a] or (-inf; a)-type intervals (left_endpoint==FALSE), the
       sought sub-array starts at array start and continues till some last
       index idx.
       The function returns first number idx, such that the interval
       represented by range_int_array[idx] has EMPTY intersection with the
       passed interval.
       If the interval represented by the last array element has non-empty 
       intersection with the passed interval, part_info->no_parts is
       returned.
       
  RETURN
    The edge of corresponding part_info->range_int_array sub-array.
*/

uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
                                           bool left_endpoint,
                                           bool include_endpoint)
{
  DBUG_ENTER("get_partition_id_range_for_endpoint");
  longlong *range_array= part_info->range_int_array;
  uint max_partition= part_info->no_parts - 1;
  uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
  /* Get the partitioning function value for the endpoint */
  longlong part_func_value= part_info->part_expr->val_int();
  while (max_part_id > min_part_id)
  {
    loc_part_id= (max_part_id + min_part_id + 1) >> 1;
    if (range_array[loc_part_id] < part_func_value)
      min_part_id= loc_part_id + 1;
    else
      max_part_id= loc_part_id - 1;
  }
  loc_part_id= max_part_id;
  if (loc_part_id < max_partition && 
      part_func_value >= range_array[loc_part_id+1])
  {
     loc_part_id++;
  }
  if (left_endpoint)
  {
    if (part_func_value >= range_array[loc_part_id])
      loc_part_id++;
  }
  else 
  {
    if (part_func_value == range_array[loc_part_id])
      loc_part_id += test(include_endpoint);
    else if (part_func_value > range_array[loc_part_id])
      loc_part_id++;
    loc_part_id++;
  }
  DBUG_RETURN(loc_part_id);
}


bool get_partition_id_hash_nosub(partition_info *part_info,
                                 uint32 *part_id)
{
  *part_id= get_part_id_hash(part_info->no_parts, part_info->part_expr);
  return FALSE;
}


bool get_partition_id_linear_hash_nosub(partition_info *part_info,
                                        uint32 *part_id)
{
  *part_id= get_part_id_linear_hash(part_info, part_info->no_parts,
                                    part_info->part_expr);
  return FALSE;
}


bool get_partition_id_key_nosub(partition_info *part_info,
                                uint32 *part_id)
{
  *part_id= get_part_id_key(part_info->part_field_array, part_info->no_parts);
  return FALSE;
}


bool get_partition_id_linear_key_nosub(partition_info *part_info,
                                       uint32 *part_id)
{
  *part_id= get_part_id_linear_key(part_info,
                                   part_info->part_field_array,
                                   part_info->no_parts);
  return FALSE;
}


bool get_partition_id_range_sub_hash(partition_info *part_info,
                                     uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_range_sub_hash");
  if (unlikely(get_partition_id_range(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_hash(no_subparts, part_info->subpart_expr);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_range_sub_linear_hash(partition_info *part_info,
                                            uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_range_sub_linear_hash");
  if (unlikely(get_partition_id_range(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_linear_hash(part_info, no_subparts,
                                       part_info->subpart_expr);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_range_sub_key(partition_info *part_info,
                                    uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_range_sub_key");
  if (unlikely(get_partition_id_range(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_key(part_info->subpart_field_array, no_subparts);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_range_sub_linear_key(partition_info *part_info,
                                           uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_range_sub_linear_key");
  if (unlikely(get_partition_id_range(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_linear_key(part_info,
                                      part_info->subpart_field_array,
                                      no_subparts);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_list_sub_hash(partition_info *part_info,
                                    uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_list_sub_hash");
  if (unlikely(get_partition_id_list(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_hash(no_subparts, part_info->subpart_expr);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_list_sub_linear_hash(partition_info *part_info,
                                           uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_list_sub_linear_hash");
  if (unlikely(get_partition_id_list(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_hash(no_subparts, part_info->subpart_expr);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_list_sub_key(partition_info *part_info,
                                   uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_range_sub_key");
  if (unlikely(get_partition_id_list(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_key(part_info->subpart_field_array, no_subparts);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


bool get_partition_id_list_sub_linear_key(partition_info *part_info,
                                          uint32 *part_id)
{
  uint32 loc_part_id, sub_part_id;
  uint no_subparts;
  DBUG_ENTER("get_partition_id_list_sub_linear_key");
  if (unlikely(get_partition_id_list(part_info, &loc_part_id)))
  {
    DBUG_RETURN(TRUE);
  }
  no_subparts= part_info->no_subparts;
  sub_part_id= get_part_id_linear_key(part_info,
                                      part_info->subpart_field_array,
                                      no_subparts);
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
  DBUG_RETURN(FALSE);
}


/*
  This function is used to calculate the subpartition id
  SYNOPSIS
    get_subpartition_id()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
  RETURN VALUE
    part_id
    The subpartition identity
  DESCRIPTION
    A routine used in some SELECT's when only partial knowledge of the
    partitions is known.
    
    It is actually 4 different variants of this function which are called
    through a function pointer.

    get_partition_id_hash_sub
    get_partition_id_key_sub
    get_partition_id_linear_hash_sub
    get_partition_id_linear_key_sub
*/

uint32 get_partition_id_hash_sub(partition_info *part_info)
{
  return get_part_id_hash(part_info->no_subparts, part_info->subpart_expr);
}


uint32 get_partition_id_linear_hash_sub(partition_info *part_info)
{
  return get_part_id_linear_hash(part_info, part_info->no_subparts,
                                 part_info->subpart_expr);
}


uint32 get_partition_id_key_sub(partition_info *part_info)
{
  return get_part_id_key(part_info->subpart_field_array,
                         part_info->no_subparts);
}


uint32 get_partition_id_linear_key_sub(partition_info *part_info)
{
  return get_part_id_linear_key(part_info,
                                part_info->subpart_field_array,
                                part_info->no_subparts);
}


/*
  Set an indicator on all partition fields that are set by the key 
  SYNOPSIS
    set_PF_fields_in_key()
    key_info                   Information about the index
    key_length                 Length of key
  RETURN VALUE
    TRUE                       Found partition field set by key
    FALSE                      No partition field set by key
*/

static bool set_PF_fields_in_key(KEY *key_info, uint key_length)
{
  KEY_PART_INFO *key_part;
  bool found_part_field= FALSE;
  DBUG_ENTER("set_PF_fields_in_key");

  for (key_part= key_info->key_part; (int)key_length > 0; key_part++)
  {
    if (key_part->null_bit)
      key_length--;
    if (key_part->type == HA_KEYTYPE_BIT)
    {
      if (((Field_bit*)key_part->field)->bit_len)
        key_length--;
    }
    if (key_part->key_part_flag & (HA_BLOB_PART + HA_VAR_LENGTH_PART))
    {
      key_length-= HA_KEY_BLOB_LENGTH;
    }
    if (key_length < key_part->length)
      break;
    key_length-= key_part->length;
    if (key_part->field->flags & FIELD_IN_PART_FUNC_FLAG)
    {
      found_part_field= TRUE;
      key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
    }
  }
  DBUG_RETURN(found_part_field);
}


/*
  We have found that at least one partition field was set by a key, now
  check if a partition function has all its fields bound or not.
  SYNOPSIS
    check_part_func_bound()
    ptr                     Array of fields NULL terminated (partition fields)
  RETURN VALUE
    TRUE                    All fields in partition function are set
    FALSE                   Not all fields in partition function are set
*/

static bool check_part_func_bound(Field **ptr)
{
  bool result= TRUE;
  DBUG_ENTER("check_part_func_bound");

  for (; *ptr; ptr++)
  {
    if (!((*ptr)->flags & GET_FIXED_FIELDS_FLAG))
    {
      result= FALSE;
      break;
    }
  }
  DBUG_RETURN(result);
}


/*
  Get the id of the subpartitioning part by using the key buffer of the
  index scan.
  SYNOPSIS
    get_sub_part_id_from_key()
    table         The table object
    buf           A buffer that can be used to evaluate the partition function
    key_info      The index object
    key_spec      A key_range containing key and key length
  RETURN VALUES
    part_id       Subpartition id to use
  DESCRIPTION
    Use key buffer to set-up record in buf, move field pointers and
    get the partition identity and restore field pointers afterwards.
*/

static uint32 get_sub_part_id_from_key(const TABLE *table,byte *buf,
                                       KEY *key_info,
                                       const key_range *key_spec)
{
  byte *rec0= table->record[0];
  partition_info *part_info= table->part_info;
  uint32 part_id;
  DBUG_ENTER("get_sub_part_id_from_key");

  key_restore(buf, (byte*)key_spec->key, key_info, key_spec->length);
  if (likely(rec0 == buf))
    part_id= part_info->get_subpartition_id(part_info);
  else
  {
    Field **part_field_array= part_info->subpart_field_array;
    set_field_ptr(part_field_array, buf, rec0);
    part_id= part_info->get_subpartition_id(part_info);
    set_field_ptr(part_field_array, rec0, buf);
  }
  DBUG_RETURN(part_id);
}

/*
  Get the id of the partitioning part by using the key buffer of the
  index scan.
  SYNOPSIS
    get_part_id_from_key()
    table         The table object
    buf           A buffer that can be used to evaluate the partition function
    key_info      The index object
    key_spec      A key_range containing key and key length
    part_id       Partition to use
  RETURN VALUES
    TRUE          Partition to use not found
    FALSE         Ok, part_id indicates partition to use
  DESCRIPTION
    Use key buffer to set-up record in buf, move field pointers and
    get the partition identity and restore field pointers afterwards.
*/
bool get_part_id_from_key(const TABLE *table, byte *buf, KEY *key_info,
                          const key_range *key_spec, uint32 *part_id)
{
  bool result;
  byte *rec0= table->record[0];
  partition_info *part_info= table->part_info;
  DBUG_ENTER("get_part_id_from_key");

  key_restore(buf, (byte*)key_spec->key, key_info, key_spec->length);
  if (likely(rec0 == buf))
    result= part_info->get_part_partition_id(part_info, part_id);
  else
  {
    Field **part_field_array= part_info->part_field_array;
    set_field_ptr(part_field_array, buf, rec0);
    result= part_info->get_part_partition_id(part_info, part_id);
    set_field_ptr(part_field_array, rec0, buf);
  }
  DBUG_RETURN(result);
}

/*
  Get the partitioning id of the full PF by using the key buffer of the
  index scan.
  SYNOPSIS
    get_full_part_id_from_key()
    table         The table object
    buf           A buffer that is used to evaluate the partition function
    key_info      The index object
    key_spec      A key_range containing key and key length
    part_spec     A partition id containing start part and end part
  RETURN VALUES
    part_spec
    No partitions to scan is indicated by end_part > start_part when returning
  DESCRIPTION
    Use key buffer to set-up record in buf, move field pointers if needed and
    get the partition identity and restore field pointers afterwards.
*/

void get_full_part_id_from_key(const TABLE *table, byte *buf,
                               KEY *key_info,
                               const key_range *key_spec,
                               part_id_range *part_spec)
{
  bool result;
  partition_info *part_info= table->part_info;
  byte *rec0= table->record[0];
  DBUG_ENTER("get_full_part_id_from_key");

  key_restore(buf, (byte*)key_spec->key, key_info, key_spec->length);
  if (likely(rec0 == buf))
    result= part_info->get_partition_id(part_info, &part_spec->start_part);
  else
  {
    Field **part_field_array= part_info->full_part_field_array;
    set_field_ptr(part_field_array, buf, rec0);
    result= part_info->get_partition_id(part_info, &part_spec->start_part);
    set_field_ptr(part_field_array, rec0, buf);
  }
  part_spec->end_part= part_spec->start_part;
  if (unlikely(result))
    part_spec->start_part++;
  DBUG_VOID_RETURN;
}
    
/*
  Get the set of partitions to use in query.
  SYNOPSIS
    get_partition_set()
    table         The table object
    buf           A buffer that can be used to evaluate the partition function
    index         The index of the key used, if MAX_KEY no index used
    key_spec      A key_range containing key and key length
    part_spec     Contains start part, end part and indicator if bitmap is
                  used for which partitions to scan
  DESCRIPTION
    This function is called to discover which partitions to use in an index
    scan or a full table scan.
    It returns a range of partitions to scan. If there are holes in this
    range with partitions that are not needed to scan a bit array is used
    to signal which partitions to use and which not to use.
    If start_part > end_part at return it means no partition needs to be
    scanned. If start_part == end_part it always means a single partition
    needs to be scanned.
  RETURN VALUE
    part_spec
*/
void get_partition_set(const TABLE *table, byte *buf, const uint index,
                       const key_range *key_spec, part_id_range *part_spec)
{
  partition_info *part_info= table->part_info;
  uint no_parts= get_tot_partitions(part_info), i, part_id;
  uint sub_part= no_parts;
  uint32 part_part= no_parts;
  KEY *key_info= NULL;
  bool found_part_field= FALSE;
  DBUG_ENTER("get_partition_set");

  part_spec->use_bit_array= FALSE;
  part_spec->start_part= 0;
  part_spec->end_part= no_parts - 1;
  if ((index < MAX_KEY) && 
       key_spec->flag == (uint)HA_READ_KEY_EXACT &&
       part_info->some_fields_in_PF.is_set(index))
  {
    key_info= table->key_info+index;
    /*
      The index can potentially provide at least one PF-field (field in the
      partition function). Thus it is interesting to continue our probe.
    */
    if (key_spec->length == key_info->key_length)
    {
      /*
        The entire key is set so we can check whether we can immediately
        derive either the complete PF or if we can derive either
        the top PF or the subpartitioning PF. This can be established by
        checking precalculated bits on each index.
      */
      if (part_info->all_fields_in_PF.is_set(index))
      {
        /*
          We can derive the exact partition to use, no more than this one
          is needed.
        */
        get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
        DBUG_VOID_RETURN;
      }
      else if (is_sub_partitioned(part_info))
      {
        if (part_info->all_fields_in_SPF.is_set(index))
          sub_part= get_sub_part_id_from_key(table, buf, key_info, key_spec);
        else if (part_info->all_fields_in_PPF.is_set(index))
        {
          if (get_part_id_from_key(table,buf,key_info,key_spec,(uint32*)&part_part))
          {
            /*
              The value of the RANGE or LIST partitioning was outside of
              allowed values. Thus it is certain that the result of this
              scan will be empty.
            */
            part_spec->start_part= no_parts;
            DBUG_VOID_RETURN;
          }
        }
      }
    }
    else
    {
      /*
        Set an indicator on all partition fields that are bound.
        If at least one PF-field was bound it pays off to check whether
        the PF or PPF or SPF has been bound.
        (PF = Partition Function, SPF = Subpartition Function and
         PPF = Partition Function part of subpartitioning)
      */
      if ((found_part_field= set_PF_fields_in_key(key_info,
                                                  key_spec->length)))
      {
        if (check_part_func_bound(part_info->full_part_field_array))
        {
          /*
            We were able to bind all fields in the partition function even
            by using only a part of the key. Calculate the partition to use.
          */
          get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
          clear_indicator_in_key_fields(key_info);
          DBUG_VOID_RETURN; 
        }
        else if (check_part_func_bound(part_info->part_field_array))
          sub_part= get_sub_part_id_from_key(table, buf, key_info, key_spec);
        else if (check_part_func_bound(part_info->subpart_field_array))
        {
          if (get_part_id_from_key(table,buf,key_info,key_spec,(uint32*)&part_part))
          {
            part_spec->start_part= no_parts;
            clear_indicator_in_key_fields(key_info);
            DBUG_VOID_RETURN;
          }
        }
      }
    }
  }
  {
    /*
      The next step is to analyse the table condition to see whether any
      information about which partitions to scan can be derived from there.
      Currently not implemented.
    */
  }
  /*
    If we come here we have found a range of sorts we have either discovered
    nothing or we have discovered a range of partitions with possible holes
    in it. We need a bitvector to further the work here.
  */
  if (!(part_part == no_parts && sub_part == no_parts))
  {
    /*
      We can only arrive here if we are using subpartitioning.
    */
    if (part_part != no_parts)
    {
      /*
        We know the top partition and need to scan all underlying
        subpartitions. This is a range without holes.
      */
      DBUG_ASSERT(sub_part == no_parts);
      part_spec->start_part= part_part * part_info->no_parts;
      part_spec->end_part= part_spec->start_part+part_info->no_subparts - 1;
    }
    else
    {
      DBUG_ASSERT(sub_part != no_parts);
      part_spec->use_bit_array= TRUE;
      part_spec->start_part= sub_part;
      part_spec->end_part=sub_part+
                           (part_info->no_subparts*(part_info->no_parts-1));
      for (i= 0, part_id= sub_part; i < part_info->no_parts;
           i++, part_id+= part_info->no_subparts)
        ; //Set bit part_id in bit array
    }
  }
  if (found_part_field)
    clear_indicator_in_key_fields(key_info);
  DBUG_VOID_RETURN;
}


/*
   If the table is partitioned we will read the partition info into the
   .frm file here.
   -------------------------------
   |  Fileinfo     64 bytes      |
   -------------------------------
   | Formnames     7 bytes       |
   -------------------------------
   | Not used    4021 bytes      |
   -------------------------------
   | Keyinfo + record            |
   -------------------------------
   | Padded to next multiple     |
   | of IO_SIZE                  |
   -------------------------------
   | Forminfo     288 bytes      |
   -------------------------------
   | Screen buffer, to make      |
   | field names readable        |
   -------------------------------
   | Packed field info           |
   | 17 + 1 + strlen(field_name) |
   | + 1 end of file character   |
   -------------------------------
   | Partition info              |
   -------------------------------
   We provide the length of partition length in Fileinfo[55-58].

   Read the partition syntax from the frm file and parse it to get the
   data structures of the partitioning.
   SYNOPSIS
     mysql_unpack_partition()
     file                          File reference of frm file
     thd                           Thread object
     part_info_len                 Length of partition syntax
     table                         Table object of partitioned table
   RETURN VALUE
     TRUE                          Error
     FALSE                         Sucess
   DESCRIPTION
     Read the partition syntax from the current position in the frm file.
     Initiate a LEX object, save the list of item tree objects to free after
     the query is done. Set-up partition info object such that parser knows
     it is called from internally. Call parser to create data structures
     (best possible recreation of item trees and so forth since there is no
     serialisation of these objects other than in parseable text format).
     We need to save the text of the partition functions since it is not
     possible to retrace this given an item tree.
*/

bool mysql_unpack_partition(THD *thd, const uchar *part_buf,
                            uint part_info_len, TABLE* table,
                            handlerton *default_db_type)
{
  Item *thd_free_list= thd->free_list;
  bool result= TRUE;
  partition_info *part_info;
  LEX *old_lex= thd->lex, lex;
  DBUG_ENTER("mysql_unpack_partition");

  thd->lex= &lex;
  lex_start(thd, part_buf, part_info_len);
  /*
    We need to use the current SELECT_LEX since I need to keep the
    Name_resolution_context object which is referenced from the
    Item_field objects.
    This is not a nice solution since if the parser uses current_select
    for anything else it will corrupt the current LEX object.
  */
  thd->lex->current_select= old_lex->current_select; 
  /*
    All Items created is put into a free list on the THD object. This list
    is used to free all Item objects after completing a query. We don't
    want that to happen with the Item tree created as part of the partition
    info. This should be attached to the table object and remain so until
    the table object is released.
    Thus we move away the current list temporarily and start a new list that
    we then save in the partition info structure.
  */
  thd->free_list= NULL;
  lex.part_info= (partition_info*)1; //Indicate yyparse from this place
  if (yyparse((void*)thd) || thd->is_fatal_error)
  {
    free_items(thd->free_list);
    goto end;
  }
  part_info= lex.part_info;
  table->part_info= part_info;
  table->file->set_part_info(part_info);
  if (part_info->default_engine_type == NULL)
    part_info->default_engine_type= default_db_type;
  else
  {
    DBUG_ASSERT(part_info->default_engine_type == default_db_type);
  }
  part_info->item_free_list= thd->free_list;

  {
  /*
    This code part allocates memory for the serialised item information for
    the partition functions. In most cases this is not needed but if the
    table is used for SHOW CREATE TABLES or ALTER TABLE that modifies
    partition information it is needed and the info is lost if we don't
    save it here so unfortunately we have to do it here even if in most
    cases it is not needed. This is a consequence of that item trees are
    not serialisable.
  */
    uint part_func_len= part_info->part_func_len;
    uint subpart_func_len= part_info->subpart_func_len; 
    uint bitmap_bits= part_info->no_subparts? 
                       (part_info->no_subparts* part_info->no_parts):
                        part_info->no_parts;
    uint bitmap_bytes= bitmap_buffer_size(bitmap_bits);
    uint32 *bitmap_buf;
    char *part_func_string, *subpart_func_string= NULL;
    if (!((part_func_string= thd->alloc(part_func_len))) ||
        (subpart_func_len &&
        !((subpart_func_string= thd->alloc(subpart_func_len)))) ||
        !((bitmap_buf= (uint32*)thd->alloc(bitmap_bytes))))
    {
      my_error(ER_OUTOFMEMORY, MYF(0), part_func_len);
      free_items(thd->free_list);
      part_info->item_free_list= 0;
      goto end;
    }
    memcpy(part_func_string, part_info->part_func_string, part_func_len);
    if (subpart_func_len)
      memcpy(subpart_func_string, part_info->subpart_func_string,
             subpart_func_len);
    part_info->part_func_string= part_func_string;
    part_info->subpart_func_string= subpart_func_string;

    bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE);
  }

  result= FALSE;
end:
  thd->free_list= thd_free_list;
  thd->lex= old_lex;
  DBUG_RETURN(result);
}
#endif

/*
  Prepare for calling val_int on partition function by setting fields to
  point to the record where the values of the PF-fields are stored.
  SYNOPSIS
    set_field_ptr()
    ptr                 Array of fields to change ptr
    new_buf             New record pointer
    old_buf             Old record pointer
  DESCRIPTION
    Set ptr in field objects of field array to refer to new_buf record
    instead of previously old_buf. Used before calling val_int and after
    it is used to restore pointers to table->record[0].
    This routine is placed outside of partition code since it can be useful
    also for other programs.
*/

void set_field_ptr(Field **ptr, const byte *new_buf,
                            const byte *old_buf)
{
  my_ptrdiff_t diff= (new_buf - old_buf);
  DBUG_ENTER("set_nullable_field_ptr");

  do
  {
    (*ptr)->move_field_offset(diff);
  } while (*(++ptr));
  DBUG_VOID_RETURN;
}


/*
  Prepare for calling val_int on partition function by setting fields to
  point to the record where the values of the PF-fields are stored.
  This variant works on a key_part reference.
  It is not required that all fields are NOT NULL fields.
  SYNOPSIS
    set_key_field_ptr()
    key_part            key part with a set of fields to change ptr
    new_buf             New record pointer
    old_buf             Old record pointer
  DESCRIPTION
    Set ptr in field objects of field array to refer to new_buf record
    instead of previously old_buf. Used before calling val_int and after
    it is used to restore pointers to table->record[0].
    This routine is placed outside of partition code since it can be useful
    also for other programs.
*/

void set_key_field_ptr(KEY *key_info, const byte *new_buf,
                       const byte *old_buf)
{
  KEY_PART_INFO *key_part= key_info->key_part;
  uint key_parts= key_info->key_parts, i= 0;
  my_ptrdiff_t diff= (new_buf - old_buf);
  DBUG_ENTER("set_key_field_ptr");

  do
  {
    key_part->field->move_field_offset(diff);
    key_part++;
  } while (++i < key_parts);
  DBUG_VOID_RETURN;
}


/*
  Fill the string comma-separated line of used partitions names
  SYNOPSIS
    make_used_partitions_str()
      part_info  IN  Partitioning info
      parts_str  OUT The string to fill
*/

void make_used_partitions_str(partition_info *part_info, String *parts_str)
{
  parts_str->length(0);
  partition_element *pe;
  uint partition_id= 0;
  List_iterator<partition_element> it(part_info->partitions);
  
  if (part_info->subpart_type != NOT_A_PARTITION)
  {
    partition_element *head_pe;
    while ((head_pe= it++))
    {
      List_iterator<partition_element> it2(head_pe->subpartitions);
      while ((pe= it2++))
      {
        if (bitmap_is_set(&part_info->used_partitions, partition_id))
        {
          if (parts_str->length())
            parts_str->append(',');
          parts_str->append(head_pe->partition_name,
                           strlen(head_pe->partition_name),
                           system_charset_info);
          parts_str->append('_');
          parts_str->append(pe->partition_name,
                           strlen(pe->partition_name),
                           system_charset_info);
        }
        partition_id++;
      }
    }
  }
  else
  {
    while ((pe= it++))
    {
      if (bitmap_is_set(&part_info->used_partitions, partition_id))
      {
        if (parts_str->length())
          parts_str->append(',');
        parts_str->append(pe->partition_name, strlen(pe->partition_name),
                         system_charset_info);
      }
      partition_id++;
    }
  }
}

