sql_partition.cc 260 KB
Newer Older
Mikael Ronstrom's avatar
Mikael Ronstrom committed
1
/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
2 3 4

   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
5
   the Free Software Foundation; version 2 of the License.
6 7 8 9 10 11 12 13 14 15 16

   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 */

/*
17
  This file is a container for general functionality related
18
  to partitioning introduced in MySQL version 5.1. It contains functionality
19 20
  used by all handlers that support partitioning, such as
  the partitioning handler itself and the NDB handler.
21 22
  (Much of the code in this file has been split into partition_info.cc and
   the header files partition_info.h + partition_element.h + sql_partition.h)
23

24 25 26 27 28 29
  The first version was written by Mikael Ronstrom 2004-2006.
  Various parts of the optimizer code was written by Sergey Petrunia.
  Code have been maintained by Mattias Jonsson.
  The second version was written by Mikael Ronstrom 2006-2007 with some
  final fixes for partition pruning in 2008-2009 with assistance from Sergey
  Petrunia and Mattias Jonsson.
30

31
  The first version supports RANGE partitioning, LIST partitioning, HASH
32 33 34
  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
35
  called KEY partitioning), where the MySQL server will use an internal
36 37
  hash function.
  There are quite a few defaults that can be used as well.
38 39 40 41 42 43

  The second version introduces a new variant of RANGE and LIST partitioning
  which is often referred to as column lists in the code variables. This
  enables a user to specify a set of columns and their concatenated value
  as the partition value. By comparing the concatenation of these values
  the proper partition can be choosen.
44 45 46 47
*/

/* Some general useful functions */

48
#define MYSQL_LEX 1
49 50 51
#include "mysql_priv.h"
#include <errno.h>
#include <m_ctype.h>
52
#include "my_md5.h"
53

54
#ifdef WITH_PARTITION_STORAGE_ENGINE
kent@mysql.com's avatar
kent@mysql.com committed
55
#include "ha_partition.h"
Alfranio Correia's avatar
Alfranio Correia committed
56 57 58 59

#define ERROR_INJECT_CRASH(code) \
  DBUG_EVALUATE_IF(code, (abort(), 0), 0)

60 61 62
/*
  Partition related functions declarations and some static constants;
*/
63 64
const LEX_STRING partition_keywords[]=
{
andrey@example.com's avatar
andrey@example.com committed
65 66 67 68 69
  { C_STRING_WITH_LEN("HASH") },
  { C_STRING_WITH_LEN("RANGE") },
  { C_STRING_WITH_LEN("LIST") }, 
  { C_STRING_WITH_LEN("KEY") },
  { C_STRING_WITH_LEN("MAXVALUE") },
70
  { C_STRING_WITH_LEN("LINEAR ") },
71
  { C_STRING_WITH_LEN(" COLUMNS") }
72
};
73
static const char *part_str= "PARTITION";
74
static const char *subpart_str= "SUBPARTITION";
75 76 77 78 79 80 81
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= ",";
82

83 84 85
int get_partition_id_list_col(partition_info *part_info,
                              uint32 *part_id,
                              longlong *func_value);
86
int get_partition_id_list(partition_info *part_info,
87 88
                          uint32 *part_id,
                          longlong *func_value);
89 90 91
int get_partition_id_range_col(partition_info *part_info,
                               uint32 *part_id,
                               longlong *func_value);
92
int get_partition_id_range(partition_info *part_info,
93 94
                           uint32 *part_id,
                           longlong *func_value);
95 96 97 98 99
static int get_part_id_charset_func_part(partition_info *part_info,
                                         uint32 *part_id,
                                         longlong *func_value);
static int get_part_id_charset_func_subpart(partition_info *part_info,
                                            uint32 *part_id);
100
int get_partition_id_hash_nosub(partition_info *part_info,
101 102
                                uint32 *part_id,
                                longlong *func_value);
103 104 105
int get_partition_id_key_nosub(partition_info *part_info,
                               uint32 *part_id,
                               longlong *func_value);
106
int get_partition_id_linear_hash_nosub(partition_info *part_info,
107 108
                                       uint32 *part_id,
                                       longlong *func_value);
109 110 111
int get_partition_id_linear_key_nosub(partition_info *part_info,
                                      uint32 *part_id,
                                      longlong *func_value);
112 113 114
int get_partition_id_with_sub(partition_info *part_info,
                              uint32 *part_id,
                              longlong *func_value);
115 116 117 118 119 120 121 122
int get_partition_id_hash_sub(partition_info *part_info,
                              uint32 *part_id); 
int get_partition_id_key_sub(partition_info *part_info,
                             uint32 *part_id); 
int get_partition_id_linear_hash_sub(partition_info *part_info,
                                     uint32 *part_id); 
int get_partition_id_linear_key_sub(partition_info *part_info,
                                    uint32 *part_id); 
123
static uint32 get_next_partition_via_walking(PARTITION_ITERATOR*);
124
static void set_up_range_analysis_info(partition_info *part_info);
125
static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*);
126 127
#endif

128 129 130 131
uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter);
uint32 get_next_partition_id_list(PARTITION_ITERATOR* part_iter);
int get_part_iter_for_interval_via_mapping(partition_info *part_info,
                                           bool is_subpart,
132
                                           uint32 *store_length_array,
133
                                           uchar *min_value, uchar *max_value,
134
                                           uint min_len, uint max_len,
135 136
                                           uint flags,
                                           PARTITION_ITERATOR *part_iter);
137 138 139 140 141 142 143
int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
                                            bool is_subpart,
                                            uint32 *store_length_array,
                                            uchar *min_value, uchar *max_value,
                                            uint min_len, uint max_len,
                                            uint flags,
                                            PARTITION_ITERATOR *part_iter);
144 145
int get_part_iter_for_interval_via_walking(partition_info *part_info,
                                           bool is_subpart,
146
                                           uint32 *store_length_array,
147
                                           uchar *min_value, uchar *max_value,
148
                                           uint min_len, uint max_len,
149 150
                                           uint flags,
                                           PARTITION_ITERATOR *part_iter);
151 152 153 154
static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec);
static int cmp_rec_and_tuple_prune(part_column_list_val *val,
                                   uint32 n_vals_in_rec,
                                   bool tail_is_min);
155

156
#ifdef WITH_PARTITION_STORAGE_ENGINE
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
/*
  Convert constants in VALUES definition to the character set the
  corresponding field uses.

  SYNOPSIS
    convert_charset_partition_constant()
    item                                Item to convert
    cs                                  Character set to convert to

  RETURN VALUE
    NULL                                Error
    item                                New converted item
*/

Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
{
  THD *thd= current_thd;
  Name_resolution_context *context= &thd->lex->current_select->context;
  TABLE_LIST *save_list= context->table_list;
  const char *save_where= thd->where;

  item= item->safe_charset_converter(cs);
  context->table_list= NULL;
  thd->where= "convert character set partition constant";
  if (!item || item->fix_fields(thd, (Item**)NULL))
    item= NULL;
  thd->where= save_where;
  context->table_list= save_list;
  return item;
}


189
/*
190 191
  A support function to check if a name is in a list of strings

192
  SYNOPSIS
193 194 195 196
    is_name_in_list()
    name               String searched for
    list_names         A list of names searched in

197 198 199 200 201
  RETURN VALUES
    TRUE               String found
    FALSE              String not found
*/

202 203
bool is_name_in_list(char *name,
                          List<char> list_names)
204
{
205
  List_iterator<char> names_it(list_names);
206
  uint num_names= list_names.elements;
207
  uint i= 0;
208

209 210
  do
  {
211 212
    char *list_name= names_it++;
    if (!(my_strcasecmp(system_charset_info, name, list_name)))
213
      return TRUE;
214
  } while (++i < num_names);
215 216 217 218
  return FALSE;
}


219 220 221 222 223 224 225 226

/*
  Set-up defaults for partitions. 

  SYNOPSIS
    partition_default_handling()
    table                         Table object
    part_info                     Partition info to set up
227 228
    is_create_table_ind           Is this part of a table creation
    normalized_path               Normalized path name of table and database
229 230 231 232 233 234

  RETURN VALUES
    TRUE                          Error
    FALSE                         Success
*/

235
bool partition_default_handling(TABLE *table, partition_info *part_info,
236
                                bool is_create_table_ind,
237
                                const char *normalized_path)
238 239 240
{
  DBUG_ENTER("partition_default_handling");

241
  if (!is_create_table_ind)
242
  {
243
    if (part_info->use_default_num_partitions)
244
    {
245
      if (table->file->get_no_parts(normalized_path, &part_info->num_parts))
246 247 248
      {
        DBUG_RETURN(TRUE);
      }
249
    }
250
    else if (part_info->is_sub_partitioned() &&
251
             part_info->use_default_num_subpartitions)
252
    {
253 254
      uint num_parts;
      if (table->file->get_no_parts(normalized_path, &num_parts))
255 256 257
      {
        DBUG_RETURN(TRUE);
      }
258 259 260
      DBUG_ASSERT(part_info->num_parts > 0);
      DBUG_ASSERT((num_parts % part_info->num_parts) == 0);
      part_info->num_subparts= num_parts / part_info->num_parts;
261 262
    }
  }
263 264
  part_info->set_up_defaults_for_partitioning(table->file,
                                              (ulonglong)0, (uint)0);
265 266 267 268
  DBUG_RETURN(FALSE);
}


269 270 271 272 273 274 275
/*
  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
276 277
    list_part_names    The list of partition names that will go away and
                       can be reused in the new table.
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

  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;
294 295
  uint num_new_parts= new_part_info->partitions.elements;
  uint num_old_parts= old_part_info->partitions.elements;
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
  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)))
      {
315
        if (!is_name_in_list(old_name, list_part_names))
316 317
          DBUG_RETURN(TRUE);
      }
318 319
    } while (old_count < num_old_parts);
  } while (new_count < num_new_parts);
320 321 322 323
  DBUG_RETURN(FALSE);
}


324 325 326
/*
  A useful routine used by update_row for partition handlers to calculate
  the partition ids of the old and the new record.
327

328 329 330 331 332 333
  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
334 335 336
    out:old_part_id         The returned partition id of old record 
    out:new_part_id         The returned partition id of new record

337 338 339 340 341
  RETURN VALUE
    0                       Success
    > 0                     Error code
*/

342 343
int get_parts_for_update(const uchar *old_data, uchar *new_data,
                         const uchar *rec0, partition_info *part_info,
344 345
                         uint32 *old_part_id, uint32 *new_part_id,
                         longlong *new_func_value)
346 347 348
{
  Field **part_field_array= part_info->full_part_field_array;
  int error;
349
  longlong old_func_value;
350 351
  DBUG_ENTER("get_parts_for_update");

352
  DBUG_ASSERT(new_data == rec0);
353
  set_field_ptr(part_field_array, old_data, rec0);
354 355
  error= part_info->get_partition_id(part_info, old_part_id,
                                     &old_func_value);
356 357 358 359 360 361 362 363 364 365
  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
  {
366 367 368
    if (unlikely(error= part_info->get_partition_id(part_info,
                                                    new_part_id,
                                                    new_func_value)))
369 370 371 372 373 374 375 376 377 378 379 380 381
    {
      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);
382 383
    error= part_info->get_partition_id(part_info, new_part_id,
                                       new_func_value);
384 385 386 387 388 389 390 391 392 393 394 395 396 397
    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.
398

399 400 401 402 403
  SYNOPSIS
    get_part_for_delete()
    buf                     Buffer of old record
    rec0                    Reference to table->record[0]
    part_info               Reference to partition information
404 405
    out:part_id             The returned partition id to delete from

406 407 408
  RETURN VALUE
    0                       Success
    > 0                     Error code
409

410 411 412 413 414 415
  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.
*/

416
int get_part_for_delete(const uchar *buf, const uchar *rec0,
417 418 419
                        partition_info *part_info, uint32 *part_id)
{
  int error;
420
  longlong func_value;
421 422 423 424
  DBUG_ENTER("get_part_for_delete");

  if (likely(buf == rec0))
  {
425 426
    if (unlikely((error= part_info->get_partition_id(part_info, part_id,
                                                     &func_value))))
427 428 429 430 431 432 433 434 435
    {
      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);
436
    error= part_info->get_partition_id(part_info, part_id, &func_value);
437 438 439 440 441 442 443 444 445 446 447 448
    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);
}


/*
449 450 451
  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.
452 453 454 455 456

  SYNOPSIS
    set_up_field_array()
    table                TABLE object for which partition fields are set-up
    sub_part             Is the table subpartitioned as well
457

458 459 460
  RETURN VALUE
    TRUE                 Error, some field didn't meet requirements
    FALSE                Ok, partition field array set-up
461

462
  DESCRIPTION
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487

    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) All fields of partition function in Primary keys and unique indexes
       (if not supported)


    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.
488
*/
489

490
static bool set_up_field_array(TABLE *table,
491
                              bool is_sub_part)
492 493
{
  Field **ptr, *field, **field_array;
494
  uint num_fields= 0;
495 496
  uint size_field_array;
  uint i= 0;
497
  uint inx;
498
  partition_info *part_info= table->part_info;
499 500 501 502 503 504 505
  int result= FALSE;
  DBUG_ENTER("set_up_field_array");

  ptr= table->field;
  while ((field= *(ptr++))) 
  {
    if (field->flags & GET_FIXED_FIELDS_FLAG)
506
      num_fields++;
507
  }
508
  if (num_fields > MAX_REF_PARTS)
509 510 511 512 513 514 515 516 517
  {
    char *ptr;
    if (is_sub_part)
      ptr= (char*)"subpartition function";
    else
      ptr= (char*)"partition function";
    my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), ptr);
    DBUG_RETURN(TRUE);
  }
518
  if (num_fields == 0)
519 520 521 522 523 524 525
  {
    /*
      We are using hidden key as partitioning field
    */
    DBUG_ASSERT(!is_sub_part);
    DBUG_RETURN(result);
  }
526
  size_field_array= (num_fields+1)*sizeof(Field*);
527
  field_array= (Field**)sql_calloc(size_field_array);
528 529
  if (unlikely(!field_array))
  {
530
    mem_alloc_error(size_field_array);
531 532 533 534 535 536 537 538 539 540 541
    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))
      {
542 543 544 545 546
        if (!is_sub_part && part_info->column_list)
        {
          List_iterator<char> it(part_info->part_field_list);
          char *field_name;

547
          DBUG_ASSERT(num_fields == part_info->part_field_list.elements);
548 549 550 551
          inx= 0;
          do
          {
            field_name= it++;
552 553 554
            if (!my_strcasecmp(system_charset_info,
                               field_name,
                               field->field_name))
555
              break;
556 557
          } while (++inx < num_fields);
          if (inx == num_fields)
558 559 560 561 562 563 564 565 566 567
          {
            mem_alloc_error(1);
            result= TRUE;
            continue;
          }
        }
        else
          inx= i;
        field_array[inx]= field;
        i++;
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584

        /*
          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.
        */

        if (unlikely(field->flags & BLOB_FLAG))
        {
          my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
          result= TRUE;
        }
      }
    }
  }
585
  field_array[num_fields]= 0;
586
  if (!is_sub_part)
587 588
  {
    part_info->part_field_array= field_array;
589
    part_info->num_part_fields= num_fields;
590 591 592 593
  }
  else
  {
    part_info->subpart_field_array= field_array;
594
    part_info->num_subpart_fields= num_fields;
595 596 597 598 599
  }
  DBUG_RETURN(result);
}


600

601 602 603
/*
  Create a field array including all fields of both the partitioning and the
  subpartitioning functions.
604

605 606
  SYNOPSIS
    create_full_part_field_array()
607
    thd                  Thread handle
608 609
    table                TABLE object for which partition fields are set-up
    part_info            Reference to partitioning data structure
610

611 612 613
  RETURN VALUE
    TRUE                 Memory allocation of field array failed
    FALSE                Ok
614

615 616 617 618 619 620 621
  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
*/

622
static bool create_full_part_field_array(THD *thd, TABLE *table,
623 624 625
                                         partition_info *part_info)
{
  bool result= FALSE;
626
  Field **ptr;
627
  my_bitmap_map *bitmap_buf;
628 629
  DBUG_ENTER("create_full_part_field_array");

630
  if (!part_info->is_sub_partitioned())
631 632
  {
    part_info->full_part_field_array= part_info->part_field_array;
633
    part_info->num_full_part_fields= part_info->num_part_fields;
634 635 636
  }
  else
  {
637
    Field *field, **field_array;
638
    uint num_part_fields=0, size_field_array;
639 640 641 642
    ptr= table->field;
    while ((field= *(ptr++)))
    {
      if (field->flags & FIELD_IN_PART_FUNC_FLAG)
643
        num_part_fields++;
644
    }
645
    size_field_array= (num_part_fields+1)*sizeof(Field*);
646
    field_array= (Field**)sql_calloc(size_field_array);
647 648
    if (unlikely(!field_array))
    {
649
      mem_alloc_error(size_field_array);
650 651 652
      result= TRUE;
      goto end;
    }
653
    num_part_fields= 0;
654 655 656 657
    ptr= table->field;
    while ((field= *(ptr++)))
    {
      if (field->flags & FIELD_IN_PART_FUNC_FLAG)
658
        field_array[num_part_fields++]= field;
659
    }
660
    field_array[num_part_fields]=0;
661
    part_info->full_part_field_array= field_array;
662
    part_info->num_full_part_fields= num_part_fields;
663
  }
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689

  /*
    Initialize the set of all fields used in partition and subpartition
    expression. Required for testing of partition fields in write_set
    when updating. We need to set all bits in read_set because the row
    may need to be inserted in a different [sub]partition.
  */
  if (!(bitmap_buf= (my_bitmap_map*)
        thd->alloc(bitmap_buffer_size(table->s->fields))))
  {
    mem_alloc_error(bitmap_buffer_size(table->s->fields));
    result= TRUE;
    goto end;
  }
  if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
                  table->s->fields, FALSE))
  {
    mem_alloc_error(table->s->fields);
    result= TRUE;
    goto end;
  }
  /*
    full_part_field_array may be NULL if storage engine supports native
    partitioning.
  */
  if ((ptr= part_info->full_part_field_array))
690 691
    for (; *ptr; ptr++)
      bitmap_set_bit(&part_info->full_part_field_set, (*ptr)->field_index);
692

693 694 695 696 697 698 699 700 701
end:
  DBUG_RETURN(result);
}


/*

  Clear flag GET_FIXED_FIELDS_FLAG in all fields of a key previously set by
  set_indicator_in_key_fields (always used in pairs).
702

703 704 705
  SYNOPSIS
    clear_indicator_in_key_fields()
    key_info                  Reference to find the key fields
706 707 708 709 710 711 712 713 714 715 716 717

  RETURN VALUE
    NONE

  DESCRIPTION
    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.
718 719 720 721 722 723 724 725 726 727 728 729 730
*/

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.
731

732 733 734
  SYNOPSIS
    set_indicator_in_key_fields
    key_info                  Reference to find the key fields
735 736 737

  RETURN VALUE
    NONE
738 739 740 741 742 743 744 745 746 747 748 749 750 751
*/

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.
752

753 754 755
  SYNOPSIS
    check_fields_in_PF()
    ptr                  Partition field array
756 757 758
    out:all_fields       Is all fields of partition field array used in key
    out:some_fields      Is some fields of partition field array used in key

759 760 761 762 763 764 765 766
  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");
767

768 769
  *all_fields= TRUE;
  *some_fields= FALSE;
770 771 772 773 774
  if ((!ptr) || !(*ptr))
  {
    *all_fields= FALSE;
    DBUG_VOID_RETURN;
  }
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
  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.
790

791 792 793
  SYNOPSIS
    clear_field_flag()
    table                TABLE object for which partition fields are set-up
794 795 796

  RETURN VALUE
    NONE
797 798 799 800 801 802 803 804 805 806 807 808 809 810
*/

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;
}


/*
811 812 813
  find_field_in_table_sef finds the field given its name. All fields get
  GET_FIXED_FIELDS_FLAG set.

814 815 816 817 818 819
  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
820

821 822 823
  RETURN VALUE
    TRUE                 Fields in list of fields not part of table
    FALSE                All fields ok and array created
824

825
  DESCRIPTION
826 827 828 829
    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.

830 831
*/

832

833 834 835
static bool handle_list_of_fields(List_iterator<char> it,
                                  TABLE *table,
                                  partition_info *part_info,
836
                                  bool is_sub_part)
837 838 839 840
{
  Field *field;
  bool result;
  char *field_name;
841
  bool is_list_empty= TRUE;
842 843 844 845
  DBUG_ENTER("handle_list_of_fields");

  while ((field_name= it++))
  {
846
    is_list_empty= FALSE;
847 848 849 850 851 852 853 854 855 856 857
    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;
    }
  }
858
  if (is_list_empty && part_info->part_type == HASH_PARTITION)
859 860 861 862
  {
    uint primary_key= table->s->primary_key;
    if (primary_key != MAX_KEY)
    {
863
      uint num_key_parts= table->key_info[primary_key].key_parts, i;
864 865 866
      /*
        In the case of an empty list we use primary key as partition key.
      */
867
      for (i= 0; i < num_key_parts; i++)
868 869 870 871 872 873 874
      {
        Field *field= table->key_info[primary_key].key_part[i].field;
        field->flags|= GET_FIXED_FIELDS_FLAG;
      }
    }
    else
    {
antony@ppcg5.local's avatar
antony@ppcg5.local committed
875 876 877
      if (table->s->db_type()->partition_flags &&
          (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) &&
          (table->s->db_type()->partition_flags() & HA_CAN_PARTITION))
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
      {
        /*
          This engine can handle automatic partitioning and there is no
          primary key. In this case we rely on that the engine handles
          partitioning based on a hidden key. Thus we allocate no
          array for partitioning fields.
        */
        DBUG_RETURN(FALSE);
      }
      else
      {
        my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
      }
    }
  }
  result= set_up_field_array(table, is_sub_part);
895 896 897 898 899
end:
  DBUG_RETURN(result);
}


900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
/*
  Support function to check if all VALUES * (expression) is of the
  right sign (no signed constants when unsigned partition function)

  SYNOPSIS
    check_signed_flag()
    part_info                Partition info object

  RETURN VALUES
    0                        No errors due to sign errors
    >0                       Sign error
*/

int check_signed_flag(partition_info *part_info)
{
  int error= 0;
  uint i= 0;
  if (part_info->part_type != HASH_PARTITION &&
      part_info->part_expr->unsigned_flag)
  {
    List_iterator<partition_element> part_it(part_info->partitions);
    do
    {
      partition_element *part_elem= part_it++;

      if (part_elem->signed_flag)
      {
927 928
        my_error(ER_PARTITION_CONST_DOMAIN_ERROR, MYF(0));
        error= ER_PARTITION_CONST_DOMAIN_ERROR;
929 930
        break;
      }
931
    } while (++i < part_info->num_parts);
932 933 934 935
  }
  return error;
}

936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
/**
  Initialize lex object for use in fix_fields and parsing.

  SYNOPSIS
    init_lex_with_single_table()
    @param thd                 The thread object
    @param table               The table object
  @return Operation status
    @retval TRUE                An error occurred, memory allocation error
    @retval FALSE               Ok

  DESCRIPTION
    This function is used to initialize a lex object on the
    stack for use by fix_fields and for parsing. In order to
    work properly it also needs to initialize the
    Name_resolution_context object of the lexer.
    Finally it needs to set a couple of variables to ensure
    proper functioning of fix_fields.
*/

static int
init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
{
  TABLE_LIST *table_list;
  Table_ident *table_ident;
  SELECT_LEX *select_lex= &lex->select_lex;
  Name_resolution_context *context= &select_lex->context;
  /*
    We will call the parser to create a part_info struct based on the
    partition string stored in the frm file.
    We will use a local lex object for this purpose. However we also
    need to set the Name_resolution_object for this lex object. We
    do this by using add_table_to_list where we add the table that
    we're working with to the Name_resolution_context.
  */
  thd->lex= lex;
  lex_start(thd);
  context->init();
  if ((!(table_ident= new Table_ident(thd,
                                      table->s->table_name,
                                      table->s->db, TRUE))) ||
      (!(table_list= select_lex->add_table_to_list(thd,
                                                   table_ident,
                                                   NULL,
                                                   0))))
    return TRUE;
  context->resolve_in_table_list_only(table_list);
  lex->use_only_table_context= TRUE;
  select_lex->cur_pos_in_select_list= UNDEF_POS;
  table->map= 1; //To ensure correct calculation of const item
  table->get_fields_in_item_tree= TRUE;
  table_list->table= table;
  return FALSE;
}

/**
  End use of local lex with single table

  SYNOPSIS
    end_lex_with_single_table()
    @param thd               The thread object
    @param table             The table object
    @param old_lex           The real lex object connected to THD

  DESCRIPTION
    This function restores the real lex object after calling
    init_lex_with_single_table and also restores some table
    variables temporarily set.
*/

static void
end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex)
{
  LEX *lex= thd->lex;
  table->map= 0;
  table->get_fields_in_item_tree= FALSE;
  lex_end(lex);
  thd->lex= old_lex;
}
1015

1016
/*
1017 1018 1019 1020 1021
  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.

1022 1023 1024 1025
  SYNOPSIS
    fix_fields_part_func()
    thd                  The thread object
    func_expr            The item tree reference of the partition function
1026
    table                The table object
1027
    part_info            Reference to partitioning data structure
1028
    is_sub_part          Is the table subpartitioned as well
1029 1030
    is_create_table_ind  Indicator of whether openfrm was called as part of
                         CREATE or ALTER TABLE
1031

1032 1033 1034 1035
  RETURN VALUE
    TRUE                 An error occurred, something was wrong with the
                         partition function.
    FALSE                Ok, a partition field array was created
1036

1037
  DESCRIPTION
1038 1039 1040 1041 1042 1043
    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.
1044 1045 1046 1047 1048 1049 1050

    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.
1051
*/
1052

1053
static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
1054
                          bool is_sub_part, bool is_create_table_ind)
1055
{
1056
  partition_info *part_info= table->part_info;
1057 1058
  bool result= TRUE;
  int error;
1059
  const char *save_where;
1060 1061
  LEX *old_lex= thd->lex;
  LEX lex;
1062 1063
  uint8 saved_full_group_by_flag;
  nesting_map saved_allow_sum_func;
1064 1065
  DBUG_ENTER("fix_fields_part_func");

1066 1067
  if (init_lex_with_single_table(thd, table, &lex))
    goto end;
1068

1069 1070
  func_expr->walk(&Item::change_context_processor, 0,
                  (uchar*) &lex.select_lex.context);
1071
  save_where= thd->where;
1072
  thd->where= "partition function";
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
  /*
    In execution we must avoid the use of thd->change_item_tree since
    we might release memory before statement is completed. We do this
    by temporarily setting the stmt_arena->mem_root to be the mem_root
    of the table object, this also ensures that any memory allocated
    during fix_fields will not be released at end of execution of this
    statement. Thus the item tree will remain valid also in subsequent
    executions of this table object. We do however not at the moment
    support allocations during execution of val_int so any item class
    that does this during val_int must be disallowed as partition
    function.
    SEE Bug #21658
1085

1086 1087 1088
    This is a tricky call to prepare for since it can have a large number
    of interesting side effects, both desirable and undesirable.
  */
1089 1090 1091
  saved_full_group_by_flag= thd->lex->current_select->full_group_by_flag;
  saved_allow_sum_func= thd->lex->allow_sum_func;
  thd->lex->allow_sum_func= 0;
1092

1093
  error= func_expr->fix_fields(thd, (Item**)&func_expr);
1094

1095 1096 1097 1098 1099 1100 1101
  /*
    Restore full_group_by_flag and allow_sum_func,
    fix_fields should not affect mysql_select later, see Bug#46923.
  */
  thd->lex->current_select->full_group_by_flag= saved_full_group_by_flag;
  thd->lex->allow_sum_func= saved_allow_sum_func;

1102 1103 1104
  if (unlikely(error))
  {
    DBUG_PRINT("info", ("Field in partition function not part of table"));
1105
    clear_field_flag(table);
1106 1107 1108 1109
    goto end;
  }
  if (unlikely(func_expr->const_item()))
  {
1110
    my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
1111 1112 1113
    clear_field_flag(table);
    goto end;
  }
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134

  /*
    We don't allow creating partitions with timezone-dependent expressions as
    a (sub)partitioning function, but we want to allow such expressions when
    opening existing tables for easier maintenance. This exception should be
    deprecated at some point in future so that we always throw an error.
  */
  if (func_expr->walk(&Item::is_timezone_dependent_processor,
                      0, NULL))
  {
    if (is_create_table_ind)
    {
      my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
      goto end;
    }
    else
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                   ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR,
                   ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
  }

1135 1136
  if ((!is_sub_part) && (error= check_signed_flag(part_info)))
    goto end;
1137
  result= set_up_field_array(table, is_sub_part);
1138
end:
1139 1140 1141 1142 1143
  end_lex_with_single_table(thd, table, old_lex);
#if !defined(DBUG_OFF)
  func_expr->walk(&Item::change_context_processor, 0,
                  (uchar*) 0);
#endif
1144 1145 1146 1147 1148
  DBUG_RETURN(result);
}


/*
1149 1150
  Check that the primary key contains all partition fields if defined

1151 1152 1153
  SYNOPSIS
    check_primary_key()
    table                TABLE object for which partition fields are set-up
1154

1155 1156 1157 1158 1159
  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
1160 1161 1162 1163 1164 1165

  DESCRIPTION
    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.
1166 1167 1168 1169 1170
*/

static bool check_primary_key(TABLE *table)
{
  uint primary_key= table->s->primary_key;
1171 1172
  bool all_fields, some_fields;
  bool result= FALSE;
1173 1174 1175 1176 1177
  DBUG_ENTER("check_primary_key");

  if (primary_key < MAX_KEY)
  {
    set_indicator_in_key_fields(table->key_info+primary_key);
1178
    check_fields_in_PF(table->part_info->full_part_field_array,
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
                        &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);
}


/*
1192 1193
  Check that unique keys contains all partition fields

1194 1195 1196
  SYNOPSIS
    check_unique_keys()
    table                TABLE object for which partition fields are set-up
1197

1198 1199 1200 1201 1202
  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
1203 1204 1205 1206 1207 1208

  DESCRIPTION
    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.
1209 1210 1211 1212
*/

static bool check_unique_keys(TABLE *table)
{
1213 1214 1215 1216
  bool all_fields, some_fields;
  bool result= FALSE;
  uint keys= table->s->keys;
  uint i;
1217
  DBUG_ENTER("check_unique_keys");
1218

1219 1220 1221 1222 1223
  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);
1224
      check_fields_in_PF(table->part_info->full_part_field_array,
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
                         &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.
1282

1283 1284 1285
  SYNOPSIS
    check_range_capable_PF()
    table                TABLE object for which partition fields are set-up
1286

1287 1288 1289 1290 1291 1292 1293
  DESCRIPTION
    Support for this is not implemented yet.
*/

void check_range_capable_PF(TABLE *table)
{
  DBUG_ENTER("check_range_capable_PF");
1294

1295 1296 1297 1298
  DBUG_VOID_RETURN;
}


1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
/*
  Set up partition bitmap

  SYNOPSIS
    set_up_partition_bitmap()
    thd                  Thread object
    part_info            Reference to partitioning data structure

  RETURN VALUE
    TRUE                 Memory allocation failure
    FALSE                Success

  DESCRIPTION
    Allocate memory for bitmap of the partitioned table
    and initialise it.
*/

static bool set_up_partition_bitmap(THD *thd, partition_info *part_info)
{
  uint32 *bitmap_buf;
1319 1320 1321
  uint bitmap_bits= part_info->num_subparts? 
                     (part_info->num_subparts* part_info->num_parts):
                      part_info->num_parts;
1322 1323 1324 1325 1326 1327 1328 1329 1330
  uint bitmap_bytes= bitmap_buffer_size(bitmap_bits);
  DBUG_ENTER("set_up_partition_bitmap");

  if (!(bitmap_buf= (uint32*)thd->alloc(bitmap_bytes)))
  {
    mem_alloc_error(bitmap_bytes);
    DBUG_RETURN(TRUE);
  }
  bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE);
1331
  bitmap_set_all(&part_info->used_partitions);
1332 1333 1334 1335
  DBUG_RETURN(FALSE);
}


1336 1337
/*
  Set up partition key maps
1338

1339 1340 1341 1342
  SYNOPSIS
    set_up_partition_key_maps()
    table                TABLE object for which partition fields are set-up
    part_info            Reference to partitioning data structure
1343

1344 1345
  RETURN VALUES
    None
1346

1347
  DESCRIPTION
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
    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
1358 1359 1360 1361 1362
*/

static void set_up_partition_key_maps(TABLE *table,
                                      partition_info *part_info)
{
1363 1364
  uint keys= table->s->keys;
  uint i;
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
  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);
1381
    if (part_info->is_sub_partitioned())
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
    {
      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;
}


/*
1399 1400
  Set up function pointers for partition function

1401
  SYNOPSIS
1402
    set_up_partition_func_pointers()
1403
    part_info            Reference to partitioning data structure
1404 1405 1406 1407 1408 1409 1410 1411 1412

  RETURN VALUE
    NONE

  DESCRIPTION
    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.
1413 1414 1415 1416
*/

static void set_up_partition_func_pointers(partition_info *part_info)
{
1417 1418
  DBUG_ENTER("set_up_partition_func_pointers");

1419
  if (part_info->is_sub_partitioned())
1420
  {
1421
    part_info->get_partition_id= get_partition_id_with_sub;
1422 1423
    if (part_info->part_type == RANGE_PARTITION)
    {
1424 1425 1426 1427
      if (part_info->column_list)
        part_info->get_part_partition_id= get_partition_id_range_col;
      else
        part_info->get_part_partition_id= get_partition_id_range;
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442
      if (part_info->list_of_subpart_fields)
      {
        if (part_info->linear_hash_ind)
          part_info->get_subpartition_id= get_partition_id_linear_key_sub;
        else
          part_info->get_subpartition_id= get_partition_id_key_sub;
      }
      else
      {
        if (part_info->linear_hash_ind)
          part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
        else
          part_info->get_subpartition_id= get_partition_id_hash_sub;
      }
    }
1443
    else /* LIST Partitioning */
1444
    {
1445 1446 1447 1448
      if (part_info->column_list)
        part_info->get_part_partition_id= get_partition_id_list_col;
      else
        part_info->get_part_partition_id= get_partition_id_list;
1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
      if (part_info->list_of_subpart_fields)
      {
        if (part_info->linear_hash_ind)
          part_info->get_subpartition_id= get_partition_id_linear_key_sub;
        else
          part_info->get_subpartition_id= get_partition_id_key_sub;
      }
      else
      {
        if (part_info->linear_hash_ind)
          part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
        else
          part_info->get_subpartition_id= get_partition_id_hash_sub;
      }
    }
  }
1465
  else /* No subpartitioning */
1466 1467 1468 1469
  {
    part_info->get_part_partition_id= NULL;
    part_info->get_subpartition_id= NULL;
    if (part_info->part_type == RANGE_PARTITION)
1470 1471 1472 1473 1474 1475
    {
      if (part_info->column_list)
        part_info->get_partition_id= get_partition_id_range_col;
      else
        part_info->get_partition_id= get_partition_id_range;
    }
1476
    else if (part_info->part_type == LIST_PARTITION)
1477 1478 1479 1480 1481 1482
    {
      if (part_info->column_list)
        part_info->get_partition_id= get_partition_id_list_col;
      else
        part_info->get_partition_id= get_partition_id_list;
    }
1483
    else /* HASH partitioning */
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500
    {
      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;
      }
    }
  }
1501 1502 1503 1504 1505 1506 1507 1508 1509 1510
  /*
    We need special functions to handle character sets since they require copy
    of field pointers and restore afterwards. For subpartitioned tables we do
    the copy and restore individually on the part and subpart parts. For non-
    subpartitioned tables we use the same functions as used for the parts part
    of subpartioning.
    Thus for subpartitioned tables the get_partition_id is always
    get_partition_id_with_sub, even when character sets exists.
  */
  if (part_info->part_charset_field_array)
1511
  {
1512 1513 1514
    if (part_info->is_sub_partitioned())
    {
      DBUG_ASSERT(part_info->get_part_partition_id);
1515 1516
      if (!part_info->column_list)
      {
Mikael Ronstrom's avatar
Mikael Ronstrom committed
1517
        part_info->get_part_partition_id_charset=
1518
          part_info->get_part_partition_id;
1519 1520
        part_info->get_part_partition_id= get_part_id_charset_func_part;
      }
1521
    }
1522
    else
1523 1524
    {
      DBUG_ASSERT(part_info->get_partition_id);
1525 1526 1527 1528 1529
      if (!part_info->column_list)
      {
        part_info->get_part_partition_id_charset= part_info->get_partition_id;
        part_info->get_part_partition_id= get_part_id_charset_func_part;
      }
1530
    }
1531
  }
1532
  if (part_info->subpart_charset_field_array)
1533 1534 1535 1536
  {
    DBUG_ASSERT(part_info->get_subpartition_id);
    part_info->get_subpartition_id_charset=
          part_info->get_subpartition_id;
1537
    part_info->get_subpartition_id= get_part_id_charset_func_subpart;
1538
  }
1539
  DBUG_VOID_RETURN;
1540
}
1541 1542


1543 1544
/*
  For linear hashing we need a mask which is on the form 2**n - 1 where
1545
  2**n >= num_parts. Thus if num_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7.
1546

1547 1548 1549
  SYNOPSIS
    set_linear_hash_mask()
    part_info            Reference to partitioning data structure
1550
    num_parts            Number of parts in linear hash partitioning
1551 1552 1553

  RETURN VALUE
    NONE
1554 1555
*/

1556
void set_linear_hash_mask(partition_info *part_info, uint num_parts)
1557 1558
{
  uint mask;
1559

1560
  for (mask= 1; mask < num_parts; mask<<=1)
1561 1562 1563 1564 1565 1566 1567 1568
    ;
  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.
1569

1570 1571 1572 1573
  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
1574
    num_parts           Number of partitions in HASH partitioned part
1575

1576 1577
  RETURN VALUE
    part_id             The calculated partition identity (starting at 0)
1578

1579 1580 1581 1582 1583 1584 1585 1586
  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,
1587
                                           uint num_parts)
1588 1589
{
  uint32 part_id= (uint32)(hash_value & mask);
1590

1591
  if (part_id >= num_parts)
1592 1593
  {
    uint new_mask= ((mask + 1) >> 1) - 1;
1594
    part_id= (uint32)(hash_value & new_mask);
1595 1596 1597 1598
  }
  return part_id;
}

1599

1600 1601 1602
/*
  Check if a particular field is in need of character set
  handling for partition functions.
1603

1604 1605 1606
  SYNOPSIS
    field_is_partition_charset()
    field                         The field to check
1607

1608 1609 1610 1611 1612 1613 1614
  RETURN VALUES
    FALSE                        Not in need of character set handling
    TRUE                         In need of character set handling
*/

bool field_is_partition_charset(Field *field)
{
mikael/pappa@dator5.(none)'s avatar
mikael/pappa@dator5.(none) committed
1615 1616
  if (!(field->type() == MYSQL_TYPE_STRING) &&
      !(field->type() == MYSQL_TYPE_VARCHAR))
1617 1618 1619
    return FALSE;
  {
    CHARSET_INFO *cs= ((Field_str*)field)->charset();
mikael/pappa@dator5.(none)'s avatar
mikael/pappa@dator5.(none) committed
1620
    if (!(field->type() == MYSQL_TYPE_STRING) ||
1621 1622 1623 1624 1625 1626 1627
        !(cs->state & MY_CS_BINSORT))
      return TRUE;
    return FALSE;
  }
}


1628
/*
1629
  Check that partition function doesn't contain any forbidden
1630
  character sets and collations.
1631

1632
  SYNOPSIS
1633
    check_part_func_fields()
1634
    ptr                                 Array of Field pointers
1635 1636
    ok_with_charsets                    Will we report allowed charset
                                        fields as ok
1637 1638 1639
  RETURN VALUES
    FALSE                               Success
    TRUE                                Error
1640

1641 1642 1643 1644 1645
  DESCRIPTION
    We will check in this routine that the fields of the partition functions
    do not contain unallowed parts. It can also be used to check if there
    are fields that require special care by calling my_strnxfrm before
    calling the functions to calculate partition id.
1646 1647
*/

1648
bool check_part_func_fields(Field **ptr, bool ok_with_charsets)
1649 1650
{
  Field *field;
mikael/pappa@dator5.(none)'s avatar
mikael/pappa@dator5.(none) committed
1651
  DBUG_ENTER("check_part_func_fields");
1652

1653 1654
  while ((field= *(ptr++)))
  {
1655 1656 1657 1658 1659
    /*
      For CHAR/VARCHAR fields we need to take special precautions.
      Binary collation with CHAR is automatically supported. Other
      types need some kind of standardisation function handling
    */
1660
    if (field_is_partition_charset(field))
1661 1662
    {
      CHARSET_INFO *cs= ((Field_str*)field)->charset();
1663 1664 1665 1666 1667 1668
      if (!ok_with_charsets ||
          cs->mbmaxlen > 1 ||
          cs->strxfrm_multiply > 1)
      {
        DBUG_RETURN(TRUE);
      }
1669 1670
    }
  }
1671
  DBUG_RETURN(FALSE);
1672 1673 1674
}


1675
/*
1676 1677
  fix partition functions

1678 1679 1680 1681
  SYNOPSIS
    fix_partition_func()
    thd                  The thread object
    table                TABLE object for which partition fields are set-up
1682
    is_create_table_ind  Indicator of whether openfrm was called as part of
1683
                         CREATE or ALTER TABLE
1684

1685
  RETURN VALUE
1686 1687
    TRUE                 Error
    FALSE                Success
1688

1689 1690 1691 1692
  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.
1693 1694 1695 1696 1697 1698 1699

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.
1700 1701
*/

1702
bool fix_partition_func(THD *thd, TABLE *table,
1703
                        bool is_create_table_ind)
1704 1705
{
  bool result= TRUE;
1706
  partition_info *part_info= table->part_info;
1707
  enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
1708 1709
  DBUG_ENTER("fix_partition_func");

1710 1711 1712 1713
  if (part_info->fixed)
  {
    DBUG_RETURN(FALSE);
  }
1714 1715
  thd->mark_used_columns= MARK_COLUMNS_NONE;
  DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
1716

1717
  if (!is_create_table_ind ||
1718
       thd->lex->sql_command != SQLCOM_CREATE_TABLE)
1719
  {
1720
    if (partition_default_handling(table, part_info,
1721
                                   is_create_table_ind,
1722
                                   table->s->normalized_path.str))
1723 1724 1725 1726
    {
      DBUG_RETURN(TRUE);
    }
  }
1727
  if (part_info->is_sub_partitioned())
1728 1729 1730
  {
    DBUG_ASSERT(part_info->subpart_type == HASH_PARTITION);
    /*
1731 1732
      Subpartition is defined. We need to verify that subpartitioning
      function is correct.
1733 1734
    */
    if (part_info->linear_hash_ind)
1735
      set_linear_hash_mask(part_info, part_info->num_subparts);
1736 1737 1738 1739 1740 1741 1742 1743
    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
    {
1744
      if (unlikely(fix_fields_part_func(thd, part_info->subpart_expr,
1745
                                        table, TRUE, is_create_table_ind)))
1746 1747 1748
        goto end;
      if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT))
      {
1749
        my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0),
1750
                 subpart_str);
1751 1752 1753 1754 1755 1756
        goto end;
      }
    }
  }
  DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION);
  /*
1757 1758
    Partition is defined. We need to verify that partitioning
    function is correct.
1759 1760 1761 1762
  */
  if (part_info->part_type == HASH_PARTITION)
  {
    if (part_info->linear_hash_ind)
1763
      set_linear_hash_mask(part_info, part_info->num_parts);
1764 1765 1766 1767 1768 1769 1770 1771
    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
    {
1772
      if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
1773
                                        table, FALSE, is_create_table_ind)))
1774 1775 1776
        goto end;
      if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
      {
1777
        my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0), part_str);
1778 1779 1780 1781
        goto end;
      }
      part_info->part_result_type= INT_RESULT;
    }
1782
    part_info->fixed= TRUE;
1783 1784 1785
  }
  else
  {
1786
    const char *error_str;
1787 1788 1789 1790 1791 1792 1793 1794 1795
    if (part_info->column_list)
    {
      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, part_info->part_expr,
1796
                                        table, FALSE, is_create_table_ind)))
1797 1798
        goto end;
    }
1799
    part_info->fixed= TRUE;
1800 1801
    if (part_info->part_type == RANGE_PARTITION)
    {
1802
      error_str= partition_keywords[PKW_RANGE].str; 
1803
      if (unlikely(part_info->check_range_constants(thd)))
1804 1805 1806 1807
        goto end;
    }
    else if (part_info->part_type == LIST_PARTITION)
    {
1808
      error_str= partition_keywords[PKW_LIST].str; 
1809
      if (unlikely(part_info->check_list_constants(thd)))
1810 1811 1812 1813 1814 1815 1816 1817
        goto end;
    }
    else
    {
      DBUG_ASSERT(0);
      my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0));
      goto end;
    }
1818
    if (unlikely(part_info->num_parts < 1))
1819 1820 1821 1822
    {
      my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_str);
      goto end;
    }
1823 1824
    if (unlikely(!part_info->column_list &&
                  part_info->part_expr->result_type() != INT_RESULT))
1825 1826 1827 1828 1829
    {
      my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
      goto end;
    }
  }
1830 1831
  if (((part_info->part_type != HASH_PARTITION ||
      part_info->list_of_part_fields == FALSE) &&
1832
      (!part_info->column_list &&
1833
      check_part_func_fields(part_info->part_field_array, TRUE))) ||
1834
      (part_info->list_of_subpart_fields == FALSE &&
1835
       part_info->is_sub_partitioned() &&
1836
       check_part_func_fields(part_info->subpart_field_array, TRUE)))
1837 1838 1839 1840
  {
    my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
    goto end;
  }
1841
  if (unlikely(create_full_part_field_array(thd, table, part_info)))
1842 1843 1844
    goto end;
  if (unlikely(check_primary_key(table)))
    goto end;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1845 1846
  if (unlikely((!(table->s->db_type()->partition_flags &&
      (table->s->db_type()->partition_flags() & HA_CAN_PARTITION_UNIQUE))) &&
1847 1848
               check_unique_keys(table)))
    goto end;
1849 1850
  if (unlikely(set_up_partition_bitmap(thd, part_info)))
    goto end;
1851
  if (unlikely(part_info->set_up_charset_field_preps()))
1852 1853 1854 1855
  {
    my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
    goto end;
  }
1856 1857 1858 1859 1860
  if (unlikely(part_info->check_partition_field_length()))
  {
    my_error(ER_PARTITION_FIELDS_TOO_LONG, MYF(0));
    goto end;
  }
1861 1862 1863
  check_range_capable_PF(table);
  set_up_partition_key_maps(table, part_info);
  set_up_partition_func_pointers(part_info);
1864
  set_up_range_analysis_info(part_info);
1865 1866
  result= FALSE;
end:
1867 1868
  thd->mark_used_columns= save_mark_used_columns;
  DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882
  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)
{
Marc Alff's avatar
Marc Alff committed
1883
  uint ret_code= mysql_file_write(fptr, (const uchar*)buf, len, MYF(MY_FNABP));
1884

1885
  if (likely(ret_code == 0))
1886 1887 1888 1889 1890
    return 0;
  else
    return 1;
}

1891 1892 1893 1894 1895
static int add_string_object(File fptr, String *string)
{
  return add_write(fptr, string->ptr(), string->length());
}

1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934
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);
1935
  return err;
1936 1937 1938 1939
}

static int add_partition(File fptr)
{
1940
  char buff[22];
1941 1942 1943 1944 1945 1946 1947
  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);
1948

1949 1950 1951 1952 1953
  return err + add_partition(fptr);
}

static int add_partition_by(File fptr)
{
1954
  char buff[22];
1955 1956 1957 1958 1959 1960 1961
  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);
1962

1963 1964 1965
  return err + add_partition_by(fptr);
}

1966
static int add_part_field_list(File fptr, List<char> field_list)
1967
{
1968
  uint i, num_fields;
1969
  int err= 0;
1970

1971
  List_iterator<char> part_it(field_list);
1972
  num_fields= field_list.elements;
1973
  i= 0;
1974
  err+= add_begin_parenthesis(fptr);
1975
  while (i < num_fields)
1976 1977
  {
    const char *field_str= part_it++;
1978 1979
    String field_string("", 0, system_charset_info);
    THD *thd= current_thd;
1980 1981
    ulonglong save_options= thd->variables.option_bits;
    thd->variables.option_bits= 0;
1982 1983
    append_identifier(thd, &field_string, field_str,
                      strlen(field_str));
1984
    thd->variables.option_bits= save_options;
1985
    err+= add_string_object(fptr, &field_string);
1986
    if (i != (num_fields-1))
1987
      err+= add_comma(fptr);
1988 1989
    i++;
  }
1990
  err+= add_end_parenthesis(fptr);
1991 1992 1993
  return err;
}

1994 1995 1996 1997 1998
static int add_name_string(File fptr, const char *name)
{
  int err;
  String name_string("", 0, system_charset_info);
  THD *thd= current_thd;
1999
  ulonglong save_options= thd->variables.option_bits;
2000

2001
  thd->variables.option_bits= 0;
2002 2003
  append_identifier(thd, &name_string, name,
                    strlen(name));
2004
  thd->variables.option_bits= save_options;
2005 2006 2007 2008
  err= add_string_object(fptr, &name_string);
  return err;
}

2009 2010
static int add_int(File fptr, longlong number)
{
2011
  char buff[32];
2012 2013 2014 2015
  llstr(number, buff);
  return add_string(fptr, buff);
}

2016 2017 2018 2019 2020 2021 2022
static int add_uint(File fptr, ulonglong number)
{
  char buff[32];
  longlong2str(number, buff, 10);
  return add_string(fptr, buff);
}

2023 2024 2025 2026 2027 2028 2029 2030 2031 2032
/*
   Must escape strings in partitioned tables frm-files,
   parsing it later with mysql_unpack_partition will fail otherwise.
*/
static int add_quoted_string(File fptr, const char *quotestr)
{
  String orgstr(quotestr, system_charset_info);
  String escapedstr;
  int err= add_string(fptr, "'");
  err+= append_escaped(&escapedstr, &orgstr);
2033
  err+= add_string(fptr, escapedstr.c_ptr_safe());
2034 2035 2036
  return err + add_string(fptr, "'");
}

2037
static int add_keyword_string(File fptr, const char *keyword,
2038
                              bool should_use_quotes, 
2039 2040 2041
                              const char *keystr)
{
  int err= add_string(fptr, keyword);
2042

2043 2044 2045
  err+= add_space(fptr);
  err+= add_equal(fptr);
  err+= add_space(fptr);
2046
  if (should_use_quotes)
2047 2048 2049
    err+= add_quoted_string(fptr, keystr);
  else
    err+= add_string(fptr, keystr);
2050 2051 2052 2053 2054 2055
  return err + add_space(fptr);
}

static int add_keyword_int(File fptr, const char *keyword, longlong num)
{
  int err= add_string(fptr, keyword);
2056

2057 2058 2059 2060 2061 2062 2063
  err+= add_space(fptr);
  err+= add_equal(fptr);
  err+= add_space(fptr);
  err+= add_int(fptr, num);
  return err + add_space(fptr);
}

2064
static int add_engine(File fptr, handlerton *engine_type)
2065
{
antony@ppcg5.local's avatar
antony@ppcg5.local committed
2066
  const char *engine_str= ha_resolve_storage_engine_name(engine_type);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2067
  DBUG_PRINT("info", ("ENGINE: %s", engine_str));
2068 2069 2070 2071 2072 2073 2074
  int err= add_string(fptr, "ENGINE = ");
  return err + add_string(fptr, engine_str);
}

static int add_partition_options(File fptr, partition_element *p_elem)
{
  int err= 0;
2075

2076
  err+= add_space(fptr);
2077
  if (p_elem->tablespace_name)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2078
    err+= add_keyword_string(fptr,"TABLESPACE", FALSE,
2079
                             p_elem->tablespace_name);
2080 2081 2082 2083 2084 2085
  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);
2086 2087 2088 2089 2090 2091 2092 2093 2094
  if (!(current_thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
  {
    if (p_elem->data_file_name)
      err+= add_keyword_string(fptr, "DATA DIRECTORY", TRUE, 
                               p_elem->data_file_name);
    if (p_elem->index_file_name)
      err+= add_keyword_string(fptr, "INDEX DIRECTORY", TRUE, 
                               p_elem->index_file_name);
  }
2095
  if (p_elem->part_comment)
2096
    err+= add_keyword_string(fptr, "COMMENT", TRUE, p_elem->part_comment);
2097 2098 2099
  return err + add_engine(fptr,p_elem->engine_type);
}

2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116

/*
  Check partition fields for result type and if they need
  to check the character set.

  SYNOPSIS
    check_part_field()
    sql_type              Type provided by user
    field_name            Name of field, used for error handling
    result_type           Out value: Result type of field
    need_cs_check         Out value: Do we need character set check

  RETURN VALUES
    TRUE                  Error
    FALSE                 Ok
*/

2117 2118 2119
static int check_part_field(enum_field_types sql_type,
                            const char *field_name,
                            Item_result *result_type,
2120 2121
                            bool *need_cs_check)
{
2122 2123
  if (sql_type >= MYSQL_TYPE_TINY_BLOB &&
      sql_type <= MYSQL_TYPE_BLOB)
2124 2125 2126 2127
  {
    my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
    return TRUE;
  }
2128
  switch (sql_type)
2129
  {
2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144
    case MYSQL_TYPE_NEWDECIMAL:
    case MYSQL_TYPE_DECIMAL:
    case MYSQL_TYPE_TINY:
    case MYSQL_TYPE_SHORT:
    case MYSQL_TYPE_LONG:
    case MYSQL_TYPE_LONGLONG:
    case MYSQL_TYPE_INT24:
      *result_type= INT_RESULT;
      *need_cs_check= FALSE;
      return FALSE;
    case MYSQL_TYPE_NEWDATE:
    case MYSQL_TYPE_DATE:
    case MYSQL_TYPE_TIME:
    case MYSQL_TYPE_DATETIME:
      *result_type= STRING_RESULT;
2145
      *need_cs_check= TRUE;
2146
      return FALSE;
2147 2148 2149
    case MYSQL_TYPE_VARCHAR:
    case MYSQL_TYPE_STRING:
    case MYSQL_TYPE_VAR_STRING:
2150
      *result_type= STRING_RESULT;
2151 2152
      *need_cs_check= TRUE;
      return FALSE;
2153 2154 2155 2156 2157 2158 2159 2160 2161
    case MYSQL_TYPE_TIMESTAMP:
    case MYSQL_TYPE_NULL:
    case MYSQL_TYPE_FLOAT:
    case MYSQL_TYPE_DOUBLE:
    case MYSQL_TYPE_BIT:
    case MYSQL_TYPE_ENUM:
    case MYSQL_TYPE_SET:
    case MYSQL_TYPE_GEOMETRY:
      goto error;
2162 2163 2164 2165 2166
    default:
      goto error;
  }
error:
  my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0),
2167
           field_name);
2168 2169 2170
  return TRUE;
}

2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184

/*
  Find the given field's Create_field object using name of field

  SYNOPSIS
    get_sql_field()
    field_name                   Field name
    alter_info                   Info from ALTER TABLE/CREATE TABLE

  RETURN VALUE
    sql_field                    Object filled in by parser about field
    NULL                         No field found
*/

2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203
static Create_field* get_sql_field(char *field_name,
                                   Alter_info *alter_info)
{
  List_iterator<Create_field> it(alter_info->create_list);
  Create_field *sql_field;
  DBUG_ENTER("get_sql_field");

  while ((sql_field= it++))
  {
    if (!(my_strcasecmp(system_charset_info,
                        sql_field->field_name,
                        field_name)))
    {
      DBUG_RETURN(sql_field);
    }
  }
  DBUG_RETURN(NULL);
}

2204

2205
static int add_column_list_values(File fptr, partition_info *part_info,
2206 2207 2208
                                  part_elem_value *list_value,
                                  HA_CREATE_INFO *create_info,
                                  Alter_info *alter_info)
2209 2210 2211
{
  int err= 0;
  uint i;
2212
  List_iterator<char> it(part_info->part_field_list);
2213
  uint num_elements= part_info->part_field_list.elements;
2214 2215 2216 2217 2218
  bool use_parenthesis= (part_info->part_type == LIST_PARTITION &&
                         part_info->num_columns > 1U);

  if (use_parenthesis)
    err+= add_begin_parenthesis(fptr);
2219
  for (i= 0; i < num_elements; i++)
2220 2221
  {
    part_column_list_val *col_val= &list_value->col_val_array[i];
2222
    char *field_name= it++;
2223 2224 2225 2226 2227 2228
    if (col_val->max_value)
      err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str);
    else if (col_val->null_value)
      err+= add_string(fptr, "NULL");
    else
    {
2229
      char buffer[MAX_KEY_LENGTH];
2230 2231 2232 2233 2234 2235
      String str(buffer, sizeof(buffer), &my_charset_bin);
      Item *item_expr= col_val->item_expression;
      if (item_expr->null_value)
        err+= add_string(fptr, "NULL");
      else
      {
2236 2237
        String *res;
        CHARSET_INFO *field_cs;
2238 2239
        bool need_cs_check= FALSE;
        Item_result result_type= STRING_RESULT;
2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256

        /*
          This function is called at a very early stage, even before
          we have prepared the sql_field objects. Thus we have to
          find the proper sql_field object and get the character set
          from that object.
        */
        if (create_info)
        {
          Create_field *sql_field;

          if (!(sql_field= get_sql_field(field_name,
                                         alter_info)))
          {
            my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
            return 1;
          }
2257 2258 2259 2260
          if (check_part_field(sql_field->sql_type,
                               sql_field->field_name,
                               &result_type,
                               &need_cs_check))
2261 2262 2263 2264 2265 2266 2267
            return 1;
          if (need_cs_check)
            field_cs= get_sql_field_charset(sql_field, create_info);
          else
            field_cs= NULL;
        }
        else
2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281
        {
          Field *field= part_info->part_field_array[i];
          result_type= field->result_type();
          if (check_part_field(field->real_type(),
                               field->field_name,
                               &result_type,
                               &need_cs_check))
            return 1;
          DBUG_ASSERT(result_type == field->result_type());
          if (need_cs_check)
            field_cs= field->charset();
          else
            field_cs= NULL;
        }
2282 2283 2284 2285 2286
        if (result_type != item_expr->result_type())
        {
          my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
          return 1;
        }
2287 2288 2289 2290 2291 2292 2293 2294 2295
        if (field_cs && field_cs != item_expr->collation.collation)
        {
          if (!(item_expr= convert_charset_partition_constant(item_expr,
                                                              field_cs)))
          {
            my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
            return 1;
          }
        }
2296
        {
2297
          String val_conv;
2298
          val_conv.set_charset(system_charset_info);
2299
          res= item_expr->val_str(&str);
2300 2301 2302
          if (get_cs_converted_part_value_from_string(current_thd,
                                                      item_expr, res,
                                                      &val_conv, field_cs,
2303
                                                   (bool)(alter_info != NULL)))
2304
            return 1;
2305
          err+= add_string_object(fptr, &val_conv);
2306
        }
2307 2308
      }
    }
2309
    if (i != (num_elements - 1))
2310 2311
      err+= add_string(fptr, comma_str);
  }
2312 2313
  if (use_parenthesis)
    err+= add_end_parenthesis(fptr);
2314 2315 2316 2317
  return err;
}

static int add_partition_values(File fptr, partition_info *part_info,
2318 2319 2320
                                partition_element *p_elem,
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info)
2321 2322
{
  int err= 0;
2323

2324 2325
  if (part_info->part_type == RANGE_PARTITION)
  {
2326
    err+= add_string(fptr, " VALUES LESS THAN ");
2327
    if (part_info->column_list)
2328
    {
2329 2330
      List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
      part_elem_value *list_value= list_val_it++;
2331
      err+= add_begin_parenthesis(fptr);
2332 2333
      err+= add_column_list_values(fptr, part_info, list_value,
                                   create_info, alter_info);
2334 2335 2336
      err+= add_end_parenthesis(fptr);
    }
    else
2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349
    {
      if (!p_elem->max_value)
      {
        err+= add_begin_parenthesis(fptr);
        if (p_elem->signed_flag)
          err+= add_int(fptr, p_elem->range_value);
        else
          err+= add_uint(fptr, p_elem->range_value);
        err+= add_end_parenthesis(fptr);
      }
      else
        err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str);
    }
2350 2351 2352 2353
  }
  else if (part_info->part_type == LIST_PARTITION)
  {
    uint i;
2354
    List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
2355
    err+= add_string(fptr, " VALUES IN ");
2356
    uint num_items= p_elem->list_val_list.elements;
2357

2358
    err+= add_begin_parenthesis(fptr);
2359 2360 2361
    if (p_elem->has_null_value)
    {
      err+= add_string(fptr, "NULL");
2362
      if (num_items == 0)
2363 2364 2365 2366 2367 2368
      {
        err+= add_end_parenthesis(fptr);
        goto end;
      }
      err+= add_comma(fptr);
    }
2369 2370 2371
    i= 0;
    do
    {
2372 2373
      part_elem_value *list_value= list_val_it++;

2374
      if (part_info->column_list)
2375 2376
        err+= add_column_list_values(fptr, part_info, list_value,
                                     create_info, alter_info);
2377
      else
2378 2379 2380 2381 2382 2383
      {
        if (!list_value->unsigned_flag)
          err+= add_int(fptr, list_value->value);
        else
          err+= add_uint(fptr, list_value->value);
      }
2384
      if (i != (num_items-1))
2385
        err+= add_comma(fptr);
2386
    } while (++i < num_items);
2387 2388
    err+= add_end_parenthesis(fptr);
  }
2389
end:
2390
  return err;
2391 2392 2393 2394 2395 2396
}

/*
  Generate the partition syntax from the partition data structure.
  Useful for support of generating defaults, SHOW CREATE TABLES
  and easy partition management.
2397

2398 2399 2400 2401 2402 2403
  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
2404
    show_partition_options     Should we display partition options
2405 2406
    create_info                Info generated by parser
    alter_info                 Info generated by parser
2407

2408 2409 2410
  RETURN VALUES
    NULL error
    buf, buf_length            Buffer and its length
2411

2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433
  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,
2434
                                bool use_sql_alloc,
2435 2436 2437
                                bool show_partition_options,
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info)
2438
{
2439
  uint i,j, tot_num_parts, num_subparts;
2440 2441 2442 2443
  partition_element *part_elem;
  ulonglong buffer_length;
  char path[FN_REFLEN];
  int err= 0;
2444
  List_iterator<partition_element> part_it(part_info->partitions);
2445 2446
  File fptr;
  char *buf= NULL; //Return buffer
2447 2448
  DBUG_ENTER("generate_partition_syntax");

2449 2450 2451
  if (unlikely(((fptr= create_temp_file(path,mysql_tmpdir,"psy", 
                                        O_RDWR | O_BINARY | O_TRUNC |  
                                        O_TEMPORARY, MYF(MY_WME)))) < 0))
2452
    DBUG_RETURN(NULL);
2453 2454
#ifndef __WIN__
  unlink(path);
2455 2456 2457 2458 2459 2460
#endif
  err+= add_space(fptr);
  err+= add_partition_by(fptr);
  switch (part_info->part_type)
  {
    case RANGE_PARTITION:
2461
      err+= add_part_key_word(fptr, partition_keywords[PKW_RANGE].str);
2462 2463
      break;
    case LIST_PARTITION:
2464
      err+= add_part_key_word(fptr, partition_keywords[PKW_LIST].str);
2465 2466 2467
      break;
    case HASH_PARTITION:
      if (part_info->linear_hash_ind)
2468
        err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
2469
      if (part_info->list_of_part_fields)
2470 2471 2472 2473
      {
        err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
        err+= add_part_field_list(fptr, part_info->part_field_list);
      }
2474
      else
2475
        err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
2476 2477 2478 2479
      break;
    default:
      DBUG_ASSERT(0);
      /* We really shouldn't get here, no use in continuing from here */
2480
      my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
2481 2482 2483
      DBUG_RETURN(NULL);
  }
  if (part_info->part_expr)
2484 2485
  {
    err+= add_begin_parenthesis(fptr);
2486 2487
    err+= add_string_len(fptr, part_info->part_func_string,
                         part_info->part_func_len);
2488 2489 2490 2491 2492 2493 2494
    err+= add_end_parenthesis(fptr);
  }
  else if (part_info->column_list)
  {
    err+= add_string(fptr, partition_keywords[PKW_COLUMNS].str);
    err+= add_part_field_list(fptr, part_info->part_field_list);
  }
2495
  if ((!part_info->use_default_num_partitions) &&
2496 2497
       part_info->use_default_partitions)
  {
2498
    err+= add_string(fptr, "\n");
2499
    err+= add_string(fptr, "PARTITIONS ");
2500
    err+= add_int(fptr, part_info->num_parts);
2501
  }
2502
  if (part_info->is_sub_partitioned())
2503
  {
2504
    err+= add_string(fptr, "\n");
2505 2506
    err+= add_subpartition_by(fptr);
    /* Must be hash partitioning for subpartitioning */
2507 2508
    if (part_info->linear_hash_ind)
      err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
2509
    if (part_info->list_of_subpart_fields)
2510 2511 2512 2513
    {
      add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
      add_part_field_list(fptr, part_info->subpart_field_list);
    }
2514
    else
2515
      err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
2516
    if (part_info->subpart_expr)
2517 2518
    {
      err+= add_begin_parenthesis(fptr);
2519 2520
      err+= add_string_len(fptr, part_info->subpart_func_string,
                           part_info->subpart_func_len);
2521 2522
      err+= add_end_parenthesis(fptr);
    }
2523
    if ((!part_info->use_default_num_subpartitions) && 
2524 2525
          part_info->use_default_subpartitions)
    {
2526
      err+= add_string(fptr, "\n");
2527
      err+= add_string(fptr, "SUBPARTITIONS ");
2528
      err+= add_int(fptr, part_info->num_subparts);
2529 2530
    }
  }
2531 2532
  tot_num_parts= part_info->partitions.elements;
  num_subparts= part_info->num_subparts;
2533

2534
  if (!part_info->use_default_partitions)
2535
  {
2536
    bool first= TRUE;
2537
    err+= add_string(fptr, "\n");
2538 2539 2540
    err+= add_begin_parenthesis(fptr);
    i= 0;
    do
2541
    {
2542 2543 2544
      part_elem= part_it++;
      if (part_elem->part_state != PART_TO_BE_DROPPED &&
          part_elem->part_state != PART_REORGED_DROPPED)
2545
      {
2546
        if (!first)
2547
        {
2548
          err+= add_comma(fptr);
2549
          err+= add_string(fptr, "\n");
2550
          err+= add_space(fptr);
2551
        }
2552
        first= FALSE;
2553
        err+= add_partition(fptr);
2554
        err+= add_name_string(fptr, part_elem->partition_name);
2555 2556
        err+= add_partition_values(fptr, part_info, part_elem,
                                   create_info, alter_info);
2557 2558
        if (!part_info->is_sub_partitioned() ||
            part_info->use_default_subpartitions)
2559
        {
2560 2561
          if (show_partition_options)
            err+= add_partition_options(fptr, part_elem);
2562 2563
        }
        else
2564
        {
2565
          err+= add_string(fptr, "\n");
2566 2567 2568 2569 2570 2571 2572 2573
          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);
2574
            err+= add_name_string(fptr, part_elem->partition_name);
2575 2576
            if (show_partition_options)
              err+= add_partition_options(fptr, part_elem);
2577
            if (j != (num_subparts-1))
2578 2579
            {
              err+= add_comma(fptr);
2580 2581
              err+= add_string(fptr, "\n");
              err+= add_space(fptr);
2582 2583 2584 2585
              err+= add_space(fptr);
            }
            else
              err+= add_end_parenthesis(fptr);
2586
          } while (++j < num_subparts);
2587 2588
        }
      }
2589
      if (i == (tot_num_parts-1))
2590
        err+= add_end_parenthesis(fptr);
2591
    } while (++i < tot_num_parts);
2592
  }
2593 2594
  if (err)
    goto close_file;
Marc Alff's avatar
Marc Alff committed
2595
  buffer_length= mysql_file_seek(fptr, 0L, MY_SEEK_END, MYF(0));
2596 2597
  if (unlikely(buffer_length == MY_FILEPOS_ERROR))
    goto close_file;
Marc Alff's avatar
Marc Alff committed
2598 2599
  if (unlikely(mysql_file_seek(fptr, 0L, MY_SEEK_SET, MYF(0))
               == MY_FILEPOS_ERROR))
2600 2601 2602
    goto close_file;
  *buf_length= (uint)buffer_length;
  if (use_sql_alloc)
2603
    buf= (char*) sql_alloc(*buf_length+1);
2604
  else
2605
    buf= (char*) my_malloc(*buf_length+1, MYF(MY_WME));
2606 2607 2608
  if (!buf)
    goto close_file;

Marc Alff's avatar
Marc Alff committed
2609
  if (unlikely(mysql_file_read(fptr, (uchar*)buf, *buf_length, MYF(MY_FNABP))))
2610 2611 2612 2613 2614 2615 2616 2617 2618 2619
  {
    if (!use_sql_alloc)
      my_free(buf, MYF(0));
    else
      buf= NULL;
  }
  else
    buf[*buf_length]= 0;

close_file:
Marc Alff's avatar
Marc Alff committed
2620
  mysql_file_close(fptr, MYF(0));
2621 2622 2623 2624 2625 2626 2627
  DBUG_RETURN(buf);
}


/*
  Check if partition key fields are modified and if it can be handled by the
  underlying storage engine.
2628

2629 2630 2631
  SYNOPSIS
    partition_key_modified
    table                TABLE object for which partition fields are set-up
2632
    fields               Bitmap representing fields to be modified
2633

2634 2635 2636 2637 2638
  RETURN VALUES
    TRUE                 Need special handling of UPDATE
    FALSE                Normal UPDATE handling is ok
*/

2639
bool partition_key_modified(TABLE *table, const MY_BITMAP *fields)
2640
{
2641
  Field **fld;
2642
  partition_info *part_info= table->part_info;
2643
  DBUG_ENTER("partition_key_modified");
2644

2645 2646
  if (!part_info)
    DBUG_RETURN(FALSE);
antony@ppcg5.local's avatar
antony@ppcg5.local committed
2647 2648
  if (table->s->db_type()->partition_flags &&
      (table->s->db_type()->partition_flags() & HA_CAN_UPDATE_PARTITION_KEY))
2649
    DBUG_RETURN(FALSE);
2650 2651
  for (fld= part_info->full_part_field_array; *fld; fld++)
    if (bitmap_is_set(fields, (*fld)->field_index))
2652 2653 2654 2655 2656
      DBUG_RETURN(TRUE);
  DBUG_RETURN(FALSE);
}


2657 2658 2659 2660 2661 2662
/*
  A function to handle correct handling of NULL values in partition
  functions.
  SYNOPSIS
    part_val_int()
    item_expr                 The item expression to evaluate
2663 2664
    out:result                The value of the partition function,
                                LONGLONG_MIN if any null value in function
2665
  RETURN VALUES
2666 2667
    TRUE      Error in val_int()
    FALSE     ok
2668 2669
*/

2670
static inline int part_val_int(Item *item_expr, longlong *result)
2671
{
2672
  *result= item_expr->val_int();
2673
  if (item_expr->null_value)
2674 2675 2676 2677 2678 2679 2680
  {
    if (current_thd->is_error())
      return TRUE;
    else
      *result= LONGLONG_MIN;
  }
  return FALSE;
2681 2682 2683
}


2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703
/*
  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.
2704

2705 2706 2707
  SYNOPSIS
    calculate_key_value()
    field_array             An array of the fields in KEY partitioning
2708

2709 2710
  RETURN VALUE
    hash_value calculated
2711

2712 2713 2714 2715 2716 2717 2718
  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)
{
2719
  ulong nr1= 1;
2720
  ulong nr2= 4;
2721

2722 2723 2724
  do
  {
    Field *field= *field_array;
2725
    field->hash(&nr1, &nr2);
2726
  } while (*(++field_array));
2727
  return (uint32) nr1;
2728 2729 2730 2731 2732 2733
}


/*
  A simple support function to calculate part_id given local part and
  sub part.
2734

2735 2736 2737 2738
  SYNOPSIS
    get_part_id_for_sub()
    loc_part_id             Local partition id
    sub_part_id             Subpartition id
2739
    num_subparts            Number of subparts
2740 2741 2742 2743
*/

inline
static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
2744
                                  uint num_subparts)
2745
{
2746
  return (uint32)((loc_part_id * num_subparts) + sub_part_id);
2747 2748 2749 2750 2751
}


/*
  Calculate part_id for (SUB)PARTITION BY HASH
2752

2753 2754
  SYNOPSIS
    get_part_id_hash()
2755
    num_parts                Number of hash partitions
2756
    part_expr                Item tree of hash function
2757 2758
    out:part_id              The returned partition id
    out:func_value           Value of hash function
2759

2760
  RETURN VALUE
2761 2762
    != 0                          Error code
    FALSE                         Success
2763 2764
*/

2765
static int get_part_id_hash(uint num_parts,
2766 2767 2768
                            Item *part_expr,
                            uint32 *part_id,
                            longlong *func_value)
2769
{
2770
  longlong int_hash_id;
2771
  DBUG_ENTER("get_part_id_hash");
2772

2773 2774 2775
  if (part_val_int(part_expr, func_value))
    DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);

2776
  int_hash_id= *func_value % num_parts;
2777

2778 2779
  *part_id= int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id;
  DBUG_RETURN(FALSE);
2780 2781 2782 2783 2784
}


/*
  Calculate part_id for (SUB)PARTITION BY LINEAR HASH
2785

2786 2787 2788 2789
  SYNOPSIS
    get_part_id_linear_hash()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
2790
    num_parts           Number of hash partitions
2791
    part_expr           Item tree of hash function
2792
    out:part_id         The returned partition id
2793
    out:func_value      Value of hash function
2794

2795
  RETURN VALUE
2796 2797
    != 0     Error code
    0        OK
2798 2799
*/

2800
static int get_part_id_linear_hash(partition_info *part_info,
2801
                                   uint num_parts,
2802 2803 2804
                                   Item *part_expr,
                                   uint32 *part_id,
                                   longlong *func_value)
2805 2806
{
  DBUG_ENTER("get_part_id_linear_hash");
2807

2808 2809 2810 2811 2812
  if (part_val_int(part_expr, func_value))
    DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);

  *part_id= get_part_id_from_linear_hash(*func_value,
                                         part_info->linear_hash_mask,
2813
                                         num_parts);
2814
  DBUG_RETURN(FALSE);
2815 2816 2817 2818 2819
}


/*
  Calculate part_id for (SUB)PARTITION BY KEY
2820

2821 2822 2823
  SYNOPSIS
    get_part_id_key()
    field_array         Array of fields for PARTTION KEY
2824
    num_parts           Number of KEY partitions
2825

2826 2827 2828 2829 2830 2831
  RETURN VALUE
    Calculated partition id
*/

inline
static uint32 get_part_id_key(Field **field_array,
2832
                              uint num_parts,
2833
                              longlong *func_value)
2834 2835
{
  DBUG_ENTER("get_part_id_key");
2836
  *func_value= calculate_key_value(field_array);
2837
  DBUG_RETURN((uint32) (*func_value % num_parts));
2838 2839 2840 2841 2842
}


/*
  Calculate part_id for (SUB)PARTITION BY LINEAR KEY
2843

2844 2845 2846 2847 2848
  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
2849
    num_parts            Number of KEY partitions
2850

2851 2852 2853 2854 2855 2856 2857
  RETURN VALUE
    Calculated partition id
*/

inline
static uint32 get_part_id_linear_key(partition_info *part_info,
                                     Field **field_array,
2858
                                     uint num_parts,
2859
                                     longlong *func_value)
2860
{
2861
  DBUG_ENTER("get_part_id_linear_key");
2862

2863 2864
  *func_value= calculate_key_value(field_array);
  DBUG_RETURN(get_part_id_from_linear_hash(*func_value,
2865
                                           part_info->linear_hash_mask,
2866
                                           num_parts));
2867 2868
}

2869 2870
/*
  Copy to field buffers and set up field pointers
2871

2872 2873 2874
  SYNOPSIS
    copy_to_part_field_buffers()
    ptr                          Array of fields to copy
2875 2876 2877
    field_bufs                   Array of field buffers to copy to
    restore_ptr                  Array of pointers to restore to

2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888
  RETURN VALUES
    NONE
  DESCRIPTION
    This routine is used to take the data from field pointer, convert
    it to a standard format and store this format in a field buffer
    allocated for this purpose. Next the field pointers are moved to
    point to the field buffers. There is a separate to restore the
    field pointers after this call.
*/

static void copy_to_part_field_buffers(Field **ptr,
2889 2890
                                       uchar **field_bufs,
                                       uchar **restore_ptr)
2891 2892 2893 2894 2895 2896
{
  Field *field;
  while ((field= *(ptr++)))
  {
    *restore_ptr= field->ptr;
    restore_ptr++;
2897
    if (!field->maybe_null() || !field->is_null())
2898 2899
    {
      CHARSET_INFO *cs= ((Field_str*)field)->charset();
2900 2901
      uint max_len= field->pack_length();
      uint data_len= field->data_length();
2902
      uchar *field_buf= *field_bufs;
2903 2904 2905 2906 2907 2908 2909 2910 2911 2912
      /*
         We only use the field buffer for VARCHAR and CHAR strings
         which isn't of a binary collation. We also only use the
         field buffer for fields which are not currently NULL.
         The field buffer will store a normalised string. We use
         the strnxfrm method to normalise the string.
       */
      if (field->type() == MYSQL_TYPE_VARCHAR)
      {
        uint len_bytes= ((Field_varstring*)field)->length_bytes;
2913 2914
        my_strnxfrm(cs, field_buf + len_bytes, max_len,
                    field->ptr + len_bytes, data_len);
2915
        if (len_bytes == 1)
2916
          *field_buf= (uchar) data_len;
2917
        else
2918
          int2store(field_buf, data_len);
2919 2920 2921
      }
      else
      {
2922 2923
        my_strnxfrm(cs, field_buf, max_len,
                    field->ptr, max_len);
2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936
      }
      field->ptr= field_buf;
    }
    field_bufs++;
  }
  return;
}

/*
  Restore field pointers
  SYNOPSIS
    restore_part_field_pointers()
    ptr                            Array of fields to restore
2937 2938
    restore_ptr                    Array of field pointers to restore to

2939 2940 2941
  RETURN VALUES
*/

2942
static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr)
2943 2944 2945 2946 2947 2948 2949 2950 2951
{
  Field *field;
  while ((field= *(ptr++)))
  {
    field->ptr= *restore_ptr;
    restore_ptr++;
  }
  return;
}
2952

2953 2954 2955 2956 2957 2958 2959 2960 2961 2962
/*
  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
    out:part_id         The partition id is returned through this pointer
Mikael Ronstrom's avatar
Mikael Ronstrom committed
2963
    out:func_value      Value of partition function (longlong)
2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990

  RETURN VALUE
    part_id                     Partition id of partition that would contain
                                row with given values of PF-fields
    HA_ERR_NO_PARTITION_FOUND   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 9 different variants of this function which are called
    through a function pointer.

    get_partition_id_list
    get_partition_id_list_col
    get_partition_id_range
    get_partition_id_range_col
    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_with_sub
*/
2991 2992 2993 2994 2995

/*
  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.
2996

2997 2998 2999 3000
  SYNOPSIS
    get_part_partition_id()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
3001
    out:part_id         The partition id is returned through this pointer
3002
    out:func_value      The value calculated by partition function
3003

3004
  RETURN VALUE
3005 3006 3007
    HA_ERR_NO_PARTITION_FOUND   The fields of the partition function didn't
                                fit into any partition and thus the values of 
                                the PF-fields are not allowed.
3008
    0                           OK
3009

3010 3011
  DESCRIPTION
    
3012
    It is actually 8 different variants of this function which are called
3013 3014 3015
    through a function pointer.

    get_partition_id_list
3016
    get_partition_id_list_col
3017
    get_partition_id_range
3018
    get_partition_id_range_col
3019 3020 3021 3022 3023 3024
    get_partition_id_hash_nosub
    get_partition_id_key_nosub
    get_partition_id_linear_hash_nosub
    get_partition_id_linear_key_nosub
*/

3025 3026 3027 3028 3029
static int get_part_id_charset_func_part(partition_info *part_info,
                                         uint32 *part_id,
                                         longlong *func_value)
{
  int res;
3030
  DBUG_ENTER("get_part_id_charset_func_part");
3031

3032
  copy_to_part_field_buffers(part_info->part_charset_field_array,
3033 3034 3035 3036
                             part_info->part_field_buffers,
                             part_info->restore_part_field_ptrs);
  res= part_info->get_part_partition_id_charset(part_info,
                                                part_id, func_value);
3037
  restore_part_field_pointers(part_info->part_charset_field_array,
3038
                              part_info->restore_part_field_ptrs);
3039
  DBUG_RETURN(res);
3040 3041
}

3042

3043 3044
static int get_part_id_charset_func_subpart(partition_info *part_info,
                                            uint32 *part_id)
3045 3046
{
  int res;
3047 3048
  DBUG_ENTER("get_part_id_charset_func_subpart");

3049
  copy_to_part_field_buffers(part_info->subpart_charset_field_array,
3050 3051
                             part_info->subpart_field_buffers,
                             part_info->restore_subpart_field_ptrs);
3052
  res= part_info->get_subpartition_id_charset(part_info, part_id);
3053
  restore_part_field_pointers(part_info->subpart_charset_field_array,
3054
                              part_info->restore_subpart_field_ptrs);
3055 3056 3057 3058 3059 3060 3061 3062
  DBUG_RETURN(res);
}

int get_partition_id_list_col(partition_info *part_info,
                              uint32 *part_id,
                              longlong *func_value)
{
  part_column_list_val *list_col_array= part_info->list_col_array;
3063
  uint num_columns= part_info->part_field_list.elements;
3064 3065
  int list_index, cmp;
  int min_list_index= 0;
3066
  int max_list_index= part_info->num_list_values - 1;
3067 3068 3069 3070 3071
  DBUG_ENTER("get_partition_id_list_col");

  while (max_list_index >= min_list_index)
  {
    list_index= (max_list_index + min_list_index) >> 1;
3072 3073
    cmp= cmp_rec_and_tuple(list_col_array + list_index*num_columns,
                          num_columns);
3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090
    if (cmp > 0)
      min_list_index= list_index + 1;
    else if (cmp < 0)
    {
      if (!list_index)
        goto notfound;
      max_list_index= list_index - 1;
    }
    else
    {
      *part_id= (uint32)list_col_array[list_index].partition_id;
      DBUG_RETURN(0);
    }
  }
notfound:
  *part_id= 0;
  DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
3091
}
3092 3093


3094
int get_partition_id_list(partition_info *part_info,
3095 3096
                          uint32 *part_id,
                          longlong *func_value)
3097 3098
{
  LIST_PART_ENTRY *list_array= part_info->list_array;
3099 3100
  int list_index;
  int min_list_index= 0;
3101
  int max_list_index= part_info->num_list_values - 1;
3102 3103
  longlong part_func_value;
  int error= part_val_int(part_info->part_expr, &part_func_value);
3104
  longlong list_value;
3105
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
3106 3107
  DBUG_ENTER("get_partition_id_list");

3108 3109 3110
  if (error)
    goto notfound;

3111 3112 3113 3114 3115 3116 3117 3118 3119
  if (part_info->part_expr->null_value)
  {
    if (part_info->has_null_value)
    {
      *part_id= part_info->has_null_part_id;
      DBUG_RETURN(0);
    }
    goto notfound;
  }
3120
  *func_value= part_func_value;
3121 3122
  if (unsigned_flag)
    part_func_value-= 0x8000000000000000ULL;
3123 3124 3125 3126 3127 3128 3129
  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)
3130 3131 3132
    {
      if (!list_index)
        goto notfound;
3133
      max_list_index= list_index - 1;
3134 3135 3136
    }
    else
    {
3137
      *part_id= (uint32)list_array[list_index].partition_id;
3138
      DBUG_RETURN(0);
3139 3140
    }
  }
3141
notfound:
3142
  *part_id= 0;
3143
  DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
3144 3145 3146
}


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

3150 3151 3152 3153 3154 3155 3156 3157
  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
3158
    This function finds the sub-array of part_info->list_array where values of
3159 3160 3161
    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 
3162
       sought sub-array starts at some index idx and continues till array end.
3163 3164 3165 3166
       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
3167
       sought sub-array starts at array start and continues till some last 
3168 3169 3170
       index idx.
       The function returns first number idx, such that 
       list_array[idx].list_value is NOT contained within the passed interval.
3171
       If all array elements are contained, part_info->num_list_values is
3172 3173 3174
       returned.

  NOTE
3175
    The caller will call this function and then will run along the sub-array of
3176 3177 3178 3179 3180 3181
    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
3182
    The edge of corresponding sub-array of part_info->list_array
3183 3184
*/

3185 3186 3187 3188 3189 3190
uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info,
                                               bool left_endpoint,
                                               bool include_endpoint,
                                               uint32 nparts)
{
  part_column_list_val *list_col_array= part_info->list_col_array;
3191
  uint num_columns= part_info->part_field_list.elements;
3192 3193
  int list_index, cmp;
  uint min_list_index= 0;
3194
  uint max_list_index= part_info->num_list_values - 1;
3195 3196 3197 3198 3199 3200
  bool tailf= !(left_endpoint ^ include_endpoint);
  DBUG_ENTER("get_partition_id_cols_list_for_endpoint");

  do
  {
    list_index= (max_list_index + min_list_index) >> 1;
3201
    cmp= cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns,
3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212
                                 nparts, tailf);
    if (cmp > 0)
      min_list_index= list_index + 1;
    else if (cmp < 0)
    {
      if (!list_index)
        goto notfound;
      max_list_index= list_index - 1;
    }
    else 
    {
Mikael Ronstrom's avatar
Mikael Ronstrom committed
3213
      DBUG_RETURN(list_index + test(!tailf));
3214 3215 3216 3217
    }
  } while (max_list_index >= min_list_index);
  if (cmp > 0)
    list_index++;
Mikael Ronstrom's avatar
Mikael Ronstrom committed
3218
notfound:
3219 3220 3221 3222
  DBUG_RETURN(list_index);
}


3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237
uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info,
                                               bool left_endpoint,
                                               bool include_endpoint)
{
  uint32 res;
  copy_to_part_field_buffers(part_info->part_field_array,
                             part_info->part_field_buffers,
                             part_info->restore_part_field_ptrs);
  res= get_list_array_idx_for_endpoint(part_info, left_endpoint,
                                       include_endpoint);
  restore_part_field_pointers(part_info->part_field_array,
                              part_info->restore_part_field_ptrs);
  return res;
}

3238 3239 3240 3241 3242 3243
uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
                                       bool left_endpoint,
                                       bool include_endpoint)
{
  LIST_PART_ENTRY *list_array= part_info->list_array;
  uint list_index;
3244
  uint min_list_index= 0, max_list_index= part_info->num_list_values - 1;
3245
  longlong list_value;
3246
  /* Get the partitioning function value for the endpoint */
3247 3248
  longlong part_func_value= 
    part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint);
3249 3250 3251
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
  DBUG_ENTER("get_list_array_idx_for_endpoint");

3252 3253
  if (part_info->part_expr->null_value)
  {
3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269
    /*
      Special handling for MONOTONIC functions that can return NULL for
      values that are comparable. I.e.
      '2000-00-00' can be compared to '2000-01-01' but TO_DAYS('2000-00-00')
      returns NULL which cannot be compared used <, >, <=, >= etc.

      Otherwise, just return the the first index (lowest value).
    */
    enum_monotonicity_info monotonic;
    monotonic= part_info->part_expr->get_monotonicity_info();
    if (monotonic != MONOTONIC_INCREASING_NOT_NULL && 
        monotonic != MONOTONIC_STRICT_INCREASING_NOT_NULL)
    {
      /* F(col) can not return NULL, return index with lowest value */
      DBUG_RETURN(0);
    }
3270
  }
3271

3272 3273
  if (unsigned_flag)
    part_func_value-= 0x8000000000000000ULL;
3274
  DBUG_ASSERT(part_info->num_list_values);
3275
  do
3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290
  {
    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));
    }
3291
  } while (max_list_index >= min_list_index);
3292 3293 3294 3295 3296 3297
notfound:
  if (list_value < part_func_value)
    list_index++;
  DBUG_RETURN(list_index);
}

3298

3299 3300 3301 3302 3303
int get_partition_id_range_col(partition_info *part_info,
                               uint32 *part_id,
                               longlong *func_value)
{
  part_column_list_val *range_col_array= part_info->range_col_array;
3304 3305
  uint num_columns= part_info->part_field_list.elements;
  uint max_partition= part_info->num_parts - 1;
3306 3307 3308 3309 3310 3311 3312 3313
  uint min_part_id= 0;
  uint max_part_id= max_partition;
  uint loc_part_id;
  DBUG_ENTER("get_partition_id_range_col");

  while (max_part_id > min_part_id)
  {
    loc_part_id= (max_part_id + min_part_id + 1) >> 1;
3314
    if (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns,
3315
                          num_columns) >= 0)
3316 3317 3318 3319 3320 3321
      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)
3322 3323
    if (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns,
                          num_columns) >= 0)
3324 3325 3326
      loc_part_id++;
  *part_id= (uint32)loc_part_id;
  if (loc_part_id == max_partition &&
3327 3328
      (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns,
                         num_columns) >= 0))
3329 3330 3331 3332 3333 3334 3335
    DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);

  DBUG_PRINT("exit",("partition: %d", *part_id));
  DBUG_RETURN(0);
}


3336
int get_partition_id_range(partition_info *part_info,
3337 3338
                           uint32 *part_id,
                           longlong *func_value)
3339 3340
{
  longlong *range_array= part_info->range_int_array;
3341
  uint max_partition= part_info->num_parts - 1;
3342 3343 3344
  uint min_part_id= 0;
  uint max_part_id= max_partition;
  uint loc_part_id;
3345 3346
  longlong part_func_value;
  int error= part_val_int(part_info->part_expr, &part_func_value);
3347
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
3348
  DBUG_ENTER("get_partition_id_range");
3349

3350 3351 3352
  if (error)
    DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);

3353 3354 3355 3356 3357
  if (part_info->part_expr->null_value)
  {
    *part_id= 0;
    DBUG_RETURN(0);
  }
3358
  *func_value= part_func_value;
3359 3360
  if (unsigned_flag)
    part_func_value-= 0x8000000000000000ULL;
3361 3362
  while (max_part_id > min_part_id)
  {
3363
    loc_part_id= (max_part_id + min_part_id) / 2;
patg@govinda.patg.net's avatar
patg@govinda.patg.net committed
3364
    if (range_array[loc_part_id] <= part_func_value)
3365 3366
      min_part_id= loc_part_id + 1;
    else
3367
      max_part_id= loc_part_id;
3368 3369 3370
  }
  loc_part_id= max_part_id;
  *part_id= (uint32)loc_part_id;
3371
  if (loc_part_id == max_partition &&
3372 3373
      part_func_value >= range_array[loc_part_id] &&
      !part_info->defined_max_value)
3374 3375 3376
    DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);

  DBUG_PRINT("exit",("partition: %d", *part_id));
3377
  DBUG_RETURN(0);
3378 3379
}

3380 3381

/*
3382 3383
  Find the sub-array of part_info->range_int_array that covers given interval
 
3384 3385 3386 3387 3388 3389 3390 3391 3392
  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
3393
    This function finds the sub-array of part_info->range_int_array where the
3394
    elements have non-empty intersections with the given interval.
3395
 
3396 3397 3398 3399 3400 3401 3402
    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
3403
       sought sub-array starts at some index idx and continues till array end.
3404 3405 3406 3407 3408
       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
3409
       sought sub-array starts at array start and continues till some last
3410 3411 3412 3413 3414
       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 
3415
       intersection with the passed interval, part_info->num_parts is
3416 3417 3418
       returned.
       
  RETURN
3419
    The edge of corresponding part_info->range_int_array sub-array.
3420 3421
*/

3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437
static uint32
get_partition_id_range_for_endpoint_charset(partition_info *part_info,
                                            bool left_endpoint,
                                            bool include_endpoint)
{
  uint32 res;
  copy_to_part_field_buffers(part_info->part_field_array,
                             part_info->part_field_buffers,
                             part_info->restore_part_field_ptrs);
  res= get_partition_id_range_for_endpoint(part_info, left_endpoint,
                                           include_endpoint);
  restore_part_field_pointers(part_info->part_field_array,
                              part_info->restore_part_field_ptrs);
  return res;
}

3438 3439 3440 3441 3442
uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
                                           bool left_endpoint,
                                           bool include_endpoint)
{
  longlong *range_array= part_info->range_int_array;
3443
  longlong part_end_val;
3444
  uint max_partition= part_info->num_parts - 1;
3445
  uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
3446
  /* Get the partitioning function value for the endpoint */
3447 3448 3449
  longlong part_func_value= 
    part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint);

3450 3451
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
  DBUG_ENTER("get_partition_id_range_for_endpoint");
3452

3453 3454
  if (part_info->part_expr->null_value)
  {
3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474
    /*
      Special handling for MONOTONIC functions that can return NULL for
      values that are comparable. I.e.
      '2000-00-00' can be compared to '2000-01-01' but TO_DAYS('2000-00-00')
      returns NULL which cannot be compared used <, >, <=, >= etc.

      Otherwise, just return the first partition
      (may be included if not left endpoint)
    */
    enum_monotonicity_info monotonic;
    monotonic= part_info->part_expr->get_monotonicity_info();
    if (monotonic != MONOTONIC_INCREASING_NOT_NULL &&
        monotonic != MONOTONIC_STRICT_INCREASING_NOT_NULL)
    {
      /* F(col) can not return NULL, return partition with lowest value */
      if (!left_endpoint && include_endpoint)
        DBUG_RETURN(1);
      DBUG_RETURN(0);               

    }
3475
  }
3476

3477 3478
  if (unsigned_flag)
    part_func_value-= 0x8000000000000000ULL;
3479 3480
  if (left_endpoint && !include_endpoint)
    part_func_value++;
3481 3482 3483 3484 3485

  /*
    Search for the partition containing part_func_value
    (including the right endpoint).
  */
3486 3487
  while (max_part_id > min_part_id)
  {
3488 3489
    loc_part_id= (max_part_id + min_part_id) / 2;
    if (range_array[loc_part_id] < part_func_value)
3490 3491
      min_part_id= loc_part_id + 1;
    else
3492
      max_part_id= loc_part_id;
3493 3494
  }
  loc_part_id= max_part_id;
3495 3496 3497

  /* Adjust for endpoints */
  part_end_val= range_array[loc_part_id];
3498 3499
  if (left_endpoint)
  {
3500 3501 3502 3503
    /*
      In case of PARTITION p VALUES LESS THAN MAXVALUE
      the maximum value is in the current partition.
    */
3504 3505
    if (part_func_value == part_end_val &&
        (loc_part_id < max_partition || !part_info->defined_max_value))
3506 3507 3508 3509
      loc_part_id++;
  }
  else 
  {
3510 3511 3512 3513 3514
    /* if 'WHERE <= X' and partition is LESS THAN (X) include next partition */
    if (include_endpoint && loc_part_id < max_partition &&
        part_func_value == part_end_val)
      loc_part_id++;

3515
    /* Right endpoint, set end after correct partition */
3516
    loc_part_id++;
3517
  }
3518
  DBUG_RETURN(loc_part_id);
3519 3520 3521
}


3522 3523 3524
int get_partition_id_hash_nosub(partition_info *part_info,
                                 uint32 *part_id,
                                 longlong *func_value)
3525
{
3526
  return get_part_id_hash(part_info->num_parts, part_info->part_expr,
3527
                          part_id, func_value);
3528 3529 3530
}


3531 3532 3533
int get_partition_id_linear_hash_nosub(partition_info *part_info,
                                        uint32 *part_id,
                                        longlong *func_value)
3534
{
3535
  return get_part_id_linear_hash(part_info, part_info->num_parts,
3536
                                 part_info->part_expr, part_id, func_value);
3537 3538 3539
}


3540 3541 3542
int get_partition_id_key_nosub(partition_info *part_info,
                                uint32 *part_id,
                                longlong *func_value)
3543
{
3544
  *part_id= get_part_id_key(part_info->part_field_array,
3545
                            part_info->num_parts, func_value);
3546
  return 0;
3547 3548 3549
}


3550 3551 3552
int get_partition_id_linear_key_nosub(partition_info *part_info,
                                      uint32 *part_id,
                                      longlong *func_value)
3553
{
3554 3555
  *part_id= get_part_id_linear_key(part_info,
                                   part_info->part_field_array,
3556
                                   part_info->num_parts, func_value);
3557
  return 0;
3558 3559 3560
}


3561 3562 3563
int get_partition_id_with_sub(partition_info *part_info,
                              uint32 *part_id,
                              longlong *func_value)
3564 3565
{
  uint32 loc_part_id, sub_part_id;
3566
  uint num_subparts;
3567
  int error;
3568
  DBUG_ENTER("get_partition_id_with_sub");
3569

3570 3571 3572
  if (unlikely((error= part_info->get_part_partition_id(part_info,
                                                        &loc_part_id,
                                                        func_value))))
3573
  {
3574
    DBUG_RETURN(error);
3575
  }
3576
  num_subparts= part_info->num_subparts;
3577 3578
  if (unlikely((error= part_info->get_subpartition_id(part_info,
                                                      &sub_part_id))))
3579
  {
3580
    DBUG_RETURN(error);
3581
  } 
3582
  *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, num_subparts);
3583
  DBUG_RETURN(0);
3584 3585 3586 3587 3588
}


/*
  This function is used to calculate the subpartition id
3589

3590 3591 3592 3593
  SYNOPSIS
    get_subpartition_id()
    part_info           A reference to the partition_info struct where all the
                        desired information is given
3594

3595
  RETURN VALUE
3596 3597
    part_id             The subpartition identity

3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610
  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
*/

3611 3612
int get_partition_id_hash_sub(partition_info *part_info,
                              uint32 *part_id)
3613
{
3614
  longlong func_value;
3615
  return get_part_id_hash(part_info->num_subparts, part_info->subpart_expr,
3616
                          part_id, &func_value);
3617 3618 3619
}


3620 3621
int get_partition_id_linear_hash_sub(partition_info *part_info,
                                     uint32 *part_id)
3622
{
3623
  longlong func_value;
3624
  return get_part_id_linear_hash(part_info, part_info->num_subparts,
3625 3626
                                 part_info->subpart_expr, part_id,
                                 &func_value);
3627 3628 3629
}


3630 3631
int get_partition_id_key_sub(partition_info *part_info,
                             uint32 *part_id)
3632
{
3633
  longlong func_value;
3634
  *part_id= get_part_id_key(part_info->subpart_field_array,
3635
                            part_info->num_subparts, &func_value);
3636
  return FALSE;
3637 3638 3639
}


3640 3641
int get_partition_id_linear_key_sub(partition_info *part_info,
                                       uint32 *part_id)
3642
{
3643
  longlong func_value;
3644 3645
  *part_id= get_part_id_linear_key(part_info,
                                   part_info->subpart_field_array,
3646
                                   part_info->num_subparts, &func_value);
3647
  return FALSE;
3648 3649 3650 3651
}


/*
3652 3653
  Set an indicator on all partition fields that are set by the key

3654 3655 3656 3657
  SYNOPSIS
    set_PF_fields_in_key()
    key_info                   Information about the index
    key_length                 Length of key
3658

3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698
  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.
3699

3700 3701 3702
  SYNOPSIS
    check_part_func_bound()
    ptr                     Array of fields NULL terminated (partition fields)
3703

3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728
  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.
3729

3730 3731 3732 3733 3734 3735
  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
3736
    out:part_id   The returned partition id
3737

3738
  RETURN VALUES
3739 3740
    TRUE                    All fields in partition function are set
    FALSE                   Not all fields in partition function are set
3741

3742 3743 3744 3745 3746
  DESCRIPTION
    Use key buffer to set-up record in buf, move field pointers and
    get the partition identity and restore field pointers afterwards.
*/

3747 3748 3749 3750
static int get_sub_part_id_from_key(const TABLE *table,uchar *buf,
                                    KEY *key_info,
                                    const key_range *key_spec,
                                    uint32 *part_id)
3751
{
3752
  uchar *rec0= table->record[0];
3753
  partition_info *part_info= table->part_info;
3754
  int res;
3755 3756
  DBUG_ENTER("get_sub_part_id_from_key");

3757
  key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
3758
  if (likely(rec0 == buf))
mikael@dator3.(none)'s avatar
mikael@dator3.(none) committed
3759
  {
3760
    res= part_info->get_subpartition_id(part_info, part_id);
mikael@dator3.(none)'s avatar
mikael@dator3.(none) committed
3761
  }
3762 3763 3764 3765
  else
  {
    Field **part_field_array= part_info->subpart_field_array;
    set_field_ptr(part_field_array, buf, rec0);
3766
    res= part_info->get_subpartition_id(part_info, part_id);
3767 3768
    set_field_ptr(part_field_array, rec0, buf);
  }
3769
  DBUG_RETURN(res);
3770 3771 3772 3773 3774
}

/*
  Get the id of the partitioning part by using the key buffer of the
  index scan.
3775

3776 3777 3778 3779 3780 3781
  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
3782 3783
    out:part_id   Partition to use

3784 3785 3786
  RETURN VALUES
    TRUE          Partition to use not found
    FALSE         Ok, part_id indicates partition to use
3787

3788 3789 3790 3791
  DESCRIPTION
    Use key buffer to set-up record in buf, move field pointers and
    get the partition identity and restore field pointers afterwards.
*/
3792

3793
bool get_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info,
3794 3795 3796
                          const key_range *key_spec, uint32 *part_id)
{
  bool result;
3797
  uchar *rec0= table->record[0];
3798
  partition_info *part_info= table->part_info;
3799
  longlong func_value;
3800 3801
  DBUG_ENTER("get_part_id_from_key");

3802
  key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
3803
  if (likely(rec0 == buf))
mikael@dator3.(none)'s avatar
mikael@dator3.(none) committed
3804
  {
3805 3806
    result= part_info->get_part_partition_id(part_info, part_id,
                                             &func_value);
mikael@dator3.(none)'s avatar
mikael@dator3.(none) committed
3807
  }
3808 3809 3810 3811
  else
  {
    Field **part_field_array= part_info->part_field_array;
    set_field_ptr(part_field_array, buf, rec0);
3812 3813
    result= part_info->get_part_partition_id(part_info, part_id,
                                             &func_value);
3814 3815 3816 3817 3818 3819 3820 3821
    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.
3822

3823 3824 3825 3826 3827 3828
  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
3829 3830
    out:part_spec A partition id containing start part and end part

3831 3832 3833
  RETURN VALUES
    part_spec
    No partitions to scan is indicated by end_part > start_part when returning
3834

3835 3836 3837 3838 3839
  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.
*/

3840
void get_full_part_id_from_key(const TABLE *table, uchar *buf,
3841 3842 3843 3844 3845
                               KEY *key_info,
                               const key_range *key_spec,
                               part_id_range *part_spec)
{
  bool result;
3846
  partition_info *part_info= table->part_info;
3847
  uchar *rec0= table->record[0];
3848
  longlong func_value;
3849 3850
  DBUG_ENTER("get_full_part_id_from_key");

3851
  key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
3852
  if (likely(rec0 == buf))
mikael@dator3.(none)'s avatar
mikael@dator3.(none) committed
3853
  {
3854 3855
    result= part_info->get_partition_id(part_info, &part_spec->start_part,
                                        &func_value);
mikael@dator3.(none)'s avatar
mikael@dator3.(none) committed
3856
  }
3857 3858 3859 3860
  else
  {
    Field **part_field_array= part_info->full_part_field_array;
    set_field_ptr(part_field_array, buf, rec0);
3861 3862
    result= part_info->get_partition_id(part_info, &part_spec->start_part,
                                        &func_value);
3863 3864 3865 3866 3867 3868 3869
    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;
}
3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906

/*
  Prune the set of partitions to use in query 

  SYNOPSIS
    prune_partition_set()
    table         The table object
    out:part_spec Contains start part, end part 

  DESCRIPTION
    This function is called to prune the range of partitions to scan by
    checking the used_partitions bitmap.
    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 prune_partition_set(const TABLE *table, part_id_range *part_spec)
{
  int last_partition= -1;
  uint i;
  partition_info *part_info= table->part_info;

  DBUG_ENTER("prune_partition_set");
  for (i= part_spec->start_part; i <= part_spec->end_part; i++)
  {
    if (bitmap_is_set(&(part_info->used_partitions), i))
    {
      DBUG_PRINT("info", ("Partition %d is set", i));
      if (last_partition == -1)
        /* First partition found in set and pruned bitmap */
        part_spec->start_part= i;
      last_partition= i;
    }
  }
mskold@mysql.com's avatar
mskold@mysql.com committed
3907 3908 3909 3910
  if (last_partition == -1)
    /* No partition found in pruned bitmap */
    part_spec->start_part= part_spec->end_part + 1;  
  else //if (last_partition != -1)
3911 3912 3913 3914 3915
    part_spec->end_part= last_partition;

  DBUG_VOID_RETURN;
}

3916 3917
/*
  Get the set of partitions to use in query.
3918

3919 3920 3921 3922 3923 3924
  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
3925
    out:part_spec Contains start part, end part and indicator if bitmap is
3926
                  used for which partitions to scan
3927

3928 3929 3930 3931 3932 3933 3934 3935 3936
  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.
3937

3938 3939 3940
  RETURN VALUE
    part_spec
*/
3941
void get_partition_set(const TABLE *table, uchar *buf, const uint index,
3942 3943
                       const key_range *key_spec, part_id_range *part_spec)
{
3944
  partition_info *part_info= table->part_info;
3945
  uint num_parts= part_info->get_tot_partitions();
3946
  uint i, part_id;
3947 3948
  uint sub_part= num_parts;
  uint32 part_part= num_parts;
3949 3950 3951 3952 3953
  KEY *key_info= NULL;
  bool found_part_field= FALSE;
  DBUG_ENTER("get_partition_set");

  part_spec->start_part= 0;
3954
  part_spec->end_part= num_parts - 1;
3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978
  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);
3979 3980 3981 3982
        /*
          Check if range can be adjusted by looking in used_partitions
        */
        prune_partition_set(table, part_spec);
3983 3984
        DBUG_VOID_RETURN;
      }
3985
      else if (part_info->is_sub_partitioned())
3986 3987
      {
        if (part_info->all_fields_in_SPF.is_set(index))
3988 3989 3990
        {
          if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
          {
3991
            part_spec->start_part= num_parts;
3992 3993 3994
            DBUG_VOID_RETURN;
          }
        }
3995 3996
        else if (part_info->all_fields_in_PPF.is_set(index))
        {
3997 3998
          if (get_part_id_from_key(table,buf,key_info,
                                   key_spec,(uint32*)&part_part))
3999 4000 4001 4002 4003 4004
          {
            /*
              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.
            */
4005
            part_spec->start_part= num_parts;
4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030
            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);
4031 4032 4033 4034
          /*
            Check if range can be adjusted by looking in used_partitions
          */
          prune_partition_set(table, part_spec);
4035 4036
          DBUG_VOID_RETURN; 
        }
4037
        else if (part_info->is_sub_partitioned())
4038
        {
4039
          if (check_part_func_bound(part_info->subpart_field_array))
4040 4041 4042
          {
            if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
            {
4043
              part_spec->start_part= num_parts;
4044 4045 4046 4047
              clear_indicator_in_key_fields(key_info);
              DBUG_VOID_RETURN;
            }
          }
4048
          else if (check_part_func_bound(part_info->part_field_array))
4049
          {
4050 4051
            if (get_part_id_from_key(table,buf,key_info,key_spec,&part_part))
            {
4052
              part_spec->start_part= num_parts;
4053 4054 4055
              clear_indicator_in_key_fields(key_info);
              DBUG_VOID_RETURN;
            }
4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072
          }
        }
      }
    }
  }
  {
    /*
      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.
  */
4073
  if (!(part_part == num_parts && sub_part == num_parts))
4074 4075 4076 4077
  {
    /*
      We can only arrive here if we are using subpartitioning.
    */
4078
    if (part_part != num_parts)
4079 4080 4081 4082 4083
    {
      /*
        We know the top partition and need to scan all underlying
        subpartitions. This is a range without holes.
      */
4084 4085 4086
      DBUG_ASSERT(sub_part == num_parts);
      part_spec->start_part= part_part * part_info->num_subparts;
      part_spec->end_part= part_spec->start_part+part_info->num_subparts - 1;
4087 4088 4089
    }
    else
    {
4090
      DBUG_ASSERT(sub_part != num_parts);
4091 4092
      part_spec->start_part= sub_part;
      part_spec->end_part=sub_part+
4093 4094 4095
                           (part_info->num_subparts*(part_info->num_parts-1));
      for (i= 0, part_id= sub_part; i < part_info->num_parts;
           i++, part_id+= part_info->num_subparts)
4096 4097 4098 4099 4100
        ; //Set bit part_id in bit array
    }
  }
  if (found_part_field)
    clear_indicator_in_key_fields(key_info);
4101 4102 4103 4104
  /*
    Check if range can be adjusted by looking in used_partitions
  */
  prune_partition_set(table, part_spec);
4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125
  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      |
4126
   |field names readable        |
4127 4128
   -------------------------------
   | Packed field info           |
4129
   |17 + 1 + strlen(field_name) |
4130 4131 4132 4133 4134 4135 4136 4137
   | + 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.
4138

4139 4140 4141
   SYNOPSIS
     mysql_unpack_partition()
     thd                           Thread object
4142
     part_buf                      Partition info from frm file
4143 4144
     part_info_len                 Length of partition syntax
     table                         Table object of partitioned table
4145 4146
     create_table_ind              Is it called from CREATE TABLE
     default_db_type               What is the default engine of the table
4147 4148
     work_part_info_used           Flag is raised if we don't create new
                                   part_info, but used thd->work_part_info
4149

4150 4151 4152
   RETURN VALUE
     TRUE                          Error
     FALSE                         Sucess
4153

4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164
   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.
*/

4165 4166 4167
bool mysql_unpack_partition(THD *thd,
                            const char *part_buf, uint part_info_len,
                            const char *part_state, uint part_state_len,
4168
                            TABLE* table, bool is_create_table_ind,
4169 4170
                            handlerton *default_db_type,
                            bool *work_part_info_used)
4171 4172 4173
{
  bool result= TRUE;
  partition_info *part_info;
4174
  CHARSET_INFO *old_character_set_client= thd->variables.character_set_client;
4175 4176
  LEX *old_lex= thd->lex;
  LEX lex;
4177
  DBUG_ENTER("mysql_unpack_partition");
4178

4179
  thd->variables.character_set_client= system_charset_info;
4180

4181
  Parser_state parser_state(thd, part_buf, part_info_len);
4182

4183
  if (init_lex_with_single_table(thd, table, &lex))
4184
    goto end;
4185

4186 4187 4188 4189 4190 4191 4192 4193 4194
  /*
    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.
  */
4195
  *work_part_info_used= FALSE;
4196
  lex.part_info= new partition_info();/* Indicates MYSQLparse from this place */
4197 4198 4199 4200 4201
  if (!lex.part_info)
  {
    mem_alloc_error(sizeof(partition_info));
    goto end;
  }
4202 4203 4204
  part_info= lex.part_info;
  part_info->part_state= part_state;
  part_info->part_state_len= part_state_len;
4205
  DBUG_PRINT("info", ("Parse: %s", part_buf));
4206 4207
  if (parse_sql(thd, & parser_state, NULL) ||
      part_info->fix_parser_data(thd))
4208
  {
4209
    thd->free_items();
4210 4211
    goto end;
  }
4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227
  /*
    The parsed syntax residing in the frm file can still contain defaults.
    The reason is that the frm file is sometimes saved outside of this
    MySQL Server and used in backup and restore of clusters or partitioned
    tables. It is not certain that the restore will restore exactly the
    same default partitioning.
    
    The easiest manner of handling this is to simply continue using the
    part_info we already built up during mysql_create_table if we are
    in the process of creating a table. If the table already exists we
    need to discover the number of partitions for the default parts. Since
    the handler object hasn't been created here yet we need to postpone this
    to the fix_partition_func method.
  */

  DBUG_PRINT("info", ("Successful parse"));
mattiasj@witty's avatar
mattiasj@witty committed
4228 4229 4230
  DBUG_PRINT("info", ("default engine = %s, default_db_type = %s",
             ha_resolve_storage_engine_name(part_info->default_engine_type),
             ha_resolve_storage_engine_name(default_db_type)));
4231
  if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
4232
  {
4233
    if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
4234 4235
    {
      /*
4236 4237 4238
        This code is executed when we create table in CREATE TABLE t1 LIKE t2.
        old_lex->query_tables contains table list element for t2 and the table
        we are opening has name t1.
4239
      */
4240 4241
      if (partition_default_handling(table, part_info, FALSE,
                                     old_lex->query_tables->table->s->path.str))
4242
      {
4243 4244
        result= TRUE;
        goto end;
4245 4246 4247
      }
    }
    else
4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260
    {
      /*
        When we come here we are doing a create table. In this case we
        have already done some preparatory work on the old part_info
        object. We don't really need this new partition_info object.
        Thus we go back to the old partition info object.
        We need to free any memory objects allocated on item_free_list
        by the parser since we are keeping the old info from the first
        parser call in CREATE TABLE.
        We'll ensure that this object isn't put into table cache also
        just to ensure we don't get into strange situations with the
        item objects.
      */
4261
      thd->free_items();
4262
      part_info= thd->work_part_info;
4263
      table->s->version= 0UL;
4264
      *work_part_info_used= true;
4265
    }
4266
  }
4267
  table->part_info= part_info;
4268
  table->file->set_part_info(part_info);
4269
  if (!part_info->default_engine_type)
4270
    part_info->default_engine_type= default_db_type;
4271
  DBUG_ASSERT(part_info->default_engine_type == default_db_type);
4272 4273
  DBUG_ASSERT(part_info->default_engine_type->db_type != DB_TYPE_UNKNOWN);
  DBUG_ASSERT(part_info->default_engine_type != partition_hton);
4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286

  {
  /*
    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; 
4287 4288 4289
    char *part_func_string= NULL;
    char *subpart_func_string= NULL;
    if ((part_func_len &&
4290
         !((part_func_string= (char*) thd->alloc(part_func_len)))) ||
4291
        (subpart_func_len &&
4292
         !((subpart_func_string= (char*) thd->alloc(subpart_func_len)))))
4293
    {
4294
      mem_alloc_error(part_func_len);
4295
      thd->free_items();
4296 4297
      goto end;
    }
4298 4299
    if (part_func_len)
      memcpy(part_func_string, part_info->part_func_string, part_func_len);
4300 4301 4302 4303 4304 4305 4306 4307 4308
    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;
  }

  result= FALSE;
end:
4309
  end_lex_with_single_table(thd, table, old_lex);
4310
  thd->variables.character_set_client= old_character_set_client;
4311 4312
  DBUG_RETURN(result);
}
4313

4314

4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346
/*
  Set engine type on all partition element objects
  SYNOPSIS
    set_engine_all_partitions()
    part_info                  Partition info
    engine_type                Handlerton reference of engine
  RETURN VALUES
    NONE
*/

static
void
set_engine_all_partitions(partition_info *part_info,
                          handlerton *engine_type)
{
  uint i= 0;
  List_iterator<partition_element> part_it(part_info->partitions);
  do
  {
    partition_element *part_elem= part_it++;

    part_elem->engine_type= engine_type;
    if (part_info->is_sub_partitioned())
    {
      List_iterator<partition_element> sub_it(part_elem->subpartitions);
      uint j= 0;

      do
      {
        partition_element *sub_elem= sub_it++;

        sub_elem->engine_type= engine_type;
4347
      } while (++j < part_info->num_subparts);
4348
    }
4349
  } while (++i < part_info->num_parts);
4350
}
4351 4352
/*
  SYNOPSIS
4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364
    fast_end_partition()
    thd                           Thread object
    out:copied                    Number of records copied
    out:deleted                   Number of records deleted
    table_list                    Table list with the one table in it
    empty                         Has nothing been done
    lpt                           Struct to be used by error handler

  RETURN VALUES
    FALSE                         Success
    TRUE                          Failure

4365
  DESCRIPTION
4366 4367
    Support routine to handle the successful cases for partition
    management.
4368 4369
*/

4370 4371
static int fast_end_partition(THD *thd, ulonglong copied,
                              ulonglong deleted,
4372
                              TABLE *table,
4373 4374 4375
                              TABLE_LIST *table_list, bool is_empty,
                              ALTER_PARTITION_PARAM_TYPE *lpt,
                              bool written_bin_log)
4376
{
4377
  int error;
4378
  char tmp_name[80];
4379 4380 4381
  DBUG_ENTER("fast_end_partition");

  thd->proc_info="end";
4382

4383 4384
  if (!is_empty)
    query_cache_invalidate3(thd, table_list, 0);
4385 4386 4387

  error= ha_autocommit_or_rollback(thd, 0);
  if (end_active_trans(thd))
4388
    error= 1;
4389 4390 4391 4392 4393 4394

  if (error)
  {
    /* If error during commit, no need to rollback, it's done. */
    table->file->print_error(error, MYF(0));
    DBUG_RETURN(TRUE);
4395
  }
4396 4397

  if ((!is_empty) && (!written_bin_log) &&
4398 4399 4400
      (!thd->lex->no_write_to_binlog) &&
    write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
    DBUG_RETURN(TRUE);
4401 4402 4403 4404 4405

  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
              (ulong) (copied + deleted),
              (ulong) deleted,
              (ulong) 0);
4406
  my_ok(thd, (ha_rows) (copied+deleted),0L, tmp_name);
4407
  DBUG_RETURN(FALSE);
4408 4409 4410 4411 4412 4413
}


/*
  We need to check if engine used by all partitions can handle
  partitioning natively.
4414

4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434
  SYNOPSIS
    check_native_partitioned()
    create_info            Create info in CREATE TABLE
    out:ret_val            Return value
    part_info              Partition info
    thd                    Thread object

  RETURN VALUES
  Value returned in bool ret_value
    TRUE                   Native partitioning supported by engine
    FALSE                  Need to use partition handler

  Return value from function
    TRUE                   Error
    FALSE                  Success
*/

static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val,
                                     partition_info *part_info, THD *thd)
{
4435 4436
  bool table_engine_set;
  handlerton *engine_type= part_info->default_engine_type;
4437
  handlerton *old_engine_type= engine_type;
4438 4439
  DBUG_ENTER("check_native_partitioned");

4440
  if (create_info->used_fields & HA_CREATE_USED_ENGINE)
4441
  {
4442 4443 4444 4445 4446 4447 4448
    table_engine_set= TRUE;
    engine_type= create_info->db_type;
  }
  else
  {
    table_engine_set= FALSE;
    if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
4449
    {
4450 4451 4452
      table_engine_set= TRUE;
      DBUG_ASSERT(engine_type && engine_type != partition_hton);
    }
4453
  }
mattiasj@witty's avatar
mattiasj@witty committed
4454 4455
  DBUG_PRINT("info", ("engine_type = %s, table_engine_set = %u",
                       ha_resolve_storage_engine_name(engine_type),
4456 4457 4458
                       table_engine_set));
  if (part_info->check_engine_mix(engine_type, table_engine_set))
    goto error;
4459

4460 4461 4462 4463
  /*
    All engines are of the same type. Check if this engine supports
    native partitioning.
  */
4464 4465 4466 4467 4468

  if (!engine_type)
    engine_type= old_engine_type;
  DBUG_PRINT("info", ("engine_type = %s",
              ha_resolve_storage_engine_name(engine_type)));
4469 4470 4471 4472 4473 4474 4475 4476
  if (engine_type->partition_flags &&
      (engine_type->partition_flags() & HA_CAN_PARTITION))
  {
    create_info->db_type= engine_type;
    DBUG_PRINT("info", ("Changed to native partitioning"));
    *ret_val= TRUE;
  }
  DBUG_RETURN(FALSE);
4477 4478 4479 4480 4481
error:
  /*
    Mixed engines not yet supported but when supported it will need
    the partition handler
  */
4482
  my_error(ER_MIX_HANDLER_ERROR, MYF(0));
4483 4484
  *ret_val= FALSE;
  DBUG_RETURN(TRUE);
4485 4486 4487
}


4488 4489 4490 4491 4492 4493 4494
/*
  Sets which partitions to be used in the command
*/
uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
               enum partition_state part_state)
{
  uint part_count= 0;
4495
  uint num_parts_found= 0;
4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507
  List_iterator<partition_element> part_it(tab_part_info->partitions);

  do
  {
    partition_element *part_elem= part_it++;
    if ((alter_info->flags & ALTER_ALL_PARTITION) ||
         (is_name_in_list(part_elem->partition_name,
          alter_info->partition_names)))
    {
      /*
        Mark the partition.
        I.e mark the partition as a partition to be "changed" by
4508
        analyzing/optimizing/rebuilding/checking/repairing/...
4509
      */
4510
      num_parts_found++;
4511 4512 4513 4514
      part_elem->part_state= part_state;
      DBUG_PRINT("info", ("Setting part_state to %u for partition %s",
                          part_state, part_elem->partition_name));
    }
4515 4516
    else
      part_elem->part_state= PART_NORMAL;
4517 4518
  } while (++part_count < tab_part_info->num_parts);
  return num_parts_found;
4519 4520 4521
}


4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551
/*
  Prepare for ALTER TABLE of partition structure

  SYNOPSIS
    prep_alter_part_table()
    thd                        Thread object
    table                      Table object
    inout:alter_info           Alter information
    inout:create_info          Create info for CREATE TABLE
    old_db_type                Old engine type
    out:partition_changed      Boolean indicating whether partition changed
    out:fast_alter_partition   Boolean indicating whether fast partition
                               change is requested

  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
    partition_changed
    fast_alter_partition

  DESCRIPTION
    This method handles all preparations for ALTER TABLE for partitioned
    tables
    We need to handle both partition management command such as Add Partition
    and others here as well as an ALTER TABLE that completely changes the
    partitioning and yet others that don't change anything at all. We start
    by checking the partition management variants and then check the general
    change patterns.
*/

4552
uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
4553 4554 4555 4556 4557 4558 4559
                           HA_CREATE_INFO *create_info,
                           handlerton *old_db_type,
                           bool *partition_changed,
                           uint *fast_alter_partition)
{
  DBUG_ENTER("prep_alter_part_table");

4560 4561 4562 4563 4564 4565 4566 4567 4568
  /*
    We are going to manipulate the partition info on the table object
    so we need to ensure that the data structure of the table object
    is freed by setting version to 0. table->s->version= 0 forces a
    flush of the table object in close_thread_tables().
  */
  if (table->part_info)
    table->s->version= 0L;

4569 4570 4571
  thd->work_part_info= thd->lex->part_info;
  if (thd->work_part_info &&
      !(thd->work_part_info= thd->lex->part_info->get_clone()))
4572 4573
    DBUG_RETURN(TRUE);

4574 4575 4576
  /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */
  DBUG_ASSERT(!(alter_info->flags & ALTER_ADMIN_PARTITION));

4577 4578 4579
  if (alter_info->flags &
      (ALTER_ADD_PARTITION | ALTER_DROP_PARTITION |
       ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION |
4580
       ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION))
4581 4582
  {
    partition_info *tab_part_info= table->part_info;
4583
    partition_info *alt_part_info= thd->work_part_info;
4584
    uint flags= 0;
4585 4586 4587 4588 4589
    bool is_last_partition_reorged;
    part_elem_value *tab_max_elem_val= NULL;
    part_elem_value *alt_max_elem_val= NULL;
    longlong tab_max_range= 0, alt_max_range= 0;

4590 4591 4592 4593 4594
    if (!tab_part_info)
    {
      my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
      DBUG_RETURN(TRUE);
    }
4595
    if (alter_info->flags & ALTER_TABLE_REORG)
4596 4597 4598
    {
      uint new_part_no, curr_part_no;
      if (tab_part_info->part_type != HASH_PARTITION ||
4599
          tab_part_info->use_default_num_partitions)
4600 4601 4602 4603
      {
        my_error(ER_REORG_NO_PARAM_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
      }
4604
      new_part_no= table->file->get_default_no_partitions(create_info);
4605
      curr_part_no= tab_part_info->num_parts;
4606 4607 4608 4609 4610 4611 4612
      if (new_part_no == curr_part_no)
      {
        /*
          No change is needed, we will have the same number of partitions
          after the change as before. Thus we can reply ok immediately
          without any changes at all.
        */
4613 4614
        *fast_alter_partition= TRUE;
        DBUG_RETURN(FALSE);
4615 4616 4617 4618 4619 4620 4621 4622
      }
      else if (new_part_no > curr_part_no)
      {
        /*
          We will add more partitions, we use the ADD PARTITION without
          setting the flag for no default number of partitions
        */
        alter_info->flags|= ALTER_ADD_PARTITION;
4623
        thd->work_part_info->num_parts= new_part_no - curr_part_no;
4624 4625 4626 4627 4628 4629 4630 4631
      }
      else
      {
        /*
          We will remove hash partitions, we use the COALESCE PARTITION
          without setting the flag for no default number of partitions
        */
        alter_info->flags|= ALTER_COALESCE_PARTITION;
4632
        alter_info->num_parts= curr_part_no - new_part_no;
4633 4634
      }
    }
4635
    if (!(flags= table->file->alter_table_flags(alter_info->flags)))
4636 4637 4638 4639
    {
      my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
      DBUG_RETURN(1);
    }
4640 4641 4642 4643
    *fast_alter_partition=
      ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0);
    DBUG_PRINT("info", ("*fast_alter_partition: %d  flags: 0x%x",
                        *fast_alter_partition, flags));
4644 4645
    if ((alter_info->flags & ALTER_ADD_PARTITION) ||
         (alter_info->flags & ALTER_REORGANIZE_PARTITION))
4646
    {
4647
      if (thd->work_part_info->part_type != tab_part_info->part_type)
4648
      {
4649
        if (thd->work_part_info->part_type == NOT_A_PARTITION)
4650
        {
4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664
          if (tab_part_info->part_type == RANGE_PARTITION)
          {
            my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "RANGE");
            DBUG_RETURN(TRUE);
          }
          else if (tab_part_info->part_type == LIST_PARTITION)
          {
            my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "LIST");
            DBUG_RETURN(TRUE);
          }
          /*
            Hash partitions can be altered without parser finds out about
            that it is HASH partitioned. So no error here.
          */
4665 4666 4667
        }
        else
        {
4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690
          if (thd->work_part_info->part_type == RANGE_PARTITION)
          {
            my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
                     "RANGE", "LESS THAN");
          }
          else if (thd->work_part_info->part_type == LIST_PARTITION)
          {
            DBUG_ASSERT(thd->work_part_info->part_type == LIST_PARTITION);
            my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
                     "LIST", "IN");
          }
          else if (tab_part_info->part_type == RANGE_PARTITION)
          {
            my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
                     "RANGE", "LESS THAN");
          }
          else
          {
            DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION);
            my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
                     "LIST", "IN");
          }
          DBUG_RETURN(TRUE);
4691
        }
4692
      }
4693 4694 4695 4696 4697 4698 4699 4700 4701
      if ((tab_part_info->column_list &&
          alt_part_info->num_columns != tab_part_info->num_columns) ||
          (!tab_part_info->column_list &&
            (tab_part_info->part_type == RANGE_PARTITION ||
             tab_part_info->part_type == LIST_PARTITION) &&
            alt_part_info->num_columns != 1U) ||
          (!tab_part_info->column_list &&
            tab_part_info->part_type == HASH_PARTITION &&
            alt_part_info->num_columns != 0))
4702
      {
4703
        my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
4704
        DBUG_RETURN(TRUE);
4705
      }
4706 4707
      alt_part_info->column_list= tab_part_info->column_list;
      if (alt_part_info->fix_parser_data(thd))
4708
      {
4709
        DBUG_RETURN(TRUE);
4710 4711
      }
    }
4712 4713 4714 4715 4716 4717 4718 4719
    if (alter_info->flags & ALTER_ADD_PARTITION)
    {
      /*
        We start by moving the new partitions to the list of temporary
        partitions. We will then check that the new partitions fit in the
        partitioning scheme as currently set-up.
        Partitions are always added at the end in ADD PARTITION.
      */
4720 4721 4722
      uint num_new_partitions= alt_part_info->num_parts;
      uint num_orig_partitions= tab_part_info->num_parts;
      uint check_total_partitions= num_new_partitions + num_orig_partitions;
4723 4724 4725 4726 4727 4728 4729 4730 4731 4732
      uint new_total_partitions= check_total_partitions;
      /*
        We allow quite a lot of values to be supplied by defaults, however we
        must know the number of new partitions in this case.
      */
      if (thd->lex->no_write_to_binlog &&
          tab_part_info->part_type != HASH_PARTITION)
      {
        my_error(ER_NO_BINLOG_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
4733 4734 4735 4736 4737 4738
      }
      if (tab_part_info->defined_max_value)
      {
        my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
      }
4739
      if (num_new_partitions == 0)
4740 4741 4742 4743
      {
        my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0));
        DBUG_RETURN(TRUE);
      }
4744
      if (tab_part_info->is_sub_partitioned())
4745
      {
4746 4747 4748
        if (alt_part_info->num_subparts == 0)
          alt_part_info->num_subparts= tab_part_info->num_subparts;
        else if (alt_part_info->num_subparts != tab_part_info->num_subparts)
4749 4750 4751 4752 4753
        {
          my_error(ER_ADD_PARTITION_SUBPART_ERROR, MYF(0));
          DBUG_RETURN(TRUE);
        }
        check_total_partitions= new_total_partitions*
4754
                                alt_part_info->num_subparts;
4755 4756 4757 4758 4759 4760 4761
      }
      if (check_total_partitions > MAX_PARTITIONS)
      {
        my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
      }
      alt_part_info->part_type= tab_part_info->part_type;
4762
      alt_part_info->subpart_type= tab_part_info->subpart_type;
4763
      if (alt_part_info->set_up_defaults_for_partitioning(table->file,
4764 4765
                                                    ULL(0), 
                                                    tab_part_info->num_parts))
4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839
      {
        DBUG_RETURN(TRUE);
      }
/*
Handling of on-line cases:

ADD PARTITION for RANGE/LIST PARTITIONING:
------------------------------------------
For range and list partitions add partition is simply adding a
new empty partition to the table. If the handler support this we
will use the simple method of doing this. The figure below shows
an example of this and the states involved in making this change.
            
Existing partitions                                     New added partitions
------       ------        ------        ------      |  ------    ------
|    |       |    |        |    |        |    |      |  |    |    |    |
| p0 |       | p1 |        | p2 |        | p3 |      |  | p4 |    | p5 |
------       ------        ------        ------      |  ------    ------
PART_NORMAL  PART_NORMAL   PART_NORMAL   PART_NORMAL    PART_TO_BE_ADDED*2
PART_NORMAL  PART_NORMAL   PART_NORMAL   PART_NORMAL    PART_IS_ADDED*2

The first line is the states before adding the new partitions and the 
second line is after the new partitions are added. All the partitions are
in the partitions list, no partitions are placed in the temp_partitions
list.

ADD PARTITION for HASH PARTITIONING
-----------------------------------
This little figure tries to show the various partitions involved when
adding two new partitions to a linear hash based partitioned table with
four partitions to start with, which lists are used and the states they
pass through. Adding partitions to a normal hash based is similar except
that it is always all the existing partitions that are reorganised not
only a subset of them.

Existing partitions                                     New added partitions
------       ------        ------        ------      |  ------    ------
|    |       |    |        |    |        |    |      |  |    |    |    |
| p0 |       | p1 |        | p2 |        | p3 |      |  | p4 |    | p5 |
------       ------        ------        ------      |  ------    ------
PART_CHANGED PART_CHANGED  PART_NORMAL   PART_NORMAL    PART_TO_BE_ADDED
PART_IS_CHANGED*2          PART_NORMAL   PART_NORMAL    PART_IS_ADDED
PART_NORMAL  PART_NORMAL   PART_NORMAL   PART_NORMAL    PART_IS_ADDED

Reorganised existing partitions
------      ------
|    |      |    |
| p0'|      | p1'|
------      ------

p0 - p5 will be in the partitions list of partitions.
p0' and p1' will actually not exist as separate objects, there presence can
be deduced from the state of the partition and also the names of those
partitions can be deduced this way.

After adding the partitions and copying the partition data to p0', p1',
p4 and p5 from p0 and p1 the states change to adapt for the new situation
where p0 and p1 is dropped and replaced by p0' and p1' and the new p4 and
p5 are in the table again.

The first line above shows the states of the partitions before we start
adding and copying partitions, the second after completing the adding
and copying and finally the third line after also dropping the partitions
that are reorganised.
*/
      if (*fast_alter_partition &&
          tab_part_info->part_type == HASH_PARTITION)
      {
        uint part_no= 0, start_part= 1, start_sec_part= 1;
        uint end_part= 0, end_sec_part= 0;
        uint upper_2n= tab_part_info->linear_hash_mask + 1;
        uint lower_2n= upper_2n >> 1;
        bool all_parts= TRUE;
        if (tab_part_info->linear_hash_ind &&
4840
            num_new_partitions < upper_2n)
4841 4842 4843 4844 4845 4846 4847 4848
        {
          /*
            An analysis of which parts needs reorganisation shows that it is
            divided into two intervals. The first interval is those parts
            that are reorganised up until upper_2n - 1. From upper_2n and
            onwards it starts again from partition 0 and goes on until
            it reaches p(upper_2n - 1). If the last new partition reaches
            beyond upper_2n - 1 then the first interval will end with
4849
            p(lower_2n - 1) and start with p(num_orig_partitions - lower_2n).
4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876
            If lower_2n partitions are added then p0 to p(lower_2n - 1) will
            be reorganised which means that the two interval becomes one
            interval at this point. Thus only when adding less than
            lower_2n partitions and going beyond a total of upper_2n we
            actually get two intervals.

            To exemplify this assume we have 6 partitions to start with and
            add 1, 2, 3, 5, 6, 7, 8, 9 partitions.
            The first to add after p5 is p6 = 110 in bit numbers. Thus we
            can see that 10 = p2 will be partition to reorganise if only one
            partition.
            If 2 partitions are added we reorganise [p2, p3]. Those two
            cases are covered by the second if part below.
            If 3 partitions are added we reorganise [p2, p3] U [p0,p0]. This
            part is covered by the else part below.
            If 5 partitions are added we get [p2,p3] U [p0, p2] = [p0, p3].
            This is covered by the first if part where we need the max check
            to here use lower_2n - 1.
            If 7 partitions are added we get [p2,p3] U [p0, p4] = [p0, p4].
            This is covered by the first if part but here we use the first
            calculated end_part.
            Finally with 9 new partitions we would also reorganise p6 if we
            used the method below but we cannot reorganise more partitions
            than what we had from the start and thus we simply set all_parts
            to TRUE. In this case we don't get into this if-part at all.
          */
          all_parts= FALSE;
4877
          if (num_new_partitions >= lower_2n)
4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892
          {
            /*
              In this case there is only one interval since the two intervals
              overlap and this starts from zero to last_part_no - upper_2n
            */
            start_part= 0;
            end_part= new_total_partitions - (upper_2n + 1);
            end_part= max(lower_2n - 1, end_part);
          }
          else if (new_total_partitions <= upper_2n)
          {
            /*
              Also in this case there is only one interval since we are not
              going over a 2**n boundary
            */
4893 4894
            start_part= num_orig_partitions - lower_2n;
            end_part= start_part + (num_new_partitions - 1);
4895 4896 4897 4898 4899 4900 4901 4902
          }
          else
          {
            /* We have two non-overlapping intervals since we are not
               passing a 2**n border and we have not at least lower_2n
               new parts that would ensure that the intervals become
               overlapping.
            */
4903
            start_part= num_orig_partitions - lower_2n;
4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919
            end_part= upper_2n - 1;
            start_sec_part= 0;
            end_sec_part= new_total_partitions - (upper_2n + 1);
          }
        }
        List_iterator<partition_element> tab_it(tab_part_info->partitions);
        part_no= 0;
        do
        {
          partition_element *p_elem= tab_it++;
          if (all_parts ||
              (part_no >= start_part && part_no <= end_part) ||
              (part_no >= start_sec_part && part_no <= end_sec_part))
          {
            p_elem->part_state= PART_CHANGED;
          }
4920
        } while (++part_no < num_orig_partitions);
4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942
      }
      /*
        Need to concatenate the lists here to make it possible to check the
        partition info for correctness using check_partition_info.
        For on-line add partition we set the state of this partition to
        PART_TO_BE_ADDED to ensure that it is known that it is not yet
        usable (becomes usable when partition is created and the switch of
        partition configuration is made.
      */
      {
        List_iterator<partition_element> alt_it(alt_part_info->partitions);
        uint part_count= 0;
        do
        {
          partition_element *part_elem= alt_it++;
          if (*fast_alter_partition)
            part_elem->part_state= PART_TO_BE_ADDED;
          if (tab_part_info->partitions.push_back(part_elem))
          {
            mem_alloc_error(1);
            DBUG_RETURN(TRUE);
          }
4943 4944
        } while (++part_count < num_new_partitions);
        tab_part_info->num_parts+= num_new_partitions;
4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955
      }
      /*
        If we specify partitions explicitly we don't use defaults anymore.
        Using ADD PARTITION also means that we don't have the default number
        of partitions anymore. We use this code also for Table reorganisations
        and here we don't set any default flags to FALSE.
      */
      if (!(alter_info->flags & ALTER_TABLE_REORG))
      {
        if (!alt_part_info->use_default_partitions)
        {
4956
          DBUG_PRINT("info", ("part_info: 0x%lx", (long) tab_part_info));
4957 4958
          tab_part_info->use_default_partitions= FALSE;
        }
4959
        tab_part_info->use_default_num_partitions= FALSE;
4960
        tab_part_info->is_auto_partitioned= FALSE;
4961 4962
      }
    }
4963
    else if (alter_info->flags & ALTER_DROP_PARTITION)
4964 4965 4966 4967 4968 4969 4970 4971 4972
    {
      /*
        Drop a partition from a range partition and list partitioning is
        always safe and can be made more or less immediate. It is necessary
        however to ensure that the partition to be removed is safely removed
        and that REPAIR TABLE can remove the partition if for some reason the
        command to drop the partition failed in the middle.
      */
      uint part_count= 0;
4973 4974
      uint num_parts_dropped= alter_info->partition_names.elements;
      uint num_parts_found= 0;
4975
      List_iterator<partition_element> part_it(tab_part_info->partitions);
4976 4977

      tab_part_info->is_auto_partitioned= FALSE;
4978 4979 4980 4981 4982 4983
      if (!(tab_part_info->part_type == RANGE_PARTITION ||
            tab_part_info->part_type == LIST_PARTITION))
      {
        my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP");
        DBUG_RETURN(TRUE);
      }
4984
      if (num_parts_dropped >= tab_part_info->num_parts)
4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997
      {
        my_error(ER_DROP_LAST_PARTITION, MYF(0));
        DBUG_RETURN(TRUE);
      }
      do
      {
        partition_element *part_elem= part_it++;
        if (is_name_in_list(part_elem->partition_name,
                            alter_info->partition_names))
        {
          /*
            Set state to indicate that the partition is to be dropped.
          */
4998
          num_parts_found++;
4999 5000
          part_elem->part_state= PART_TO_BE_DROPPED;
        }
5001 5002
      } while (++part_count < tab_part_info->num_parts);
      if (num_parts_found != num_parts_dropped)
5003 5004 5005 5006 5007 5008 5009 5010 5011
      {
        my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP");
        DBUG_RETURN(TRUE);
      }
      if (table->file->is_fk_defined_on_table_or_index(MAX_KEY))
      {
        my_error(ER_ROW_IS_REFERENCED, MYF(0));
        DBUG_RETURN(TRUE);
      }
5012
      tab_part_info->num_parts-= num_parts_dropped;
5013
    }
5014
    else if (alter_info->flags & ALTER_REBUILD_PARTITION)
5015
    {
5016 5017 5018 5019
      uint num_parts_found;
      uint num_parts_opt= alter_info->partition_names.elements;
      num_parts_found= set_part_state(alter_info, tab_part_info, PART_CHANGED);
      if (num_parts_found != num_parts_opt &&
5020 5021
          (!(alter_info->flags & ALTER_ALL_PARTITION)))
      {
5022
        my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD");
5023 5024
        DBUG_RETURN(TRUE);
      }
5025 5026 5027 5028 5029
      if (!(*fast_alter_partition))
      {
        table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
        DBUG_RETURN(TRUE);
      }
5030 5031 5032
    }
    else if (alter_info->flags & ALTER_COALESCE_PARTITION)
    {
5033 5034
      uint num_parts_coalesced= alter_info->num_parts;
      uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced;
5035 5036 5037 5038 5039 5040
      List_iterator<partition_element> part_it(tab_part_info->partitions);
      if (tab_part_info->part_type != HASH_PARTITION)
      {
        my_error(ER_COALESCE_ONLY_ON_HASH_PARTITION, MYF(0));
        DBUG_RETURN(TRUE);
      }
5041
      if (num_parts_coalesced == 0)
5042 5043 5044 5045
      {
        my_error(ER_COALESCE_PARTITION_NO_PARTITION, MYF(0));
        DBUG_RETURN(TRUE);
      }
5046
      if (num_parts_coalesced >= tab_part_info->num_parts)
5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093
      {
        my_error(ER_DROP_LAST_PARTITION, MYF(0));
        DBUG_RETURN(TRUE);
      }
/*
Online handling:
COALESCE PARTITION:
-------------------
The figure below shows the manner in which partitions are handled when
performing an on-line coalesce partition and which states they go through
at start, after adding and copying partitions and finally after dropping
the partitions to drop. The figure shows an example using four partitions
to start with, using linear hash and coalescing one partition (always the
last partition).

Using linear hash then all remaining partitions will have a new reorganised
part.

Existing partitions                     Coalesced partition 
------       ------              ------   |      ------
|    |       |    |              |    |   |      |    |
| p0 |       | p1 |              | p2 |   |      | p3 |
------       ------              ------   |      ------
PART_NORMAL  PART_CHANGED        PART_NORMAL     PART_REORGED_DROPPED
PART_NORMAL  PART_IS_CHANGED     PART_NORMAL     PART_TO_BE_DROPPED
PART_NORMAL  PART_NORMAL         PART_NORMAL     PART_IS_DROPPED

Reorganised existing partitions
            ------
            |    |
            | p1'|
            ------

p0 - p3 is in the partitions list.
The p1' partition will actually not be in any list it is deduced from the
state of p1.
*/
      {
        uint part_count= 0, start_part= 1, start_sec_part= 1;
        uint end_part= 0, end_sec_part= 0;
        bool all_parts= TRUE;
        if (*fast_alter_partition &&
            tab_part_info->linear_hash_ind)
        {
          uint upper_2n= tab_part_info->linear_hash_mask + 1;
          uint lower_2n= upper_2n >> 1;
          all_parts= FALSE;
5094
          if (num_parts_coalesced >= lower_2n)
5095 5096 5097
          {
            all_parts= TRUE;
          }
5098
          else if (num_parts_remain >= lower_2n)
5099
          {
5100 5101
            end_part= tab_part_info->num_parts - (lower_2n + 1);
            start_part= num_parts_remain - lower_2n;
5102 5103 5104 5105
          }
          else
          {
            start_part= 0;
5106
            end_part= tab_part_info->num_parts - (lower_2n + 1);
5107
            end_sec_part= (lower_2n >> 1) - 1;
5108
            start_sec_part= end_sec_part - (lower_2n - (num_parts_remain + 1));
5109 5110 5111 5112 5113 5114 5115 5116 5117 5118
          }
        }
        do
        {
          partition_element *p_elem= part_it++;
          if (*fast_alter_partition &&
              (all_parts ||
              (part_count >= start_part && part_count <= end_part) ||
              (part_count >= start_sec_part && part_count <= end_sec_part)))
            p_elem->part_state= PART_CHANGED;
5119
          if (++part_count > num_parts_remain)
5120 5121 5122 5123 5124 5125
          {
            if (*fast_alter_partition)
              p_elem->part_state= PART_REORGED_DROPPED;
            else
              part_it.remove();
          }
5126 5127
        } while (part_count < tab_part_info->num_parts);
        tab_part_info->num_parts= num_parts_remain;
5128 5129
      }
      if (!(alter_info->flags & ALTER_TABLE_REORG))
5130
      {
5131
        tab_part_info->use_default_num_partitions= FALSE;
5132 5133
        tab_part_info->is_auto_partitioned= FALSE;
      }
5134
    }
5135
    else if (alter_info->flags & ALTER_REORGANIZE_PARTITION)
5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147
    {
      /*
        Reorganise partitions takes a number of partitions that are next
        to each other (at least for RANGE PARTITIONS) and then uses those
        to create a set of new partitions. So data is copied from those
        partitions into the new set of partitions. Those new partitions
        can have more values in the LIST value specifications or less both
        are allowed. The ranges can be different but since they are 
        changing a set of consecutive partitions they must cover the same
        range as those changed from.
        This command can be used on RANGE and LIST partitions.
      */
5148 5149
      uint num_parts_reorged= alter_info->partition_names.elements;
      uint num_parts_new= thd->work_part_info->partitions.elements;
5150
      uint check_total_partitions;
5151 5152

      tab_part_info->is_auto_partitioned= FALSE;
5153
      if (num_parts_reorged > tab_part_info->num_parts)
5154 5155 5156 5157 5158 5159
      {
        my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0));
        DBUG_RETURN(TRUE);
      }
      if (!(tab_part_info->part_type == RANGE_PARTITION ||
            tab_part_info->part_type == LIST_PARTITION) &&
5160
           (num_parts_new != num_parts_reorged))
5161 5162 5163 5164
      {
        my_error(ER_REORG_HASH_ONLY_ON_SAME_NO, MYF(0));
        DBUG_RETURN(TRUE);
      }
5165
      if (tab_part_info->is_sub_partitioned() &&
5166 5167
          alt_part_info->num_subparts &&
          alt_part_info->num_subparts != tab_part_info->num_subparts)
5168 5169 5170 5171
      {
        my_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
      }
5172 5173
      check_total_partitions= tab_part_info->num_parts + num_parts_new;
      check_total_partitions-= num_parts_reorged;
5174 5175 5176 5177 5178
      if (check_total_partitions > MAX_PARTITIONS)
      {
        my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
        DBUG_RETURN(TRUE);
      }
5179 5180
      alt_part_info->part_type= tab_part_info->part_type;
      alt_part_info->subpart_type= tab_part_info->subpart_type;
5181
      alt_part_info->num_subparts= tab_part_info->num_subparts;
5182 5183 5184 5185 5186 5187 5188
      DBUG_ASSERT(!alt_part_info->use_default_partitions);
      if (alt_part_info->set_up_defaults_for_partitioning(table->file,
                                                          ULL(0), 
                                                          0))
      {
        DBUG_RETURN(TRUE);
      }
5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238
/*
Online handling:
REORGANIZE PARTITION:
---------------------
The figure exemplifies the handling of partitions, their state changes and
how they are organised. It exemplifies four partitions where two of the
partitions are reorganised (p1 and p2) into two new partitions (p4 and p5).
The reason of this change could be to change range limits, change list
values or for hash partitions simply reorganise the partition which could
also involve moving them to new disks or new node groups (MySQL Cluster).

Existing partitions                                  
------       ------        ------        ------
|    |       |    |        |    |        |    |
| p0 |       | p1 |        | p2 |        | p3 |
------       ------        ------        ------
PART_NORMAL  PART_TO_BE_REORGED          PART_NORMAL
PART_NORMAL  PART_TO_BE_DROPPED          PART_NORMAL
PART_NORMAL  PART_IS_DROPPED             PART_NORMAL

Reorganised new partitions (replacing p1 and p2)
------      ------
|    |      |    |
| p4 |      | p5 |
------      ------
PART_TO_BE_ADDED
PART_IS_ADDED
PART_IS_ADDED

All unchanged partitions and the new partitions are in the partitions list
in the order they will have when the change is completed. The reorganised
partitions are placed in the temp_partitions list. PART_IS_ADDED is only a
temporary state not written in the frm file. It is used to ensure we write
the generated partition syntax in a correct manner.
*/
      {
        List_iterator<partition_element> tab_it(tab_part_info->partitions);
        uint part_count= 0;
        bool found_first= FALSE;
        bool found_last= FALSE;
        uint drop_count= 0;
        do
        {
          partition_element *part_elem= tab_it++;
          is_last_partition_reorged= FALSE;
          if (is_name_in_list(part_elem->partition_name,
                              alter_info->partition_names))
          {
            is_last_partition_reorged= TRUE;
            drop_count++;
5239 5240 5241 5242 5243 5244 5245
            if (tab_part_info->column_list)
            {
              List_iterator<part_elem_value> p(part_elem->list_val_list);
              tab_max_elem_val= p++;
            }
            else
              tab_max_range= part_elem->range_value;
5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256
            if (*fast_alter_partition &&
                tab_part_info->temp_partitions.push_back(part_elem))
            {
              mem_alloc_error(1);
              DBUG_RETURN(TRUE);
            }
            if (*fast_alter_partition)
              part_elem->part_state= PART_TO_BE_REORGED;
            if (!found_first)
            {
              uint alt_part_count= 0;
5257
              partition_element *alt_part_elem;
5258 5259
              List_iterator<partition_element>
                                 alt_it(alt_part_info->partitions);
5260
              found_first= TRUE;
5261 5262
              do
              {
5263 5264 5265 5266 5267 5268 5269 5270 5271
                alt_part_elem= alt_it++;
                if (tab_part_info->column_list)
                {
                  List_iterator<part_elem_value> p(alt_part_elem->list_val_list);
                  alt_max_elem_val= p++;
                }
                else
                  alt_max_range= alt_part_elem->range_value;

5272 5273 5274 5275 5276 5277
                if (*fast_alter_partition)
                  alt_part_elem->part_state= PART_TO_BE_ADDED;
                if (alt_part_count == 0)
                  tab_it.replace(alt_part_elem);
                else
                  tab_it.after(alt_part_elem);
5278
              } while (++alt_part_count < num_parts_new);
5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292
            }
            else if (found_last)
            {
              my_error(ER_CONSECUTIVE_REORG_PARTITIONS, MYF(0));
              DBUG_RETURN(TRUE);
            }
            else
              tab_it.remove();
          }
          else
          {
            if (found_first)
              found_last= TRUE;
          }
5293 5294
        } while (++part_count < tab_part_info->num_parts);
        if (drop_count != num_parts_reorged)
5295 5296 5297 5298
        {
          my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REORGANIZE");
          DBUG_RETURN(TRUE);
        }
5299
        tab_part_info->num_parts= check_total_partitions;
5300 5301 5302 5303 5304 5305 5306
      }
    }
    else
    {
      DBUG_ASSERT(FALSE);
    }
    *partition_changed= TRUE;
5307
    thd->work_part_info= tab_part_info;
5308 5309
    if (alter_info->flags & ALTER_ADD_PARTITION ||
        alter_info->flags & ALTER_REORGANIZE_PARTITION)
5310
    {
5311
      if (tab_part_info->use_default_subpartitions &&
5312 5313 5314
          !alt_part_info->use_default_subpartitions)
      {
        tab_part_info->use_default_subpartitions= FALSE;
5315
        tab_part_info->use_default_num_subpartitions= FALSE;
5316
      }
5317
      if (tab_part_info->check_partition_info(thd, (handlerton**)NULL,
5318
                                              table->file, ULL(0), TRUE))
5319 5320 5321
      {
        DBUG_RETURN(TRUE);
      }
5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340
      /*
        The check below needs to be performed after check_partition_info
        since this function "fixes" the item trees of the new partitions
        to reorganize into
      */
      if (alter_info->flags == ALTER_REORGANIZE_PARTITION &&
          tab_part_info->part_type == RANGE_PARTITION &&
          ((is_last_partition_reorged &&
            (tab_part_info->column_list ?
             (tab_part_info->compare_column_values(
                              alt_max_elem_val->col_val_array,
                              tab_max_elem_val->col_val_array) < 0) :
             alt_max_range < tab_max_range)) ||
            (!is_last_partition_reorged &&
             (tab_part_info->column_list ?
              (tab_part_info->compare_column_values(
                              alt_max_elem_val->col_val_array,
                              tab_max_elem_val->col_val_array) != 0) :
              alt_max_range != tab_max_range))))
5341
      {
5342 5343 5344 5345 5346 5347 5348 5349 5350 5351
        /*
          For range partitioning the total resulting range before and
          after the change must be the same except in one case. This is
          when the last partition is reorganised, in this case it is
          acceptable to increase the total range.
          The reason is that it is not allowed to have "holes" in the
          middle of the ranges and thus we should not allow to reorganise
          to create "holes".
        */
        my_error(ER_REORG_OUTSIDE_RANGE, MYF(0));
5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371
        DBUG_RETURN(TRUE);
      }
    }
  }
  else
  {
    /*
     When thd->lex->part_info has a reference to a partition_info the
     ALTER TABLE contained a definition of a partitioning.

     Case I:
       If there was a partition before and there is a new one defined.
       We use the new partitioning. The new partitioning is already
       defined in the correct variable so no work is needed to
       accomplish this.
       We do however need to update partition_changed to ensure that not
       only the frm file is changed in the ALTER TABLE command.

     Case IIa:
       There was a partitioning before and there is no new one defined.
5372
       Also the user has not specified to remove partitioning explicitly.
5373 5374 5375

       We use the old partitioning also for the new table. We do this
       by assigning the partition_info from the table loaded in
5376
       open_table to the partition_info struct used by mysql_create_table
5377 5378 5379 5380
       later in this method.

     Case IIb:
       There was a partitioning before and there is no new one defined.
5381
       The user has specified explicitly to remove partitioning
5382

5383 5384 5385
       Since the user has specified explicitly to remove partitioning
       we override the old partitioning info and create a new table using
       the specified engine.
5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407
       In this case the partition also is changed.

     Case III:
       There was no partitioning before altering the table, there is
       partitioning defined in the altered table. Use the new partitioning.
       No work needed since the partitioning info is already in the
       correct variable.

       In this case we discover one case where the new partitioning is using
       the same partition function as the default (PARTITION BY KEY or
       PARTITION BY LINEAR KEY with the list of fields equal to the primary
       key fields OR PARTITION BY [LINEAR] KEY() for tables without primary
       key)
       Also here partition has changed and thus a new table must be
       created.

     Case IV:
       There was no partitioning before and no partitioning defined.
       Obviously no work needed.
    */
    if (table->part_info)
    {
5408
      if (alter_info->flags & ALTER_REMOVE_PARTITIONING)
5409 5410
      {
        DBUG_PRINT("info", ("Remove partitioning"));
5411
        if (!(create_info->used_fields & HA_CREATE_USED_ENGINE))
5412 5413 5414 5415
        {
          DBUG_PRINT("info", ("No explicit engine used"));
          create_info->db_type= table->part_info->default_engine_type;
        }
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
5416
        DBUG_PRINT("info", ("New engine type: %s",
antony@ppcg5.local's avatar
antony@ppcg5.local committed
5417
                   ha_resolve_storage_engine_name(create_info->db_type)));
5418
        thd->work_part_info= NULL;
5419 5420
        *partition_changed= TRUE;
      }
5421
      else if (!thd->work_part_info)
5422 5423 5424 5425 5426
      {
        /*
          Retain partitioning but possibly with a new storage engine
          beneath.
        */
5427
        thd->work_part_info= table->part_info;
5428
        if (create_info->used_fields & HA_CREATE_USED_ENGINE &&
5429 5430 5431 5432 5433
            create_info->db_type != table->part_info->default_engine_type)
        {
          /*
            Make sure change of engine happens to all partitions.
          */
5434
          DBUG_PRINT("info", ("partition changed"));
5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450
          if (table->part_info->is_auto_partitioned)
          {
            /*
              If the user originally didn't specify partitioning to be
              used we can remove it now.
            */
            thd->work_part_info= NULL;
          }
          else
          {
            /*
              Ensure that all partitions have the proper engine set-up
            */
            set_engine_all_partitions(thd->work_part_info,
                                      create_info->db_type);
          }
5451 5452 5453
          *partition_changed= TRUE;
        }
      }
5454
    }
5455
    if (thd->work_part_info)
5456
    {
5457
      partition_info *part_info= thd->work_part_info;
5458
      bool is_native_partitioned= FALSE;
5459 5460 5461 5462
      /*
        Need to cater for engine types that can handle partition without
        using the partition handler.
      */
5463
      if (thd->work_part_info != table->part_info)
5464 5465
      {
        DBUG_PRINT("info", ("partition changed"));
5466
        *partition_changed= TRUE;
5467 5468 5469 5470
        if (thd->work_part_info->fix_parser_data(thd))
        {
          DBUG_RETURN(TRUE);
        }
5471
      }
5472 5473 5474 5475 5476 5477 5478
      /*
        Set up partition default_engine_type either from the create_info
        or from the previus table
      */
      if (create_info->used_fields & HA_CREATE_USED_ENGINE)
        part_info->default_engine_type= create_info->db_type;
      else
5479
      {
5480
        if (table->part_info)
5481
          part_info->default_engine_type= table->part_info->default_engine_type;
5482 5483
        else
          part_info->default_engine_type= create_info->db_type;
5484
      }
5485 5486
      DBUG_ASSERT(part_info->default_engine_type &&
                  part_info->default_engine_type != partition_hton);
5487 5488
      if (check_native_partitioned(create_info, &is_native_partitioned,
                                   part_info, thd))
5489
      {
5490
        DBUG_RETURN(TRUE);
5491
      }
5492
      if (!is_native_partitioned)
5493
      {
5494
        DBUG_ASSERT(create_info->db_type);
5495
        create_info->db_type= partition_hton;
5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531
      }
    }
  }
  DBUG_RETURN(FALSE);
}


/*
  Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE
  partitions. This method is used to implement both single-phase and multi-
  phase implementations of ADD/REORGANIZE/COALESCE partitions.

  SYNOPSIS
    mysql_change_partitions()
    lpt                        Struct containing parameters

  RETURN VALUES
    TRUE                          Failure
    FALSE                         Success

  DESCRIPTION
    Request handler to add partitions as set in states of the partition

    Elements of the lpt parameters used:
    create_info                Create information used to create partitions
    db                         Database name
    table_name                 Table name
    copied                     Output parameter where number of copied
                               records are added
    deleted                    Output parameter where number of deleted
                               records are added
*/

static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
{
  char path[FN_REFLEN+1];
5532 5533
  int error;
  handler *file= lpt->table->file;
5534 5535
  DBUG_ENTER("mysql_change_partitions");

5536
  build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
5537 5538 5539
  if ((error= file->ha_change_partitions(lpt->create_info, path, &lpt->copied,
                                         &lpt->deleted, lpt->pack_frm_data,
                                         lpt->pack_frm_len)))
5540
  {
5541
    file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
5542 5543 5544
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569
}


/*
  Rename partitions in an ALTER TABLE of partitions

  SYNOPSIS
    mysql_rename_partitions()
    lpt                        Struct containing parameters

  RETURN VALUES
    TRUE                          Failure
    FALSE                         Success

  DESCRIPTION
    Request handler to rename partitions as set in states of the partition

    Parameters used:
    db                         Database name
    table_name                 Table name
*/

static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
{
  char path[FN_REFLEN+1];
5570
  int error;
5571 5572
  DBUG_ENTER("mysql_rename_partitions");

5573
  build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
5574
  if ((error= lpt->table->file->ha_rename_partitions(path)))
5575 5576 5577 5578 5579 5580
  {
    if (error != 1)
      lpt->table->file->print_error(error, MYF(0));
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610
}


/*
  Drop partitions in an ALTER TABLE of partitions

  SYNOPSIS
    mysql_drop_partitions()
    lpt                        Struct containing parameters

  RETURN VALUES
    TRUE                          Failure
    FALSE                         Success
  DESCRIPTION
    Drop the partitions marked with PART_TO_BE_DROPPED state and remove
    those partitions from the list.

    Parameters used:
    table                       Table object
    db                          Database name
    table_name                  Table name
*/

static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
{
  char path[FN_REFLEN+1];
  partition_info *part_info= lpt->table->part_info;
  List_iterator<partition_element> part_it(part_info->partitions);
  uint i= 0;
  uint remove_count= 0;
5611
  int error;
5612 5613
  DBUG_ENTER("mysql_drop_partitions");

5614
  build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
5615
  if ((error= lpt->table->file->ha_drop_partitions(path)))
5616
  {
5617
    lpt->table->file->print_error(error, MYF(0));
5618 5619 5620 5621 5622 5623 5624 5625 5626 5627
    DBUG_RETURN(TRUE);
  }
  do
  {
    partition_element *part_elem= part_it++;
    if (part_elem->part_state == PART_IS_DROPPED)
    {
      part_it.remove();
      remove_count++;
    }
5628 5629
  } while (++i < part_info->num_parts);
  part_info->num_parts-= remove_count;
5630 5631 5632 5633
  DBUG_RETURN(FALSE);
}


5634 5635 5636 5637 5638 5639 5640 5641 5642
/*
  Insert log entry into list
  SYNOPSIS
    insert_part_info_log_entry_list()
    log_entry
  RETURN VALUES
    NONE
*/

5643 5644
static void insert_part_info_log_entry_list(partition_info *part_info,
                                            DDL_LOG_MEMORY_ENTRY *log_entry)
5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659
{
  log_entry->next_active_log_entry= part_info->first_log_entry;
  part_info->first_log_entry= log_entry;
}


/*
  Release all log entries for this partition info struct
  SYNOPSIS
    release_part_info_log_entries()
    first_log_entry                 First log entry in list to release
  RETURN VALUES
    NONE
*/

5660
static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry)
5661 5662 5663 5664 5665
{
  DBUG_ENTER("release_part_info_log_entries");

  while (log_entry)
  {
5666
    release_ddl_log_memory_entry(log_entry);
5667
    log_entry= log_entry->next_active_log_entry;
5668 5669 5670 5671 5672
  }
  DBUG_VOID_RETURN;
}


5673
/*
5674
  Log an delete/rename frm file
5675
  SYNOPSIS
5676
    write_log_replace_delete_frm()
5677 5678
    lpt                            Struct for parameters
    next_entry                     Next reference to use in log record
5679 5680 5681
    from_path                      Name to rename from
    to_path                        Name to rename to
    replace_flag                   TRUE if replace, else delete
5682
  RETURN VALUES
5683 5684
    TRUE                           Error
    FALSE                          Success
5685
  DESCRIPTION
5686
    Support routine that writes a replace or delete of an frm file into the
5687
    ddl log. It also inserts an entry that keeps track of used space into
5688
    the partition info object
5689 5690
*/

5691 5692 5693 5694 5695
static bool write_log_replace_delete_frm(ALTER_PARTITION_PARAM_TYPE *lpt,
                                         uint next_entry,
                                         const char *from_path,
                                         const char *to_path,
                                         bool replace_flag)
5696
{
5697 5698
  DDL_LOG_ENTRY ddl_log_entry;
  DDL_LOG_MEMORY_ENTRY *log_entry;
5699
  DBUG_ENTER("write_log_replace_delete_frm");
5700

5701
  if (replace_flag)
5702
    ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
5703
  else
5704 5705
    ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
  ddl_log_entry.next_entry= next_entry;
5706
  ddl_log_entry.handler_name= reg_ext;
5707
  ddl_log_entry.name= to_path;
5708
  if (replace_flag)
5709 5710
    ddl_log_entry.from_name= from_path;
  if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
5711 5712 5713
  {
    DBUG_RETURN(TRUE);
  }
5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726
  insert_part_info_log_entry_list(lpt->part_info, log_entry);
  DBUG_RETURN(FALSE);
}


/*
  Log final partition changes in change partition
  SYNOPSIS
    write_log_changed_partitions()
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738
  DESCRIPTION
    This code is used to perform safe ADD PARTITION for HASH partitions
    and COALESCE for HASH partitions and REORGANIZE for any type of
    partitions.
    We prepare entries for all partitions except the reorganised partitions
    in REORGANIZE partition, those are handled by
    write_log_dropped_partitions. For those partitions that are replaced
    special care is needed to ensure that this is performed correctly and
    this requires a two-phased approach with this log as a helper for this.

    This code is closely intertwined with the code in rename_partitions in
    the partition handler.
5739 5740
*/

5741 5742
static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
                                         uint *next_entry, const char *path)
5743
{
5744
  DDL_LOG_ENTRY ddl_log_entry;
5745
  partition_info *part_info= lpt->part_info;
5746
  DDL_LOG_MEMORY_ENTRY *log_entry;
5747 5748
  char tmp_path[FN_REFLEN];
  char normal_path[FN_REFLEN];
5749 5750
  List_iterator<partition_element> part_it(part_info->partitions);
  uint temp_partitions= part_info->temp_partitions.elements;
5751
  uint num_elements= part_info->partitions.elements;
5752
  uint i= 0;
5753
  DBUG_ENTER("write_log_changed_partitions");
5754 5755 5756 5757 5758 5759 5760

  do
  {
    partition_element *part_elem= part_it++;
    if (part_elem->part_state == PART_IS_CHANGED ||
        (part_elem->part_state == PART_IS_ADDED && temp_partitions))
    {
5761
      if (part_info->is_sub_partitioned())
5762 5763
      {
        List_iterator<partition_element> sub_it(part_elem->subpartitions);
5764
        uint num_subparts= part_info->num_subparts;
5765 5766 5767 5768
        uint j= 0;
        do
        {
          partition_element *sub_elem= sub_it++;
5769 5770
          ddl_log_entry.next_entry= *next_entry;
          ddl_log_entry.handler_name=
5771 5772 5773 5774 5775 5776 5777 5778 5779
               ha_resolve_storage_engine_name(sub_elem->engine_type);
          create_subpartition_name(tmp_path, path,
                                   part_elem->partition_name,
                                   sub_elem->partition_name,
                                   TEMP_PART_NAME);
          create_subpartition_name(normal_path, path,
                                   part_elem->partition_name,
                                   sub_elem->partition_name,
                                   NORMAL_PART_NAME);
5780 5781
          ddl_log_entry.name= normal_path;
          ddl_log_entry.from_name= tmp_path;
5782
          if (part_elem->part_state == PART_IS_CHANGED)
5783
            ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
5784
          else
5785 5786
            ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
          if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
5787 5788 5789 5790 5791 5792
          {
            DBUG_RETURN(TRUE);
          }
          *next_entry= log_entry->entry_pos;
          sub_elem->log_entry= log_entry;
          insert_part_info_log_entry_list(part_info, log_entry);
5793
        } while (++j < num_subparts);
5794 5795 5796
      }
      else
      {
5797 5798
        ddl_log_entry.next_entry= *next_entry;
        ddl_log_entry.handler_name=
5799 5800 5801 5802 5803 5804 5805
               ha_resolve_storage_engine_name(part_elem->engine_type);
        create_partition_name(tmp_path, path,
                              part_elem->partition_name,
                              TEMP_PART_NAME, TRUE);
        create_partition_name(normal_path, path,
                              part_elem->partition_name,
                              NORMAL_PART_NAME, TRUE);
5806 5807
        ddl_log_entry.name= normal_path;
        ddl_log_entry.from_name= tmp_path;
5808
        if (part_elem->part_state == PART_IS_CHANGED)
5809
          ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
5810
        else
5811 5812
          ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
        if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
5813 5814 5815 5816
        {
          DBUG_RETURN(TRUE);
        }
        *next_entry= log_entry->entry_pos;
5817
        part_elem->log_entry= log_entry;
5818 5819 5820
        insert_part_info_log_entry_list(part_info, log_entry);
      }
    }
5821
  } while (++i < num_elements);
5822
  DBUG_RETURN(FALSE);
5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835
}


/*
  Log dropped partitions
  SYNOPSIS
    write_log_dropped_partitions()
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
*/

5836 5837 5838 5839
static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
                                         uint *next_entry,
                                         const char *path,
                                         bool temp_list)
5840
{
5841
  DDL_LOG_ENTRY ddl_log_entry;
5842
  partition_info *part_info= lpt->part_info;
5843
  DDL_LOG_MEMORY_ENTRY *log_entry;
5844 5845
  char tmp_path[FN_LEN];
  List_iterator<partition_element> part_it(part_info->partitions);
5846
  List_iterator<partition_element> temp_it(part_info->temp_partitions);
5847 5848
  uint num_temp_partitions= part_info->temp_partitions.elements;
  uint num_elements= part_info->partitions.elements;
5849 5850
  DBUG_ENTER("write_log_dropped_partitions");

5851
  ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
5852
  if (temp_list)
5853 5854
    num_elements= num_temp_partitions;
  while (num_elements--)
5855
  {
5856 5857 5858 5859 5860
    partition_element *part_elem;
    if (temp_list)
      part_elem= temp_it++;
    else
      part_elem= part_it++;
5861
    if (part_elem->part_state == PART_TO_BE_DROPPED ||
5862 5863
        part_elem->part_state == PART_TO_BE_ADDED ||
        part_elem->part_state == PART_CHANGED)
5864
    {
5865 5866 5867
      uint name_variant;
      if (part_elem->part_state == PART_CHANGED ||
          (part_elem->part_state == PART_TO_BE_ADDED &&
5868
           num_temp_partitions))
5869 5870 5871
        name_variant= TEMP_PART_NAME;
      else
        name_variant= NORMAL_PART_NAME;
5872
      if (part_info->is_sub_partitioned())
5873 5874
      {
        List_iterator<partition_element> sub_it(part_elem->subpartitions);
5875
        uint num_subparts= part_info->num_subparts;
5876
        uint j= 0;
5877 5878 5879
        do
        {
          partition_element *sub_elem= sub_it++;
5880 5881
          ddl_log_entry.next_entry= *next_entry;
          ddl_log_entry.handler_name=
5882
               ha_resolve_storage_engine_name(sub_elem->engine_type);
5883 5884 5885
          create_subpartition_name(tmp_path, path,
                                   part_elem->partition_name,
                                   sub_elem->partition_name,
5886
                                   name_variant);
5887 5888
          ddl_log_entry.name= tmp_path;
          if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
5889 5890 5891 5892
          {
            DBUG_RETURN(TRUE);
          }
          *next_entry= log_entry->entry_pos;
5893
          sub_elem->log_entry= log_entry;
5894
          insert_part_info_log_entry_list(part_info, log_entry);
5895
        } while (++j < num_subparts);
5896 5897 5898
      }
      else
      {
5899 5900
        ddl_log_entry.next_entry= *next_entry;
        ddl_log_entry.handler_name=
5901 5902 5903
               ha_resolve_storage_engine_name(part_elem->engine_type);
        create_partition_name(tmp_path, path,
                              part_elem->partition_name,
5904
                              name_variant, TRUE);
5905 5906
        ddl_log_entry.name= tmp_path;
        if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
5907 5908 5909 5910
        {
          DBUG_RETURN(TRUE);
        }
        *next_entry= log_entry->entry_pos;
5911
        part_elem->log_entry= log_entry;
5912 5913 5914
        insert_part_info_log_entry_list(part_info, log_entry);
      }
    }
5915
  }
5916 5917 5918 5919
  DBUG_RETURN(FALSE);
}


5920
/*
5921
  Set execute log entry in ddl log for this partitioned table
5922 5923 5924 5925 5926 5927 5928 5929
  SYNOPSIS
    set_part_info_exec_log_entry()
    part_info                      Partition info object
    exec_log_entry                 Log entry
  RETURN VALUES
    NONE
*/

5930 5931
static void set_part_info_exec_log_entry(partition_info *part_info,
                                         DDL_LOG_MEMORY_ENTRY *exec_log_entry)
5932 5933 5934 5935 5936 5937
{
  part_info->exec_log_entry= exec_log_entry;
  exec_log_entry->next_active_log_entry= NULL;
}


5938
/*
5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949
  Write the log entry to ensure that the shadow frm file is removed at
  crash.
  SYNOPSIS
    write_log_drop_shadow_frm()
    lpt                      Struct containing parameters
    install_frm              Should we log action to install shadow frm or should
                             the action be to remove the shadow frm file.
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
5950
    Prepare an entry to the ddl log indicating a drop/install of the shadow frm
5951 5952 5953
    file and its corresponding handler file.
*/

5954
static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
5955 5956
{
  partition_info *part_info= lpt->part_info;
5957 5958
  DDL_LOG_MEMORY_ENTRY *log_entry;
  DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
5959
  char shadow_path[FN_REFLEN + 1];
5960
  DBUG_ENTER("write_log_drop_shadow_frm");
5961

5962
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
Marc Alff's avatar
Marc Alff committed
5963
  mysql_mutex_lock(&LOCK_gdl);
5964 5965 5966 5967 5968 5969 5970
  if (write_log_replace_delete_frm(lpt, 0UL, NULL,
                                  (const char*)shadow_path, FALSE))
    goto error;
  log_entry= part_info->first_log_entry;
  if (write_execute_ddl_log_entry(log_entry->entry_pos,
                                    FALSE, &exec_log_entry))
    goto error;
Marc Alff's avatar
Marc Alff committed
5971
  mysql_mutex_unlock(&LOCK_gdl);
5972 5973 5974 5975
  set_part_info_exec_log_entry(part_info, exec_log_entry);
  DBUG_RETURN(FALSE);

error:
5976
  release_part_info_log_entries(part_info->first_log_entry);
Marc Alff's avatar
Marc Alff committed
5977
  mysql_mutex_unlock(&LOCK_gdl);
5978
  part_info->first_log_entry= NULL;
5979
  my_error(ER_DDL_LOG_ERROR, MYF(0));
5980 5981 5982 5983 5984 5985
  DBUG_RETURN(TRUE);
}


/*
  Log renaming of shadow frm to real frm name and dropping of old frm
5986
  SYNOPSIS
5987
    write_log_rename_frm()
5988 5989 5990 5991 5992
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
5993 5994
    Prepare an entry to ensure that we complete the renaming of the frm
    file if failure occurs in the middle of the rename process.
5995 5996
*/

5997
static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
5998
{
5999
  partition_info *part_info= lpt->part_info;
6000 6001
  DDL_LOG_MEMORY_ENTRY *log_entry;
  DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
6002 6003
  char path[FN_REFLEN + 1];
  char shadow_path[FN_REFLEN + 1];
6004
  DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
6005
  DBUG_ENTER("write_log_rename_frm");
6006

6007
  part_info->first_log_entry= NULL;
6008
  build_table_filename(path, sizeof(path) - 1, lpt->db,
6009
                       lpt->table_name, "", 0);
6010
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
Marc Alff's avatar
Marc Alff committed
6011
  mysql_mutex_lock(&LOCK_gdl);
6012
  if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
6013 6014 6015 6016 6017 6018 6019
    goto error;
  log_entry= part_info->first_log_entry;
  part_info->frm_log_entry= log_entry;
  if (write_execute_ddl_log_entry(log_entry->entry_pos,
                                    FALSE, &exec_log_entry))
    goto error;
  release_part_info_log_entries(old_first_log_entry);
Marc Alff's avatar
Marc Alff committed
6020
  mysql_mutex_unlock(&LOCK_gdl);
6021 6022 6023
  DBUG_RETURN(FALSE);

error:
6024
  release_part_info_log_entries(part_info->first_log_entry);
Marc Alff's avatar
Marc Alff committed
6025
  mysql_mutex_unlock(&LOCK_gdl);
6026
  part_info->first_log_entry= old_first_log_entry;
6027
  part_info->frm_log_entry= NULL;
6028
  my_error(ER_DDL_LOG_ERROR, MYF(0));
6029
  DBUG_RETURN(TRUE);
6030 6031 6032 6033
}


/*
6034 6035
  Write the log entries to ensure that the drop partition command is completed
  even in the presence of a crash.
6036 6037

  SYNOPSIS
6038
    write_log_drop_partition()
6039 6040 6041 6042 6043
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
6044
    Prepare entries to the ddl log indicating all partitions to drop and to
6045
    install the shadow frm file and remove the old frm file.
6046 6047
*/

6048
static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
6049
{
6050
  partition_info *part_info= lpt->part_info;
6051 6052
  DDL_LOG_MEMORY_ENTRY *log_entry;
  DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
6053 6054
  char tmp_path[FN_REFLEN + 1];
  char path[FN_REFLEN + 1];
6055
  uint next_entry= 0;
6056
  DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
6057
  DBUG_ENTER("write_log_drop_partition");
6058

6059
  part_info->first_log_entry= NULL;
6060
  build_table_filename(path, sizeof(path) - 1, lpt->db,
6061
                       lpt->table_name, "", 0);
6062
  build_table_filename(tmp_path, sizeof(tmp_path) - 1, lpt->db,
6063
                       lpt->table_name, "#", 0);
Marc Alff's avatar
Marc Alff committed
6064
  mysql_mutex_lock(&LOCK_gdl);
6065 6066 6067
  if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
                                   FALSE))
    goto error;
6068 6069
  if (write_log_replace_delete_frm(lpt, next_entry, (const char*)tmp_path,
                                  (const char*)path, TRUE))
6070 6071 6072 6073 6074 6075 6076
    goto error;
  log_entry= part_info->first_log_entry;
  part_info->frm_log_entry= log_entry;
  if (write_execute_ddl_log_entry(log_entry->entry_pos,
                                    FALSE, &exec_log_entry))
    goto error;
  release_part_info_log_entries(old_first_log_entry);
Marc Alff's avatar
Marc Alff committed
6077
  mysql_mutex_unlock(&LOCK_gdl);
6078 6079 6080
  DBUG_RETURN(FALSE);

error:
6081
  release_part_info_log_entries(part_info->first_log_entry);
Marc Alff's avatar
Marc Alff committed
6082
  mysql_mutex_unlock(&LOCK_gdl);
6083
  part_info->first_log_entry= old_first_log_entry;
6084
  part_info->frm_log_entry= NULL;
6085
  my_error(ER_DDL_LOG_ERROR, MYF(0));
6086
  DBUG_RETURN(TRUE);
6087 6088 6089 6090
}


/*
6091 6092 6093
  Write the log entries to ensure that the add partition command is not
  executed at all if a crash before it has completed

6094
  SYNOPSIS
6095
    write_log_add_change_partition()
6096 6097 6098 6099 6100
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
6101
    Prepare entries to the ddl log indicating all partitions to drop and to
6102
    remove the shadow frm file.
6103
    We always inject entries backwards in the list in the ddl log since we
6104
    don't know the entry position until we have written it.
6105 6106
*/

6107
static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
6108
{
6109
  partition_info *part_info= lpt->part_info;
6110 6111
  DDL_LOG_MEMORY_ENTRY *log_entry;
  DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
6112 6113
  char tmp_path[FN_REFLEN + 1];
  char path[FN_REFLEN + 1];
6114 6115
  uint next_entry= 0;
  DBUG_ENTER("write_log_add_change_partition");
6116

6117
  build_table_filename(path, sizeof(path) - 1, lpt->db,
6118
                       lpt->table_name, "", 0);
6119
  build_table_filename(tmp_path, sizeof(tmp_path) - 1, lpt->db,
6120
                       lpt->table_name, "#", 0);
Marc Alff's avatar
Marc Alff committed
6121
  mysql_mutex_lock(&LOCK_gdl);
6122 6123 6124 6125 6126 6127 6128 6129 6130 6131
  if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
                                   FALSE))
    goto error;
  if (write_log_replace_delete_frm(lpt, next_entry, NULL, tmp_path,
                                  FALSE))
    goto error;
  log_entry= part_info->first_log_entry;
  if (write_execute_ddl_log_entry(log_entry->entry_pos,
                                    FALSE, &exec_log_entry))
    goto error;
Marc Alff's avatar
Marc Alff committed
6132
  mysql_mutex_unlock(&LOCK_gdl);
6133 6134 6135 6136
  set_part_info_exec_log_entry(part_info, exec_log_entry);
  DBUG_RETURN(FALSE);

error:
6137
  release_part_info_log_entries(part_info->first_log_entry);
Marc Alff's avatar
Marc Alff committed
6138
  mysql_mutex_unlock(&LOCK_gdl);
6139
  part_info->first_log_entry= NULL;
6140
  my_error(ER_DDL_LOG_ERROR, MYF(0));
6141
  DBUG_RETURN(TRUE);
6142 6143 6144 6145 6146 6147 6148 6149
}


/*
  Write description of how to complete the operation after first phase of
  change partitions.

  SYNOPSIS
6150
    write_log_final_change_partition()
6151 6152 6153 6154 6155 6156 6157 6158 6159 6160
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
    We will write log entries that specify to remove all partitions reorganised,
    to rename others to reflect the new naming scheme and to install the shadow
    frm file.
*/

6161
static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
6162
{
6163
  partition_info *part_info= lpt->part_info;
6164 6165
  DDL_LOG_MEMORY_ENTRY *log_entry;
  DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
6166 6167
  char path[FN_REFLEN + 1];
  char shadow_path[FN_REFLEN + 1];
6168
  DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
6169 6170
  uint next_entry= 0;
  DBUG_ENTER("write_log_final_change_partition");
6171

6172
  part_info->first_log_entry= NULL;
6173
  build_table_filename(path, sizeof(path) - 1, lpt->db,
6174
                       lpt->table_name, "", 0);
6175
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
Marc Alff's avatar
Marc Alff committed
6176
  mysql_mutex_lock(&LOCK_gdl);
6177
  if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
6178
                      lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION))
6179 6180 6181
    goto error;
  if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
    goto error;
6182
  if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
6183 6184 6185 6186 6187 6188 6189
    goto error;
  log_entry= part_info->first_log_entry;
  part_info->frm_log_entry= log_entry;
  if (write_execute_ddl_log_entry(log_entry->entry_pos,
                                    FALSE, &exec_log_entry))
    goto error;
  release_part_info_log_entries(old_first_log_entry);
Marc Alff's avatar
Marc Alff committed
6190
  mysql_mutex_unlock(&LOCK_gdl);
6191 6192 6193
  DBUG_RETURN(FALSE);

error:
6194
  release_part_info_log_entries(part_info->first_log_entry);
Marc Alff's avatar
Marc Alff committed
6195
  mysql_mutex_unlock(&LOCK_gdl);
6196
  part_info->first_log_entry= old_first_log_entry;
6197
  part_info->frm_log_entry= NULL;
6198
  my_error(ER_DDL_LOG_ERROR, MYF(0));
6199
  DBUG_RETURN(TRUE);
6200 6201 6202
}


6203
/*
6204
  Remove entry from ddl log and release resources for others to use
6205 6206 6207 6208 6209 6210 6211 6212

  SYNOPSIS
    write_log_completed()
    lpt                      Struct containing parameters
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
*/
6213

6214 6215
static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
                                bool dont_crash)
6216
{
6217
  partition_info *part_info= lpt->part_info;
6218
  DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry;
6219
  DBUG_ENTER("write_log_completed");
6220

6221
  DBUG_ASSERT(log_entry);
Marc Alff's avatar
Marc Alff committed
6222
  mysql_mutex_lock(&LOCK_gdl);
6223
  if (write_execute_ddl_log_entry(0UL, TRUE, &log_entry))
6224 6225
  {
    /*
6226
      Failed to write, Bad...
6227 6228
      We have completed the operation but have log records to REMOVE
      stuff that shouldn't be removed. What clever things could one do
6229 6230
      here? An error output was written to the error output by the
      above method so we don't do anything here.
6231
    */
6232
    ;
6233 6234 6235
  }
  release_part_info_log_entries(part_info->first_log_entry);
  release_part_info_log_entries(part_info->exec_log_entry);
Marc Alff's avatar
Marc Alff committed
6236
  mysql_mutex_unlock(&LOCK_gdl);
6237 6238
  part_info->exec_log_entry= NULL;
  part_info->first_log_entry= NULL;
6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251
  DBUG_VOID_RETURN;
}


/*
   Release all log entries
   SYNOPSIS
     release_log_entries()
     part_info                  Partition info struct
   RETURN VALUES
     NONE
*/

6252
static void release_log_entries(partition_info *part_info)
6253
{
Marc Alff's avatar
Marc Alff committed
6254
  mysql_mutex_lock(&LOCK_gdl);
6255 6256
  release_part_info_log_entries(part_info->first_log_entry);
  release_part_info_log_entries(part_info->exec_log_entry);
Marc Alff's avatar
Marc Alff committed
6257
  mysql_mutex_unlock(&LOCK_gdl);
6258 6259
  part_info->first_log_entry= NULL;
  part_info->exec_log_entry= NULL;
6260 6261 6262
}


6263
/*
6264 6265 6266 6267
  Final part of partition changes to handle things when under
  LOCK TABLES.
  SYNPOSIS
    alter_partition_lock_handling()
6268 6269
    lpt                        Struct carrying parameters
  RETURN VALUES
6270
    NONE
6271
*/
6272
static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
6273
{
6274 6275 6276
  int err;
  if (lpt->thd->locked_tables)
  {
6277 6278 6279 6280 6281
    /*
      When we have the table locked, it is necessary to reopen the table
      since all table objects were closed and removed as part of the
      ALTER TABLE of partitioning structure.
    */
Marc Alff's avatar
Marc Alff committed
6282
    mysql_mutex_lock(&LOCK_open);
6283 6284 6285 6286 6287
    lpt->thd->in_lock_tables= 1;
    err= reopen_tables(lpt->thd, 1, 1);
    lpt->thd->in_lock_tables= 0;
    if (err)
    {
6288 6289 6290 6291 6292 6293
      /*
       Issue a warning since we weren't able to regain the lock again.
       We also need to unlink table from thread's open list and from
       table_cache
     */
      unlink_open_table(lpt->thd, lpt->table, FALSE);
6294 6295
      sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
    }
Marc Alff's avatar
Marc Alff committed
6296
    mysql_mutex_unlock(&LOCK_open);
6297
  }
6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311
}

/*
  Unlock and close table before renaming and dropping partitions
  SYNOPSIS
    alter_close_tables()
    lpt                        Struct carrying parameters
  RETURN VALUES
    0
*/

static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
{
  THD *thd= lpt->thd;
6312 6313
  const char *db= lpt->db;
  const char *table_name= lpt->table_name;
6314 6315 6316 6317 6318 6319
  DBUG_ENTER("alter_close_tables");
  /*
    We need to also unlock tables and close all handlers.
    We set lock to zero to ensure we don't do this twice
    and we set db_stat to zero to ensure we don't close twice.
  */
Marc Alff's avatar
Marc Alff committed
6320
  mysql_mutex_lock(&LOCK_open);
6321
  close_data_files_and_morph_locks(thd, db, table_name);
Marc Alff's avatar
Marc Alff committed
6322
  mysql_mutex_unlock(&LOCK_open);
6323 6324 6325 6326
  DBUG_RETURN(0);
}


6327 6328 6329 6330 6331 6332 6333 6334 6335 6336
/*
  Handle errors for ALTER TABLE for partitioning
  SYNOPSIS
    handle_alter_part_error()
    lpt                        Struct carrying parameters
    not_completed              Was request in complete phase when error occurred
  RETURN VALUES
    NONE
*/

6337 6338 6339 6340
void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
                             bool not_completed,
                             bool drop_partition,
                             bool frm_install)
6341 6342 6343 6344 6345
{
  partition_info *part_info= lpt->part_info;
  DBUG_ENTER("handle_alter_part_error");

  if (!part_info->first_log_entry &&
6346 6347
      execute_ddl_log_entry(current_thd,
                            part_info->first_log_entry->entry_pos))
6348 6349
  {
    /*
6350 6351
      We couldn't recover from error, most likely manual interaction
      is required.
6352
    */
6353 6354
    write_log_completed(lpt, FALSE);
    release_log_entries(part_info);
6355 6356 6357 6358 6359
    if (not_completed)
    {
      if (drop_partition)
      {
        /* Table is still ok, but we left a shadow frm file behind. */
6360
        push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
6361 6362 6363
                            "%s %s",
           "Operation was unsuccessful, table is still intact,",
           "but it is possible that a shadow frm file was left behind");
6364 6365 6366 6367
      }
      else
      {
        push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
6368 6369 6370 6371 6372
                            "%s %s %s %s",
           "Operation was unsuccessful, table is still intact,",
           "but it is possible that a shadow frm file was left behind.",
           "It is also possible that temporary partitions are left behind,",
           "these could be empty or more or less filled with records");
6373 6374 6375 6376
      }
    }
    else
    {
6377
      if (frm_install)
6378 6379 6380 6381 6382
      {
        /*
           Failed during install of shadow frm file, table isn't intact
           and dropped partitions are still there
        */
6383
        push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
6384 6385 6386 6387
                            "%s %s %s",
          "Failed during alter of partitions, table is no longer intact.",
          "The frm file is in an unknown state, and a backup",
          "is required.");
6388 6389 6390 6391
      }
      else if (drop_partition)
      {
        /*
6392 6393 6394 6395
          Table is ok, we have switched to new table but left dropped
          partitions still in their places. We remove the log records and
          ask the user to perform the action manually. We remove the log
          records and ask the user to perform the action manually.
6396
        */
6397
        push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
6398 6399 6400
                            "%s %s",
              "Failed during drop of partitions, table is intact.",
              "Manual drop of remaining partitions is required");
6401
      }
6402
      else
6403
      {
6404
        /*
6405 6406 6407
          We failed during renaming of partitions. The table is most
          certainly in a very bad state so we give user warning and disable
          the table by writing an ancient frm version into it.
6408
        */
6409
        push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
6410 6411 6412 6413
                            "%s %s %s",
           "Failed during renaming of partitions. We are now in a position",
           "where table is not reusable",
           "Table is disabled by writing ancient frm file version into it");
6414 6415
      }
    }
6416 6417 6418
  }
  else
  {
6419
    release_log_entries(part_info);
6420 6421 6422 6423
    if (not_completed)
    {
      /*
        We hit an error before things were completed but managed
6424 6425
        to recover from the error. An error occurred and we have
        restored things to original so no need for further action.
6426
      */
6427
      ;
6428 6429 6430 6431 6432 6433
    }
    else
    {
      /*
        We hit an error after we had completed most of the operation
        and were successful in a second attempt so the operation
6434 6435 6436
        actually is successful now. We need to issue a warning that
        even though we reported an error the operation was successfully
        completed.
6437
      */
6438 6439 6440
      push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s",
         "Operation was successfully completed by failure handling,",
         "after failure of normal operation");
6441 6442 6443 6444 6445 6446
    }
  }
  DBUG_VOID_RETURN;
}


6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470
/*
  Actually perform the change requested by ALTER TABLE of partitions
  previously prepared.

  SYNOPSIS
    fast_alter_partition_table()
    thd                           Thread object
    table                         Table object
    alter_info                    ALTER TABLE info
    create_info                   Create info for CREATE TABLE
    table_list                    List of the table involved
    db                            Database name of new table
    table_name                    Table name of new table

  RETURN VALUES
    TRUE                          Error
    FALSE                         Success

  DESCRIPTION
    Perform all ALTER TABLE operations for partitioned tables that can be
    performed fast without a full copy of the original table.
*/

uint fast_alter_partition_table(THD *thd, TABLE *table,
6471
                                Alter_info *alter_info,
6472 6473
                                HA_CREATE_INFO *create_info,
                                TABLE_LIST *table_list,
6474
                                char *db,
6475 6476 6477 6478 6479 6480 6481 6482
                                const char *table_name,
                                uint fast_alter_partition)
{
  /* Set-up struct used to write frm files */
  partition_info *part_info= table->part_info;
  ALTER_PARTITION_PARAM_TYPE lpt_obj;
  ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj;
  bool written_bin_log= TRUE;
6483 6484
  bool not_completed= TRUE;
  bool frm_install= FALSE;
6485 6486 6487
  DBUG_ENTER("fast_alter_partition_table");

  lpt->thd= thd;
6488
  lpt->part_info= part_info;
6489
  lpt->alter_info= alter_info;
6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502
  lpt->create_info= create_info;
  lpt->db_options= create_info->table_options;
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    lpt->db_options|= HA_OPTION_PACK_RECORD;
  lpt->table= table;
  lpt->key_info_buffer= 0;
  lpt->key_count= 0;
  lpt->db= db;
  lpt->table_name= table_name;
  lpt->copied= 0;
  lpt->deleted= 0;
  lpt->pack_frm_data= NULL;
  lpt->pack_frm_len= 0;
6503
  thd->work_part_info= part_info;
6504

6505 6506 6507
  /* Never update timestamp columns when alter */
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;

6508
  if (fast_alter_partition & HA_PARTITION_ONE_PHASE)
6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541
  {
    /*
      In the case where the engine supports one phase online partition
      changes it is not necessary to have any exclusive locks. The
      correctness is upheld instead by transactions being aborted if they
      access the table after its partition definition has changed (if they
      are still using the old partition definition).

      The handler is in this case responsible to ensure that all users
      start using the new frm file after it has changed. To implement
      one phase it is necessary for the handler to have the master copy
      of the frm file and use discovery mechanisms to renew it. Thus
      write frm will write the frm, pack the new frm and finally
      the frm is deleted and the discovery mechanisms will either restore
      back to the old or installing the new after the change is activated.

      Thus all open tables will be discovered that they are old, if not
      earlier as soon as they try an operation using the old table. One
      should ensure that this is checked already when opening a table,
      even if it is found in the cache of open tables.

      change_partitions will perform all operations and it is the duty of
      the handler to ensure that the frm files in the system gets updated
      in synch with the changes made and if an error occurs that a proper
      error handling is done.

      If the MySQL Server crashes at this moment but the handler succeeds
      in performing the change then the binlog is not written for the
      change. There is no way to solve this as long as the binlog is not
      transactional and even then it is hard to solve it completely.
 
      The first approach here was to downgrade locks. Now a different approach
      is decided upon. The idea is that the handler will have access to the
6542
      Alter_info when store_lock arrives with TL_WRITE_ALLOW_READ. So if the
6543 6544 6545 6546 6547 6548
      handler knows that this functionality can be handled with a lower lock
      level it will set the lock level to TL_WRITE_ALLOW_WRITE immediately.
      Thus the need to downgrade the lock disappears.
      1) Write the new frm, pack it and then delete it
      2) Perform the change within the handler
    */
6549 6550
    if (mysql_write_frm(lpt, WFRM_WRITE_SHADOW | WFRM_PACK_FRM) ||
        mysql_change_partitions(lpt))
6551
    {
6552
      goto err;
6553 6554
    }
  }
6555
  else if (alter_info->flags & ALTER_DROP_PARTITION)
6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578
  {
    /*
      Now after all checks and setting state on dropped partitions we can
      start the actual dropping of the partitions.

      Drop partition is actually two things happening. The first is that
      a lot of records are deleted. The second is that the behaviour of
      subsequent updates and writes and deletes will change. The delete
      part can be handled without any particular high lock level by
      transactional engines whereas non-transactional engines need to
      ensure that this change is done with an exclusive lock on the table.
      The second part, the change of partitioning does however require
      an exclusive lock to install the new partitioning as one atomic
      operation. If this is not the case, it is possible for two
      transactions to see the change in a different order than their
      serialisation order. Thus we need an exclusive lock for both
      transactional and non-transactional engines.

      For LIST partitions it could be possible to avoid the exclusive lock
      (and for RANGE partitions if they didn't rearrange range definitions
      after a DROP PARTITION) if one ensured that failed accesses to the
      dropped partitions was aborted for sure (thus only possible for
      transactional engines).
6579 6580 6581

      0) Write an entry that removes the shadow frm file if crash occurs 
      1) Write the new frm file as a shadow frm
6582
      2) Write the ddl log to ensure that the operation is completed
6583 6584
         even in the presence of a MySQL Server crash
      3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to
6585 6586 6587 6588 6589 6590 6591
         the table have completed. This ensures that other threads can not
         execute on the table in parallel.
      4) Get a name lock on the table. This ensures that we can release all
         locks on the table and since no one can open the table, there can
         be no new threads accessing the table. They will be hanging on the
         name lock.
      5) Close all tables that have already been opened but didn't stumble on
6592
         the abort locked previously. This is done as part of the
6593
         close_data_files_and_morph_locks call.
6594 6595
      6) We are now ready to release all locks we got in this thread.
      7) Write the bin log
6596 6597 6598 6599 6600 6601
         Unfortunately the writing of the binlog is not synchronised with
         other logging activities. So no matter in which order the binlog
         is written compared to other activities there will always be cases
         where crashes make strange things occur. In this placement it can
         happen that the ALTER TABLE DROP PARTITION gets performed in the
         master but not in the slaves if we have a crash, after writing the
6602 6603
         ddl log but before writing the binlog. A solution to this would
         require writing the statement first in the ddl log and then
6604 6605
         when recovering from the crash read the binlog and insert it into
         the binlog if not written already.
6606 6607 6608 6609
      8) Install the previously written shadow frm file
      9) Prepare handlers for drop of partitions
      10) Drop the partitions
      11) Remove entries from ddl log
6610
      12) Reopen table if under lock tables
6611
      13) Complete query
6612 6613 6614

      We insert Error injections at all places where it could be interesting
      to test if recovery is properly done.
6615
    */
6616
    if (write_log_drop_shadow_frm(lpt) ||
6617
        ERROR_INJECT_CRASH("crash_drop_partition_1") ||
6618
        mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
6619
        ERROR_INJECT_CRASH("crash_drop_partition_2") ||
6620
        write_log_drop_partition(lpt) ||
6621
        ERROR_INJECT_CRASH("crash_drop_partition_3") ||
6622 6623
        (not_completed= FALSE) ||
        abort_and_upgrade_lock(lpt) || /* Always returns 0 */
6624
        ERROR_INJECT_CRASH("crash_drop_partition_4") ||
6625
        alter_close_tables(lpt) ||
6626
        ERROR_INJECT_CRASH("crash_drop_partition_5") ||
6627 6628
        ((!thd->lex->no_write_to_binlog) &&
         (write_bin_log(thd, FALSE,
6629
                        thd->query(), thd->query_length()), FALSE)) ||
6630
        ERROR_INJECT_CRASH("crash_drop_partition_6") ||
6631
        ((frm_install= TRUE), FALSE) ||
6632
        mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
6633
        ((frm_install= FALSE), FALSE) ||
6634
        ERROR_INJECT_CRASH("crash_drop_partition_7") ||
6635
        mysql_drop_partitions(lpt) ||
6636
        ERROR_INJECT_CRASH("crash_drop_partition_8") ||
6637
        (write_log_completed(lpt, FALSE), FALSE) ||
6638 6639
        ERROR_INJECT_CRASH("crash_drop_partition_9") ||
        (alter_partition_lock_handling(lpt), FALSE)) 
6640
    {
6641
      handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
6642
      goto err;
6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657
    }
  }
  else if ((alter_info->flags & ALTER_ADD_PARTITION) &&
           (part_info->part_type == RANGE_PARTITION ||
            part_info->part_type == LIST_PARTITION))
  {
    /*
      ADD RANGE/LIST PARTITIONS
      In this case there are no tuples removed and no tuples are added.
      Thus the operation is merely adding a new partition. Thus it is
      necessary to perform the change as an atomic operation. Otherwise
      someone reading without seeing the new partition could potentially
      miss updates made by a transaction serialised before it that are
      inserted into the new partition.

6658 6659
      0) Write an entry that removes the shadow frm file if crash occurs 
      1) Write the new frm file as a shadow frm file
6660
      2) Log the changes to happen in ddl log
6661 6662 6663 6664
      2) Add the new partitions
      3) Lock all partitions in TL_WRITE_ONLY to ensure that no users
         are still using the old partitioning scheme. Wait until all
         ongoing users have completed before progressing.
6665 6666 6667 6668 6669 6670
      4) Get a name lock on the table. This ensures that we can release all
         locks on the table and since no one can open the table, there can
         be no new threads accessing the table. They will be hanging on the
         name lock.
      5) Close all tables that have already been opened but didn't stumble on
         the abort locked previously. This is done as part of the
6671
         close_data_files_and_morph_locks call.
6672 6673 6674
      6) Close all table handlers and unlock all handlers but retain name lock
      7) Write binlog
      8) Now the change is completed except for the installation of the
6675 6676
         new frm file. We thus write an action in the log to change to
         the shadow frm file
6677
      9) Install the new frm file of the table where the partitions are
6678
         added to the table.
6679 6680
      10)Wait until all accesses using the old frm file has completed
      11)Remove entries from ddl log
6681
      12)Reopen tables if under lock tables
6682
      13)Complete query
6683
    */
6684
    if (write_log_add_change_partition(lpt) ||
6685
        ERROR_INJECT_CRASH("crash_add_partition_1") ||
6686
        mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
6687
        ERROR_INJECT_CRASH("crash_add_partition_2") ||
6688
        mysql_change_partitions(lpt) ||
6689
        ERROR_INJECT_CRASH("crash_add_partition_3") ||
6690
        abort_and_upgrade_lock(lpt) || /* Always returns 0 */
6691 6692 6693
        ERROR_INJECT_CRASH("crash_add_partition_4") ||
        alter_close_tables(lpt) ||
        ERROR_INJECT_CRASH("crash_add_partition_5") ||
6694 6695
        ((!thd->lex->no_write_to_binlog) &&
         (write_bin_log(thd, FALSE,
6696
                        thd->query(), thd->query_length()), FALSE)) ||
6697
        ERROR_INJECT_CRASH("crash_add_partition_6") ||
6698
        write_log_rename_frm(lpt) ||
6699
        (not_completed= FALSE) ||
6700
        ERROR_INJECT_CRASH("crash_add_partition_7") ||
6701
        ((frm_install= TRUE), FALSE) ||
6702
        mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
6703
        ERROR_INJECT_CRASH("crash_add_partition_8") ||
6704
        (write_log_completed(lpt, FALSE), FALSE) ||
6705
        ERROR_INJECT_CRASH("crash_add_partition_9") ||
6706
        (alter_partition_lock_handling(lpt), FALSE)) 
6707
    {
6708
      handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
6709
      goto err;
6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744
    }
  }
  else
  {
    /*
      ADD HASH PARTITION/
      COALESCE PARTITION/
      REBUILD PARTITION/
      REORGANIZE PARTITION
 
      In this case all records are still around after the change although
      possibly organised into new partitions, thus by ensuring that all
      updates go to both the old and the new partitioning scheme we can
      actually perform this operation lock-free. The only exception to
      this is when REORGANIZE PARTITION adds/drops ranges. In this case
      there needs to be an exclusive lock during the time when the range
      changes occur.
      This is only possible if the handler can ensure double-write for a
      period. The double write will ensure that it doesn't matter where the
      data is read from since both places are updated for writes. If such
      double writing is not performed then it is necessary to perform the
      change with the usual exclusive lock. With double writes it is even
      possible to perform writes in parallel with the reorganisation of
      partitions.

      Without double write procedure we get the following procedure.
      The only difference with using double write is that we can downgrade
      the lock to TL_WRITE_ALLOW_WRITE. Double write in this case only
      double writes from old to new. If we had double writing in both
      directions we could perform the change completely without exclusive
      lock for HASH partitions.
      Handlers that perform double writing during the copy phase can actually
      use a lower lock level. This can be handled inside store_lock in the
      respective handler.

6745 6746 6747 6748 6749
      0) Write an entry that removes the shadow frm file if crash occurs 
      1) Write the shadow frm file of new partitioning
      2) Log such that temporary partitions added in change phase are
         removed in a crash situation
      3) Add the new partitions
6750
         Copy from the reorganised partitions to the new partitions
6751 6752 6753
      4) Log that operation is completed and log all complete actions
         needed to complete operation from here
      5) Lock all partitions in TL_WRITE_ONLY to ensure that no users
6754 6755
         are still using the old partitioning scheme. Wait until all
         ongoing users have completed before progressing.
6756 6757 6758 6759
      6) Get a name lock of the table
      7) Close all tables opened but not yet locked, after this call we are
         certain that no other thread is in the lock wait queue or has
         opened the table. The name lock will ensure that they are blocked
6760 6761
         on the open call.
         This is achieved also by close_data_files_and_morph_locks call.
6762 6763 6764 6765 6766 6767
      8) Close all partitions opened by this thread, but retain name lock.
      9) Write bin log
      10) Prepare handlers for rename and delete of partitions
      11) Rename and drop the reorged partitions such that they are no
          longer used and rename those added to their real new names.
      12) Install the shadow frm file
6768
      13) Reopen the table if under lock tables
6769
      14) Complete query
6770
    */
6771
    if (write_log_add_change_partition(lpt) ||
6772
        ERROR_INJECT_CRASH("crash_change_partition_1") ||
6773
        mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
6774
        ERROR_INJECT_CRASH("crash_change_partition_2") ||
6775
        mysql_change_partitions(lpt) ||
6776 6777
        ERROR_INJECT_CRASH("crash_change_partition_3") ||
        write_log_final_change_partition(lpt) ||
6778
        ERROR_INJECT_CRASH("crash_change_partition_4") ||
6779 6780
        (not_completed= FALSE) ||
        abort_and_upgrade_lock(lpt) || /* Always returns 0 */
6781
        ERROR_INJECT_CRASH("crash_change_partition_5") ||
6782
        alter_close_tables(lpt) ||
6783
        ERROR_INJECT_CRASH("crash_change_partition_6") ||
6784 6785
        ((!thd->lex->no_write_to_binlog) &&
         (write_bin_log(thd, FALSE,
6786
                        thd->query(), thd->query_length()), FALSE)) ||
6787
        ERROR_INJECT_CRASH("crash_change_partition_7") ||
6788
        mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
6789
        ERROR_INJECT_CRASH("crash_change_partition_8") ||
6790
        mysql_drop_partitions(lpt) ||
6791
        ERROR_INJECT_CRASH("crash_change_partition_9") ||
6792
        mysql_rename_partitions(lpt) ||
6793
        ((frm_install= TRUE), FALSE) ||
6794
        ERROR_INJECT_CRASH("crash_change_partition_10") ||
6795
        (write_log_completed(lpt, FALSE), FALSE) ||
6796 6797
        ERROR_INJECT_CRASH("crash_change_partition_11") ||
        (alter_partition_lock_handling(lpt), FALSE))
6798
    {
6799
      handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
6800
      goto err;
6801 6802 6803 6804 6805 6806 6807
    }
  }
  /*
    A final step is to write the query to the binlog and send ok to the
    user
  */
  DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted,
6808
                                 table, table_list, FALSE, NULL,
6809
                                 written_bin_log));
6810 6811 6812
err:
  close_thread_tables(thd);
  DBUG_RETURN(TRUE);
6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834
}
#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.
*/

6835 6836
void set_field_ptr(Field **ptr, const uchar *new_buf,
                   const uchar *old_buf)
6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868
{
  my_ptrdiff_t diff= (new_buf - old_buf);
  DBUG_ENTER("set_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_info            key info 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.
*/

6869 6870
void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
                       const uchar *old_buf)
6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904
{
  KEY_PART_INFO *key_part= key_info->key_part;
  uint key_parts= key_info->key_parts;
  uint 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;
}


/*
  SYNOPSIS
    mem_alloc_error()
    size                Size of memory attempted to allocate
    None

  RETURN VALUES
    None

  DESCRIPTION
    A routine to use for all the many places in the code where memory
    allocation error can happen, a tremendous amount of them, needs
    simple routine that signals this error.
*/

void mem_alloc_error(size_t size)
{
  my_error(ER_OUTOFMEMORY, MYF(0), size);
6905
}
6906

6907
#ifdef WITH_PARTITION_STORAGE_ENGINE
6908
/*
6909 6910
  Return comma-separated list of used partitions in the provided given string

6911 6912 6913 6914
  SYNOPSIS
    make_used_partitions_str()
      part_info  IN  Partitioning info
      parts_str  OUT The string to fill
6915 6916 6917 6918 6919 6920 6921

  DESCRIPTION
    Generate a list of used partitions (from bits in part_info->used_partitions
    bitmap), asd store it into the provided String object.
    
  NOTE
    The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN).
6922 6923 6924 6925 6926 6927 6928 6929 6930
*/

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);
  
6931
  if (part_info->is_sub_partitioned())
6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969
  {
    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++;
    }
  }
}
6970
#endif
6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992

/****************************************************************************
 * Partition interval analysis support
 ***************************************************************************/

/*
  Setup partition_info::* members related to partitioning range analysis

  SYNOPSIS
    set_up_partition_func_pointers()
      part_info  Partitioning info structure

  DESCRIPTION
    Assuming that passed partition_info structure already has correct values
    for members that specify [sub]partitioning type, table fields, and
    functions, set up partition_info::* members that are related to
    Partitioning Interval Analysis (see get_partitions_in_range_iter for its
    definition)

  IMPLEMENTATION
    There are two available interval analyzer functions:
    (1) get_part_iter_for_interval_via_mapping 
6993 6994
    (2) get_part_iter_for_interval_cols_via_map 
    (3) get_part_iter_for_interval_via_walking
6995 6996 6997 6998

    They both have limited applicability:
    (1) is applicable for "PARTITION BY <RANGE|LIST>(func(t.field))", where
    func is a monotonic function.
6999

7000
    (2) is applicable for "PARTITION BY <RANGE|LIST> COLUMNS (field_list)
7001 7002

    (3) is applicable for 
7003 7004
      "[SUB]PARTITION BY <any-partitioning-type>(any_func(t.integer_field))"
      
7005
    If both (1) and (3) are applicable, (1) is preferred over (3).
7006 7007 7008 7009 7010
    
    This function sets part_info::get_part_iter_for_interval according to
    this criteria, and also sets some auxilary fields that the function
    uses.
*/
7011
#ifdef WITH_PARTITION_STORAGE_ENGINE
7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024
static void set_up_range_analysis_info(partition_info *part_info)
{
  /* Set the catch-all default */
  part_info->get_part_iter_for_interval= NULL;
  part_info->get_subpart_iter_for_interval= NULL;

  /* 
    Check if get_part_iter_for_interval_via_mapping() can be used for 
    partitioning
  */
  switch (part_info->part_type) {
  case RANGE_PARTITION:
  case LIST_PARTITION:
7025 7026 7027 7028 7029 7030 7031 7032 7033 7034
    if (!part_info->column_list)
    {
      if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC)
      {
        part_info->get_part_iter_for_interval=
          get_part_iter_for_interval_via_mapping;
        goto setup_subparts;
      }
    }
    else
7035 7036
    {
      part_info->get_part_iter_for_interval=
7037
        get_part_iter_for_interval_cols_via_map;
7038 7039 7040 7041 7042 7043 7044
      goto setup_subparts;
    }
  default:
    ;
  }
   
  /*
7045
    Check if get_part_iter_for_interval_via_walking() can be used for
7046 7047
    partitioning
  */
7048
  if (part_info->num_part_fields == 1)
7049 7050 7051 7052 7053
  {
    Field *field= part_info->part_field_array[0];
    switch (field->type()) {
    case MYSQL_TYPE_TINY:
    case MYSQL_TYPE_SHORT:
7054
    case MYSQL_TYPE_INT24:
7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066
    case MYSQL_TYPE_LONG:
    case MYSQL_TYPE_LONGLONG:
      part_info->get_part_iter_for_interval=
        get_part_iter_for_interval_via_walking;
      break;
    default:
      ;
    }
  }

setup_subparts:
  /*
7067
    Check if get_part_iter_for_interval_via_walking() can be used for
7068 7069
    subpartitioning
  */
7070
  if (part_info->num_subpart_fields == 1)
7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087
  {
    Field *field= part_info->subpart_field_array[0];
    switch (field->type()) {
    case MYSQL_TYPE_TINY:
    case MYSQL_TYPE_SHORT:
    case MYSQL_TYPE_LONG:
    case MYSQL_TYPE_LONGLONG:
      part_info->get_subpart_iter_for_interval=
        get_part_iter_for_interval_via_walking;
      break;
    default:
      ;
    }
  }
}


Mikael Ronstrom's avatar
Mikael Ronstrom committed
7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103
/*
  This function takes a memory of packed fields in opt-range format
  and stores it in record format. To avoid having to worry about how
  the length of fields are calculated in opt-range format we send
  an array of lengths used for each field in store_length_array.

  SYNOPSIS
  store_tuple_to_record()
  pfield                         Field array
  store_length_array             Array of field lengths
  value                          Memory where fields are stored
  value_end                      End of memory

  RETURN VALUE
  nparts                         Number of fields assigned
*/
7104 7105 7106 7107 7108
uint32 store_tuple_to_record(Field **pfield,
                             uint32 *store_length_array,
                             uchar *value,
                             uchar *value_end)
{
Mikael Ronstrom's avatar
Mikael Ronstrom committed
7109
  /* This function is inspired by store_key_image_rec. */
7110 7111 7112 7113 7114 7115 7116 7117 7118
  uint32 nparts= 0;
  uchar *loc_value;
  while (value < value_end)
  {
    loc_value= value;
    if ((*pfield)->real_maybe_null())
    {
      if (*loc_value)
        (*pfield)->set_null();
7119 7120
      else
        (*pfield)->set_notnull();
7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135
      loc_value++;
    }
    uint len= (*pfield)->pack_length();
    (*pfield)->set_key_image(loc_value, len);
    value+= *store_length_array;
    store_length_array++;
    nparts++;
    pfield++;
  }
  return nparts;
}

/*
  RANGE(columns) partitioning: compare value bound and probe tuple.

7136
  The value bound always is a full tuple (but may include the MAXVALUE
Mikael Ronstrom's avatar
Mikael Ronstrom committed
7137
  special value).
7138 7139 7140

  The probe tuple may be a prefix of partitioning tuple. The tail_is_min
  parameter specifies whether the suffix components should be assumed to
7141
  hold MAXVALUE
7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192
*/

static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec)
{
  partition_info *part_info= val->part_info;
  Field **field= part_info->part_field_array;
  Field **fields_end= field + nvals_in_rec;
  int res;

  for (; field != fields_end; field++, val++)
  {
    if (val->max_value)
      return -1;
    if ((*field)->is_null())
    {
      if (val->null_value)
        continue;
      return -1;
    }
    if (val->null_value)
      return +1;
    res= (*field)->cmp((const uchar*)val->column_value);
    if (res)
      return res;
  }
  return 0;
}


static int cmp_rec_and_tuple_prune(part_column_list_val *val,
                                   uint32 n_vals_in_rec,
                                   bool tail_is_min)
{
  int cmp;
  Field **field;
  partition_info *part_info;
  if ((cmp= cmp_rec_and_tuple(val, n_vals_in_rec)))
    return cmp;
  part_info= val->part_info;
  field= part_info->part_field_array + n_vals_in_rec;
  for (; *field; field++, val++)
  {
    if (tail_is_min)
      return -1;
    if (!tail_is_min && !val->max_value)
      return +1;
  }
  return 0;
}


7193 7194 7195
typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint,
                                    bool include_endpoint);

7196 7197
typedef uint32 (*get_col_endpoint_func)(partition_info*, bool left_endpoint,
                                        bool include_endpoint,
7198
                                        uint32 num_parts);
7199

7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215
/*
  Partitioning Interval Analysis: Initialize the iterator for "mapping" case

  SYNOPSIS
    get_part_iter_for_interval_via_mapping()
      part_info   Partition info
      is_subpart  TRUE  - act for subpartitioning
                  FALSE - act for partitioning
      min_value   minimum field value, in opt_range key format.
      max_value   minimum field value, in opt_range key format.
      flags       Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
                  NO_MAX_RANGE.
      part_iter   Iterator structure to be initialized

  DESCRIPTION
    Initialize partition set iterator to walk over the interval in
7216 7217
    ordered-array-of-partitions (for RANGE partitioning) or 
    ordered-array-of-list-constants (for LIST partitioning) space.
7218 7219

  IMPLEMENTATION
7220
    This function is used when partitioning is done by
7221 7222 7223 7224 7225 7226 7227 7228
    <RANGE|LIST>(ascending_func(t.field)), and we can map an interval in
    t.field space into a sub-array of partition_info::range_int_array or
    partition_info::list_array (see get_partition_id_range_for_endpoint,
    get_list_array_idx_for_endpoint for details).
    
    The function performs this interval mapping, and sets the iterator to
    traverse the sub-array and return appropriate partitions.
    
7229
  RETURN
7230 7231 7232 7233 7234
    0 - No matching partitions (iterator not initialized)
    1 - Ok, iterator intialized for traversal of matching partitions.
   -1 - All partitions would match (iterator not initialized)
*/

7235 7236 7237 7238 7239
uint32 get_partition_id_cols_range_for_endpoint(partition_info *part_info,
                                                bool left_endpoint,
                                                bool include_endpoint,
                                                uint32 nparts)
{
7240
  uint max_partition= part_info->num_parts - 1;
7241 7242
  uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
  part_column_list_val *range_col_array= part_info->range_col_array;
7243
  uint num_columns= part_info->part_field_list.elements;
7244 7245 7246 7247 7248 7249 7250
  bool tailf= !(left_endpoint ^ include_endpoint);
  DBUG_ENTER("get_partition_id_cols_range_for_endpoint");

  /* Get the partitioning function value for the endpoint */
  while (max_part_id > min_part_id)
  {
    loc_part_id= (max_part_id + min_part_id + 1) >> 1;
7251
    if (cmp_rec_and_tuple_prune(range_col_array + loc_part_id*num_columns,
7252 7253 7254 7255 7256 7257 7258
                                nparts, tailf) >= 0)
      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 && 
7259
      cmp_rec_and_tuple_prune(range_col_array + (loc_part_id+1)*num_columns,
7260 7261 7262 7263 7264 7265 7266
                              nparts, tailf) >= 0
      )
  {
     loc_part_id++;
  }
  if (left_endpoint)
  {
7267
    if (cmp_rec_and_tuple_prune(range_col_array + loc_part_id*num_columns,
7268 7269 7270 7271 7272 7273 7274 7275
                                nparts, tailf) >= 0)
      loc_part_id++;
  }
  else 
  {
    if (loc_part_id < max_partition)
    {
      int res= cmp_rec_and_tuple_prune(range_col_array +
7276
                                       loc_part_id * num_columns,
7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305
                                       nparts, tailf);
      if (!res)
        loc_part_id += test(include_endpoint);
      else if (res > 0)
        loc_part_id++;
    }
    loc_part_id++;
  }
  DBUG_RETURN(loc_part_id);
}


int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
                                            bool is_subpart,
                                            uint32 *store_length_array,
                                            uchar *min_value, uchar *max_value,
                                            uint min_len, uint max_len, 
                                            uint flags,
                                            PARTITION_ITERATOR *part_iter)
{
  uint32 nparts;
  get_col_endpoint_func  get_col_endpoint;
  DBUG_ENTER("get_part_iter_for_interval_cols_via_map");

  if (part_info->part_type == RANGE_PARTITION)
  {
    get_col_endpoint= get_partition_id_cols_range_for_endpoint;
    part_iter->get_next= get_next_partition_id_range;
  }
7306
  else if (part_info->part_type == LIST_PARTITION)
7307 7308 7309 7310
  {
    get_col_endpoint= get_partition_id_cols_list_for_endpoint;
    part_iter->get_next= get_next_partition_id_list;
    part_iter->part_info= part_info;
7311
    DBUG_ASSERT(part_info->num_list_values);
7312
  }
7313 7314 7315
  else
    assert(0);

7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329
  if (flags & NO_MIN_RANGE)
    part_iter->part_nums.start= part_iter->part_nums.cur= 0;
  else
  {
    // Copy from min_value to record
    nparts= store_tuple_to_record(part_info->part_field_array,
                                  store_length_array,
                                  min_value,
                                  min_value + min_len);
    part_iter->part_nums.start= part_iter->part_nums.cur=
      get_col_endpoint(part_info, TRUE, !(flags & NEAR_MIN),
                       nparts);
  }
  if (flags & NO_MAX_RANGE)
7330 7331 7332 7333 7334 7335 7336 7337 7338
  {
    if (part_info->part_type == RANGE_PARTITION)
      part_iter->part_nums.end= part_info->num_parts;
    else /* LIST_PARTITION */
    {
      DBUG_ASSERT(part_info->part_type == LIST_PARTITION);
      part_iter->part_nums.end= part_info->num_list_values;
    }
  }
7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355
  else
  {
    // Copy from max_value to record
    nparts= store_tuple_to_record(part_info->part_field_array,
                                  store_length_array,
                                  max_value,
                                  max_value + max_len);
    part_iter->part_nums.end= get_col_endpoint(part_info, FALSE,
                                               !(flags & NEAR_MAX),
                                               nparts);
  }
  if (part_iter->part_nums.start == part_iter->part_nums.end)
    DBUG_RETURN(0);
  DBUG_RETURN(1);
}


7356 7357
int get_part_iter_for_interval_via_mapping(partition_info *part_info,
                                           bool is_subpart,
7358
                                           uint32 *store_length_array, /* ignored */
7359
                                           uchar *min_value, uchar *max_value,
7360
                                           uint min_len, uint max_len, /* ignored */
7361 7362 7363 7364 7365 7366
                                           uint flags,
                                           PARTITION_ITERATOR *part_iter)
{
  Field *field= part_info->part_field_array[0];
  uint32             max_endpoint_val;
  get_endpoint_func  get_endpoint;
7367
  bool               can_match_multiple_values;  /* is not '=' */
7368
  uint field_len= field->pack_length_in_rec();
7369 7370 7371 7372 7373
  DBUG_ENTER("get_part_iter_for_interval_via_mapping");
  DBUG_ASSERT(!is_subpart);
  (void) store_length_array;
  (void)min_len;
  (void)max_len;
7374
  part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
7375 7376 7377

  if (part_info->part_type == RANGE_PARTITION)
  {
7378
    if (part_info->part_charset_field_array)
7379 7380 7381
      get_endpoint=        get_partition_id_range_for_endpoint_charset;
    else
      get_endpoint=        get_partition_id_range_for_endpoint;
7382
    max_endpoint_val=    part_info->num_parts;
7383 7384 7385 7386
    part_iter->get_next= get_next_partition_id_range;
  }
  else if (part_info->part_type == LIST_PARTITION)
  {
7387

7388
    if (part_info->part_charset_field_array)
7389 7390 7391
      get_endpoint=        get_list_array_idx_for_endpoint_charset;
    else
      get_endpoint=        get_list_array_idx_for_endpoint;
7392
    max_endpoint_val=    part_info->num_list_values;
7393 7394
    part_iter->get_next= get_next_partition_id_list;
    part_iter->part_info= part_info;
7395 7396 7397 7398 7399 7400 7401 7402 7403 7404
    if (max_endpoint_val == 0)
    {
      /*
        We handle this special case without optimisations since it is
        of little practical value but causes a great number of complex
        checks later in the code.
      */
      part_iter->part_nums.start= part_iter->part_nums.end= 0;
      part_iter->part_nums.cur= 0;
      part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
7405
      DBUG_RETURN(-1);
7406
    }
7407 7408
  }
  else
7409
    assert(0);
7410
  
7411 7412 7413 7414 7415
  can_match_multiple_values= (flags || !min_value || !max_value ||
                              memcmp(min_value, max_value, field_len));
  if (can_match_multiple_values &&
      (part_info->part_type == RANGE_PARTITION ||
       part_info->has_null_value))
7416
  {
7417
    /* Range scan on RANGE or LIST partitioned table */
7418 7419 7420 7421 7422 7423 7424 7425 7426
    enum_monotonicity_info monotonic;
    monotonic= part_info->part_expr->get_monotonicity_info();
    if (monotonic == MONOTONIC_INCREASING_NOT_NULL ||
        monotonic == MONOTONIC_STRICT_INCREASING_NOT_NULL)
    {
      /* col is NOT NULL, but F(col) can return NULL, add NULL partition */
      part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
    }
  }
7427

7428 7429 7430 7431 7432 7433
  /* 
    Find minimum: Do special handling if the interval has left bound in form
     " NULL <= X ":
  */
  if (field->real_maybe_null() && part_info->has_null_value && 
      !(flags & (NO_MIN_RANGE | NEAR_MIN)) && *min_value)
7434
  {
7435 7436
    part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
    part_iter->part_nums.start= part_iter->part_nums.cur= 0;
7437
    if (!(flags & NO_MAX_RANGE) && *max_value)
7438
    {
7439 7440
      /* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
      part_iter->part_nums.end= 0;
7441
      DBUG_RETURN(1);
7442 7443
    }
  }
7444 7445
  else
  {
7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456
    if (flags & NO_MIN_RANGE)
      part_iter->part_nums.start= part_iter->part_nums.cur= 0;
    else
    {
      /*
        Store the interval edge in the record buffer, and call the
        function that maps the edge in table-field space to an edge
        in ordered-set-of-partitions (for RANGE partitioning) or 
        index-in-ordered-array-of-list-constants (for LIST) space.
      */
      store_key_image_to_rec(field, min_value, field_len);
7457
      bool include_endp= !test(flags & NEAR_MIN);
7458
      part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
7459 7460 7461 7462 7463 7464
      if (!can_match_multiple_values && part_info->part_expr->null_value)
      {
        /* col = x and F(x) = NULL -> only search NULL partition */
        part_iter->part_nums.cur= part_iter->part_nums.start= 0;
        part_iter->part_nums.end= 0;
        part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
7465
        DBUG_RETURN(1);
7466
      }
7467 7468
      part_iter->part_nums.cur= part_iter->part_nums.start;
      if (part_iter->part_nums.start == max_endpoint_val)
7469
        DBUG_RETURN(0); /* No partitions */
7470
    }
7471 7472 7473 7474
  }

  /* Find maximum, do the same as above but for right interval bound */
  if (flags & NO_MAX_RANGE)
7475
    part_iter->part_nums.end= max_endpoint_val;
7476 7477 7478
  else
  {
    store_key_image_to_rec(field, max_value, field_len);
7479
    bool include_endp= !test(flags & NEAR_MAX);
7480
    part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp);
7481
    if (part_iter->part_nums.start >= part_iter->part_nums.end &&
7482
        !part_iter->ret_null_part)
7483
      DBUG_RETURN(0); /* No partitions */
7484
  }
7485
  DBUG_RETURN(1); /* Ok, iterator initialized */
7486 7487 7488 7489
}


/* See get_part_iter_for_interval_via_walking for definition of what this is */
7490
#define MAX_RANGE_TO_WALK 32
7491 7492 7493


/*
7494
  Partitioning Interval Analysis: Initialize iterator to walk field interval
7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509

  SYNOPSIS
    get_part_iter_for_interval_via_walking()
      part_info   Partition info
      is_subpart  TRUE  - act for subpartitioning
                  FALSE - act for partitioning
      min_value   minimum field value, in opt_range key format.
      max_value   minimum field value, in opt_range key format.
      flags       Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
                  NO_MAX_RANGE.
      part_iter   Iterator structure to be initialized

  DESCRIPTION
    Initialize partition set iterator to walk over interval in integer field
    space. That is, for "const1 <=? t.field <=? const2" interval, initialize 
7510 7511
    the iterator to return a set of [sub]partitions obtained with the
    following procedure:
7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524
      get partition id for t.field = const1,   return it
      get partition id for t.field = const1+1, return it
       ...                 t.field = const1+2, ...
       ...                           ...       ...
       ...                 t.field = const2    ...

  IMPLEMENTATION
    See get_partitions_in_range_iter for general description of interval
    analysis. We support walking over the following intervals: 
      "t.field IS NULL" 
      "c1 <=? t.field <=? c2", where c1 and c2 are finite. 
    Intervals with +inf/-inf, and [NULL, c1] interval can be processed but
    that is more tricky and I don't have time to do it right now.
7525

7526 7527 7528 7529 7530 7531 7532
  RETURN
    0 - No matching partitions, iterator not initialized
    1 - Some partitions would match, iterator intialized for traversing them
   -1 - All partitions would match, iterator not initialized
*/

int get_part_iter_for_interval_via_walking(partition_info *part_info,
7533 7534 7535 7536 7537 7538
                                      bool is_subpart,
                                      uint32 *store_length_array, /* ignored */
                                      uchar *min_value, uchar *max_value,
                                      uint min_len, uint max_len, /* ignored */
                                      uint flags,
                                      PARTITION_ITERATOR *part_iter)
7539 7540 7541 7542
{
  Field *field;
  uint total_parts;
  partition_iter_func get_next_func;
7543 7544 7545 7546 7547
  DBUG_ENTER("get_part_iter_for_interval_via_walking");
  (void)store_length_array;
  (void)min_len;
  (void)max_len;

7548
  part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
7549 7550 7551
  if (is_subpart)
  {
    field= part_info->subpart_field_array[0];
7552
    total_parts= part_info->num_subparts;
7553 7554 7555 7556 7557
    get_next_func=  get_next_subpartition_via_walking;
  }
  else
  {
    field= part_info->part_field_array[0];
7558
    total_parts= part_info->num_parts;
7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574
    get_next_func=  get_next_partition_via_walking;
  }

  /* Handle the "t.field IS NULL" interval, it is a special case */
  if (field->real_maybe_null() && !(flags & (NO_MIN_RANGE | NO_MAX_RANGE)) &&
      *min_value && *max_value)
  {
    /* 
      We don't have a part_iter->get_next() function that would find which
      partition "t.field IS NULL" belongs to, so find partition that contains 
      NULL right here, and return an iterator over singleton set.
    */
    uint32 part_id;
    field->set_null();
    if (is_subpart)
    {
7575 7576 7577
      if (!part_info->get_subpartition_id(part_info, &part_id))
      {
        init_single_partition_iterator(part_id, part_iter);
7578
        DBUG_RETURN(1); /* Ok, iterator initialized */
7579
      }
7580 7581 7582
    }
    else
    {
sergefp@mysql.com's avatar
sergefp@mysql.com committed
7583
      longlong dummy;
7584 7585 7586 7587 7588
      int res= part_info->is_sub_partitioned() ?
                  part_info->get_part_partition_id(part_info, &part_id,
                                                   &dummy):
                  part_info->get_partition_id(part_info, &part_id, &dummy);
      if (!res)
7589 7590
      {
        init_single_partition_iterator(part_id, part_iter);
7591
        DBUG_RETURN(1); /* Ok, iterator initialized */
7592 7593
      }
    }
7594
    DBUG_RETURN(0); /* No partitions match */
7595 7596
  }

7597 7598 7599 7600 7601
  if ((field->real_maybe_null() && 
       ((!(flags & NO_MIN_RANGE) && *min_value) ||  // NULL <? X
        (!(flags & NO_MAX_RANGE) && *max_value))) ||  // X <? NULL
      (flags & (NO_MIN_RANGE | NO_MAX_RANGE)))    // -inf at any bound
  {
7602
    DBUG_RETURN(-1); /* Can't handle this interval, have to use all partitions */
7603
  }
7604 7605 7606 7607 7608 7609 7610 7611 7612
  
  /* Get integers for left and right interval bound */
  longlong a, b;
  uint len= field->pack_length_in_rec();
  store_key_image_to_rec(field, min_value, len);
  a= field->val_int();
  
  store_key_image_to_rec(field, max_value, len);
  b= field->val_int();
7613 7614 7615 7616 7617 7618 7619 7620
  
  /* 
    Handle a special case where the distance between interval bounds is 
    exactly 4G-1. This interval is too big for range walking, and if it is an
    (x,y]-type interval then the following "b +=..." code will convert it to 
    an empty interval by "wrapping around" a + 4G-1 + 1 = a. 
  */
  if ((ulonglong)b - (ulonglong)a == ~0ULL)
7621
    DBUG_RETURN(-1);
7622 7623 7624

  a += test(flags & NEAR_MIN);
  b += test(!(flags & NEAR_MAX));
7625
  ulonglong n_values= b - a;
7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643

  /*
    Will it pay off to enumerate all values in the [a..b] range and evaluate
    the partitioning function for every value? It depends on 
     1. whether we'll be able to infer that some partitions are not used 
     2. if time savings from not scanning these partitions will be greater
        than time spent in enumeration.
    We will assume that the cost of accessing one extra partition is greater
    than the cost of evaluating the partitioning function O(#partitions).
    This means we should jump at any chance to eliminate a partition, which
    gives us this logic:

    Do the enumeration if
     - the number of values to enumerate is comparable to the number of
       partitions, or
     - there are not many values to enumerate.
  */
  if ((n_values > 2*total_parts) && n_values > MAX_RANGE_TO_WALK)
7644
    DBUG_RETURN(-1);
7645

7646
  part_iter->field_vals.start= part_iter->field_vals.cur= a;
7647
  part_iter->field_vals.end=   b;
7648 7649
  part_iter->part_info= part_info;
  part_iter->get_next=  get_next_func;
7650
  DBUG_RETURN(1);
7651 7652 7653 7654 7655 7656 7657
}


/*
  PARTITION_ITERATOR::get_next implementation: enumerate partitions in range

  SYNOPSIS
7658
    get_next_partition_id_range()
7659 7660 7661 7662 7663
      part_iter  Partition set iterator structure

  DESCRIPTION
    This is implementation of PARTITION_ITERATOR::get_next() that returns
    [sub]partition ids in [min_partition_id, max_partition_id] range.
7664
    The function conforms to partition_iter_func type.
7665 7666 7667 7668 7669 7670 7671 7672

  RETURN
    partition id
    NOT_A_PARTITION_ID if there are no more partitions
*/

uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
{
7673
  if (part_iter->part_nums.cur >= part_iter->part_nums.end)
7674
  {
7675 7676 7677 7678 7679
    if (part_iter->ret_null_part)
    {
      part_iter->ret_null_part= FALSE;
      return 0;                    /* NULL always in first range partition */
    }
7680
    part_iter->part_nums.cur= part_iter->part_nums.start;
7681
    part_iter->ret_null_part= part_iter->ret_null_part_orig;
7682
    return NOT_A_PARTITION_ID;
7683
  }
7684
  else
7685
    return part_iter->part_nums.cur++;
7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696
}


/*
  PARTITION_ITERATOR::get_next implementation for LIST partitioning

  SYNOPSIS
    get_next_partition_id_list()
      part_iter  Partition set iterator structure

  DESCRIPTION
7697
    This implementation of PARTITION_ITERATOR::get_next() is special for 
7698
    LIST partitioning: it enumerates partition ids in
7699
    part_info->list_array[i] (list_col_array[i] for COLUMNS LIST
7700
    partitioning) where i runs over [min_idx, max_idx] interval.
7701
    The function conforms to partition_iter_func type.
7702 7703 7704 7705 7706 7707 7708 7709

  RETURN 
    partition id
    NOT_A_PARTITION_ID if there are no more partitions
*/

uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
{
7710
  if (part_iter->part_nums.cur >= part_iter->part_nums.end)
7711
  {
7712
    if (part_iter->ret_null_part)
7713
    {
7714
      part_iter->ret_null_part= FALSE;
7715 7716
      return part_iter->part_info->has_null_part_id;
    }
7717 7718
    part_iter->part_nums.cur= part_iter->part_nums.start;
    part_iter->ret_null_part= part_iter->ret_null_part_orig;
7719
    return NOT_A_PARTITION_ID;
7720
  }
7721
  else
7722 7723 7724 7725 7726 7727 7728
  {
    partition_info *part_info= part_iter->part_info;
    uint32 num_part= part_iter->part_nums.cur++;
    return part_info->column_list ?
           part_info->list_col_array[num_part].partition_id :
           part_info->list_array[num_part].partition_id;
  }
7729 7730 7731 7732
}


/*
7733
  PARTITION_ITERATOR::get_next implementation: walk over field-space interval
7734 7735 7736 7737 7738 7739

  SYNOPSIS
    get_next_partition_via_walking()
      part_iter  Partitioning iterator

  DESCRIPTION
7740 7741 7742
    This implementation of PARTITION_ITERATOR::get_next() returns ids of
    partitions that contain records with partitioning field value within
    [start_val, end_val] interval.
7743
    The function conforms to partition_iter_func type.
7744 7745 7746 7747 7748 7749 7750 7751 7752 7753

  RETURN 
    partition id
    NOT_A_PARTITION_ID if there are no more partitioning.
*/

static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter)
{
  uint32 part_id;
  Field *field= part_iter->part_info->part_field_array[0];
7754
  while (part_iter->field_vals.cur != part_iter->field_vals.end)
7755
  {
sergefp@mysql.com's avatar
sergefp@mysql.com committed
7756
    longlong dummy;
7757 7758
    field->store(part_iter->field_vals.cur++,
                 ((Field_num*)field)->unsigned_flag);
7759
    if ((part_iter->part_info->is_sub_partitioned() &&
7760
        !part_iter->part_info->get_part_partition_id(part_iter->part_info,
7761
                                                     &part_id, &dummy)) ||
7762
        !part_iter->part_info->get_partition_id(part_iter->part_info,
sergefp@mysql.com's avatar
sergefp@mysql.com committed
7763
                                                &part_id, &dummy))
7764 7765
      return part_id;
  }
7766
  part_iter->field_vals.cur= part_iter->field_vals.start;
7767 7768 7769 7770 7771 7772 7773 7774 7775
  return NOT_A_PARTITION_ID;
}


/* Same as get_next_partition_via_walking, but for subpartitions */

static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter)
{
  Field *field= part_iter->part_info->subpart_field_array[0];
7776
  uint32 res;
7777 7778 7779
  if (part_iter->field_vals.cur == part_iter->field_vals.end)
  {
    part_iter->field_vals.cur= part_iter->field_vals.start;
7780
    return NOT_A_PARTITION_ID;
7781 7782
  }
  field->store(part_iter->field_vals.cur++, FALSE);
7783 7784 7785 7786 7787
  if (part_iter->part_info->get_subpartition_id(part_iter->part_info,
                                                &res))
    return NOT_A_PARTITION_ID;
  return res;

7788
}
7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808


/*
  Create partition names

  SYNOPSIS
    create_partition_name()
    out:out                   Created partition name string
    in1                       First part
    in2                       Second part
    name_variant              Normal, temporary or renamed partition name

  RETURN VALUE
    NONE

  DESCRIPTION
    This method is used to calculate the partition name, service routine to
    the del_ren_cre_table method.
*/

7809 7810 7811
void create_partition_name(char *out, const char *in1,
                           const char *in2, uint name_variant,
                           bool translate)
7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850
{
  char transl_part_name[FN_REFLEN];
  const char *transl_part;

  if (translate)
  {
    tablename_to_filename(in2, transl_part_name, FN_REFLEN);
    transl_part= transl_part_name;
  }
  else
    transl_part= in2;
  if (name_variant == NORMAL_PART_NAME)
    strxmov(out, in1, "#P#", transl_part, NullS);
  else if (name_variant == TEMP_PART_NAME)
    strxmov(out, in1, "#P#", transl_part, "#TMP#", NullS);
  else if (name_variant == RENAMED_PART_NAME)
    strxmov(out, in1, "#P#", transl_part, "#REN#", NullS);
}


/*
  Create subpartition name

  SYNOPSIS
    create_subpartition_name()
    out:out                   Created partition name string
    in1                       First part
    in2                       Second part
    in3                       Third part
    name_variant              Normal, temporary or renamed partition name

  RETURN VALUE
    NONE

  DESCRIPTION
  This method is used to calculate the subpartition name, service routine to
  the del_ren_cre_table method.
*/

7851 7852 7853
void create_subpartition_name(char *out, const char *in1,
                              const char *in2, const char *in3,
                              uint name_variant)
7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868
{
  char transl_part_name[FN_REFLEN], transl_subpart_name[FN_REFLEN];

  tablename_to_filename(in2, transl_part_name, FN_REFLEN);
  tablename_to_filename(in3, transl_subpart_name, FN_REFLEN);
  if (name_variant == NORMAL_PART_NAME)
    strxmov(out, in1, "#P#", transl_part_name,
            "#SP#", transl_subpart_name, NullS);
  else if (name_variant == TEMP_PART_NAME)
    strxmov(out, in1, "#P#", transl_part_name,
            "#SP#", transl_subpart_name, "#TMP#", NullS);
  else if (name_variant == RENAMED_PART_NAME)
    strxmov(out, in1, "#P#", transl_part_name,
            "#SP#", transl_subpart_name, "#REN#", NullS);
}
7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880

uint get_partition_field_store_length(Field *field)
{
  uint store_length;

  store_length= field->key_length();
  if (field->real_maybe_null())
    store_length+= HA_KEY_NULL_LENGTH;
  if (field->real_type() == MYSQL_TYPE_VARCHAR)
    store_length+= HA_KEY_BLOB_LENGTH;
  return store_length;
}
7881
#endif
7882