mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 14:33:32 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			3529 lines
		
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3529 lines
		
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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;
 | 
						||
*/
 | 
						||
const LEX_STRING partition_keywords[]=
 | 
						||
{
 | 
						||
  { (char *) STRING_WITH_LEN("HASH") },
 | 
						||
  { (char *) STRING_WITH_LEN("RANGE") },
 | 
						||
  { (char *) STRING_WITH_LEN("LIST") }, 
 | 
						||
  { (char *) STRING_WITH_LEN("KEY") },
 | 
						||
  { (char *) STRING_WITH_LEN("MAXVALUE") },
 | 
						||
  { (char *) STRING_WITH_LEN("LINEAR ") }
 | 
						||
};
 | 
						||
static const char *part_str= "PARTITION";
 | 
						||
static const char *sub_str= "SUB";
 | 
						||
static const char *by_str= "BY";
 | 
						||
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= partition_keywords[PKW_RANGE].str;
 | 
						||
    else
 | 
						||
      error_string= partition_keywords[PKW_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= partition_keywords[PKW_RANGE].str; 
 | 
						||
      if (unlikely(check_range_constants(part_info)))
 | 
						||
        goto end;
 | 
						||
    }
 | 
						||
    else if (part_info->part_type == LIST_PARTITION)
 | 
						||
    {
 | 
						||
      error_str= partition_keywords[PKW_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, partition_keywords[PKW_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, partition_keywords[PKW_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, partition_keywords[PKW_MAXVALUE].str);
 | 
						||
  }
 | 
						||
  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, partition_keywords[PKW_RANGE].str);
 | 
						||
      break;
 | 
						||
    case LIST_PARTITION:
 | 
						||
      add_default_info= TRUE;
 | 
						||
      err+= add_part_key_word(fptr, partition_keywords[PKW_LIST].str);
 | 
						||
      break;
 | 
						||
    case HASH_PARTITION:
 | 
						||
      if (part_info->linear_hash_ind)
 | 
						||
        err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
 | 
						||
      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++;
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 |