sql_table.cc 209 KB
Newer Older
1
/* Copyright (C) 2000-2004 MySQL AB
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

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

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

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

/* drop and alter of tables */

#include "mysql_priv.h"
20
#include <hash.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21
#include <myisam.h>
22
#include <my_dir.h>
23 24
#include "sp_head.h"
#include "sql_trigger.h"
25
#include "sql_show.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
26 27 28 29 30

#ifdef __WIN__
#include <io.h>
#endif

31 32
int creating_table= 0;        // How many mysql_create_table are running

serg@serg.mylan's avatar
serg@serg.mylan committed
33
const char *primary_key_name="PRIMARY";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
34 35 36 37

static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
static int copy_data_between_tables(TABLE *from,TABLE *to,
38
                                    List<create_field> &create, bool ignore,
39
				    uint order_num, ORDER *order,
40
				    ha_rows *copied,ha_rows *deleted);
41
static bool prepare_blob_field(THD *thd, create_field *sql_field);
42
static bool check_engine(THD *thd, const char *table_name,
43
                         HA_CREATE_INFO *create_info);                             
44 45 46 47 48 49
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
                               List<create_field> *fields,
                               List<Key> *keys, bool tmp_table,
                               uint *db_options,
                               handler *file, KEY **key_info_buffer,
                               uint *key_count, int select_field_count);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
50

51 52 53
#define MYSQL50_TABLE_NAME_PREFIX         "#mysql50#"
#define MYSQL50_TABLE_NAME_PREFIX_LENGTH  9

54 55 56 57 58 59 60 61 62 63 64 65 66 67

/*
  Translate a file name to a table name (WL #1324).

  SYNOPSIS
    filename_to_tablename()
      from                      The file name in my_charset_filename.
      to                OUT     The table name in system_charset_info.
      to_length                 The size of the table name buffer.

  RETURN
    Table name length.
*/

68 69
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
70 71 72 73 74 75
  uint errors;
  uint res;
  DBUG_ENTER("filename_to_tablename");
  DBUG_PRINT("enter", ("from '%s'", from));

  if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length))
76
  {
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
    /* Temporary table name. */
    res= (strnmov(to, from, to_length) - to);
  }
  else
  {
    res= strconvert(&my_charset_filename, from,
                    system_charset_info,  to, to_length, &errors);
    if (errors) // Old 5.0 name
    {
      res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX,  from, NullS) -
            to);
      sql_print_error("Invalid (old?) table or database name '%s'", from);
      /*
        TODO: add a stored procedure for fix table and database names,
        and mention its name in error log.
      */
    }
94
  }
95 96 97

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
98 99 100
}


101 102 103 104 105 106 107 108 109 110 111 112 113
/*
  Translate a table name to a file name (WL #1324).

  SYNOPSIS
    tablename_to_filename()
      from                      The table name in system_charset_info.
      to                OUT     The file name in my_charset_filename.
      to_length                 The size of the file name buffer.

  RETURN
    File name length.
*/

114 115
uint tablename_to_filename(const char *from, char *to, uint to_length)
{
116
  uint errors, length;
117 118 119
  DBUG_ENTER("tablename_to_filename");
  DBUG_PRINT("enter", ("from '%s'", from));

bar@mysql.com's avatar
bar@mysql.com committed
120 121
  if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
                                 MYSQL50_TABLE_NAME_PREFIX_LENGTH))
122 123 124
    DBUG_RETURN((uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
                                to_length-1) -
                        (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH)));
125 126 127 128 129 130 131 132
  length= strconvert(system_charset_info, from,
                     &my_charset_filename, to, to_length, &errors);
  if (check_if_legal_tablename(to) &&
      length + 4 < to_length)
  {
    memcpy(to + length, "@@@", 4);
    length+= 3;
  }
133 134
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
135 136 137
}


138
/*
139
  Creates path to a file: mysql_data_dir/db/table.ext
140 141

  SYNOPSIS
142
   build_table_filename()
143 144 145 146 147 148 149
     buff                       Where to write result in my_charset_filename.
     bufflen                    buff size
     db                         Database name in system_charset_info.
     table_name                 Table name in system_charset_info.
     ext                        File extension.
     flags                      FN_FROM_IS_TMP or FN_TO_IS_TMP or FN_IS_TMP
                                table_name is temporary, do not change.
150 151 152 153 154 155

  NOTES

    Uses database and table name, and extension to create
    a file name in mysql_data_dir. Database and table
    names are converted from system_charset_info into "fscs".
156 157
    Unless flags indicate a temporary table name.
    'db' is always converted.
158
    'ext' is not converted.
159

160 161 162 163 164
    The conversion suppression is required for ALTER TABLE. This
    statement creates intermediate tables. These are regular
    (non-temporary) tables with a temporary name. Their path names must
    be derivable from the table name. So we cannot use
    build_tmptable_filename() for them.
165

166 167
  RETURN
    path length
168 169 170
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
171
                          const char *table_name, const char *ext, uint flags)
172 173 174 175
{
  uint length;
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
176 177 178 179 180 181 182
  DBUG_ENTER("build_table_filename");

  if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP
    strnmov(tbbuff, table_name, sizeof(tbbuff));
  else
    VOID(tablename_to_filename(table_name, tbbuff, sizeof(tbbuff)));

183
  VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
184 185 186 187
  length= strxnmov(buff, bufflen, mysql_data_home, "/", dbbuff,
                   "/", tbbuff, ext, NullS) - buff;
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
188 189 190
}


191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
/*
  Creates path to a file: mysql_tmpdir/#sql1234_12_1.ext

  SYNOPSIS
   build_tmptable_filename()
     thd                        The thread handle.
     buff                       Where to write result in my_charset_filename.
     bufflen                    buff size

  NOTES

    Uses current_pid, thread_id, and tmp_table counter to create
    a file name in mysql_tmpdir.

  RETURN
    path length
*/

209
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
210
{
211
  uint length;
212
  char tmp_table_name[tmp_file_prefix_length+22+22+22+3];
213 214
  DBUG_ENTER("build_tmptable_filename");

215 216 217 218
  my_snprintf(tmp_table_name, sizeof(tmp_table_name),
	      "%s%lx_%lx_%x",
	      tmp_file_prefix, current_pid,
	      thd->thread_id, thd->tmp_table++);
219 220

  strxnmov(buff, bufflen, mysql_tmpdir, "/", tmp_table_name, reg_ext, NullS);
221
  length= unpack_filename(buff, buff);
222 223
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
224 225
}

226 227 228 229 230 231 232
/*
  Return values for compare_tables().
  If you make compare_tables() non-static, move them to a header file.
*/
#define ALTER_TABLE_DATA_CHANGED  1
#define ALTER_TABLE_INDEX_CHANGED 2

233

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
/*
  SYNOPSIS
    mysql_copy_create_list()
    orig_create_list          Original list of created fields
    inout::new_create_list    Copy of original list

  RETURN VALUES
    FALSE                     Success
    TRUE                      Memory allocation error

  DESCRIPTION
    mysql_prepare_table destroys the create_list and in some cases we need
    this lists for more purposes. Thus we copy it specifically for use
    by mysql_prepare_table
*/

static int mysql_copy_create_list(List<create_field> *orig_create_list,
251
                                  List<create_field> *new_create_list)
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
{
  List_iterator<create_field> prep_field_it(*orig_create_list);
  create_field *prep_field;
  DBUG_ENTER("mysql_copy_create_list");

  while ((prep_field= prep_field_it++))
  {
    create_field *field= new create_field(*prep_field);
    if (!field || new_create_list->push_back(field))
    {
      mem_alloc_error(2);
      DBUG_RETURN(TRUE);
    }
  }
  DBUG_RETURN(FALSE);
}


/*
  SYNOPSIS
    mysql_copy_key_list()
    orig_key                  Original list of keys
    inout::new_key            Copy of original list

  RETURN VALUES
    FALSE                     Success
    TRUE                      Memory allocation error

  DESCRIPTION
    mysql_prepare_table destroys the key list and in some cases we need
    this lists for more purposes. Thus we copy it specifically for use
    by mysql_prepare_table
*/

static int mysql_copy_key_list(List<Key> *orig_key,
                               List<Key> *new_key)
{
  List_iterator<Key> prep_key_it(*orig_key);
  Key *prep_key;
291
  DBUG_ENTER("mysql_copy_key_list");
292 293 294 295 296 297 298 299 300 301 302

  while ((prep_key= prep_key_it++))
  {
    List<key_part_spec> prep_columns;
    List_iterator<key_part_spec> prep_col_it(prep_key->columns);
    key_part_spec *prep_col;
    Key *temp_key;

    while ((prep_col= prep_col_it++))
    {
      key_part_spec *prep_key_part;
303 304

      if (!(prep_key_part= new key_part_spec(*prep_col)))
305 306 307 308 309 310 311 312 313 314
      {
        mem_alloc_error(sizeof(key_part_spec));
        DBUG_RETURN(TRUE);
      }
      if (prep_columns.push_back(prep_key_part))
      {
        mem_alloc_error(2);
        DBUG_RETURN(TRUE);
      }
    }
315
    if (!(temp_key= new Key(prep_key->type, prep_key->name,
316
                            &prep_key->key_create_info,
317
                            prep_key->generated,
318
                            prep_columns)))
319 320 321 322 323 324 325 326 327 328 329 330 331
    {
      mem_alloc_error(sizeof(Key));
      DBUG_RETURN(TRUE);
    }
    if (new_key->push_back(temp_key))
    {
      mem_alloc_error(2);
      DBUG_RETURN(TRUE);
    }
  }
  DBUG_RETURN(FALSE);
}

332 333 334
/*
--------------------------------------------------------------------------

335
   MODULE: DDL log
336 337 338 339 340 341 342 343
   -----------------

   This module is used to ensure that we can recover from crashes that occur
   in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2;
   We need to ensure that both t1 and t2 are dropped and not only t1 and
   also that each table drop is entirely done and not "half-baked".

   To support this we create log entries for each meta-data statement in the
344
   ddl log while we are executing. These entries are dropped when the
345 346 347 348
   operation is completed.

   At recovery those entries that were not completed will be executed.

349
   There is only one ddl log in the system and it is protected by a mutex
350 351 352
   and there is a global struct that contains information about its current
   state.

353 354
   History:
   First version written in 2006 by Mikael Ronstrom
355 356 357 358
--------------------------------------------------------------------------
*/


359
typedef struct st_global_ddl_log
360
{
361 362 363 364 365 366
  /*
    We need to adjust buffer size to be able to handle downgrades/upgrades
    where IO_SIZE has changed. We'll set the buffer size such that we can
    handle that the buffer size was upto 4 times bigger in the version
    that wrote the DDL log.
  */
367
  char file_entry_buf[4*IO_SIZE];
368 369
  char file_name_str[FN_REFLEN];
  char *file_name;
370 371 372
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
373 374
  File file_id;
  uint name_len;
375
  uint io_size;
376
  bool inited;
377
  bool recovery_phase;
378
} GLOBAL_DDL_LOG;
379

380
GLOBAL_DDL_LOG global_ddl_log;
381

382
pthread_mutex_t LOCK_gdl;
383

384 385 386 387 388
#define DDL_LOG_ENTRY_TYPE_POS 0
#define DDL_LOG_ACTION_TYPE_POS 1
#define DDL_LOG_PHASE_POS 2
#define DDL_LOG_NEXT_ENTRY_POS 4
#define DDL_LOG_NAME_POS 8
389

390 391
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
392
#define DDL_LOG_IO_SIZE_POS 8
393

394
/*
395
  Read one entry from ddl log file
396
  SYNOPSIS
397
    read_ddl_log_file_entry()
398
    entry_no                     Entry number to read
399
  RETURN VALUES
400 401
    TRUE                         Error
    FALSE                        Success
402 403
*/

404
static bool read_ddl_log_file_entry(uint entry_no)
405 406
{
  bool error= FALSE;
407 408 409 410
  File file_id= global_ddl_log.file_id;
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
411

Magnus@hugo's avatar
Magnus@hugo committed
412
  if (my_pread(file_id, (byte*)file_entry_buf, io_size, io_size * entry_no,
413
               MYF(MY_WME)) != io_size)
414 415 416 417 418 419
    error= TRUE;
  DBUG_RETURN(error);
}


/*
420
  Write one entry from ddl log file
421
  SYNOPSIS
422
    write_ddl_log_file_entry()
423 424 425 426 427 428
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

429
static bool write_ddl_log_file_entry(uint entry_no)
430 431
{
  bool error= FALSE;
432 433 434
  File file_id= global_ddl_log.file_id;
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_ddl_log_file_entry");
435

Magnus@hugo's avatar
Magnus@hugo committed
436
  if (my_pwrite(file_id, (byte*)file_entry_buf,
437
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
438 439 440 441 442 443
    error= TRUE;
  DBUG_RETURN(error);
}


/*
444
  Write ddl log header
445
  SYNOPSIS
446
    write_ddl_log_header()
447 448 449 450 451
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

452
static bool write_ddl_log_header()
453 454
{
  uint16 const_var;
455
  bool error= FALSE;
456
  DBUG_ENTER("write_ddl_log_header");
457

458 459
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
460
  const_var= FN_LEN;
461
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
462
            const_var);
463
  const_var= IO_SIZE;
464
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
465
            const_var);
466
  if (write_ddl_log_file_entry(0UL))
467 468 469 470 471
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
472
  DBUG_RETURN(error);
473 474 475
}


476
/*
477
  Create ddl log file name
478
  SYNOPSIS
479
    create_ddl_log_file_name()
480 481 482 483 484
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

485
static inline void create_ddl_log_file_name(char *file_name)
486
{
487
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
488 489 490
}


491
/*
492
  Read header of ddl log file
493
  SYNOPSIS
494
    read_ddl_log_header()
495
  RETURN VALUES
496 497
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
498
  DESCRIPTION
499 500 501
    When we read the ddl log header we get information about maximum sizes
    of names in the ddl log and we also get information about the number
    of entries in the ddl log.
502 503
*/

504
static uint read_ddl_log_header()
505
{
506
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
507
  char file_name[FN_REFLEN];
508
  uint entry_no;
509
  bool successful_open= FALSE;
510
  DBUG_ENTER("read_ddl_log_header");
511

512
  create_ddl_log_file_name(file_name);
513 514
  if ((global_ddl_log.file_id= my_open(file_name,
                                        O_RDWR | O_BINARY, MYF(MY_WME))) >= 0)
515
  {
516
    if (read_ddl_log_file_entry(0UL))
517
    {
518 519
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
520
    }
521 522
    else
      successful_open= TRUE;
523
  }
524 525
  entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
  global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
526 527
  if (successful_open)
  {
528
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
529 530 531
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
532
  else
533 534 535
  {
    entry_no= 0;
  }
536 537 538 539
  global_ddl_log.first_free= NULL;
  global_ddl_log.first_used= NULL;
  global_ddl_log.num_entries= 0;
  VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
540
  DBUG_RETURN(entry_no);
541 542 543 544
}


/*
545
  Read a ddl log entry
546
  SYNOPSIS
547
    read_ddl_log_entry()
548 549 550 551 552 553
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
554
    Read a specified entry in the ddl log
555 556
*/

557
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
558
{
559
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
560
  uint inx;
561
  uchar single_char;
562
  DBUG_ENTER("read_ddl_log_entry");
563

564
  if (read_ddl_log_file_entry(read_entry))
565 566 567
  {
    DBUG_RETURN(TRUE);
  }
568
  ddl_log_entry->entry_pos= read_entry;
569 570 571 572
  single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
  ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
  single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS];
  ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
573 574 575 576 577 578 579
  ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
  ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
  ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
  inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
  ddl_log_entry->from_name= &file_entry_buf[inx];
  inx+= global_ddl_log.name_len;
  ddl_log_entry->handler_name= &file_entry_buf[inx];
580 581 582 583 584
  DBUG_RETURN(FALSE);
}


/*
585
  Initialise ddl log
586
  SYNOPSIS
587
    init_ddl_log()
588

589
  DESCRIPTION
590
    Write the header of the ddl log file and length of names. Also set
591
    number of entries to zero.
592 593 594 595

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
596 597
*/

598
static bool init_ddl_log()
599
{
600
  bool error= FALSE;
601
  char file_name[FN_REFLEN];
602
  DBUG_ENTER("init_ddl_log");
603

604
  if (global_ddl_log.inited)
605 606
    goto end;

607
  global_ddl_log.io_size= IO_SIZE;
608
  create_ddl_log_file_name(file_name);
609 610 611 612
  if ((global_ddl_log.file_id= my_create(file_name,
                                         CREATE_MODE,
                                         O_RDWR | O_TRUNC | O_BINARY,
                                         MYF(MY_WME))) < 0)
613
  {
614
    /* Couldn't create ddl log file, this is serious error */
615 616
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
617
  }
618
  global_ddl_log.inited= TRUE;
619
  if (write_ddl_log_header())
620
  {
621
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
622
    global_ddl_log.inited= FALSE;
623
    DBUG_RETURN(TRUE);
624
  }
625 626

end:
627
  DBUG_RETURN(FALSE);
628 629 630 631
}


/*
632
  Execute one action in a ddl log entry
633
  SYNOPSIS
634 635
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
636
  RETURN VALUES
637 638
    TRUE                       Error
    FALSE                      Success
639 640
*/

641
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
642
{
643 644
  bool frm_action= FALSE;
  LEX_STRING handler_name;
645
  handler *file= NULL;
646
  MEM_ROOT mem_root;
647
  int error= TRUE;
648
  char to_path[FN_REFLEN];
649
  char from_path[FN_REFLEN];
650 651
  char *par_ext= (char*)".par";
  handlerton *hton;
652
  DBUG_ENTER("execute_ddl_log_action");
653

654
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
655 656 657
  {
    DBUG_RETURN(FALSE);
  }
658 659
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
660
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
661
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
662 663 664
    frm_action= TRUE;
  else
  {
665
    TABLE_SHARE dummy;
666 667 668 669 670 671 672

    hton= ha_resolve_by_name(thd, &handler_name);
    if (!hton)
    {
      my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
      goto error;
    }
673 674
    bzero(&dummy, sizeof(TABLE_SHARE));
    file= get_new_handler(&dummy, &mem_root, hton);
675
    if (!file)
676 677
    {
      mem_alloc_error(sizeof(handler));
678
      goto error;
679
    }
680
  }
681
  switch (ddl_log_entry->action_type)
682
  {
683
    case DDL_LOG_REPLACE_ACTION:
684
    case DDL_LOG_DELETE_ACTION:
685
    {
686
      if (ddl_log_entry->phase == 0)
687 688 689
      {
        if (frm_action)
        {
690 691 692
          strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
          if ((error= my_delete(to_path, MYF(MY_WME))))
          {
693
            if (my_errno != ENOENT)
694 695
              break;
          }
696
#ifdef WITH_PARTITION_STORAGE_ENGINE
697 698
          strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
          VOID(my_delete(to_path, MYF(MY_WME)));
699
#endif
700 701 702
        }
        else
        {
703 704 705 706 707
          if ((error= file->delete_table(ddl_log_entry->name)))
          {
            if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
              break;
          }
708
        }
709
        if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
710 711 712
          break;
        VOID(sync_ddl_log());
        error= FALSE;
713 714
        if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
          break;
715
      }
716 717 718 719 720 721
      DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION);
      /*
        Fall through and perform the rename action of the replace
        action. We have already indicated the success of the delete
        action in the log entry by stepping up the phase.
      */
722
    }
723
    case DDL_LOG_RENAME_ACTION:
724
    {
725 726 727
      error= TRUE;
      if (frm_action)
      {
728
        strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
729
        strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
730
        if (my_rename(from_path, to_path, MYF(MY_WME)))
731
          break;
732
#ifdef WITH_PARTITION_STORAGE_ENGINE
733
        strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
734
        strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
735
        VOID(my_rename(from_path, to_path, MYF(MY_WME)));
736
#endif
737 738 739
      }
      else
      {
740 741
        if (file->rename_table(ddl_log_entry->from_name,
                               ddl_log_entry->name))
742
          break;
743 744
      }
      if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
745 746 747
        break;
      VOID(sync_ddl_log());
      error= FALSE;
748
      break;
749
    }
750 751 752 753 754 755 756 757
    default:
      DBUG_ASSERT(0);
      break;
  }
  delete file;
error:
  free_root(&mem_root, MYF(0)); 
  DBUG_RETURN(error);
758 759 760
}


761
/*
762
  Get a free entry in the ddl log
763
  SYNOPSIS
764 765
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
766
  RETURN VALUES
767 768
    TRUE                       Error
    FALSE                      Success
769 770
*/

771 772
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
773
{
774 775 776
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
777

778
  if (global_ddl_log.first_free == NULL)
779
  {
780 781
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
782
    {
783
      sql_print_error("Failed to allocate memory for ddl log free list");
784 785
      DBUG_RETURN(TRUE);
    }
786
    global_ddl_log.num_entries++;
787
    used_entry->entry_pos= global_ddl_log.num_entries;
788
    *write_header= TRUE;
789 790 791
  }
  else
  {
792 793
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
794
    *write_header= FALSE;
795 796 797 798 799 800
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
801
  global_ddl_log.first_used= used_entry;
802 803 804 805
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
806
  DBUG_RETURN(FALSE);
807 808 809 810
}


/*
811
  External interface methods for the DDL log Module
812 813 814 815
  ---------------------------------------------------
*/

/*
816
  SYNOPSIS
817 818 819
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
820

821
  RETURN VALUES
822 823 824 825
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
826
    A careful write of the ddl log is performed to ensure that we can
827
    handle crashes occurring during CREATE and ALTER TABLE processing.
828 829
*/

830 831
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
832
{
833
  bool error, write_header;
834 835 836 837 838 839
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
840 841
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
842
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
843
                                    (char)ddl_log_entry->action_type;
844 845 846 847
  global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
            ddl_log_entry->next_entry);
  DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN);
848 849
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
850 851
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
852
  {
853
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
854 855
    strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
          ddl_log_entry->from_name, FN_LEN - 1);
856
  }
857
  else
858 859
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
860 861
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
862
  if (get_free_ddl_log_entry(active_entry, &write_header))
863 864 865 866
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
867
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
868
  {
869
    error= TRUE;
870 871 872
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
873 874
  if (write_header && !error)
  {
875 876
    VOID(sync_ddl_log());
    if (write_ddl_log_header())
877 878
      error= TRUE;
  }
879
  if (error)
880
    release_ddl_log_memory_entry(*active_entry);
881
  DBUG_RETURN(error);
882 883 884 885
}


/*
886
  Write final entry in the ddl log
887
  SYNOPSIS
888
    write_execute_ddl_log_entry()
889 890 891 892
    first_entry                    First entry in linked list of entries
                                   to execute, if 0 = NULL it means that
                                   the entry is removed and the entries
                                   are put into the free list.
893 894 895
    complete                       Flag indicating we are simply writing
                                   info about that entry has been completed
    in:out:active_entry            Entry to execute, 0 = NULL if the entry
896 897 898
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
899
  RETURN VALUES
900 901 902 903
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
904
    This is the last write in the ddl log. The previous log entries have
905
    already been written but not yet synched to disk.
906 907 908 909
    We write a couple of log entries that describes action to perform.
    This entries are set-up in a linked list, however only when a first
    execute entry is put as the first entry these will be executed.
    This routine writes this first 
910
*/ 
911

912 913 914
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
915
{
916
  bool write_header= FALSE;
917 918
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
919

920 921 922 923
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
924 925
  if (!complete)
  {
926 927 928 929 930 931
    /*
      We haven't synched the log entries yet, we synch them now before
      writing the execute entry. If complete is true we haven't written
      any log entries before, we are only here to write the execute
      entry to indicate it is done.
    */
932
    VOID(sync_ddl_log());
933
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
934 935
  }
  else
936
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
937 938 939 940 941 942
  file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
  file_entry_buf[DDL_LOG_PHASE_POS]= 0;
  int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
  file_entry_buf[DDL_LOG_NAME_POS]= 0;
  file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0;
943
  if (!(*active_entry))
944
  {
945
    if (get_free_ddl_log_entry(active_entry, &write_header))
946 947 948
    {
      DBUG_RETURN(TRUE);
    }
949
  }
950
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
951
  {
952
    sql_print_error("Error writing execute entry in ddl log");
953
    release_ddl_log_memory_entry(*active_entry);
954 955
    DBUG_RETURN(TRUE);
  }
956
  VOID(sync_ddl_log());
957 958
  if (write_header)
  {
959
    if (write_ddl_log_header())
960
    {
961
      release_ddl_log_memory_entry(*active_entry);
962 963 964
      DBUG_RETURN(TRUE);
    }
  }
965 966 967 968
  DBUG_RETURN(FALSE);
}


969
/*
970
  For complex rename operations we need to deactivate individual entries.
971
  SYNOPSIS
972
    deactivate_ddl_log_entry()
973 974 975 976 977 978 979 980
    entry_no                      Entry position of record to change
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
  DESCRIPTION
    During replace operations where we start with an existing table called
    t1 and a replacement table called t1#temp or something else and where
    we want to delete t1 and rename t1#temp to t1 this is not possible to
981
    do in a safe manner unless the ddl log is informed of the phases in
982 983 984 985 986 987 988 989 990 991 992
    the change.

    Delete actions are 1-phase actions that can be ignored immediately after
    being executed.
    Rename actions from x to y is also a 1-phase action since there is no
    interaction with any other handlers named x and y.
    Replace action where drop y and x -> y happens needs to be a two-phase
    action. Thus the first phase will drop y and the second phase will
    rename x -> y.
*/

993
bool deactivate_ddl_log_entry(uint entry_no)
994
{
995 996
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
997

998
  if (!read_ddl_log_file_entry(entry_no))
999
  {
1000
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
1001
    {
1002 1003 1004 1005 1006 1007
      if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
          file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
          (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
           file_entry_buf[DDL_LOG_PHASE_POS] == 1))
        file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
      else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
1008
      {
1009 1010
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
1011 1012 1013 1014 1015
      }
      else
      {
        DBUG_ASSERT(0);
      }
1016 1017 1018 1019 1020 1021
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
1022 1023
    }
  }
1024 1025 1026 1027 1028 1029
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
1030 1031 1032 1033
}


/*
1034
  Sync ddl log file
1035
  SYNOPSIS
1036
    sync_ddl_log()
1037 1038 1039 1040 1041
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

1042
bool sync_ddl_log()
1043 1044
{
  bool error= FALSE;
1045
  DBUG_ENTER("sync_ddl_log");
1046

1047 1048
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
1049 1050 1051 1052
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
1053 1054
  {
    /* Write to error log */
1055
    sql_print_error("Failed to sync ddl log");
1056 1057 1058 1059 1060 1061
    error= TRUE;
  }
  DBUG_RETURN(error);
}


1062 1063 1064
/*
  Release a log memory entry
  SYNOPSIS
1065
    release_ddl_log_memory_entry()
1066 1067 1068 1069 1070
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

1071
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
1072
{
1073 1074 1075 1076
  DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free;
  DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
  DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
  DBUG_ENTER("release_ddl_log_memory_entry");
1077

1078
  global_ddl_log.first_free= log_entry;
1079 1080 1081 1082 1083
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1084
    global_ddl_log.first_used= next_log_entry;
1085 1086
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1087
  DBUG_VOID_RETURN;
1088 1089 1090
}


1091
/*
1092
  Execute one entry in the ddl log. Executing an entry means executing
1093 1094
  a linked list of actions.
  SYNOPSIS
1095
    execute_ddl_log_entry()
1096 1097 1098 1099 1100 1101
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1102
bool execute_ddl_log_entry(THD *thd, uint first_entry)
1103
{
1104
  DDL_LOG_ENTRY ddl_log_entry;
1105
  uint read_entry= first_entry;
1106
  DBUG_ENTER("execute_ddl_log_entry");
1107

1108
  pthread_mutex_lock(&LOCK_gdl);
1109 1110
  do
  {
1111
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1112 1113
    {
      /* Write to error log and continue with next log entry */
1114 1115
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1116 1117
      break;
    }
1118 1119
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1120

1121
    if (execute_ddl_log_action(thd, &ddl_log_entry))
1122
    {
1123
      /* Write to error log and continue with next log entry */
1124 1125
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1126
      break;
1127
    }
1128
    read_entry= ddl_log_entry.next_entry;
1129
  } while (read_entry);
1130
  pthread_mutex_unlock(&LOCK_gdl);
1131 1132 1133
  DBUG_RETURN(FALSE);
}

1134

1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
/*
  Close the ddl log
  SYNOPSIS
    close_ddl_log()
  RETURN VALUES
    NONE
*/

static void close_ddl_log()
{
  DBUG_ENTER("close_ddl_log");
  if (global_ddl_log.file_id >= 0)
  {
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
    global_ddl_log.file_id= (File) -1;
  }
  DBUG_VOID_RETURN;
}


1155
/*
1156
  Execute the ddl log at recovery of MySQL Server
1157
  SYNOPSIS
1158
    execute_ddl_log_recovery()
1159 1160 1161 1162
  RETURN VALUES
    NONE
*/

1163
void execute_ddl_log_recovery()
1164
{
1165
  uint num_entries, i;
1166
  THD *thd;
1167
  DDL_LOG_ENTRY ddl_log_entry;
1168
  char file_name[FN_REFLEN];
1169
  DBUG_ENTER("execute_ddl_log_recovery");
1170

1171 1172 1173 1174 1175 1176 1177
  /*
    Initialise global_ddl_log struct
  */
  bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
  global_ddl_log.inited= FALSE;
  global_ddl_log.recovery_phase= TRUE;
  global_ddl_log.io_size= IO_SIZE;
monty@mysql.com's avatar
monty@mysql.com committed
1178
  global_ddl_log.file_id= (File) -1;
1179

1180 1181 1182 1183 1184 1185 1186 1187
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
  if (!(thd=new THD))
    DBUG_VOID_RETURN;
  thd->thread_stack= (char*) &thd;
  thd->store_globals();

1188
  num_entries= read_ddl_log_header();
1189
  for (i= 1; i < num_entries + 1; i++)
1190
  {
1191
    if (read_ddl_log_entry(i, &ddl_log_entry))
1192
    {
1193 1194 1195
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1196
    }
1197
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1198
    {
1199
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1200
      {
1201 1202
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
1203 1204 1205
      }
    }
  }
1206
  close_ddl_log();
1207 1208 1209 1210 1211 1212
  create_ddl_log_file_name(file_name);
  VOID(my_delete(file_name, MYF(0)));
  global_ddl_log.recovery_phase= FALSE;
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
1213
  DBUG_VOID_RETURN;
1214 1215 1216 1217
}


/*
1218
  Release all memory allocated to the ddl log
1219
  SYNOPSIS
1220
    release_ddl_log()
1221 1222 1223 1224
  RETURN VALUES
    NONE
*/

1225
void release_ddl_log()
1226
{
1227 1228 1229
  DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free;
  DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used;
  DBUG_ENTER("release_ddl_log");
1230

1231
  pthread_mutex_lock(&LOCK_gdl);
1232 1233
  while (used_list)
  {
1234
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1235
    my_free((char*)used_list, MYF(0));
1236
    used_list= tmp;
1237 1238 1239
  }
  while (free_list)
  {
1240
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1241
    my_free((char*)free_list, MYF(0));
1242
    free_list= tmp;
1243
  }
1244
  close_ddl_log();
monty@mysql.com's avatar
monty@mysql.com committed
1245
  global_ddl_log.inited= 0;
1246
  pthread_mutex_unlock(&LOCK_gdl);
1247
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1248
  DBUG_VOID_RETURN;
1249 1250 1251
}


1252 1253 1254
/*
---------------------------------------------------------------------------

1255
  END MODULE DDL log
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 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293

/*
  SYNOPSIS
    mysql_write_frm()
    lpt                    Struct carrying many parameters needed for this
                           method
    flags                  Flags as defined below
      WFRM_INITIAL_WRITE        If set we need to prepare table before
                                creating the frm file
      WFRM_CREATE_HANDLER_FILES If set we need to create the handler file as
                                part of the creation of the frm file
      WFRM_PACK_FRM             If set we should pack the frm file and delete
                                the frm file

  RETURN VALUES
    TRUE                   Error
    FALSE                  Success

  DESCRIPTION
    A support method that creates a new frm file and in this process it
    regenerates the partition data. It works fine also for non-partitioned
    tables since it only handles partitioned data if it exists.
*/

bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
{
  /*
    Prepare table to prepare for writing a new frm file where the
    partitions in add/drop state have temporarily changed their state
    We set tmp_table to avoid get errors on naming of primary key index.
  */
  int error= 0;
  char path[FN_REFLEN+1];
1294 1295
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
1296 1297 1298
  char frm_name[FN_REFLEN+1];
  DBUG_ENTER("mysql_write_frm");

1299 1300 1301
  /*
    Build shadow frm file name
  */
1302
  build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
1303
                       lpt->table_name, "#", 0);
1304
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1305
  if (flags & WFRM_WRITE_SHADOW)
1306
  {
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
    if (mysql_copy_create_list(lpt->create_list,
                               &lpt->new_create_list) ||
        mysql_copy_key_list(lpt->key_list,
                            &lpt->new_key_list) ||
        mysql_prepare_table(lpt->thd, lpt->create_info,
                            &lpt->new_create_list,
                            &lpt->new_key_list,
                            /*tmp_table*/ 1,
                            &lpt->db_options,
                            lpt->table->file,
                            &lpt->key_info_buffer,
                            &lpt->key_count,
1319
                            /*select_field_count*/ 0))
1320 1321 1322 1323 1324
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1325 1326 1327 1328 1329
      partition_info *part_info= lpt->table->part_info;
      char *part_syntax_buf;
      uint syntax_len;

      if (part_info)
1330
      {
1331 1332
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1333
                                                         TRUE, TRUE)))
1334 1335 1336 1337 1338
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
        part_info->part_info_len= syntax_len;
1339 1340 1341
      }
    }
#endif
1342 1343 1344 1345 1346 1347
    /* Write shadow frm file */
    lpt->create_info->table_options= lpt->db_options;
    if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db,
                          lpt->table_name, lpt->create_info,
                          lpt->new_create_list, lpt->key_count,
                          lpt->key_info_buffer, lpt->table->file)) ||
1348 1349
         lpt->table->file->create_handler_files(shadow_path, NULL,
                                                CHF_CREATE_FLAG,
1350
                                                lpt->create_info))
1351 1352 1353 1354 1355
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
  }
  if (flags & WFRM_PACK_FRM)
  {
    /*
      We need to pack the frm file and after packing it we delete the
      frm file to ensure it doesn't get used. This is only used for
      handlers that have the main version of the frm file stored in the
      handler.
    */
    const void *data= 0;
    uint length= 0;
1367
    if (readfrm(shadow_path, &data, &length) ||
1368 1369 1370 1371 1372 1373 1374 1375
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
      my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*)lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1376
    error= my_delete(shadow_frm_name, MYF(MY_WME));
1377
  }
1378 1379
  if (flags & WFRM_INSTALL_SHADOW)
  {
1380 1381 1382
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1383 1384 1385 1386
    /*
      Build frm file name
    */
    build_table_filename(path, sizeof(path), lpt->db,
1387
                         lpt->table_name, "", 0);
1388 1389 1390 1391
    strxmov(frm_name, path, reg_ext, NullS);
    /*
      When we are changing to use new frm file we need to ensure that we
      don't collide with another thread in process to open the frm file.
1392 1393 1394 1395 1396 1397
      We start by deleting the .frm file and possible .par file. Then we
      write to the DDL log that we have completed the delete phase by
      increasing the phase of the log entry. Next step is to rename the
      new .frm file and the new .par file to the real name. After
      completing this we write a new phase to the log entry that will
      deactivate it.
1398 1399 1400
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1401
#ifdef WITH_PARTITION_STORAGE_ENGINE
1402
        lpt->table->file->create_handler_files(path, shadow_path,
1403
                                               CHF_DELETE_FLAG, NULL) ||
1404 1405
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1406
#endif
1407
#ifdef WITH_PARTITION_STORAGE_ENGINE
1408
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1409
        lpt->table->file->create_handler_files(path, shadow_path,
1410
                                               CHF_RENAME_FLAG, NULL))
1411 1412 1413
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1414 1415 1416 1417
    {
      error= 1;
    }
    VOID(pthread_mutex_unlock(&LOCK_open));
1418
#ifdef WITH_PARTITION_STORAGE_ENGINE
1419
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1420
    part_info->frm_log_entry= NULL;
1421
    VOID(sync_ddl_log());
1422
#endif
1423
  }
1424

1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457
end:
  DBUG_RETURN(error);
}


/*
  SYNOPSIS
    write_bin_log()
    thd                           Thread object
    clear_error                   is clear_error to be called
    query                         Query to log
    query_length                  Length of query

  RETURN VALUES
    NONE

  DESCRIPTION
    Write the binlog if open, routine used in multiple places in this
    file
*/

void write_bin_log(THD *thd, bool clear_error,
                   char const *query, ulong query_length)
{
  if (mysql_bin_log.is_open())
  {
    if (clear_error)
      thd->clear_error();
    thd->binlog_query(THD::STMT_QUERY_TYPE,
                      query, query_length, FALSE, FALSE);
  }
}

1458

1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476
/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table()
   thd			Thread handle
   tables		List of tables to delete
   if_exists		If 1, don't give error if one table doesn't exists

  NOTES
    Will delete all tables that can be deleted and give a compact error
    messages for tables that could not be deleted.
    If a table is in use, we will wait for all users to free the table
    before dropping it

    Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set.

  RETURN
1477 1478
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
1479 1480

*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1481

1482 1483
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1484
{
1485
  bool error= FALSE, need_start_waiters= FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1486 1487 1488 1489
  DBUG_ENTER("mysql_rm_table");

  /* mark for close and remove all cached entries */

1490
  if (!drop_temporary)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1491
  {
1492
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1493
    {
1494
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1495
      DBUG_RETURN(TRUE);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1496
    }
1497 1498
    else
      need_start_waiters= TRUE;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1499
  }
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509

  /*
    Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
    LOCK_open during wait_if_global_read_lock(), other threads could not
    close their tables. This would make a pretty deadlock.
  */
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1510
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1511 1512 1513 1514 1515 1516 1517 1518

  pthread_mutex_unlock(&LOCK_open);

  pthread_mutex_lock(&thd->mysys_var->mutex);
  thd->mysys_var->current_mutex= 0;
  thd->mysys_var->current_cond= 0;
  pthread_mutex_unlock(&thd->mysys_var->mutex);

1519 1520 1521
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1522
  if (error)
1523
    DBUG_RETURN(TRUE);
1524
  send_ok(thd);
1525
  DBUG_RETURN(FALSE);
1526 1527
}

1528 1529 1530 1531 1532

/*
 delete (drop) tables.

  SYNOPSIS
1533 1534 1535 1536 1537
    mysql_rm_table_part2_with_lock()
    thd			Thread handle
    tables		List of tables to delete
    if_exists		If 1, don't give error if one table doesn't exists
    dont_log_query	Don't write query to log files. This will also not
1538
                        generate warnings if the handler files doesn't exists
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548

 NOTES
   Works like documented in mysql_rm_table(), but don't check
   global_read_lock and don't send_ok packet to server.

 RETURN
  0	ok
  1	error
*/

1549 1550
int mysql_rm_table_part2_with_lock(THD *thd,
				   TABLE_LIST *tables, bool if_exists,
1551
				   bool drop_temporary, bool dont_log_query)
1552 1553 1554 1555 1556 1557
{
  int error;
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1558 1559
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
			      dont_log_query);
1560 1561 1562 1563 1564 1565 1566 1567 1568 1569

  pthread_mutex_unlock(&LOCK_open);

  pthread_mutex_lock(&thd->mysys_var->mutex);
  thd->mysys_var->current_mutex= 0;
  thd->mysys_var->current_cond= 0;
  pthread_mutex_unlock(&thd->mysys_var->mutex);
  return error;
}

1570

1571
/*
1572 1573 1574 1575 1576 1577 1578 1579 1580
  Execute the drop of a normal or temporary table

  SYNOPSIS
    mysql_rm_table_part2()
    thd			Thread handler
    tables		Tables to drop
    if_exists		If set, don't give an error if table doesn't exists.
			In this case we give an warning of level 'NOTE'
    drop_temporary	Only drop temporary tables
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1581
    drop_view		Allow to delete VIEW .frm
1582 1583
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1584

1585 1586 1587 1588 1589 1590 1591 1592 1593
  TODO:
    When logging to the binary log, we should log
    tmp_tables and transactional tables as separate statements if we
    are in a transaction;  This is needed to get these tables into the
    cached binary log that is only written on COMMIT.

   The current code only writes DROP statements that only uses temporary
   tables to the cache binary log.  This should be ok on most cases, but
   not all.
1594 1595 1596 1597 1598

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1599
*/
1600 1601

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1602 1603
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1604 1605
{
  TABLE_LIST *table;
1606 1607
  char path[FN_REFLEN], *alias;
  uint path_length;
1608 1609
  String wrong_tables;
  int error;
1610
  int non_temp_tables_count= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1611
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1612
  String built_query;
1613 1614
  DBUG_ENTER("mysql_rm_table_part2");

1615 1616 1617 1618
  LINT_INIT(alias);
  LINT_INIT(path_length);
  safe_mutex_assert_owner(&LOCK_open);

1619
  if (thd->current_stmt_binlog_row_based && !dont_log_query)
1620 1621 1622 1623 1624 1625 1626
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1627 1628 1629 1630 1631 1632 1633 1634 1635
  /*
    If we have the table in the definition cache, we don't have to check the
    .frm file to find if the table is a normal table (not view) and what
    engine to use.
  */

  for (table= tables; table; table= table->next_local)
  {
    TABLE_SHARE *share;
1636
    table->db_type= NULL;
1637 1638
    if ((share= get_cached_table_share(table->db, table->table_name)))
      table->db_type= share->db_type;
1639 1640 1641

    /* Disable drop of enabled log tables */
    if (share && share->log_table &&
1642 1643
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1644
    {
1645
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1646 1647
      DBUG_RETURN(1);
    }
1648 1649
  }

1650
  if (!drop_temporary && lock_table_names(thd, tables))
1651
    DBUG_RETURN(1);
1652

1653 1654 1655
  /* Don't give warnings for not found errors, as we already generate notes */
  thd->no_warnings_for_error= 1;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1656
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1657
  {
1658
    char *db=table->db;
1659 1660
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1661

1662
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
1663
    if (!close_temporary_table(thd, table))
1664
    {
1665
      tmp_table_deleted=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1666
      continue;					// removed temporary table
1667
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1668

1669 1670 1671 1672 1673 1674
    /*
      If row-based replication is used and the table is not a
      temporary table, we add the table name to the drop statement
      being built.  The string always end in a comma and the comma
      will be chopped off before being written to the binary log.
      */
1675
    if (thd->current_stmt_binlog_row_based && !dont_log_query)
1676
    {
1677
      non_temp_tables_count++;
1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
      /*
        Don't write the database name if it is the current one (or if
        thd->db is NULL).
      */
      built_query.append("`");
      if (thd->db == NULL || strcmp(db,thd->db) != 0)
      {
        built_query.append(db);
        built_query.append("`.`");
      }

      built_query.append(table->table_name);
      built_query.append("`,");
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1693
    error=0;
1694
    table_type= table->db_type;
1695
    if (!drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1696
    {
1697
      TABLE *locked_table;
1698 1699 1700 1701
      abort_locked_tables(thd, db, table->table_name);
      remove_table_from_cache(thd, db, table->table_name,
	                      RTFC_WAIT_OTHER_THREAD_FLAG |
			      RTFC_CHECK_KILLED_FLAG);
1702 1703 1704 1705 1706 1707 1708
      /*
        If the table was used in lock tables, remember it so that
        unlock_table_names can free it
      */
      if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
        table->table= locked_table;

1709
      if (thd->killed)
1710 1711
      {
        thd->no_warnings_for_error= 0;
1712
	DBUG_RETURN(-1);
1713
      }
1714
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
1715
      /* remove .frm file and engine files */
1716
      path_length= build_table_filename(path, sizeof(path),
1717
                                        db, alias, reg_ext, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1718
    }
1719 1720
    if (drop_temporary ||
        (table_type == NULL &&        
1721 1722 1723
         (access(path, F_OK) &&
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
1724
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1725
    {
1726
      // Table was not found on disk and table can't be created from engine
1727
      if (if_exists)
1728 1729
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
1730
			    table->table_name);
1731
      else
1732
        error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1733 1734 1735
    }
    else
    {
1736
      char *end;
1737 1738 1739 1740 1741
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
1742 1743
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
1744
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
1745
                             !dont_log_query);
1746
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
1747
	  (if_exists || table_type == NULL))
1748
	error= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1749
      if (error == HA_ERR_ROW_IS_REFERENCED)
monty@mysql.com's avatar
monty@mysql.com committed
1750 1751
      {
	/* the table is referenced by a foreign key constraint */
1752
	foreign_key_error=1;
monty@mysql.com's avatar
monty@mysql.com committed
1753
      }
1754
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
1755
      {
1756
        int new_error;
1757 1758
	/* Delete the table definition file */
	strmov(end,reg_ext);
1759
	if (!(new_error=my_delete(path,MYF(MY_WME))))
1760
        {
1761
	  some_tables_deleted=1;
1762 1763
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
1764
        }
1765
        error|= new_error;
1766
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1767 1768 1769 1770 1771
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
1772
      wrong_tables.append(String(table->table_name,system_charset_info));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1773 1774
    }
  }
1775
  thd->tmp_table_used= tmp_table_deleted;
1776 1777 1778 1779
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
1780
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
1781
                      wrong_tables.c_ptr());
1782
    else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1783
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
1784 1785 1786 1787
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1788
  {
1789
    query_cache_invalidate3(thd, tables, 0);
1790
    if (!dont_log_query)
1791
    {
1792
      if (!thd->current_stmt_binlog_row_based ||
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
          non_temp_tables_count > 0 && !tmp_table_deleted)
      {
        /*
          In this case, we are either using statement-based
          replication or using row-based replication but have only
          deleted one or more non-temporary tables (and no temporary
          tables).  In this case, we can write the original query into
          the binary log.
         */
        write_bin_log(thd, !error, thd->query, thd->query_length);
      }
1804
      else if (thd->current_stmt_binlog_row_based &&
1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831
               non_temp_tables_count > 0 &&
               tmp_table_deleted)
      {
        /*
          In this case we have deleted both temporary and
          non-temporary tables, so:
          - since we have deleted a non-temporary table we have to
            binlog the statement, but
          - since we have deleted a temporary table we cannot binlog
            the statement (since the table has not been created on the
            slave, this might cause the slave to stop).

          Instead, we write a built statement, only containing the
          non-temporary tables, to the binary log
        */
        built_query.chop();                  // Chop of the last comma
        built_query.append(" /* generated by server */");
        write_bin_log(thd, !error, built_query.ptr(), built_query.length());
      }
      /*
        The remaining cases are:
        - no tables where deleted and
        - only temporary tables where deleted and row-based
          replication is used.
        In both these cases, nothing should be written to the binary
        log.
      */
1832
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1833
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1834

1835
  if (!drop_temporary)
1836
    unlock_table_names(thd, tables, (TABLE_LIST*) 0);
1837
  thd->no_warnings_for_error= 0;
1838
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1839 1840 1841
}


1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
/*
  Quickly remove a table.

  SYNOPSIS
    quick_rm_table()
      base                      The handlerton handle.
      db                        The database name.
      table_name                The table name.
      flags                     flags for build_table_filename().

  RETURN
    0           OK
    != 0        Error
*/

1857
bool quick_rm_table(handlerton *base,const char *db,
1858
                    const char *table_name, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1859 1860
{
  char path[FN_REFLEN];
1861 1862 1863
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

1864
  uint path_length= build_table_filename(path, sizeof(path),
1865
                                         db, table_name, reg_ext, flags);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1866
  if (my_delete(path,MYF(0)))
1867
    error= 1; /* purecov: inspected */
1868
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
1869 1870
  DBUG_RETURN(ha_delete_table(current_thd, base, path, db, table_name, 0) ||
              error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1871 1872
}

1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890
/*
  Sort keys in the following order:
  - PRIMARY KEY
  - UNIQUE keyws where all column are NOT NULL
  - Other UNIQUE keys
  - Normal keys
  - Fulltext keys

  This will make checking for duplicated keys faster and ensure that
  PRIMARY keys are prioritized.
*/

static int sort_keys(KEY *a, KEY *b)
{
  if (a->flags & HA_NOSAME)
  {
    if (!(b->flags & HA_NOSAME))
      return -1;
1891
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
1892 1893
    {
      /* Sort NOT NULL keys before other keys */
1894
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
  }
  else if (b->flags & HA_NOSAME)
    return 1;					// Prefer b

  if ((a->flags ^ b->flags) & HA_FULLTEXT)
  {
    return (a->flags & HA_FULLTEXT) ? 1 : -1;
  }
1908
  /*
1909
    Prefer original key order.	usable_key_parts contains here
1910 1911 1912 1913 1914
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
1915 1916
}

1917 1918
/*
  Check TYPELIB (set or enum) for duplicates
1919

1920 1921 1922
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
1923 1924
    name	  name of the checked column
    typelib	  list of values for the column
1925 1926

  DESCRIPTION
1927
    This function prints an warning for each value in list
1928 1929 1930 1931 1932 1933 1934
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
1935 1936
                                  const char *name, TYPELIB *typelib,
                                  CHARSET_INFO *cs)
1937
{
1938
  TYPELIB tmp= *typelib;
1939
  const char **cur_value= typelib->type_names;
1940 1941 1942
  unsigned int *cur_length= typelib->type_lengths;
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
1943
  {
1944 1945 1946 1947
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
1948
    {
monty@mysql.com's avatar
monty@mysql.com committed
1949
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
1950 1951 1952 1953 1954 1955
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
    }
  }
}
1956

1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991

/*
  Check TYPELIB (set or enum) max and total lengths

  SYNOPSIS
    calculate_interval_lengths()
    cs            charset+collation pair of the interval
    typelib       list of values for the column
    max_length    length of the longest item
    tot_length    sum of the item lengths

  DESCRIPTION
    After this function call:
    - ENUM uses max_length
    - SET uses tot_length.

  RETURN VALUES
    void
*/
void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
                                uint32 *max_length, uint32 *tot_length)
{
  const char **pos;
  uint *len;
  *max_length= *tot_length= 0;
  for (pos= interval->type_names, len= interval->type_lengths;
       *pos ; pos++, len++)
  {
    uint length= cs->cset->numchars(cs, *pos, *pos + *len);
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
/*
  Prepare a create_table instance for packing

  SYNOPSIS
    prepare_create_field()
    sql_field     field to prepare for packing
    blob_columns  count for BLOBs
    timestamps    count for timestamps
    table_flags   table flags

  DESCRIPTION
    This function prepares a create_field instance.
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

int prepare_create_field(create_field *sql_field, 
monty@mysql.com's avatar
monty@mysql.com committed
2012 2013
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2014 2015 2016
			 uint table_flags)
{
  DBUG_ENTER("prepare_field");
monty@mysql.com's avatar
monty@mysql.com committed
2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035

  /*
    This code came from mysql_prepare_table.
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
  case FIELD_TYPE_BLOB:
  case FIELD_TYPE_MEDIUM_BLOB:
  case FIELD_TYPE_TINY_BLOB:
  case FIELD_TYPE_LONG_BLOB:
    sql_field->pack_flag=FIELDFLAG_BLOB |
      pack_length_to_packflag(sql_field->pack_length -
                              portable_sizeof_char_ptr);
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->length=8;			// Unireg field length
    sql_field->unireg_check=Field::BLOB_FIELD;
2036
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
2037 2038
    break;
  case FIELD_TYPE_GEOMETRY:
2039
#ifdef HAVE_SPATIAL
monty@mysql.com's avatar
monty@mysql.com committed
2040 2041 2042 2043
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
2044
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
2045 2046 2047 2048 2049 2050 2051 2052
    }
    sql_field->pack_flag=FIELDFLAG_GEOM |
      pack_length_to_packflag(sql_field->pack_length -
                              portable_sizeof_char_ptr);
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->length=8;			// Unireg field length
    sql_field->unireg_check=Field::BLOB_FIELD;
2053
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
2054 2055 2056 2057 2058
    break;
#else
    my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
                    sym_group_geom.name, sym_group_geom.needed_define);
    DBUG_RETURN(1);
2059
#endif /*HAVE_SPATIAL*/
monty@mysql.com's avatar
monty@mysql.com committed
2060
  case MYSQL_TYPE_VARCHAR:
2061
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
monty@mysql.com's avatar
monty@mysql.com committed
2062 2063 2064 2065 2066 2067 2068 2069
    if (table_flags & HA_NO_VARCHAR)
    {
      /* convert VARCHAR to CHAR because handler is not yet up to date */
      sql_field->sql_type=    MYSQL_TYPE_VAR_STRING;
      sql_field->pack_length= calc_pack_length(sql_field->sql_type,
                                               (uint) sql_field->length);
      if ((sql_field->length / sql_field->charset->mbmaxlen) >
          MAX_FIELD_CHARLENGTH)
2070
      {
monty@mysql.com's avatar
monty@mysql.com committed
2071 2072
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
2073 2074
        DBUG_RETURN(1);
      }
monty@mysql.com's avatar
monty@mysql.com committed
2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
    }
#endif
    /* fall through */
  case FIELD_TYPE_STRING:
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
  case FIELD_TYPE_ENUM:
    sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
      FIELDFLAG_INTERVAL;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->unireg_check=Field::INTERVAL_FIELD;
    check_duplicates_in_interval("ENUM",sql_field->field_name,
                                 sql_field->interval,
                                 sql_field->charset);
    break;
  case FIELD_TYPE_SET:
    sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
      FIELDFLAG_BITFIELD;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->unireg_check=Field::BIT_FIELD;
    check_duplicates_in_interval("SET",sql_field->field_name,
                                 sql_field->interval,
                                 sql_field->charset);
    break;
  case FIELD_TYPE_DATE:			// Rest of string types
  case FIELD_TYPE_NEWDATE:
  case FIELD_TYPE_TIME:
  case FIELD_TYPE_DATETIME:
  case FIELD_TYPE_NULL:
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
  case FIELD_TYPE_BIT:
ramil@mysql.com's avatar
ramil@mysql.com committed
2111 2112 2113
    /* 
      We have sql_field->pack_flag already set here, see mysql_prepare_table().
    */
monty@mysql.com's avatar
monty@mysql.com committed
2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126
    break;
  case FIELD_TYPE_NEWDECIMAL:
    sql_field->pack_flag=(FIELDFLAG_NUMBER |
                          (sql_field->flags & UNSIGNED_FLAG ? 0 :
                           FIELDFLAG_DECIMAL) |
                          (sql_field->flags & ZEROFILL_FLAG ?
                           FIELDFLAG_ZEROFILL : 0) |
                          (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
    break;
  case FIELD_TYPE_TIMESTAMP:
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2127
      if (!*timestamps)
2128
      {
monty@mysql.com's avatar
monty@mysql.com committed
2129
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2130
        (*timestamps_with_niladic)++;
2131
      }
monty@mysql.com's avatar
monty@mysql.com committed
2132 2133 2134 2135
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2136
      (*timestamps_with_niladic)++;
monty@mysql.com's avatar
monty@mysql.com committed
2137

2138
    (*timestamps)++;
monty@mysql.com's avatar
monty@mysql.com committed
2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153
    /* fall-through */
  default:
    sql_field->pack_flag=(FIELDFLAG_NUMBER |
                          (sql_field->flags & UNSIGNED_FLAG ? 0 :
                           FIELDFLAG_DECIMAL) |
                          (sql_field->flags & ZEROFILL_FLAG ?
                           FIELDFLAG_ZEROFILL : 0) |
                          f_settype((uint) sql_field->sql_type) |
                          (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
    break;
  }
  if (!(sql_field->flags & NOT_NULL_FLAG))
    sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
  if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
    sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
2154 2155 2156
  DBUG_RETURN(0);
}

2157
/*
2158
  Preparation for table creation
2159 2160

  SYNOPSIS
2161
    mysql_prepare_table()
2162 2163 2164 2165 2166 2167 2168 2169 2170 2171
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
      fields                    List of fields to create.
      keys                      List of keys to create.
      tmp_table                 If a temporary table is to be created.
      db_options          INOUT Table options (like HA_OPTION_PACK_RECORD).
      file                      The handler for the new table.
      key_info_buffer     OUT   An array of KEY structs for the indexes.
      key_count           OUT   The number of elements in the array.
      select_field_count        The number of fields coming from a select table.
2172

2173
  DESCRIPTION
2174
    Prepares the table and key structures for table creation.
2175

2176
  NOTES
2177
    sets create_info->varchar if the table has a varchar
2178

2179 2180 2181 2182
  RETURN VALUES
    0	ok
    -1	error
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2183

2184 2185 2186 2187 2188 2189
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
                               List<create_field> *fields,
                               List<Key> *keys, bool tmp_table,
                               uint *db_options,
                               handler *file, KEY **key_info_buffer,
                               uint *key_count, int select_field_count)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2190
{
2191
  const char	*key_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2192
  create_field	*sql_field,*dup_field;
monty@mysql.com's avatar
monty@mysql.com committed
2193
  uint		field,null_fields,blob_columns,max_key_length;
monty@mysql.com's avatar
monty@mysql.com committed
2194
  ulong		record_offset= 0;
2195
  KEY		*key_info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2196
  KEY_PART_INFO *key_part_info;
2197 2198 2199
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
2200
  List_iterator<create_field> it(*fields),it2(*fields);
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
2201
  uint total_uneven_bit_length= 0;
2202
  DBUG_ENTER("mysql_prepare_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2203

2204
  select_field_pos= fields->elements - select_field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2205
  null_fields=blob_columns=0;
2206
  create_info->varchar= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2207
  max_key_length= file->max_key_length();
2208

2209
  for (field_no=0; (sql_field=it++) ; field_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2210
  {
2211 2212
    CHARSET_INFO *save_cs;

2213 2214 2215 2216 2217 2218
    /*
      Initialize length from its original value (number of characters),
      which was set in the parser. This is necessary if we're
      executing a prepared statement for the second time.
    */
    sql_field->length= sql_field->char_length;
2219
    if (!sql_field->charset)
2220 2221 2222
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2223 2224 2225
      for all varchar/char columns.
      But the table charset must not affect the BLOB fields, so don't
      allow to change my_charset_bin to somethig else.
2226
    */
2227
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2228
      sql_field->charset= create_info->table_charset;
2229

2230
    save_cs= sql_field->charset;
2231 2232 2233 2234 2235
    if ((sql_field->flags & BINCMP_FLAG) &&
	!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
						    MY_CS_BINSORT,MYF(0))))
    {
      char tmp[64];
2236 2237
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2238 2239 2240
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
2241

2242
    /*
2243
      Convert the default value from client character
2244 2245 2246
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2247
        save_cs != sql_field->def->collation.collation &&
2248 2249 2250 2251 2252
        (sql_field->sql_type == FIELD_TYPE_VAR_STRING ||
         sql_field->sql_type == FIELD_TYPE_STRING ||
         sql_field->sql_type == FIELD_TYPE_SET ||
         sql_field->sql_type == FIELD_TYPE_ENUM))
    {
2253 2254
      Query_arena backup_arena;
      bool need_to_change_arena= !thd->stmt_arena->is_conventional();
2255 2256
      if (need_to_change_arena)
      {
2257 2258 2259 2260
        /* Asser that we don't do that at every PS execute */
        DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() ||
                    thd->stmt_arena->is_first_sp_execute());
        thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena);
2261 2262
      }

2263
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2264 2265

      if (need_to_change_arena)
2266
        thd->restore_active_arena(thd->stmt_arena, &backup_arena);
2267 2268 2269 2270 2271 2272 2273 2274 2275

      if (sql_field->def == NULL)
      {
        /* Could not convert */
        my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
        DBUG_RETURN(-1);
      }
    }

2276 2277
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
2278 2279 2280
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2281
      TYPELIB *interval= sql_field->interval;
2282 2283 2284 2285 2286 2287

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2288
      if (!interval)
2289
      {
2290 2291 2292 2293
        /*
          Create the typelib in prepared statement memory if we're
          executing one.
        */
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2294
        MEM_ROOT *stmt_root= thd->stmt_arena->mem_root;
2295 2296 2297

        interval= sql_field->interval= typelib(stmt_root,
                                               sql_field->interval_list);
2298 2299
        List_iterator<String> it(sql_field->interval_list);
        String conv, *tmp;
2300 2301 2302 2303 2304
        char comma_buf[2];
        int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf,
                                          (uchar*) comma_buf + 
                                          sizeof(comma_buf));
        DBUG_ASSERT(comma_length > 0);
2305
        for (uint i= 0; (tmp= it++); i++)
2306
        {
2307
          uint lengthsp;
2308 2309 2310 2311 2312
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2313
            interval->type_names[i]= strmake_root(stmt_root, conv.ptr(),
2314
                                                  conv.length());
2315 2316
            interval->type_lengths[i]= conv.length();
          }
2317

2318
          // Strip trailing spaces.
2319 2320
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2321 2322
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2323 2324 2325 2326 2327 2328
          if (sql_field->sql_type == FIELD_TYPE_SET)
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2329
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2330 2331 2332
              DBUG_RETURN(-1);
            }
          }
2333
        }
2334
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2335 2336 2337 2338
      }

      if (sql_field->sql_type == FIELD_TYPE_SET)
      {
2339
        uint32 field_length;
2340
        if (sql_field->def != NULL)
2341 2342 2343 2344 2345
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363
          if (def == NULL) /* SQL "NULL" maps to NULL */
          {
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }

            /* else, NULL is an allowed value */
            (void) find_set(interval, NULL, 0,
                            cs, &not_used, &not_used2, &not_found);
          }
          else /* not NULL */
          {
            (void) find_set(interval, def->ptr(), def->length(),
                            cs, &not_used, &not_used2, &not_found);
          }

2364 2365 2366 2367 2368 2369
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
2370 2371
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2372 2373 2374
      }
      else  /* FIELD_TYPE_ENUM */
      {
2375
        uint32 field_length;
2376 2377
        DBUG_ASSERT(sql_field->sql_type == FIELD_TYPE_ENUM);
        if (sql_field->def != NULL)
2378 2379
        {
          String str, *def= sql_field->def->val_str(&str);
2380
          if (def == NULL) /* SQL "NULL" maps to NULL */
2381
          {
2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }

            /* else, the defaults yield the correct length for NULLs. */
          } 
          else /* not NULL */
          {
            def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
            if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }
2398 2399
          }
        }
2400 2401
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2402 2403 2404 2405
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2406 2407
    if (sql_field->sql_type == FIELD_TYPE_BIT)
    { 
ramil@mysql.com's avatar
ramil@mysql.com committed
2408
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2409
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2410 2411 2412 2413 2414
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2415
    sql_field->create_length_to_internal_length();
2416 2417
    if (prepare_blob_field(thd, sql_field))
      DBUG_RETURN(-1);
2418

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2419 2420
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
2421

2422 2423
    if (check_column_name(sql_field->field_name))
    {
2424
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2425 2426
      DBUG_RETURN(-1);
    }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2427

2428 2429
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2430
    {
2431
      if (my_strcasecmp(system_charset_info,
2432 2433
			sql_field->field_name,
			dup_field->field_name) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2434
      {
2435 2436 2437 2438
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2439 2440
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2441
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2442 2443 2444 2445
	  DBUG_RETURN(-1);
	}
	else
	{
2446
	  /* Field redefined */
2447
	  sql_field->def=		dup_field->def;
2448
	  sql_field->sql_type=		dup_field->sql_type;
2449 2450 2451
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2452
	  sql_field->length=		dup_field->char_length;
2453
          sql_field->pack_length=	dup_field->pack_length;
2454
          sql_field->key_length=	dup_field->key_length;
2455
	  sql_field->create_length_to_internal_length();
2456 2457
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->unireg_check=	dup_field->unireg_check;
2458 2459 2460 2461 2462 2463 2464 2465
          /* 
            We're making one field from two, the result field will have
            dup_field->flags as flags. If we've incremented null_fields
            because of sql_field->flags, decrement it back.
          */
          if (!(sql_field->flags & NOT_NULL_FLAG))
            null_fields--;
	  sql_field->flags=		dup_field->flags;
andrey@lmy004's avatar
andrey@lmy004 committed
2466
          sql_field->interval=          dup_field->interval;
2467 2468 2469
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2470
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2471 2472
      }
    }
2473 2474 2475 2476
    /* Don't pack rows in old tables if the user has requested this */
    if ((sql_field->flags & BLOB_FLAG) ||
	sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED)
2477
      (*db_options)|= HA_OPTION_PACK_RECORD;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2478 2479
    it2.rewind();
  }
2480 2481 2482

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2483
  null_fields+= total_uneven_bit_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2484 2485 2486 2487

  it.rewind();
  while ((sql_field=it++))
  {
2488
    DBUG_ASSERT(sql_field->charset != 0);
2489

monty@mysql.com's avatar
monty@mysql.com committed
2490 2491
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2492
			     file->ha_table_flags()))
hf@deer.(none)'s avatar
hf@deer.(none) committed
2493
      DBUG_RETURN(-1);
2494
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2495
      create_info->varchar= 1;
2496
    sql_field->offset= record_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2497 2498
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2499
    record_offset+= sql_field->pack_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2500
  }
2501 2502
  if (timestamps_with_niladic > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2503 2504
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2505 2506
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2507 2508
  if (auto_increment > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2509
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2510 2511 2512
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
2513
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2514
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2515 2516
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2517 2518 2519
    DBUG_RETURN(-1);
  }

2520
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2521
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2522 2523
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2524 2525 2526 2527
    DBUG_RETURN(-1);
  }

  /* Create keys */
2528

2529
  List_iterator<Key> key_iterator(*keys), key_iterator2(*keys);
2530
  uint key_parts=0, fk_key_count=0;
2531
  bool primary_key=0,unique_key=0;
2532
  Key *key, *key2;
2533
  uint tmp, key_number;
2534 2535
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2536

2537
  /* Calculate number of key segements */
2538
  *key_count= 0;
2539

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2540 2541
  while ((key=key_iterator++))
  {
2542 2543
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2544 2545 2546 2547 2548 2549 2550
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2551 2552 2553
        my_error(ER_WRONG_FK_DEF, MYF(0),
                 (fk_key->name ?  fk_key->name : "foreign key without name"),
                 ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
2554 2555 2556 2557
	DBUG_RETURN(-1);
      }
      continue;
    }
2558
    (*key_count)++;
2559
    tmp=file->max_key_parts();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2560 2561 2562 2563 2564
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
2565
    if (key->name && strlen(key->name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2566
    {
2567
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2568 2569
      DBUG_RETURN(-1);
    }
2570
    key_iterator2.rewind ();
2571
    if (key->type != Key::FOREIGN_KEY)
2572
    {
2573
      while ((key2 = key_iterator2++) != key)
2574
      {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2575
	/*
2576 2577 2578
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2579
        */
2580 2581 2582
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2583
        {
2584
          /* TODO: issue warning message */
2585 2586 2587 2588 2589 2590 2591
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2592 2593 2594
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2595 2596 2597
          }
          break;
        }
2598 2599 2600 2601 2602 2603
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2604
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2605 2606 2607 2608 2609
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
2610
  }
2611
  tmp=file->max_keys();
2612
  if (*key_count > tmp)
2613 2614 2615 2616
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
2617

2618
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
2619
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
2620
  if (!*key_info_buffer || ! key_part_info)
2621 2622
    DBUG_RETURN(-1);				// Out of memory

2623
  key_iterator.rewind();
2624
  key_number=0;
2625
  for (; (key=key_iterator++) ; key_number++)
2626 2627 2628 2629
  {
    uint key_length=0;
    key_part_spec *column;

2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

2640
    switch (key->type) {
2641
    case Key::MULTIPLE:
2642
	key_info->flags= 0;
2643
	break;
2644
    case Key::FULLTEXT:
2645
	key_info->flags= HA_FULLTEXT;
2646
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
2647
          key_info->flags|= HA_USES_PARSER;
2648 2649
        else
          key_info->parser_name= 0;
2650
	break;
2651
    case Key::SPATIAL:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2652
#ifdef HAVE_SPATIAL
2653
	key_info->flags= HA_SPATIAL;
2654
	break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2655
#else
2656 2657
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2658 2659
	DBUG_RETURN(-1);
#endif
2660 2661 2662 2663
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
2664 2665
      key_info->flags = HA_NOSAME;
      break;
2666
    }
2667 2668
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
2669

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2670 2671
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
2672
    key_info->usable_key_parts= key_number;
2673
    key_info->algorithm= key->key_create_info.algorithm;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2674

2675 2676
    if (key->type == Key::FULLTEXT)
    {
2677
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
2678
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2679 2680
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
2681
	DBUG_RETURN(-1);
2682 2683
      }
    }
2684 2685 2686
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
2687
       actually should be replaced by special GEOM type
2688 2689 2690
       in near future when new frm file is ready
       checking for proper key parts number:
    */
2691

2692
    /* TODO: Add proper checks if handler supports key_type and algorithm */
2693
    if (key_info->flags & HA_SPATIAL)
2694
    {
2695
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
2696 2697 2698 2699 2700
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
        DBUG_RETURN(-1);
      }
2701 2702
      if (key_info->key_parts != 1)
      {
2703
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
2704
	DBUG_RETURN(-1);
2705
      }
2706
    }
2707
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
2708
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2709
#ifdef HAVE_RTREE_KEYS
2710 2711
      if ((key_info->key_parts & 1) == 1)
      {
2712
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
2713
	DBUG_RETURN(-1);
2714
      }
2715
      /* TODO: To be deleted */
2716
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
2717
      DBUG_RETURN(-1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2718
#else
2719 2720
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2721 2722
      DBUG_RETURN(-1);
#endif
2723
    }
2724

2725 2726 2727 2728 2729
    /* Take block size from key part or table part */
    /*
      TODO: Add warning if block size changes. We can't do it here, as
      this may depend on the size of the key
    */
2730 2731
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2732 2733 2734 2735 2736
                           create_info->key_block_size);

    if (key_info->block_size)
      key_info->flags|= HA_USES_BLOCK_SIZE;

2737
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
2738
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2739 2740
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2741
      uint length;
2742 2743
      key_part_spec *dup_column;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2744 2745 2746
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
2747
	     my_strcasecmp(system_charset_info,
2748 2749
			   column->field_name,
			   sql_field->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2750 2751 2752
	field++;
      if (!sql_field)
      {
2753
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2754 2755
	DBUG_RETURN(-1);
      }
2756
      while ((dup_column= cols2++) != column)
2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767
      {
        if (!my_strcasecmp(system_charset_info,
	     	           column->field_name, dup_column->field_name))
	{
	  my_printf_error(ER_DUP_FIELDNAME,
			  ER(ER_DUP_FIELDNAME),MYF(0),
			  column->field_name);
	  DBUG_RETURN(-1);
	}
      }
      cols2.rewind();
2768
      if (key->type == Key::FULLTEXT)
2769
      {
2770 2771
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
2772 2773
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
2774
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
2775 2776
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
2777
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788
	    DBUG_RETURN(-1);
	}
	ft_key_charset=sql_field->charset;
	/*
	  for fulltext keys keyseg length is 1 for blobs (it's ignored in ft
	  code anyway, and 0 (set to column width later) for char's. it has
	  to be correct col width for char's, as char data are not prefixed
	  with length (unlike blobs, where ft code takes data length from a
	  data prefix, ignoring column->length).
	*/
	column->length=test(f_is_blob(sql_field->pack_flag));
2789
      }
2790
      else
2791
      {
2792 2793
	column->length*= sql_field->charset->mbmaxlen;

2794 2795
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
2796
	{
2797
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
2798
	  {
2799
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
2800 2801
	    DBUG_RETURN(-1);
	  }
2802 2803 2804
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
            column->length= 21;
2805 2806
	  if (!column->length)
	  {
2807
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
2808 2809 2810
	    DBUG_RETURN(-1);
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
2811
#ifdef HAVE_SPATIAL
2812
	if (key->type == Key::SPATIAL)
2813
	{
2814
	  if (!column->length)
2815 2816
	  {
	    /*
2817 2818
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
2819
	    */
2820
	    column->length= 4*sizeof(double);
2821 2822
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
2823
#endif
2824 2825 2826 2827 2828 2829 2830
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
monty@mysql.com's avatar
monty@mysql.com committed
2831
            null_fields--;
2832 2833
	  }
	  else
2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847
          {
            key_info->flags|= HA_NULL_PART_KEY;
            if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
            {
              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
              DBUG_RETURN(-1);
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
              DBUG_RETURN(-1);
            }
          }
2848 2849 2850
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
2851
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
2852 2853
	    auto_increment--;			// Field is used
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2854
      }
2855

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2856 2857 2858
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
2859 2860
      length= sql_field->key_length;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2861 2862 2863 2864
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
monty@mysql.com's avatar
monty@mysql.com committed
2865
	  if ((length=column->length) > max_key_length ||
2866
	      length > file->max_key_part_length())
2867
	  {
monty@mysql.com's avatar
monty@mysql.com committed
2868
	    length=min(max_key_length, file->max_key_part_length());
2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883
	    if (key->type == Key::MULTIPLE)
	    {
	      /* not a critical problem */
	      char warn_buff[MYSQL_ERRMSG_SIZE];
	      my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
			  length);
	      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			   ER_TOO_LONG_KEY, warn_buff);
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
	      DBUG_RETURN(-1);
	    }
	  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2884
	}
2885
	else if (!f_is_geom(sql_field->pack_flag) &&
2886 2887
		  (column->length > length ||
		   ((f_is_packed(sql_field->pack_flag) ||
2888
		     ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
2889 2890 2891
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2892
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
2893 2894
	  DBUG_RETURN(-1);
	}
2895
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
2896
	  length=column->length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2897 2898 2899
      }
      else if (length == 0)
      {
2900
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2901 2902
	  DBUG_RETURN(-1);
      }
2903
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
2904
      {
2905 2906 2907
        length= file->max_key_part_length();
        /* Align key length to multibyte char boundary */
        length-= length % sql_field->charset->mbmaxlen;
2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921
	if (key->type == Key::MULTIPLE)
	{
	  /* not a critical problem */
	  char warn_buff[MYSQL_ERRMSG_SIZE];
	  my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
		      length);
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
		       ER_TOO_LONG_KEY, warn_buff);
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
	  DBUG_RETURN(-1);
	}
2922 2923
      }
      key_part_info->length=(uint16) length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2924
      /* Use packed keys for long strings on the first column */
2925
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2926
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
2927 2928
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2929 2930
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
2931 2932 2933
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB) ||
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2934 2935 2936 2937 2938 2939 2940 2941 2942 2943
	else
	  key_info->flags|= HA_PACK_KEY;
      }
      key_length+=length;
      key_part_info++;

      /* Create the key name based on the first column (if not given) */
      if (column_nr == 0)
      {
	if (key->type == Key::PRIMARY)
2944 2945 2946
	{
	  if (primary_key)
	  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2947 2948
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
2949 2950 2951 2952 2953
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
2954
	else if (!(key_name = key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2955
	  key_name=make_unique_key_name(sql_field->field_name,
2956 2957
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2958
	{
2959
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2960 2961 2962 2963 2964
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
      }
    }
2965 2966
    if (!key_info->name || check_column_name(key_info->name))
    {
2967
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
2968 2969
      DBUG_RETURN(-1);
    }
2970 2971
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2972
    key_info->key_length=(uint16) key_length;
2973
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2974
    {
2975
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2976 2977
      DBUG_RETURN(-1);
    }
2978
    key_info++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2979
  }
2980
  if (!unique_key && !primary_key &&
2981
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
2982
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2983
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
2984 2985
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2986 2987
  if (auto_increment > 0)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2988
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2989 2990
    DBUG_RETURN(-1);
  }
2991
  /* Sort keys in optimized order */
2992
  qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
2993
	(qsort_cmp) sort_keys);
monty@mysql.com's avatar
monty@mysql.com committed
2994
  create_info->null_bits= null_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2995

2996 2997 2998
  DBUG_RETURN(0);
}

2999

3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015
/*
  Set table default charset, if not set

  SYNOPSIS
    set_table_default_charset()
    create_info        Table create information

  DESCRIPTION
    If the table character set was not given explicitely,
    let's fetch the database default character set and
    apply it to the table.
*/

static void set_table_default_charset(THD *thd,
				      HA_CREATE_INFO *create_info, char *db)
{
3016 3017 3018 3019 3020
  /*
    If the table character set was not given explicitly,
    let's fetch the database default character set and
    apply it to the table.
  */
3021 3022 3023
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3024 3025 3026

    load_db_opt_by_name(thd, db, &db_info);

3027 3028 3029 3030 3031
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054
/*
  Extend long VARCHAR fields to blob & prepare field if it's a blob

  SYNOPSIS
    prepare_blob_field()
    sql_field		Field to check

  RETURN
    0	ok
    1	Error (sql_field can't be converted to blob)
        In this case the error is given
*/

static bool prepare_blob_field(THD *thd, create_field *sql_field)
{
  DBUG_ENTER("prepare_blob_field");

  if (sql_field->length > MAX_FIELD_VARCHARLENGTH &&
      !(sql_field->flags & BLOB_FLAG))
  {
    /* Convert long VARCHAR columns to TEXT or BLOB */
    char warn_buff[MYSQL_ERRMSG_SIZE];

3055 3056
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
3057 3058 3059 3060 3061 3062 3063 3064
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
    sql_field->sql_type= FIELD_TYPE_BLOB;
    sql_field->flags|= BLOB_FLAG;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
3065
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
    
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
    if (sql_field->sql_type == FIELD_TYPE_BLOB)
    {
      /* The user has given a length to the blob column */
      sql_field->sql_type= get_blob_type_from_length(sql_field->length);
      sql_field->pack_length= calc_pack_length(sql_field->sql_type, 0);
    }
    sql_field->length= 0;
  }
  DBUG_RETURN(0);
}


3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128
/*
  Preparation of create_field for SP function return values.
  Based on code used in the inner loop of mysql_prepare_table() above

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

void sp_prepare_create_field(THD *thd, create_field *sql_field)
{
  if (sql_field->sql_type == FIELD_TYPE_SET ||
      sql_field->sql_type == FIELD_TYPE_ENUM)
  {
    uint32 field_length, dummy;
    if (sql_field->sql_type == FIELD_TYPE_SET)
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
    else /* FIELD_TYPE_ENUM */
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval,
                                 &field_length, &dummy);
      sql_field->length= field_length;
    }
    set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
  }

  if (sql_field->sql_type == FIELD_TYPE_BIT)
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
3129 3130 3131 3132
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3133 3134


3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159
/*
  Copy HA_CREATE_INFO struct
  SYNOPSIS
    copy_create_info()
    lex_create_info         The create_info struct setup by parser
  RETURN VALUES
    > 0                     A pointer to a copy of the lex_create_info
    0                       Memory allocation error
  DESCRIPTION
  Allocate memory for copy of HA_CREATE_INFO structure from parser
  to ensure we can reuse the parser struct in stored procedures
  and prepared statements.
*/

static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
{
  HA_CREATE_INFO *create_info;
  if (!(create_info= (HA_CREATE_INFO*)sql_alloc(sizeof(HA_CREATE_INFO))))
    mem_alloc_error(sizeof(HA_CREATE_INFO));
  else
    memcpy((void*)create_info, (void*)lex_create_info, sizeof(HA_CREATE_INFO));
  return create_info;
}


3160 3161 3162 3163
/*
  Create a table

  SYNOPSIS
3164
    mysql_create_table_internal()
3165 3166 3167
    thd			Thread object
    db			Database
    table_name		Table name
3168
    lex_create_info	Create information (like MAX_ROWS)
3169 3170
    fields		List of fields to create
    keys		List of keys to create
3171
    internal_tmp_table  Set to 1 if this is an internal temporary table
3172
			(From ALTER TABLE)
3173 3174 3175 3176
    select_field_count  
    use_copy_create_info Should we make a copy of create info (we do this
                         when this is called from sql_parse.cc where we
                         want to ensure lex object isn't manipulated.
3177 3178

  DESCRIPTION
3179
    If one creates a temporary table, this is automatically opened
3180 3181 3182 3183 3184 3185 3186

    no_log is needed for the case of CREATE ... SELECT,
    as the logging will be done later in sql_insert.cc
    select_field_count is also used for CREATE ... SELECT,
    and must be zero for standard create of table.

  RETURN VALUES
3187 3188
    FALSE OK
    TRUE  error
3189 3190
*/

3191 3192
bool mysql_create_table_internal(THD *thd,
                                const char *db, const char *table_name,
3193
                                HA_CREATE_INFO *lex_create_info,
3194 3195
                                List<create_field> &fields,
                                List<Key> &keys,bool internal_tmp_table,
3196 3197
                                uint select_field_count,
                                bool use_copy_create_info)
3198
{
3199
  char		path[FN_REFLEN];
3200
  uint          path_length;
3201 3202 3203
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
3204
  HA_CREATE_INFO *create_info;
3205
  handler	*file;
3206
  bool		error= TRUE;
3207
  DBUG_ENTER("mysql_create_table_internal");
3208 3209
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3210

3211
  if (use_copy_create_info)
3212
  {
3213 3214 3215 3216
    if (!(create_info= copy_create_info(lex_create_info)))
    {
      DBUG_RETURN(TRUE);
    }
3217
  }
3218 3219 3220
  else
    create_info= lex_create_info;
 
3221 3222 3223
  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3224 3225
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
3226
    DBUG_RETURN(TRUE);
3227
  }
3228
  if (check_engine(thd, table_name, create_info))
3229
    DBUG_RETURN(TRUE);
3230
  db_options= create_info->table_options;
3231 3232 3233
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3234 3235
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3236
  {
3237
    mem_alloc_error(sizeof(handler));
3238 3239
    DBUG_RETURN(TRUE);
  }
3240
#ifdef WITH_PARTITION_STORAGE_ENGINE
3241 3242
  partition_info *part_info= thd->work_part_info;

3243 3244 3245 3246 3247 3248 3249 3250
  if (!part_info && create_info->db_type->partition_flags &&
      (create_info->db_type->partition_flags() & HA_USE_AUTO_PARTITION))
  {
    /*
      Table is not defined as a partitioned table but the engine handles
      all tables as partitioned. The handler will set up the partition info
      object with the default settings.
    */
3251
    thd->work_part_info= part_info= new partition_info();
3252 3253 3254 3255 3256 3257
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
3258
    part_info->default_engine_type= create_info->db_type;
3259
    part_info->is_auto_partitioned= TRUE;
3260
  }
3261 3262 3263
  if (part_info)
  {
    /*
3264 3265 3266 3267 3268 3269 3270 3271 3272
      The table has been specified as a partitioned table.
      If this is part of an ALTER TABLE the handler will be the partition
      handler but we need to specify the default handler to use for
      partitions also in the call to check_partition_info. We transport
      this information in the default_db_type variable, it is either
      DB_TYPE_DEFAULT or the engine set in the ALTER TABLE command.

      Check that we don't use foreign keys in the table since it won't
      work even with InnoDB beneath it.
3273
    */
3274 3275
    List_iterator<Key> key_iterator(keys);
    Key *key;
3276
    handlerton *part_engine_type= create_info->db_type;
3277 3278
    char *part_syntax_buf;
    uint syntax_len;
3279
    handlerton *engine_type;
3280 3281 3282 3283 3284
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
3285 3286
    while ((key= key_iterator++))
    {
3287 3288
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
3289 3290 3291 3292 3293
      {
        my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
        goto err;
      }
    }
3294
    if ((part_engine_type == partition_hton) &&
3295
        part_info->default_engine_type)
3296 3297 3298 3299 3300 3301
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
3302
      ;
3303
    }
3304 3305
    else
    {
3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317
      if (create_info->used_fields & HA_CREATE_USED_ENGINE)
      {
        part_info->default_engine_type= create_info->db_type;
      }
      else
      {
        if (part_info->default_engine_type == NULL)
        {
          part_info->default_engine_type= ha_checktype(thd,
                                          DB_TYPE_DEFAULT, 0, 0);
        }
      }
3318
    }
3319 3320
    DBUG_PRINT("info", ("db_type = %d",
                         ha_legacy_type(part_info->default_engine_type)));
3321
    if (part_info->check_partition_info(thd, &engine_type, file,
3322
                                        create_info, TRUE))
3323
      goto err;
3324
    part_info->default_engine_type= engine_type;
3325

3326 3327 3328 3329 3330 3331
    /*
      We reverse the partitioning parser and generate a standard format
      for syntax stored in frm file.
    */
    if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                     &syntax_len,
3332
                                                     TRUE, TRUE)))
3333
      goto err;
3334 3335
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
3336 3337
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3338
        create_info->db_type == partition_hton)
3339 3340 3341 3342 3343
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
3344 3345
      DBUG_PRINT("info", ("db_type: %d",
                          ha_legacy_type(create_info->db_type)));
3346
      delete file;
3347
      create_info->db_type= partition_hton;
3348 3349 3350 3351
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3352 3353 3354 3355 3356 3357 3358 3359
      /*
        If we have default number of partitions or subpartitions we
        might require to set-up the part_info object such that it
        creates a proper .par file. The current part_info object is
        only used to create the frm-file and .par-file.
      */
      if (part_info->use_default_no_partitions &&
          part_info->no_parts &&
3360 3361
          (int)part_info->no_parts !=
          file->get_default_no_partitions(create_info))
3362
      {
3363
        uint i;
3364
        List_iterator<partition_element> part_it(part_info->partitions);
3365 3366 3367 3368
        part_it++;
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
        for (i= 1; i < part_info->partitions.elements; i++)
          (part_it++)->part_state= PART_TO_BE_DROPPED;
3369 3370 3371 3372
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3373
               (int)part_info->no_subparts !=
3374
                 file->get_default_no_partitions(create_info))
3375
      {
3376
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3377
        part_info->no_subparts= file->get_default_no_partitions(create_info);
3378 3379 3380 3381
      }
    }
    else if (create_info->db_type != engine_type)
    {
3382 3383 3384 3385 3386 3387
      /*
        We come here when we don't use a partitioned handler.
        Since we use a partitioned table it must be "native partitioned".
        We have switched engine from defaults, most likely only specified
        engines in partition clauses.
      */
3388
      delete file;
3389 3390
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3391 3392 3393 3394
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3395 3396 3397
    }
  }
#endif
3398

3399
  set_table_default_charset(thd, create_info, (char*) db);
3400

3401 3402 3403
  if (mysql_prepare_table(thd, create_info, &fields,
			  &keys, internal_tmp_table, &db_options, file,
			  &key_info_buffer, &key_count,
3404
			  select_field_count))
3405
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3406 3407 3408 3409

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3410
    path_length= build_tmptable_filename(thd, path, sizeof(path));
3411
    if (lower_case_table_names)
3412
      my_casedn_str(files_charset_info, path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3413 3414
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3415 3416
  else  
  {
3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429
 #ifdef FN_DEVCHAR
    /* check if the table name contains FN_DEVCHAR when defined */
    const char *start= alias;
    while (*start != '\0')
    {
      if (*start == FN_DEVCHAR)
      {
        my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
        DBUG_RETURN(TRUE);
      }
      start++;
    }	  
#endif
3430 3431
    path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
                                      internal_tmp_table ? FN_IS_TMP : 0);
3432
  }
3433

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3434
  /* Check if table already exists */
3435 3436
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3437
  {
3438
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3439 3440
    {
      create_info->table_existed= 1;		// Mark that table existed
3441 3442 3443
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
3444 3445
      error= 0;
      goto err;
3446
    }
monty@mysql.com's avatar
monty@mysql.com committed
3447
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3448
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3449
  }
3450

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3451
  VOID(pthread_mutex_lock(&LOCK_open));
3452
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3453 3454 3455 3456
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3457 3458
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3459
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3460
    }
3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473
    /*
      We don't assert here, but check the result, because the table could be
      in the table definition cache and in the same time the .frm could be
      missing from the disk, in case of manual intervention which deletes
      the .frm file. The user has to use FLUSH TABLES; to clear the cache.
      Then she could create the table. This case is pretty obscure and
      therefore we don't introduce a new error message only for it.
    */
    if (get_cached_table_share(db, alias))
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3474 3475
  }

3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488
  /*
    Check that table with given name does not already
    exist in any storage engine. In such a case it should
    be discovered and the error ER_TABLE_EXISTS_ERROR be returned
    unless user specified CREATE TABLE IF EXISTS
    The LOCK_open mutex has been locked to make sure no
    one else is attempting to discover the table. Since
    it's not on disk as a frm file, no one could be using it!
  */
  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    bool create_if_not_exists =
      create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS;
3489
    if (ha_table_exists_in_engine(thd, db, table_name))
3490
    {
3491
      DBUG_PRINT("info", ("Table with same name already existed in handler"));
3492 3493

      if (create_if_not_exists)
3494 3495
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3496
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3497 3498 3499 3500
    }
  }

  thd->proc_info="creating table";
3501
  create_info->table_existed= 0;		// Mark that table is created
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3502

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3503
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
3504
    create_info->data_file_name= create_info->index_file_name= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3505
  create_info->table_options=db_options;
3506

3507
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3508 3509
  if (rea_create_table(thd, path, db, table_name, create_info, fields,
                       key_count, key_info_buffer, file))
3510
    goto unlock_and_end;
3511

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3512 3513 3514 3515 3516 3517
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    /* Open table and put in temporary table list */
    if (!(open_temporary_table(thd, path, db, table_name, 1)))
    {
      (void) rm_temporary_table(create_info->db_type, path);
3518
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3519
    }
3520
    thd->tmp_table_used= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3521
  }
monty@mysql.com's avatar
monty@mysql.com committed
3522

3523 3524 3525 3526 3527
  /*
    Don't write statement if:
    - It is an internal temporary table,
    - Row-based logging is used and it we are creating a temporary table, or
    - The binary log is not open.
3528
    Otherwise, the statement shall be binlogged.
3529 3530
   */
  if (!internal_tmp_table &&
3531 3532
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
3533
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3534
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
3535
  error= FALSE;
3536
unlock_and_end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3537
  VOID(pthread_mutex_unlock(&LOCK_open));
3538 3539

err:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3540
  thd->proc_info="After create";
3541
  delete file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3542
  DBUG_RETURN(error);
3543 3544

warn:
3545
  error= FALSE;
3546 3547 3548 3549
  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                      ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                      alias);
  create_info->table_existed= 1;		// Mark that table existed
3550
  goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3551 3552
}

3553 3554 3555 3556 3557 3558 3559 3560 3561

/*
  Database locking aware wrapper for mysql_create_table_internal(),
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
                        List<create_field> &fields,
                        List<Key> &keys,bool internal_tmp_table,
3562 3563
                        uint select_field_count,
                        bool use_copy_create_info)
3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586
{
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
         hash_search(&lock_db_cache,(byte*) db, strlen(db)))
  {
    wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
    pthread_mutex_lock(&LOCK_lock_db);
  }

  if (thd->killed)
  {
    pthread_mutex_unlock(&LOCK_lock_db);
    DBUG_RETURN(TRUE);
  }
  creating_table++;
  pthread_mutex_unlock(&LOCK_lock_db);

  result= mysql_create_table_internal(thd, db, table_name, create_info,
                                      fields, keys, internal_tmp_table,
3587 3588
                                      select_field_count,
                                      use_copy_create_info);
3589 3590 3591 3592 3593 3594 3595 3596 3597

  pthread_mutex_lock(&LOCK_lock_db);
  if (!--creating_table && creating_database)
    pthread_cond_signal(&COND_refresh);
  pthread_mutex_unlock(&LOCK_lock_db);
  DBUG_RETURN(result);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3598 3599 3600 3601 3602 3603 3604 3605
/*
** Give the key name after the first field with an optional '_#' after
**/

static bool
check_if_keyname_exists(const char *name, KEY *start, KEY *end)
{
  for (KEY *key=start ; key != end ; key++)
3606
    if (!my_strcasecmp(system_charset_info,name,key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3607 3608 3609 3610 3611 3612 3613 3614 3615 3616
      return 1;
  return 0;
}


static char *
make_unique_key_name(const char *field_name,KEY *start,KEY *end)
{
  char buff[MAX_FIELD_NAME],*buff_end;

3617 3618
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3619
    return (char*) field_name;			// Use fieldname
3620 3621 3622 3623 3624 3625
  buff_end=strmake(buff,field_name, sizeof(buff)-4);

  /*
    Only 3 chars + '\0' left, so need to limit to 2 digit
    This is ok as we can't have more than 100 keys anyway
  */
3626
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3627
  {
3628 3629
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3630 3631 3632
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3633
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3634 3635
}

3636

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3637 3638 3639 3640
/****************************************************************************
** Alter a table definition
****************************************************************************/

3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660

/*
  Rename a table.

  SYNOPSIS
    mysql_rename_table()
      base                      The handlerton handle.
      old_db                    The old database name.
      old_name                  The old table name.
      new_db                    The new database name.
      new_name                  The new table name.
      flags                     flags for build_table_filename().
                                FN_FROM_IS_TMP old_name is temporary.
                                FN_TO_IS_TMP   new_name is temporary.

  RETURN
    0           OK
    != 0        Error
*/

3661
bool
3662 3663 3664
mysql_rename_table(handlerton *base, const char *old_db,
                   const char *old_name, const char *new_db,
                   const char *new_name, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3665
{
3666
  THD *thd= current_thd;
3667 3668 3669
  char from[FN_REFLEN], to[FN_REFLEN], lc_from[FN_REFLEN], lc_to[FN_REFLEN];
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
3670
  handler *file;
3671
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3672
  DBUG_ENTER("mysql_rename_table");
3673 3674
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
3675

3676
  file= (base == NULL ? 0 :
3677 3678
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

3679 3680 3681 3682
  build_table_filename(from, sizeof(from), old_db, old_name, "",
                       flags & FN_FROM_IS_TMP);
  build_table_filename(to, sizeof(to), new_db, new_name, "",
                       flags & FN_TO_IS_TMP);
3683 3684 3685 3686 3687 3688

  /*
    If lower_case_table_names == 2 (case-preserving but case-insensitive
    file system) and the storage is not HA_FILE_BASED, we need to provide
    a lowercase file name, but we leave the .frm in mixed case.
   */
3689
  if (lower_case_table_names == 2 && file &&
3690
      !(file->ha_table_flags() & HA_FILE_BASED))
3691
  {
3692 3693
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
3694 3695
    build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "",
                         flags & FN_FROM_IS_TMP);
3696
    from_base= lc_from;
3697

3698 3699
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
3700 3701
    build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "",
                         flags & FN_TO_IS_TMP);
3702
    to_base= lc_to;
3703 3704
  }

3705
  if (!file || !(error=file->rename_table(from_base, to_base)))
3706 3707 3708
  {
    if (rename_file_ext(from,to,reg_ext))
    {
3709
      error=my_errno;
3710
      /* Restore old file name */
3711
      if (file)
3712
        file->rename_table(to_base, from_base);
3713 3714
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3715
  delete file;
3716 3717 3718
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
3719 3720
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3721 3722
}

3723

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3724
/*
3725 3726 3727 3728 3729 3730
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
3731
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
3732
			HA_EXTRA_FORCE_REOPEN if table is not be used
3733 3734 3735 3736 3737 3738 3739
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3740 3741
*/

3742 3743
static void wait_while_table_is_used(THD *thd,TABLE *table,
				     enum ha_extra_function function)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3744
{
3745
  DBUG_ENTER("wait_while_table_is_used");
3746 3747 3748
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %u",
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3749

3750
  VOID(table->file->extra(function));
3751
  /* Mark all tables that are in use as 'old' */
3752
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
3753 3754

  /* Wait until all there are no other threads that has this table open */
3755 3756 3757
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
3758 3759
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3760

3761 3762
/*
  Close a cached table
3763

3764
  SYNOPSIS
3765
    close_cached_table()
3766 3767 3768 3769 3770 3771
    thd			Thread handler
    table		Table to remove from cache

  NOTES
    Function ends by signaling threads waiting for the table to try to
    reopen the table.
3772

3773 3774 3775 3776
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
3777

3778
void close_cached_table(THD *thd, TABLE *table)
3779 3780
{
  DBUG_ENTER("close_cached_table");
3781

3782
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
3783 3784
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
3785
  {
3786 3787
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3788
  }
3789 3790 3791 3792
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
  thd->open_tables=unlink_open_table(thd,thd->open_tables,table);

  /* When lock on LOCK_open is freed other threads can continue */
3793
  broadcast_refresh();
monty@mysql.com's avatar
monty@mysql.com committed
3794
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3795 3796
}

3797
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
3798
			     const char* operator_name, const char* errmsg)
3799

3800
{
3801 3802
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3803 3804
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
3805
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3806
  protocol->store(errmsg, system_charset_info);
3807
  thd->clear_error();
3808
  if (protocol->write())
3809 3810 3811 3812
    return -1;
  return 1;
}

3813

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3814
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
3815
			       HA_CHECK_OPT *check_opt)
3816
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3817
  DBUG_ENTER("prepare_for_restore");
3818

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3819 3820 3821 3822 3823 3824
  if (table->table) // do not overwrite existing tables on restore
  {
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				  "table exists, will not overwrite on restore"
				  ));
  }
3825
  else
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3826
  {
3827
    char* backup_dir= thd->lex->backup_dir;
3828
    char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
3829 3830
    char* table_name= table->table_name;
    char* db= table->db;
3831

3832 3833 3834
    VOID(tablename_to_filename(table->table_name, uname, sizeof(uname)));

    if (fn_format_relative_to_data_home(src_path, uname, backup_dir, reg_ext))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3835
      DBUG_RETURN(-1); // protect buffer overflow
3836

3837 3838
    build_table_filename(dst_path, sizeof(dst_path),
                         db, table_name, reg_ext, 0);
3839

3840
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3841
      DBUG_RETURN(-1);
3842

3843
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3844
    {
3845
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3846
      unlock_table_name(thd, table);
3847
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3848 3849 3850
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
3851
    if (mysql_truncate(thd, table, 1))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3852
    {
3853
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3854
      unlock_table_name(thd, table);
3855
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3856 3857
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
3858
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3859
  }
3860

3861 3862 3863 3864
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
3865 3866
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table))
3867
  {
3868
    unlock_table_name(thd, table);
3869
    pthread_mutex_unlock(&LOCK_open);
3870 3871
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
3872
  }
3873
  pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3874
  DBUG_RETURN(0);
3875
}
3876

3877

3878
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
3879
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3880
{
3881 3882
  int error= 0;
  TABLE tmp_table, *table;
3883 3884 3885 3886
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3887 3888 3889 3890
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
3891 3892

  if (!(table= table_list->table))		/* if open_ltable failed */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3893
  {
3894 3895 3896 3897 3898 3899 3900 3901 3902
    char key[MAX_DBKEY_LENGTH];
    uint key_length;

    key_length= create_table_def_key(thd, key, table_list, 0);
    pthread_mutex_lock(&LOCK_open);
    if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
                                  &error))))
    {
      pthread_mutex_unlock(&LOCK_open);
3903
      DBUG_RETURN(0);				// Can't open frm file
3904 3905
    }

3906
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
3907 3908 3909 3910 3911
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
3912
    table= &tmp_table;
3913
    pthread_mutex_unlock(&LOCK_open);
3914
  }
3915 3916 3917 3918 3919 3920 3921 3922 3923
  /*
    REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
  */
  if (table->s->tmp_table)
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Cannot repair temporary table from .frm file");
    goto end;
  }
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3924

3925 3926 3927 3928 3929 3930 3931 3932 3933
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3934

3935 3936 3937 3938
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
3939
  ext= table->file->bas_ext();
3940 3941
  if (!ext[0] || !ext[1])
    goto end;					// No data file
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3942

3943 3944
  // Name of data file
  strxmov(from, table->s->normalized_path.str, ext[1], NullS);
3945 3946
  if (!my_stat(from, &stat_info, MYF(0)))
    goto end;				// Can't use USE_FRM flag
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3947

3948 3949
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
3950

3951 3952 3953 3954 3955 3956 3957
  /* If we could open the table, close it */
  if (table_list->table)
  {
    pthread_mutex_lock(&LOCK_open);
    close_cached_table(thd, table);
    pthread_mutex_unlock(&LOCK_open);
  }
3958
  if (lock_and_wait_for_table_name(thd,table_list))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3959
  {
3960 3961
    error= -1;
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3962
  }
3963
  if (my_rename(from, tmp, MYF(MY_WME)))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3964
  {
3965
    pthread_mutex_lock(&LOCK_open);
3966
    unlock_table_name(thd, table_list);
3967
    pthread_mutex_unlock(&LOCK_open);
3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3989 3990
  }

3991 3992 3993 3994
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
3995 3996
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table_list))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3997
  {
3998
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3999
    pthread_mutex_unlock(&LOCK_open);
4000 4001 4002
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4003
  }
4004
  pthread_mutex_unlock(&LOCK_open);
4005 4006 4007

end:
  if (table == &tmp_table)
4008 4009 4010 4011 4012
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
4013
  DBUG_RETURN(error);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4014
}
4015

4016

4017

4018 4019
/*
  RETURN VALUES
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
4020 4021 4022
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
4023
*/
4024 4025 4026 4027 4028
static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
                              HA_CHECK_OPT* check_opt,
                              const char *operator_name,
                              thr_lock_type lock_type,
                              bool open_for_modify,
4029
                              bool no_warnings_for_error,
4030 4031 4032
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4033 4034 4035
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4036
{
4037 4038
  TABLE_LIST *table, *save_next_global, *save_next_local;
  SELECT_LEX *select= &thd->lex->select_lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4039
  List<Item> field_list;
4040 4041
  Item *item;
  Protocol *protocol= thd->protocol;
4042
  LEX *lex= thd->lex;
4043
  int result_code, disable_logs= 0;
4044
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4045

4046 4047
  if (end_active_trans(thd))
    DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4048 4049 4050 4051 4052 4053 4054 4055
  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null = 1;
  field_list.push_back(item = new Item_empty_string("Op", 10));
  item->maybe_null = 1;
  field_list.push_back(item = new Item_empty_string("Msg_type", 10));
  item->maybe_null = 1;
  field_list.push_back(item = new Item_empty_string("Msg_text", 255));
  item->maybe_null = 1;
4056 4057
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4058
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4059

4060
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4061
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4062 4063
  {
    char table_name[NAME_LEN*2+2];
4064
    char* db = table->db;
4065
    bool fatal_error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4066

serg@serg.mylan's avatar
serg@serg.mylan committed
4067
    strxmov(table_name, db, ".", table->table_name, NullS);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4068
    thd->open_options|= extra_open_options;
4069 4070
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4071
    save_next_global= table->next_global;
4072
    table->next_global= 0;
4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083
    save_next_local= table->next_local;
    table->next_local= 0;
    select->table_list.first= (byte*)table;
    /*
      Time zone tables and SP tables can be add to lex->query_tables list,
      so it have to be prepared.
      TODO: Investigate if we can put extra tables into argument instead of
      using lex->query_tables
    */
    lex->query_tables= table;
    lex->query_tables_last= &table->next_global;
4084
    lex->query_tables_own_last= 0;
4085
    thd->no_warnings_for_error= no_warnings_for_error;
4086
    if (view_operator_func == NULL)
4087
      table->required_type=FRMTYPE_TABLE;
4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104

    /*
      If we want to perform an admin operation on the log table
      (E.g. rename) and lock_type >= TL_READ_NO_INSERT disable
      log tables
    */

    if (check_if_log_table(table->db_length, table->db,
                                  table->table_name_length,
                                  table->table_name, 1) &&
        lock_type >= TL_READ_NO_INSERT)
    {
      disable_logs= 1;
      logger.lock();
      logger.tmp_close_log_tables(thd);
    }

4105
    open_and_lock_tables(thd, table);
4106
    thd->no_warnings_for_error= 0;
4107 4108
    table->next_global= save_next_global;
    table->next_local= save_next_local;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4109
    thd->open_options&= ~extra_open_options;
4110

4111
    if (prepare_func)
4112
    {
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4113
      switch ((*prepare_func)(thd, table, check_opt)) {
4114
      case  1:           // error, message written to net
4115
        ha_autocommit_or_rollback(thd, 1);
4116 4117 4118 4119 4120 4121
        close_thread_tables(thd);
        continue;
      case -1:           // error, message could be written to net
        goto err;
      default:           // should be 0 otherwise
        ;
4122
      }
4123
    }
4124

4125
    /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4126 4127 4128 4129 4130 4131
      CHECK TABLE command is only command where VIEW allowed here and this
      command use only temporary teble method for VIEWs resolving => there
      can't be VIEW tree substitition of join view => if opening table
      succeed then table->table will have real TABLE pointer as value (in
      case of join view substitution table->table can be 0, but here it is
      impossible)
4132
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4133 4134
    if (!table->table)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4135
      char buf[ERRMSGSIZE+ERRMSGSIZE+2];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4136
      const char *err_msg;
4137
      protocol->prepare_for_resend();
4138 4139
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4140
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4141 4142
      if (!(err_msg=thd->net.last_error))
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
4143 4144 4145 4146
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4147
        strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
4148 4149
        err_msg= (const char *)buf;
      }
4150
      protocol->store(err_msg, system_charset_info);
4151
      lex->cleanup_after_one_table_open();
4152
      thd->clear_error();
4153 4154 4155 4156
      /*
        View opening can be interrupted in the middle of process so some
        tables can be left opening
      */
4157
      ha_autocommit_or_rollback(thd, 1);
4158
      close_thread_tables(thd);
4159
      lex->reset_query_tables_list(FALSE);
4160
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4161 4162 4163
	goto err;
      continue;
    }
4164 4165 4166 4167 4168 4169 4170

    if (table->view)
    {
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4171
    table->table->pos_in_table_list= table;
4172
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4173
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
4174
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4175
      uint length;
4176
      protocol->prepare_for_resend();
4177 4178
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4179
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4180 4181 4182
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4183
      ha_autocommit_or_rollback(thd, 0);
4184
      close_thread_tables(thd);
4185
      lex->reset_query_tables_list(FALSE);
4186
      table->table=0;				// For query cache
4187
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4188 4189 4190 4191
	goto err;
      continue;
    }

4192
    /* Close all instances of the table to allow repair to rename files */
4193 4194
    if (lock_type == TL_WRITE && table->table->s->version &&
        !table->table->s->log_table)
4195 4196
    {
      pthread_mutex_lock(&LOCK_open);
4197 4198
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
4199
      mysql_lock_abort(thd,table->table, TRUE);
4200 4201
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
monty@mysql.com's avatar
monty@mysql.com committed
4202 4203
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4204
      thd->exit_cond(old_message);
4205 4206
      if (thd->killed)
	goto err;
4207 4208 4209
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4210 4211
    }

4212
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4213 4214 4215 4216
    {
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4217 4218 4219
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4220 4221 4222 4223
      if (protocol->write())
        goto err;
    }

4224 4225 4226 4227 4228 4229
    if (operator_func == &handler::ha_repair)
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4230
        ha_autocommit_or_rollback(thd, 1);
4231 4232 4233 4234 4235 4236 4237 4238 4239
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
        result_code= mysql_recreate_table(thd, table, 0);
        reenable_binlog(thd);
        goto send_result;
      }

    }

4240 4241 4242 4243
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

4244
    lex->cleanup_after_one_table_open();
4245
    thd->clear_error();  // these errors shouldn't get client
4246
    protocol->prepare_for_resend();
4247 4248
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4249

4250 4251 4252
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4253 4254
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4255
      {
4256 4257
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
4258
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4259
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4260
	protocol->store(buf, length, system_charset_info);
4261
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4262 4263
      break;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4264 4265
    case HA_ADMIN_NOT_BASE_TABLE:
      {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4266 4267
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4268
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4269
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4270
        protocol->store(buf, length, system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4271 4272 4273
      }
      break;

4274
    case HA_ADMIN_OK:
4275 4276
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4277 4278
      break;

4279
    case HA_ADMIN_FAILED:
4280 4281 4282
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4283 4284
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4285
    case HA_ADMIN_REJECT:
4286 4287 4288
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation need committed state"),
                      system_charset_info);
monty@mysql.com's avatar
monty@mysql.com committed
4289
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4290 4291
      break;

4292
    case HA_ADMIN_ALREADY_DONE:
4293 4294 4295
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4296 4297
      break;

4298
    case HA_ADMIN_CORRUPT:
4299 4300
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4301
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4302 4303
      break;

4304
    case HA_ADMIN_INVALID:
4305 4306 4307
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
4308 4309
      break;

4310 4311 4312 4313 4314 4315 4316
    case HA_ADMIN_TRY_ALTER:
    {
      /*
        This is currently used only by InnoDB. ha_innobase::optimize() answers
        "try with alter", so here we close the table, do an ALTER TABLE,
        reopen the table and do ha_innobase::analyze() on it.
      */
4317
      ha_autocommit_or_rollback(thd, 0);
4318
      close_thread_tables(thd);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4319 4320 4321
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4322
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4323
      result_code= mysql_recreate_table(thd, table, 0);
4324
      reenable_binlog(thd);
4325
      ha_autocommit_or_rollback(thd, 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4326
      close_thread_tables(thd);
4327 4328 4329 4330 4331 4332
      if (!result_code) // recreation went ok
      {
        if ((table->table= open_ltable(thd, table, lock_type)) &&
            ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
          result_code= 0; // analyze went ok
      }
4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344
      if (result_code) // either mysql_recreate_table or analyze failed
      {
        const char *err_msg;
        if ((err_msg= thd->net.last_error))
        {
          if (!thd->vio_ok())
          {
            sql_print_error(err_msg);
          }
          else
          {
            /* Hijack the row already in-progress. */
4345
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4346 4347 4348 4349 4350 4351 4352 4353 4354
            protocol->store(err_msg, system_charset_info);
            (void)protocol->write();
            /* Start off another row for HA_ADMIN_FAILED */
            protocol->prepare_for_resend();
            protocol->store(table_name, system_charset_info);
            protocol->store(operator_name, system_charset_info);
          }
        }
      }
4355
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4356 4357
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4358 4359
      goto send_result_message;
    }
4360 4361
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4362
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4363 4364
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4365 4366
      break;
    }
4367

4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
      char buf[ERRMSGSIZE];
      uint length;

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      length=my_snprintf(buf, ERRMSGSIZE, ER(ER_TABLE_NEEDS_UPGRADE), table->table_name);
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

4381
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4382 4383 4384 4385 4386
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
4387
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4388 4389 4390 4391
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4392
    }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4393
    if (table->table)
4394
    {
4395
      /* in the below check we do not refresh the log tables */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4396 4397
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4398
      else if (open_for_modify && !table->table->s->log_table)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4399
      {
holyfoot@deer.(none)'s avatar
holyfoot@deer.(none) committed
4400
        if (table->table->s->tmp_table)
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4401 4402 4403 4404
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
4405 4406
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4407 4408 4409
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4410 4411
        query_cache_invalidate3(thd, table->table, 0);
      }
4412
    }
4413
    ha_autocommit_or_rollback(thd, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4414
    close_thread_tables(thd);
4415
    table->table=0;				// For query cache
4416
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4417 4418 4419
      goto err;
  }

4420
  send_eof(thd);
4421 4422 4423 4424 4425 4426
  if (disable_logs)
  {
    if (logger.reopen_log_tables())
      my_error(ER_CANT_ACTIVATE_LOG, MYF(0));
    logger.unlock();
  }
4427
  DBUG_RETURN(FALSE);
4428

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4429
 err:
4430
  ha_autocommit_or_rollback(thd, 1);
4431
  close_thread_tables(thd);			// Shouldn't be needed
4432 4433 4434 4435 4436 4437 4438
  /* enable logging back if needed */
  if (disable_logs)
  {
    if (logger.reopen_log_tables())
      my_error(ER_CANT_ACTIVATE_LOG, MYF(0));
    logger.unlock();
  }
4439 4440
  if (table)
    table->table=0;
4441
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4442 4443
}

4444

4445
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
4446 4447 4448
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4449
				"backup", TL_READ, 0, 0, 0, 0,
4450
				&handler::backup, 0));
4451
}
4452

4453

4454
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
4455 4456 4457
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4458
				"restore", TL_WRITE, 1, 1, 0,
4459
				&prepare_for_restore,
4460
				&handler::restore, 0));
4461
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4462

4463

4464
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4465 4466 4467
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4468 4469 4470
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4471
				&prepare_for_repair,
4472
				&handler::ha_repair, 0));
4473 4474
}

4475

4476
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4477 4478 4479
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4480
				"optimize", TL_WRITE, 1,0,0,0,
4481
				&handler::optimize, 0));
4482 4483 4484
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4485 4486 4487 4488 4489
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4490 4491
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4492 4493

  RETURN VALUES
4494 4495
   FALSE ok
   TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4496 4497
*/

4498
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4499
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4500
{
4501
  HA_CHECK_OPT check_opt;
4502
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4503
  DBUG_ENTER("mysql_assign_to_keycache");
4504 4505 4506 4507 4508 4509 4510

  check_opt.init();
  pthread_mutex_lock(&LOCK_global_system_variables);
  if (!(key_cache= get_key_cache(key_cache_name)))
  {
    pthread_mutex_unlock(&LOCK_global_system_variables);
    my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
4511
    DBUG_RETURN(TRUE);
4512 4513 4514 4515
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4516
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4517
				0, 0, &handler::assign_to_keycache, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4518 4519
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4520 4521 4522 4523 4524 4525

/*
  Reassign all tables assigned to a key cache to another key cache

  SYNOPSIS
    reassign_keycache_tables()
4526 4527 4528
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4529

4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542
  NOTES
    This is called when one sets a key cache size to zero, in which
    case we have to move the tables associated to this key cache to
    the "default" one.

    One has to ensure that one never calls this function while
    some other thread is changing the key cache. This is assured by
    the caller setting src_cache->in_init before calling this function.

    We don't delete the old key cache as there may still be pointers pointing
    to it for a while after this function returns.

 RETURN VALUES
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4543 4544 4545
    0	  ok
*/

4546 4547
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4548 4549 4550
{
  DBUG_ENTER("reassign_keycache_tables");

4551 4552
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
4553
  src_cache->param_buff_size= 0;		// Free key cache
4554 4555
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4556
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4557 4558 4559
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4560 4561 4562 4563 4564
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
4565 4566
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4567 4568

  RETURN VALUES
4569 4570
    FALSE ok
    TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4571 4572
*/

4573
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4574 4575 4576
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
4577
				"preload_keys", TL_READ, 0, 0, 0, 0,
4578
				&handler::preload_keys, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4579 4580 4581
}


venu@myvenu.com's avatar
venu@myvenu.com committed
4582 4583 4584 4585 4586
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
4587 4588
    thd		Thread object
    table	Table list (one table only)
venu@myvenu.com's avatar
venu@myvenu.com committed
4589 4590 4591 4592
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
4593 4594
    FALSE OK
    TRUE  error
venu@myvenu.com's avatar
venu@myvenu.com committed
4595 4596
*/

4597
bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
4598
                             HA_CREATE_INFO *lex_create_info,
4599
                             Table_ident *table_ident)
venu@myvenu.com's avatar
venu@myvenu.com committed
4600
{
4601
  TABLE *tmp_table;
4602
  char src_path[FN_REFLEN], dst_path[FN_REFLEN], tmp_path[FN_REFLEN];
4603
  char src_table_name_buff[FN_REFLEN], src_db_name_buff[FN_REFLEN];
4604
  uint dst_path_length;
venu@myvenu.com's avatar
venu@myvenu.com committed
4605
  char *db= table->db;
4606
  char *table_name= table->table_name;
4607
  char *src_db;
venu@myvenu.com's avatar
venu@myvenu.com committed
4608
  char *src_table= table_ident->table.str;
4609
  int  err;
4610
  bool res= TRUE, unlock_dst_table= FALSE;
4611
  enum legacy_db_type not_used;
4612
  HA_CREATE_INFO *create_info;
4613

4614
  TABLE_LIST src_tables_list, dst_tables_list;
venu@myvenu.com's avatar
venu@myvenu.com committed
4615
  DBUG_ENTER("mysql_create_like_table");
4616 4617 4618 4619 4620

  if (!(create_info= copy_create_info(lex_create_info)))
  {
    DBUG_RETURN(TRUE);
  }
4621 4622
  DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */
  src_db= table_ident->db.str;
venu@myvenu.com's avatar
venu@myvenu.com committed
4623 4624 4625 4626 4627 4628

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
4629
       check_table_name(src_table,table_ident->table.length)))
venu@myvenu.com's avatar
venu@myvenu.com committed
4630
  {
4631
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
4632
    DBUG_RETURN(TRUE);
venu@myvenu.com's avatar
venu@myvenu.com committed
4633
  }
4634 4635 4636 4637 4638
  if (!src_db || check_db_name(src_db))
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
    DBUG_RETURN(-1);
  }
4639

venu@myvenu.com's avatar
venu@myvenu.com committed
4640
  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
4641
    strxmov(src_path, tmp_table->s->path.str, reg_ext, NullS);
venu@myvenu.com's avatar
venu@myvenu.com committed
4642 4643
  else
  {
4644
    build_table_filename(src_path, sizeof(src_path),
4645
                         src_db, src_table, reg_ext, 0);
4646
    /* Resolve symlinks (for windows) */
4647
    unpack_filename(src_path, src_path);
4648 4649
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
4650 4651 4652
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
4653
      goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
4654 4655 4656
    }
  }

4657 4658 4659
  /* 
     create like should be not allowed for Views, Triggers, ... 
  */
4660
  if (mysql_frm_type(thd, src_path, &not_used) != FRMTYPE_TABLE)
4661
  {
4662
    my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE");
4663 4664 4665
    goto err;
  }

4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693
  if (lower_case_table_names)
  {
    if (src_db)
    {
      strmake(src_db_name_buff, src_db,
              min(sizeof(src_db_name_buff) - 1, table_ident->db.length));
      my_casedn_str(files_charset_info, src_db_name_buff);
      src_db= src_db_name_buff;
    }
    if (src_table)
    {
      strmake(src_table_name_buff, src_table,
              min(sizeof(src_table_name_buff) - 1, table_ident->table.length));
      my_casedn_str(files_charset_info, src_table_name_buff);
      src_table= src_table_name_buff;
    }
  }

  bzero((gptr)&src_tables_list, sizeof(src_tables_list));
  src_tables_list.db= src_db;
  src_tables_list.db_length= table_ident->db.length;
  src_tables_list.lock_type= TL_READ;
  src_tables_list.table_name= src_table;
  src_tables_list.alias= src_table;

  if (simple_open_n_lock_tables(thd, &src_tables_list))
    DBUG_RETURN(TRUE);

venu@myvenu.com's avatar
venu@myvenu.com committed
4694 4695 4696
  /*
    Validate the destination table

4697
    skip the destination table name checking as this is already
venu@myvenu.com's avatar
venu@myvenu.com committed
4698 4699 4700 4701 4702 4703
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
4704
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
4705 4706
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
4707 4708 4709 4710
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
4711
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
4712
                                          db, table_name, reg_ext, 0);
venu@myvenu.com's avatar
venu@myvenu.com committed
4713 4714 4715 4716
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

4717
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
4718
    Create a new table by copying from source table
4719
  */
4720 4721 4722 4723 4724 4725
  if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
  {
    if (my_errno == ENOENT)
      my_error(ER_BAD_DB_ERROR,MYF(0),db);
    else
      my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
4726
    goto err;
4727
  }
venu@myvenu.com's avatar
venu@myvenu.com committed
4728 4729

  /*
4730 4731
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
venu@myvenu.com's avatar
venu@myvenu.com committed
4732 4733
    and temporary tables).
  */
4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746
#ifdef WITH_PARTITION_STORAGE_ENGINE
  /*
    For partitioned tables we need to copy the .par file as well since
    it is used in open_table_def to even be able to create a new handler.
    There is no way to find out here if the original table is a
    partitioned table so we copy the file and ignore any errors.
  */
  fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
  strmov(dst_path, tmp_path);
  fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
  strmov(src_path, tmp_path);
  my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
#endif
4747
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
4748
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
4749

venu@myvenu.com's avatar
venu@myvenu.com committed
4750 4751 4752 4753
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
4754 4755
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
4756
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4757 4758 4759 4760
    }
  }
  else if (err)
  {
4761
    (void) quick_rm_table(create_info->db_type, db,
4762
			  table_name, 0); /* purecov: inspected */
4763
    goto err;	    /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4764
  }
4765

4766 4767 4768
  /*
    We have to write the query before we unlock the tables.
  */
4769
  if (thd->current_stmt_binlog_row_based)
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
  {
    /*
       Since temporary tables are not replicated under row-based
       replication, CREATE TABLE ... LIKE ... needs special
       treatement.  We have four cases to consider, according to the
       following decision table:

           ==== ========= ========= ==============================
           Case    Target    Source Write to binary log
           ==== ========= ========= ==============================
           1       normal    normal Original statement
           2       normal temporary Generated statement
           3    temporary    normal Nothing
           4    temporary temporary Nothing
           ==== ========= ========= ==============================

       The variable 'tmp_table' below is used to see if the source
       table is a temporary table: if it is set, then the source table
       was a temporary table and we can take apropriate actions.
    */
    if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    {
      if (tmp_table)                            // Case 2
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
4797
        uint counter;
4798 4799

        /*
4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815
          Here we open the destination table. This is needed for
          store_create_info() to work. The table will be closed
          by close_thread_tables() at the end of the statement.
        */
        if (open_tables(thd, &table, &counter, 0))
          goto err;

        bzero((gptr)&dst_tables_list, sizeof(dst_tables_list));
        dst_tables_list.db= table->db;
        dst_tables_list.table_name= table->table_name;

        /*
          lock destination table name, to make sure that nobody
          can drop/alter the table while we execute store_create_info()
        */
        if (lock_and_wait_for_table_name(thd, &dst_tables_list))
4816
          goto err;
4817 4818
        else
          unlock_dst_table= TRUE;
4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831

        int result= store_create_info(thd, table, &query, create_info);

        DBUG_ASSERT(result == 0); // store_create_info() always return 0
        write_bin_log(thd, TRUE, query.ptr(), query.length());
      }
      else                                      // Case 1
        write_bin_log(thd, TRUE, thd->query, thd->query_length);
    }
    /*
      Case 3 and 4 does nothing under RBR
    */
  }
4832
  else
4833 4834
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

4835
  res= FALSE;
4836
  goto err;
4837

venu@myvenu.com's avatar
venu@myvenu.com committed
4838 4839 4840 4841
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
4842 4843
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
4844
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4845
		 ER_TABLE_EXISTS_ERROR,warn_buff);
4846
    res= FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
4847
  }
4848 4849 4850 4851
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
4852 4853 4854 4855 4856 4857
  if (unlock_dst_table)
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, &dst_tables_list);
    pthread_mutex_unlock(&LOCK_open);
  }
4858
  DBUG_RETURN(res);
venu@myvenu.com's avatar
venu@myvenu.com committed
4859 4860 4861
}


4862
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4863
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4864 4865
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4866 4867
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4868
				"analyze", lock_type, 1, 0, 0, 0,
4869
				&handler::analyze, 0));
4870 4871 4872
}


4873
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
4874
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4875 4876
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4877 4878
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4879
				"check", lock_type,
4880
				0, HA_OPEN_FOR_REPAIR, 0, 0,
4881
				&handler::ha_check, &view_checksum));
4882 4883
}

monty@mysql.com's avatar
monty@mysql.com committed
4884

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4885
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
4886 4887 4888 4889
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4890 4891 4892 4893 4894 4895
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
4896 4897 4898 4899
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4900 4901 4902

  thd->proc_info="discard_or_import_tablespace";

monty@mysql.com's avatar
monty@mysql.com committed
4903
  discard= test(tablespace_op == DISCARD_TABLESPACE);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4904

monty@mysql.com's avatar
monty@mysql.com committed
4905 4906 4907 4908 4909
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4910 4911 4912 4913 4914
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
4915

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4916 4917 4918 4919 4920 4921 4922
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

monty@mysql.com's avatar
monty@mysql.com committed
4923 4924 4925 4926
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4927 4928 4929 4930 4931 4932 4933 4934
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
    goto err;
4935
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
4936

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4937
err:
4938
  ha_autocommit_or_rollback(thd, error);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4939
  close_thread_tables(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4940
  thd->tablespace_op=FALSE;
4941
  
monty@mysql.com's avatar
monty@mysql.com committed
4942 4943
  if (error == 0)
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4944
    send_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
4945
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4946
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4947

4948 4949
  table->file->print_error(error, MYF(0));
    
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4950
  DBUG_RETURN(-1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4951
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4952

4953

4954 4955
/*
  SYNOPSIS
4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967
    compare_tables()
      table                     The original table.
      create_list               The fields for the new table.
      key_info_buffer           An array of KEY structs for the new indexes.
      key_count                 The number of elements in the array.
      create_info               Create options for the new table.
      alter_info                Alter options.
      order_num                 Number of order list elements.
      index_drop_buffer   OUT   An array of offsets into table->key_info.
      index_drop_count    OUT   The number of elements in the array.
      index_add_buffer    OUT   An array of offsets into key_info_buffer.
      index_add_count     OUT   The number of elements in the array.
4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978

  DESCRIPTION
    'table' (first argument) contains information of the original
    table, which includes all corresponding parts that the new
    table has in arguments create_list, key_list and create_info.

    By comparing the changes between the original and new table
    we can determine how much it has changed after ALTER TABLE
    and whether we need to make a copy of the table, or just change
    the .frm file.

4979 4980 4981 4982 4983
    If there are no data changes, but index changes, 'index_drop_buffer'
    and/or 'index_add_buffer' are populated with offsets into
    table->key_info or key_info_buffer respectively for the indexes
    that need to be dropped and/or (re-)created.

4984
  RETURN VALUES
4985 4986 4987
    0                           No copy needed
    ALTER_TABLE_DATA_CHANGED    Data changes, copy needed
    ALTER_TABLE_INDEX_CHANGED   Index changes, copy might be needed
4988 4989
*/

4990 4991 4992 4993 4994
static uint compare_tables(TABLE *table, List<create_field> *create_list,
                           KEY *key_info_buffer, uint key_count,
                           HA_CREATE_INFO *create_info,
                           ALTER_INFO *alter_info, uint order_num,
                           uint *index_drop_buffer, uint *index_drop_count,
4995 4996
                           uint *index_add_buffer, uint *index_add_count,
                           bool varchar)
4997 4998 4999 5000 5001
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
  List_iterator_fast<create_field> new_field_it(*create_list);
  create_field *new_field;
5002 5003
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5004
  DBUG_ENTER("compare_tables");
5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022

  /*
    Some very basic checks. If number of fields changes, or the
    handler, we need to run full ALTER TABLE. In the future
    new fields can be added and old dropped without copy, but
    not yet.

    Test also that engine was not given during ALTER TABLE, or
    we are force to run regular alter table (copy).
    E.g. ALTER TABLE tbl_name ENGINE=MyISAM.

    For the following ones we also want to run regular alter table:
    ALTER TABLE tbl_name ORDER BY ..
    ALTER TABLE tbl_name CONVERT TO CHARACTER SET ..

    At the moment we can't handle altering temporary tables without a copy.
    We also test if OPTIMIZE TABLE was given and was mapped to alter table.
    In that case we always do full copy.
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
5023 5024 5025 5026 5027 5028 5029 5030

    There was a bug prior to mysql-4.0.25. Number of null fields was
    calculated incorrectly. As a result frm and data files gets out of
    sync after fast alter table. There is no way to determine by which
    mysql version (in 4.0 and 4.1 branches) table was created, thus we
    disable fast alter table for all tables created by mysql versions
    prior to 5.0 branch.
    See BUG#6236.
5031 5032 5033 5034 5035 5036 5037
  */
  if (table->s->fields != create_list->elements ||
      table->s->db_type != create_info->db_type ||
      table->s->tmp_table ||
      create_info->used_fields & HA_CREATE_USED_ENGINE ||
      create_info->used_fields & HA_CREATE_USED_CHARSET ||
      create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
5038
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5039
      order_num ||
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
5040
      !table->s->mysql_version ||
5041
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5042
    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053

  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
  for (f_ptr= table->field, new_field= new_field_it++;
       (field= *f_ptr); f_ptr++, new_field= new_field_it++)
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5054

5055 5056 5057
    /* Check that NULL behavior is same for old and new fields */
    if ((new_field->flags & NOT_NULL_FLAG) !=
	(uint) (field->flags & NOT_NULL_FLAG))
5058
      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
5059 5060 5061 5062 5063 5064 5065 5066

    /* Don't pack rows in old tables if the user has requested this. */
    if (create_info->row_type == ROW_TYPE_DYNAMIC ||
	(new_field->flags & BLOB_FLAG) ||
	new_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED)
      create_info->table_options|= HA_OPTION_PACK_RECORD;

5067
    /* Check if field was renamed */
5068
    field->flags&= ~FIELD_IS_RENAMED;
5069 5070 5071 5072
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
		      new_field->field_name))
      field->flags|= FIELD_IS_RENAMED;      
5073

5074 5075
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
    if (!(tmp= field->is_equal(new_field)))
5076
      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
5077
    // Clear indexed marker
5078
    field->flags&= ~FIELD_IN_ADD_INDEX;
5079 5080 5081 5082 5083 5084 5085
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5086 5087 5088 5089
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
  KEY *new_key_end= key_info_buffer + key_count;
5090

5091 5092 5093 5094 5095 5096 5097 5098
  DBUG_PRINT("info", ("index count old: %d  new: %d",
                      table->s->keys, key_count));
  /*
    Step through all keys of the old table and search matching new keys.
  */
  *index_drop_count= 0;
  *index_add_count= 0;
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
5099
  {
5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

    /* Search a new key with the same name. */
    for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
    {
      if (! strcmp(table_key->name, new_key->name))
        break;
    }
    if (new_key >= new_key_end)
    {
      /* Key not found. Add the offset of the key to the drop buffer. */
      index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
      DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
      continue;
    }

    /* Check that the key types are compatible between old and new tables. */
    if ((table_key->algorithm != new_key->algorithm) ||
	((table_key->flags & HA_KEYFLAG_MASK) !=
         (new_key->flags & HA_KEYFLAG_MASK)) ||
        (table_key->key_parts != new_key->key_parts))
      goto index_changed;
5124 5125 5126 5127 5128

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5129 5130 5131
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
5132 5133 5134
    {
      /*
	Key definition has changed if we are using a different field or
5135 5136
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
5137
      */
5138 5139 5140
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
5141
    }
5142 5143 5144 5145 5146 5147
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
    index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
    index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
5148 5149 5150 5151 5152 5153
    key_part= new_key->key_part;
    end= key_part + new_key->key_parts;
    for(; key_part != end; key_part++)
    {
      // Mark field to be part of new key 
      field= table->field[key_part->fieldnr];
5154
      field->flags|= FIELD_IN_ADD_INDEX;
5155
    }
5156
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
5157
  }
5158
  /*end of for (; table_key < table_key_end;) */
5159

5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174
  /*
    Step through all keys of the new table and find matching old keys.
  */
  for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
  {
    /* Search an old key with the same name. */
    for (table_key= table->key_info; table_key < table_key_end; table_key++)
    {
      if (! strcmp(table_key->name, new_key->name))
        break;
    }
    if (table_key >= table_key_end)
    {
      /* Key not found. Add the offset of the key to the add buffer. */
      index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
5175 5176 5177 5178 5179 5180
      key_part= new_key->key_part;
      end= key_part + new_key->key_parts;
      for(; key_part != end; key_part++)
      {
        // Mark field to be part of new key 
        field= table->field[key_part->fieldnr];
5181
        field->flags|= FIELD_IN_ADD_INDEX;
5182
      }
marty@linux.site's avatar
marty@linux.site committed
5183
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5184 5185
    }
  }
5186 5187 5188 5189 5190

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);

5191 5192 5193 5194
  if (*index_drop_count || *index_add_count)
    DBUG_RETURN(ALTER_TABLE_INDEX_CHANGED);

  DBUG_RETURN(0); // Tables are compatible
5195 5196 5197
}


5198 5199 5200
/*
  Alter table
*/
5201

5202
bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
5203
                       HA_CREATE_INFO *lex_create_info,
5204 5205
                       TABLE_LIST *table_list,
                       List<create_field> &fields, List<Key> &keys,
5206
                       uint order_num, ORDER *order, bool ignore,
5207
                       ALTER_INFO *alter_info, bool do_send_ok)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5208
{
5209
  TABLE *table,*new_table=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5210
  int error;
5211 5212
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
5213
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
5214
  char path[FN_REFLEN];
5215
  char reg_path[FN_REFLEN+1];
5216
  ha_rows copied,deleted;
5217
  uint db_create_options, used_fields;
5218 5219
  handlerton *old_db_type, *new_db_type;
  legacy_db_type table_type;
5220
  HA_CREATE_INFO *create_info;
5221
  frm_type_enum frm_type;
5222
  uint need_copy_table= 0;
5223
  bool no_table_reopen= FALSE, varchar= FALSE;
5224
#ifdef WITH_PARTITION_STORAGE_ENGINE
5225
  uint fast_alter_partition= 0;
5226 5227
  bool partition_changed= FALSE;
#endif
5228 5229 5230 5231 5232 5233 5234 5235 5236 5237
  List<create_field> prepared_create_list;
  List<Key>          prepared_key_list;
  bool need_lock_for_indexes= TRUE;
  uint db_options= 0;
  uint key_count;
  KEY  *key_info_buffer;
  uint index_drop_count;
  uint *index_drop_buffer;
  uint index_add_count;
  uint *index_add_buffer;
5238
  bool committed= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5239 5240
  DBUG_ENTER("mysql_alter_table");

5241 5242 5243 5244 5245
  LINT_INIT(index_add_count);
  LINT_INIT(index_drop_count);
  LINT_INIT(index_add_buffer);
  LINT_INIT(index_drop_buffer);

5246
  if (table_list && table_list->db && table_list->table_name)
5247
  {
5248
    int table_kind= 0;
5249

5250 5251 5252
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
5253 5254

    /* Disable alter of enabled log tables */
5255
    if (table_kind && logger.is_log_table_enabled(table_kind))
5256
    {
5257
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER");
5258 5259 5260 5261
      DBUG_RETURN(TRUE);
    }

    /* Disable alter of log tables to unsupported engine */
5262
    if (table_kind &&
5263
        (lex_create_info->used_fields & HA_CREATE_USED_ENGINE) &&
5264
        (!lex_create_info->db_type || /* unknown engine */
5265
        !(lex_create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
5266
    {
5267
      my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0));
5268 5269 5270 5271
      DBUG_RETURN(TRUE);
    }
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5272
  thd->proc_info="init";
5273 5274 5275 5276
  if (!(create_info= copy_create_info(lex_create_info)))
  {
    DBUG_RETURN(TRUE);
  }
5277
  table_name=table_list->table_name;
5278
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5279
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
5280
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
5281
    new_db= db;
5282 5283
  build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext, 0);
  build_table_filename(path, sizeof(path), db, table_name, "", 0);
5284

5285
  used_fields=create_info->used_fields;
5286

5287
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5288

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5289
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
5290
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5291
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
5292
						   alter_info->tablespace_op));
5293 5294 5295
  strxnmov(new_name_buff, sizeof (new_name_buff) - 1, mysql_data_home, "/", db, 
           "/", table_name, reg_ext, NullS);
  (void) unpack_filename(new_name_buff, new_name_buff);
5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338
  if (lower_case_table_names != 2)
    my_casedn_str(files_charset_info, new_name_buff);
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
    /*
      Avoid problems with a rename on a table that we have locked or
      if the user is trying to to do this in a transcation context
    */

    if (thd->locked_tables || thd->active_transaction())
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      DBUG_RETURN(1);
    }

    if (wait_if_global_read_lock(thd,0,1))
      DBUG_RETURN(1);
    VOID(pthread_mutex_lock(&LOCK_open));
    if (lock_table_names(thd, table_list))
      goto view_err;
    
    error=0;
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
        mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }

    unlock_table_names(thd, table_list, (TABLE_LIST*) 0);

view_err:
    pthread_mutex_unlock(&LOCK_open);
    start_waiting_global_read_lock(thd);
    DBUG_RETURN(error);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5339
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
5340
    DBUG_RETURN(TRUE);
5341
  table->use_all_columns();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5342 5343 5344 5345

  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
5346
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5347
    strmov(new_name_buff,new_name);
5348
    strmov(new_alias= new_alias_buff, new_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5349
    if (lower_case_table_names)
5350 5351 5352
    {
      if (lower_case_table_names != 2)
      {
5353
	my_casedn_str(files_charset_info, new_name_buff);
5354 5355
	new_alias= new_name;			// Create lower case table name
      }
5356
      my_casedn_str(files_charset_info, new_name);
5357
    }
5358
    if (new_db == db &&
monty@mysql.com's avatar
monty@mysql.com committed
5359
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
5360 5361
    {
      /*
5362 5363
	Source and destination table names are equal: make later check
	easier.
5364
      */
5365
      new_alias= new_name= table_name;
5366
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5367 5368
    else
    {
5369
      if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5370 5371 5372
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
5373
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
5374
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5375 5376 5377 5378
	}
      }
      else
      {
5379 5380 5381
        build_table_filename(new_name_buff, sizeof(new_name_buff),
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5382 5383
	{
	  /* Table will be closed in do_command() */
5384
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
5385
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5386 5387 5388 5389 5390
	}
      }
    }
  }
  else
5391 5392 5393 5394
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5395

5396
  old_db_type= table->s->db_type;
5397
  if (!create_info->db_type)
5398
  {
5399
#ifdef WITH_PARTITION_STORAGE_ENGINE
5400 5401
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
5402 5403 5404 5405
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
5406 5407 5408
        We set create_info->db_type to default_engine_type
        to ensure we don't change underlying engine type
        due to a erroneously given engine name.
5409
      */
5410
      create_info->db_type= table->part_info->default_engine_type;
5411
    }
5412
    else
5413
#endif
5414
      create_info->db_type= old_db_type;
5415
  }
5416

5417
#ifdef WITH_PARTITION_STORAGE_ENGINE
5418 5419
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
5420
  {
5421
    DBUG_RETURN(TRUE);
5422 5423
  }
#endif
5424
  if (check_engine(thd, new_name, create_info))
5425 5426
    DBUG_RETURN(TRUE);
  new_db_type= create_info->db_type;
5427
  if (create_info->row_type == ROW_TYPE_NOT_USED)
5428
    create_info->row_type= table->s->row_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5429

5430 5431 5432
  DBUG_PRINT("info", ("old type: %s  new type: %s",
             ha_resolve_storage_engine_name(old_db_type),
             ha_resolve_storage_engine_name(new_db_type)));
monty@mysql.com's avatar
monty@mysql.com committed
5433
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
5434
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
5435 5436 5437 5438 5439 5440
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
    DBUG_RETURN(TRUE);
  }
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5441
  thd->proc_info="setup";
5442
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
5443
      !table->s->tmp_table) // no need to touch frm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5444 5445
  {
    error=0;
5446
    if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5447
    {
5448 5449 5450 5451 5452 5453
      thd->proc_info="rename";
      VOID(pthread_mutex_lock(&LOCK_open));
      /* Then do a 'simple' rename of the table */
      error=0;
      if (!access(new_name_buff,F_OK))
      {
5454
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
5455
	error= -1;
5456 5457 5458
      }
      else
      {
5459
	*fn_ext(new_name)=0;
5460
        table->s->version= 0;                   // Force removal of table def
5461
	close_cached_table(thd, table);
5462
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
5463
	  error= -1;
5464 5465 5466 5467
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
          VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
5468
                                  table_name, 0));
5469 5470
          error= -1;
        }
5471 5472
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5473
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5474

5475
    if (!error)
5476
    {
5477
      switch (alter_info->keys_onoff) {
5478
      case LEAVE_AS_IS:
5479
        break;
5480
      case ENABLE:
5481 5482 5483 5484 5485 5486
        VOID(pthread_mutex_lock(&LOCK_open));
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
        VOID(pthread_mutex_unlock(&LOCK_open));
        error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
        /* COND_refresh will be signaled in close_thread_tables() */
        break;
5487
      case DISABLE:
5488 5489 5490 5491 5492 5493
        VOID(pthread_mutex_lock(&LOCK_open));
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
        VOID(pthread_mutex_unlock(&LOCK_open));
        error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
        /* COND_refresh will be signaled in close_thread_tables() */
        break;
5494
      }
5495
    }
5496

5497
    if (error == HA_ERR_WRONG_COMMAND)
serg@serg.mylan's avatar
serg@serg.mylan committed
5498 5499
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5500
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
5501
			  table->alias);
serg@serg.mylan's avatar
serg@serg.mylan committed
5502 5503
      error=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5504 5505
    if (!error)
    {
5506
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
5507 5508
      if (do_send_ok)
        send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5509
    }
5510
    else if (error > 0)
5511 5512
    {
      table->file->print_error(error, MYF(0));
5513
      error= -1;
5514
    }
5515 5516
    table_list->table=0;				// For query cache
    query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5517 5518 5519 5520
    DBUG_RETURN(error);
  }

  /* Full alter table */
5521

5522
  /* Let new create options override the old ones */
5523
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
5524
    create_info->min_rows= table->s->min_rows;
5525
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
5526
    create_info->max_rows= table->s->max_rows;
5527
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
5528
    create_info->avg_row_length= table->s->avg_row_length;
5529
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
5530
    create_info->default_table_charset= table->s->table_charset;
5531 5532
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5533

5534
  restore_record(table, s->default_values);     // Empty record for DEFAULT
5535
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5536
  List_iterator<create_field> def_it(fields);
5537
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5538 5539
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
5540 5541
  create_field *def;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5542
  /*
5543
    First collect all fields from table which isn't in drop_list
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5544 5545 5546 5547 5548
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
5549 5550
    if (field->type() == MYSQL_TYPE_STRING)
      varchar= TRUE;
5551
    /* Check if field should be dropped */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5552 5553 5554 5555 5556
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
5557
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
5558 5559 5560
      {
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
5561
	    !(used_fields & HA_CREATE_USED_AUTO))
5562 5563 5564 5565
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5566
	break;
5567
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5568 5569 5570 5571 5572 5573 5574 5575 5576 5577
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
5578
      if (def->change &&
5579
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5580 5581 5582 5583 5584
	break;
    }
    if (def)
    {						// Field is changed
      def->field=field;
5585 5586 5587 5588 5589
      if (!def->after)
      {
	create_list.push_back(def);
	def_it.remove();
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5590
    }
5591 5592 5593 5594 5595 5596
    else
    {
      /*
        This field was not dropped and not changed, add it to the list
        for the new table.
      */
5597
      create_list.push_back(def=new create_field(field,field));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5598 5599 5600 5601
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
      {
5602
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5603 5604 5605 5606
	  break;
      }
      if (alter)
      {
5607 5608
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
5609
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
5610
	  DBUG_RETURN(TRUE);
5611
	}
5612 5613 5614 5615
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5616 5617 5618 5619 5620 5621 5622 5623
	alter_it.remove();
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
  while ((def=def_it++))			// Add new columns
  {
5624
    if (def->change && ! def->field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5625
    {
5626
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
5627
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638
    }
    if (!def->after)
      create_list.push_back(def);
    else if (def->after == first_keyword)
      create_list.push_front(def);
    else
    {
      create_field *find;
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
5639
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5640 5641 5642 5643
	  break;
      }
      if (!find)
      {
5644
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
5645
	DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5646 5647 5648 5649
      }
      find_it.after(def);			// Put element after this
    }
  }
5650
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5651
  {
5652 5653
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
             alter_info->alter_list.head()->name, table_name);
5654
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5655 5656 5657
  }
  if (!create_list.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5658 5659
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
5660
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5661 5662 5663
  }

  /*
5664 5665
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5666 5667 5668 5669 5670 5671 5672
  */

  List_iterator<Key> key_it(keys);
  List_iterator<create_field> field_it(create_list);
  List<key_part_spec> key_parts;

  KEY *key_info=table->key_info;
5673
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5674
  {
5675
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5676 5677 5678 5679 5680
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
5681
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702
	break;
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }

    KEY_PART_INFO *key_part= key_info->key_part;
    key_parts.empty();
    for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
    {
      if (!key_part->field)
	continue;				// Wrong field (from UNIREG)
      const char *key_part_name=key_part->field->field_name;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
5703 5704
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5705 5706
	    break;
	}
5707
	else if (!my_strcasecmp(system_charset_info,
5708
				key_part_name, cfield->field_name))
5709
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5710 5711 5712 5713 5714
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
5715 5716 5717 5718 5719 5720 5721
      {
        /*
          If the field can't have only a part used in a key according to its
          new type, or should not be used partially according to its
          previous type, or the field length is less than the key part
          length, unset the key part length.

5722 5723 5724
          We also unset the key part length if it is the same as the
          old field's length, so the whole new field will be used.

5725 5726 5727 5728 5729
          BLOBs may have cfield->length == 0, which is why we test it before
          checking whether cfield->length < key_part_length (in chars).
         */
        if (!Field::type_can_have_key_part(cfield->field->type()) ||
            !Field::type_can_have_key_part(cfield->sql_type) ||
bar@mysql.com's avatar
bar@mysql.com committed
5730 5731
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
5732 5733 5734
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5735
      }
5736
      key_part_length /= key_part->field->charset()->mbmaxlen;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5737 5738 5739 5740
      key_parts.push_back(new key_part_spec(cfield->field_name,
					    key_part_length));
    }
    if (key_parts.elements)
5741 5742 5743 5744 5745 5746 5747 5748 5749 5750
    {
      KEY_CREATE_INFO key_create_info;
      bzero((char*) &key_create_info, sizeof(key_create_info));

      key_create_info.algorithm= key_info->algorithm;
      if (key_info->flags & HA_USES_BLOCK_SIZE)
        key_create_info.block_size= key_info->block_size;
      if (key_info->flags & HA_USES_PARSER)
        key_create_info.parser_name= *key_info->parser_name;

5751
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
5752
				 (key_info->flags & HA_NOSAME ?
5753
				 (!my_strcasecmp(system_charset_info,
5754 5755
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
5756 5757 5758
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
5759
                                 &key_create_info,
5760
                                 test(key_info->flags & HA_GENERATED_KEY),
5761 5762
				 key_parts));
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5763 5764 5765 5766
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
5767 5768 5769
    {
      if (key->type != Key::FOREIGN_KEY)
	key_list.push_back(key);
5770 5771 5772 5773
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
5774
	DBUG_RETURN(TRUE);
5775
      }
5776
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5777 5778
  }

5779
  if (alter_info->drop_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5780
  {
5781 5782
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5783 5784
    goto err;
  }
5785
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5786
  {
5787 5788
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5789 5790 5791
    goto err;
  }

5792
  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
5793 5794
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
5795 5796
  /* Safety fix for innodb */
  if (lower_case_table_names)
5797
    my_casedn_str(files_charset_info, tmp_name);
5798 5799 5800 5801
  if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5802
  create_info->db_type=new_db_type;
5803 5804 5805 5806 5807
  if (!create_info->comment.str)
  {
    create_info->comment.str= table->s->comment.str;
    create_info->comment.length= table->s->comment.length;
  }
5808 5809 5810 5811 5812

  table->file->update_create_info(create_info);
  if ((create_info->table_options &
       (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
      (used_fields & HA_CREATE_USED_PACK_KEYS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5813 5814 5815 5816 5817 5818 5819 5820 5821 5822
    db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
  if (create_info->table_options &
      (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
    db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
  if (create_info->table_options &
      (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
    db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
			  HA_OPTION_NO_DELAY_KEY_WRITE);
  create_info->table_options|= db_create_options;

5823
  if (table->s->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5824 5825
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

5826 5827
  set_table_default_charset(thd, create_info, db);

5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860
  {
    /*
      For some purposes we need prepared table structures and translated
      key descriptions with proper default key name assignment.

      Unfortunately, mysql_prepare_table() modifies the field and key
      lists. mysql_create_table() needs the unmodified lists. Hence, we
      need to copy the lists and all their elements. The lists contain
      pointers to the elements only.

      We cannot copy conditionally because the partition code always
      needs prepared lists and compare_tables() needs them and is almost
      always called.
    */

    /* Copy fields. */
    List_iterator<create_field> prep_field_it(create_list);
    create_field *prep_field;
    while ((prep_field= prep_field_it++))
      prepared_create_list.push_back(new create_field(*prep_field));

    /* Copy keys and key parts. */
    List_iterator<Key> prep_key_it(key_list);
    Key *prep_key;
    while ((prep_key= prep_key_it++))
    {
      List<key_part_spec> prep_columns;
      List_iterator<key_part_spec> prep_col_it(prep_key->columns);
      key_part_spec *prep_col;

      while ((prep_col= prep_col_it++))
        prep_columns.push_back(new key_part_spec(*prep_col));
      prepared_key_list.push_back(new Key(prep_key->type, prep_key->name,
5861
                                          &prep_key->key_create_info,
5862
                                          prep_key->generated, prep_columns));
5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874
    }

    /* Create the prepared information. */
    if (mysql_prepare_table(thd, create_info, &prepared_create_list,
                            &prepared_key_list,
                            (table->s->tmp_table != NO_TMP_TABLE), &db_options,
                            table->file, &key_info_buffer, &key_count, 0))
      goto err;
  }

  if (thd->variables.old_alter_table
      || (table->s->db_type != create_info->db_type)
5875
#ifdef WITH_PARTITION_STORAGE_ENGINE
5876
      || partition_changed
5877
#endif
5878
     )
5879 5880
    need_copy_table= 1;
  else
5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892
  {
    /* Try to optimize ALTER TABLE. Allocate result buffers. */
    if (! (index_drop_buffer=
           (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
        ! (index_add_buffer=
           (uint*) thd->alloc(sizeof(uint) * prepared_key_list.elements)))
      goto err;
    /* Check how much the tables differ. */
    need_copy_table= compare_tables(table, &prepared_create_list,
                                    key_info_buffer, key_count,
                                    create_info, alter_info, order_num,
                                    index_drop_buffer, &index_drop_count,
5893 5894
                                    index_add_buffer, &index_add_count,
                                    varchar);
5895 5896 5897 5898 5899 5900 5901 5902 5903 5904
  }

  /*
    If there are index changes only, try to do them online. "Index
    changes only" means also that the handler for the table does not
    change. The table is open and locked. The handler can be accessed.
  */
  if (need_copy_table == ALTER_TABLE_INDEX_CHANGED)
  {
    int   pk_changed= 0;
5905
    ulong alter_flags= 0;
5906 5907 5908 5909 5910 5911
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

5912 5913 5914
    if (table->s->db_type->alter_table_flags)
      alter_flags= table->s->db_type->alter_table_flags(alter_info->flags);
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003
    /* Check dropped indexes. */
    for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
         idx_p < idx_end_p;
         idx_p++)
    {
      key= table->key_info + *idx_p;
      DBUG_PRINT("info", ("index dropped: '%s'", key->name));
      if (key->flags & HA_NOSAME)
      {
        /* Unique key. Check for "PRIMARY". */
        if (! my_strcasecmp(system_charset_info,
                            key->name, primary_key_name))
        {
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
        }
        else
        {
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_DROP_INDEX;
        needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
      }
    }

    /* Check added indexes. */
    for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
         idx_p < idx_end_p;
         idx_p++)
    {
      key= key_info_buffer + *idx_p;
      DBUG_PRINT("info", ("index added: '%s'", key->name));
      if (key->flags & HA_NOSAME)
      {
        /* Unique key. Check for "PRIMARY". */
        if (! my_strcasecmp(system_charset_info,
                            key->name, primary_key_name))
        {
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
          pk_changed++;
        }
        else
        {
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_ADD_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES;
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_ADD_INDEX;
        needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES;
      }
    }

    /*
      Online or fast add/drop index is possible only if
      the primary key is not added and dropped in the same statement.
      Otherwise we have to recreate the table.
      need_copy_table is no-zero at this place.
    */
    if ( pk_changed < 2 )
    {
      if ((alter_flags & needed_online_flags) == needed_online_flags)
      {
        /* All required online flags are present. */
        need_copy_table= 0;
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
        need_copy_table= 0;
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
6004

6005 6006
  /*
    better have a negative test here, instead of positive, like
monty@mysql.com's avatar
monty@mysql.com committed
6007
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
6008 6009
    so that ALTER TABLE won't break when somebody will add new flag
  */
6010 6011
  if (!need_copy_table)
    create_info->frm_only= 1;
6012

6013
#ifdef WITH_PARTITION_STORAGE_ENGINE
6014
  if (fast_alter_partition)
6015
  {
6016 6017 6018 6019 6020
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           &create_list, &key_list,
                                           db, table_name,
                                           fast_alter_partition));
6021
  }
6022
#endif
6023

6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066
  /*
    Handling of symlinked tables:
    If no rename:
      Create new data file and index file on the same disk as the
      old data and index files.
      Copy data.
      Rename new data file over old data file and new index file over
      old index file.
      Symlinks are not changed.

   If rename:
      Create new data file and index file on the same disk as the
      old data and index files.  Create also symlinks to point at
      the new tables.
      Copy data.
      At end, rename temporary tables and symlinks to temporary table
      to final table name.
      Remove old table and old symlinks

    If rename is made to another database:
      Create new tables in new database.
      Copy data.
      Remove old table and symlinks.
  */
  if (!strcmp(db, new_db))		// Ignore symlink if db changed
  {
    if (create_info->index_file_name)
    {
      /* Fix index_file_name to have 'tmp_name' as basename */
      strmov(index_file, tmp_name);
      create_info->index_file_name=fn_same(index_file,
					   create_info->index_file_name,
					   1);
    }
    if (create_info->data_file_name)
    {
      /* Fix data_file_name to have 'tmp_name' as basename */
      strmov(data_file, tmp_name);
      create_info->data_file_name=fn_same(data_file,
					  create_info->data_file_name,
					  1);
    }
  }
6067 6068
  else
    create_info->data_file_name=create_info->index_file_name=0;
monty@mysql.com's avatar
monty@mysql.com committed
6069

6070 6071 6072 6073 6074 6075 6076
  /*
    Create a table with a temporary name.
    With create_info->frm_only == 1 this creates a .frm file only.
    We don't log the statement, it will be logged later.
  */
  tmp_disable_binlog(thd);
  error= mysql_create_table(thd, new_db, tmp_name,
6077
                            create_info,create_list,key_list,1,0,0);
6078 6079 6080 6081 6082
  reenable_binlog(thd);
  if (error)
    DBUG_RETURN(error);

  /* Open the table if we need to copy the data. */
6083
  if (need_copy_table)
6084
  {
6085
    if (table->s->tmp_table)
6086 6087 6088 6089
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
6090
      tbl.table_name= tbl.alias= tmp_name;
6091
      /* Table is in thd->temporary_tables */
6092 6093
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
6094 6095 6096 6097
    }
    else
    {
      char path[FN_REFLEN];
6098
      /* table is a normal table: Create temporary table in same directory */
6099 6100
      build_table_filename(path, sizeof(path), new_db, tmp_name, "",
                           FN_IS_TMP);
6101 6102 6103
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
6104
      goto err1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6105 6106
  }

6107
  /* Copy the data if necessary. */
6108
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6109 6110
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
6111
  copied=deleted=0;
6112
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
6113
  {
6114
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
monty@mysql.com's avatar
monty@mysql.com committed
6115
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
6116
    new_table->next_number_field=new_table->found_next_number_field;
6117
    error=copy_data_between_tables(table, new_table, create_list, ignore,
6118
				   order_num, order, &copied, &deleted);
6119
  }
6120
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6121

6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134
  /* If we did not need to copy, we might still need to add/drop indexes. */
  if (! new_table)
  {
    uint          *key_numbers;
    uint          *keyno_p;
    KEY           *key_info;
    KEY           *key;
    uint          *idx_p;
    uint          *idx_end_p;
    KEY_PART_INFO *key_part;
    KEY_PART_INFO *part_end;
    DBUG_PRINT("info", ("No new_table, checking add/drop index"));

6135
    table->file->prepare_for_alter();
6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150
    if (index_add_count)
    {
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
      if (! need_lock_for_indexes)
      {
        /* Downgrade the write lock. */
        mysql_lock_downgrade_write(thd, table, TL_WRITE_ALLOW_WRITE);
      }

      /* Create a new .frm file for crash recovery. */
      /* TODO: Must set INDEX_TO_BE_ADDED flags in the frm file. */
      VOID(pthread_mutex_lock(&LOCK_open));
      error= (mysql_create_frm(thd, reg_path, db, table_name,
                               create_info, prepared_create_list, key_count,
                               key_info_buffer, table->file) ||
6151
              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
6152
                                                create_info));
6153 6154
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (error)
6155
        goto err1;
6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182
#endif

      /* The add_index() method takes an array of KEY structs. */
      key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count);
      key= key_info;
      for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
           idx_p < idx_end_p;
           idx_p++, key++)
      {
        /* Copy the KEY struct. */
        *key= key_info_buffer[*idx_p];
        /* Fix the key parts. */
        part_end= key->key_part + key->key_parts;
        for (key_part= key->key_part; key_part < part_end; key_part++)
          key_part->field= table->field[key_part->fieldnr];
      }
      /* Add the indexes. */
      if ((error= table->file->add_index(table, key_info, index_add_count)))
      {
        /*
          Exchange the key_info for the error message. If we exchange
          key number by key name in the message later, we need correct info.
        */
        KEY *save_key_info= table->key_info;
        table->key_info= key_info;
        table->file->print_error(error, MYF(0));
        table->key_info= save_key_info;
6183
        goto err1;
6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197
      }
    }
    /*end of if (index_add_count)*/

    if (index_drop_count)
    {
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
      /* Create a new .frm file for crash recovery. */
      /* TODO: Must set INDEX_IS_ADDED in the frm file. */
      /* TODO: Must set INDEX_TO_BE_DROPPED in the frm file. */
      VOID(pthread_mutex_lock(&LOCK_open));
      error= (mysql_create_frm(thd, reg_path, db, table_name,
                               create_info, prepared_create_list, key_count,
                               key_info_buffer, table->file) ||
6198
              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
6199
                                                create_info));
6200 6201
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (error)
6202
        goto err1;
6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235

      if (! need_lock_for_indexes)
      {
        LOCK_PARAM_TYPE lpt;

        lpt.thd= thd;
        lpt.table= table;
        lpt.db= db;
        lpt.table_name= table_name;
        lpt.create_info= create_info;
        lpt.create_list= &create_list;
        lpt.key_count= key_count;
        lpt.key_info_buffer= key_info_buffer;
        abort_and_upgrade_lock(lpt);
      }
#endif

      /* The prepare_drop_index() method takes an array of key numbers. */
      key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
      keyno_p= key_numbers;
      /* Get the number of each key. */
      for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
           idx_p < idx_end_p;
           idx_p++, keyno_p++)
        *keyno_p= *idx_p;
      /*
        Tell the handler to prepare for drop indexes.
        This re-numbers the indexes to get rid of gaps.
      */
      if ((error= table->file->prepare_drop_index(table, key_numbers,
                                                  index_drop_count)))
      {
        table->file->print_error(error, MYF(0));
6236
        goto err1;
6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263
      }

#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020
      if (! need_lock_for_indexes)
      {
        /* Downgrade the lock again. */
        if (table->reginfo.lock_type == TL_WRITE_ALLOW_READ)
        {
          LOCK_PARAM_TYPE lpt;

          lpt.thd= thd;
          lpt.table= table;
          lpt.db= db;
          lpt.table_name= table_name;
          lpt.create_info= create_info;
          lpt.create_list= &create_list;
          lpt.key_count= key_count;
          lpt.key_info_buffer= key_info_buffer;
          close_open_tables_and_downgrade(lpt);
        }
      }
#endif

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
6264
        goto err1;
6265 6266 6267 6268
      }
    }
    /*end of if (index_drop_count)*/

6269 6270 6271 6272
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
6273

6274 6275 6276
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
    if (ha_commit_stmt(thd) || ha_commit(thd))
6277
      goto err1;
6278
    committed= 1;
6279 6280 6281
  }
  /*end of if (! new_table) for add/drop index*/

6282
  if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6283 6284 6285
  {
    /* We changed a temporary table */
    if (error)
6286
      goto err1;
6287 6288 6289 6290 6291 6292
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6293
    /* Remove link to old table and rename the new one */
6294
    close_temporary_table(thd, table, 1, 1);
6295 6296
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
6297
      goto err1;
6298
    /* We don't replicate alter table statement on temporary tables */
6299
    if (!thd->current_stmt_binlog_row_based)
6300
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6301 6302 6303
    goto end_temporary;
  }

6304 6305
  if (new_table)
  {
6306 6307
    /* close temporary table that will be the new table */
    intern_close_table(new_table);
6308 6309
    my_free((gptr) new_table,MYF(0));
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6310 6311 6312
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
6313
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6314 6315 6316
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6317

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6318
  /*
6319 6320
    Data is copied.  Now we rename the old table to a temp name,
    rename the new one to the old name, remove all entries from the old table
6321
    from the cache, free all locks, close the old table and remove it.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6322 6323 6324
  */

  thd->proc_info="rename result table";
6325 6326
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
6327 6328
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
6329
  if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6330 6331 6332 6333
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
6334
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
6335
      VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6336 6337 6338 6339 6340
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

6341
#if !defined( __WIN__)
6342 6343 6344 6345
  if (table->file->has_transactions())
#endif
  {
    /*
6346
      Win32 and InnoDB can't drop a table that is in use, so we must
6347
      close the original table at before doing the rename
6348
    */
6349
    table->s->version= 0;                	// Force removal of table def
6350
    close_cached_table(thd, table);
6351
    table=0;					// Marker that table is closed
6352
    no_table_reopen= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6353
  }
6354
#if !defined( __WIN__)
6355 6356
  else
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6357 6358
#endif

6359

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6360
  error=0;
6361
  if (!need_copy_table)
6362
    new_db_type=old_db_type= NULL; // this type cannot happen in regular ALTER
6363 6364
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6365 6366
  {
    error=1;
6367
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6368 6369
  }
  else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db,
6370
			      new_alias, FN_FROM_IS_TMP) ||
6371 6372 6373
           (new_name != table_name || new_db != db) && // we also do rename
           Table_triggers_list::change_table_name(thd, db, table_name,
                                                  new_db, new_alias))
6374 6375
  {
    /* Try to get everything back. */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6376
    error=1;
6377 6378 6379 6380
    VOID(quick_rm_table(new_db_type,new_db,new_alias, 0));
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
    VOID(mysql_rename_table(old_db_type, db, old_name, db, alias,
                            FN_FROM_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6381 6382 6383
  }
  if (error)
  {
6384 6385 6386 6387
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
6388
    if (table)
6389 6390
    {
      table->s->version= 0;            	        // Force removal of table def
6391
      close_cached_table(thd,table);
6392
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6393 6394 6395
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6396 6397 6398 6399 6400 6401 6402 6403 6404 6405
  if (! need_copy_table)
  {
    if (! table)
    {
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (! (table= open_ltable(thd, table_list, TL_WRITE_ALLOW_READ)))
        goto err;
      VOID(pthread_mutex_lock(&LOCK_open));
    }
    /* Tell the handler that a new frm file is in place. */
6406
    if (table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
6407
                                          create_info))
6408 6409 6410 6411 6412
    {
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
6413
  if (thd->lock || new_name != table_name || no_table_reopen)  // True if WIN32
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6414
  {
6415
    /*
6416 6417
      Not table locking or alter table with rename.
      Free locks and remove old table
6418
    */
6419
    if (table)
6420 6421
    {
      table->s->version= 0;              	// Force removal of table def
6422
      close_cached_table(thd,table);
6423
    }
6424
    VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6425 6426 6427
  }
  else
  {
6428 6429 6430 6431 6432
    /*
      Using LOCK TABLES without rename.
      This code is never executed on WIN32!
      Remove old renamed table, reopen table and get new locks
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6433 6434 6435
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
monty@mysql.com's avatar
monty@mysql.com committed
6436
      /* Mark in-use copies old */
6437
      remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
monty@mysql.com's avatar
monty@mysql.com committed
6438
      /* end threads waiting on lock */
6439
      mysql_lock_abort(thd,table, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6440
    }
6441
    VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6442 6443 6444
    if (close_data_tables(thd,db,table_name) ||
	reopen_tables(thd,1,0))
    {						// This shouldn't happen
6445
      if (table)
6446 6447
      {
        table->s->version= 0;                   // Force removal of table def
6448
	close_cached_table(thd,table);		// Remove lock for table
6449
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6450 6451 6452 6453
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
6454
  VOID(pthread_mutex_unlock(&LOCK_open));
6455
  broadcast_refresh();
6456 6457 6458 6459 6460 6461
  /*
    The ALTER TABLE is always in its own transaction.
    Commit must not be called while LOCK_open is locked. It could call
    wait_if_global_read_lock(), which could create a deadlock if called
    with LOCK_open.
  */
6462 6463 6464 6465 6466 6467 6468 6469
  if (!committed)
  {
    error = ha_commit_stmt(thd);
    if (ha_commit(thd))
      error=1;
    if (error)
      goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6470
  thd->proc_info="end";
6471

6472 6473 6474 6475
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
                      thd->query, thd->query_length,
                      db, table_name);

6476
  DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->current_stmt_binlog_row_based &&
6477 6478
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
6479 6480 6481 6482
  /*
    TODO RONM: This problem needs to handled for Berkeley DB partitions
    as well
  */
6483
  if (ha_check_storage_engine_flag(old_db_type,HTON_FLUSH_AFTER_RENAME))
6484
  {
6485 6486 6487 6488 6489
    /*
      For the alter table to be properly flushed to the logs, we
      have to open the new table.  If not, we get a problem on server
      shutdown.
    */
6490
    char path[FN_REFLEN];
6491
    build_table_filename(path, sizeof(path), new_db, table_name, "", 0);
6492 6493
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
6494
    {
6495 6496
      intern_close_table(table);
      my_free((char*) table, MYF(0));
6497
    }
6498
    else
6499
      sql_print_warning("Could not open table %s.%s after rename\n",
serg@serg.mylan's avatar
serg@serg.mylan committed
6500
                        new_db,table_name);
6501
    ha_flush_logs(old_db_type);
6502
  }
6503
  table_list->table=0;				// For query cache
6504
  query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6505 6506

end_temporary:
6507 6508 6509
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
6510 6511
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6512
  thd->some_tables_deleted=0;
6513
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6514

6515 6516 6517 6518 6519 6520 6521
 err1:
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
6522
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
6523

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6524
 err:
6525
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6526
}
6527
/* mysql_alter_table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6528 6529

static int
6530
copy_data_between_tables(TABLE *from,TABLE *to,
6531
			 List<create_field> &create,
6532
                         bool ignore,
6533
			 uint order_num, ORDER *order,
6534
			 ha_rows *copied,
6535
			 ha_rows *deleted)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6536 6537 6538 6539 6540
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
6541 6542 6543 6544 6545 6546
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
6547
  ha_rows examined_rows;
6548
  bool auto_increment_field_copied= 0;
6549
  ulong save_sql_mode;
6550
  ulonglong prev_insert_id;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6551 6552
  DBUG_ENTER("copy_data_between_tables");

6553 6554 6555 6556 6557 6558
  /*
    Turn off recovery logging since rollback of an alter table is to
    delete the new table so there is no need to log the changes to it.
    
    This needs to be done before external_lock
  */
6559
  error= ha_enable_transaction(thd, FALSE);
6560 6561
  if (error)
    DBUG_RETURN(-1);
6562
  
6563
  if (!(copy= new Copy_field[to->s->fields]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6564 6565
    DBUG_RETURN(-1);				/* purecov: inspected */

6566
  if (to->file->ha_external_lock(thd, F_WRLCK))
6567
    DBUG_RETURN(-1);
6568 6569 6570 6571 6572 6573 6574

  /* We can abort alter table for any table type */
  thd->no_trans_update= 0;
  thd->abort_on_warning= !ignore && test(thd->variables.sql_mode &
                                         (MODE_STRICT_TRANS_TABLES |
                                          MODE_STRICT_ALL_TABLES));

6575
  from->file->info(HA_STATUS_VARIABLE);
6576
  to->file->ha_start_bulk_insert(from->file->stats.records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6577

6578 6579
  save_sql_mode= thd->variables.sql_mode;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6580 6581 6582 6583 6584 6585 6586
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
6587 6588
    {
      if (*ptr == to->next_number_field)
6589
      {
6590
        auto_increment_field_copied= TRUE;
6591 6592 6593 6594 6595 6596 6597 6598 6599
        /*
          If we are going to copy contents of one auto_increment column to
          another auto_increment column it is sensible to preserve zeroes.
          This condition also covers case when we are don't actually alter
          auto_increment column.
        */
        if (def->field == from->found_next_number_field)
          thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6600
      (copy_end++)->set(*ptr,def->field,0);
6601 6602
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6603 6604
  }

6605 6606
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
6607 6608
  if (order)
  {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
6609
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
6610
					      MYF(MY_FAE | MY_ZEROFILL));
6611
    bzero((char*) &tables,sizeof(tables));
6612
    tables.table= from;
6613 6614
    tables.alias= tables.table_name= from->s->table_name.str;
    tables.db=    from->s->db.str;
6615 6616
    error=1;

pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
6617
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
6618
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
6619
		    &tables, fields, all_fields, order) ||
6620
	!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
6621
	(from->sort.found_records = filesort(thd, from, sortorder, length,
6622
					     (SQL_SELECT *) 0, HA_POS_ERROR, 1,
monty@mysql.com's avatar
monty@mysql.com committed
6623 6624
					     &examined_rows)) ==
	HA_POS_ERROR)
6625 6626 6627
      goto err;
  };

6628 6629
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6630
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
6631
  if (ignore)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6632
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
6633
  thd->row_count= 0;
6634
  restore_record(to, s->default_values);        // Create empty record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6635 6636 6637 6638
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
6639
      thd->send_kill_message();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6640 6641 6642
      error= 1;
      break;
    }
6643
    thd->row_count++;
6644 6645
    if (to->next_number_field)
    {
6646
      if (auto_increment_field_copied)
6647
        to->auto_increment_field_not_null= TRUE;
6648 6649 6650
      else
        to->next_number_field->reset();
    }
6651
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6652
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
6653
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6654
      copy_ptr->do_copy(copy_ptr);
6655
    }
6656
    prev_insert_id= to->file->next_insert_id;
6657
    if ((error=to->file->ha_write_row((byte*) to->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6658
    {
6659
      if (!ignore ||
6660
          to->file->is_fatal_error(error, HA_CHECK_DUP))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6661
      {
6662
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
6663 6664 6665 6666 6667 6668
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
             const char *err_msg= ER(ER_DUP_ENTRY);
             if (key_nr == 0 &&
monty@mysql.com's avatar
monty@mysql.com committed
6669 6670
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
6671
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
monty@mysql.com's avatar
monty@mysql.com committed
6672
             to->file->print_keydup_error(key_nr, err_msg);
6673 6674 6675 6676
             break;
           }
         }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6677 6678 6679
	to->file->print_error(error,MYF(0));
	break;
      }
6680
      to->file->restore_auto_increment(prev_insert_id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6681 6682 6683
      delete_count++;
    }
    else
6684
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6685 6686
  }
  end_read_record(&info);
6687
  free_io_cache(from);
6688
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
6689

6690
  if (to->file->ha_end_bulk_insert() && error <= 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6691
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
6692
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6693 6694
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6695
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
6696

6697
  ha_enable_transaction(thd,TRUE);
6698

6699 6700 6701 6702 6703 6704 6705 6706
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
  if (ha_commit_stmt(thd))
    error=1;
  if (ha_commit(thd))
    error=1;
6707

6708
 err:
6709
  thd->variables.sql_mode= save_sql_mode;
6710
  thd->abort_on_warning= 0;
6711
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6712 6713
  *copied= found_count;
  *deleted=delete_count;
6714
  to->file->ha_release_auto_increment();
6715
  if (to->file->ha_external_lock(thd,F_UNLCK))
6716
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6717 6718
  DBUG_RETURN(error > 0 ? -1 : 0);
}
6719

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6720

6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate
    do_send_ok          If we should send_ok() or leave it to caller

 RETURN
    Like mysql_alter_table().
*/
6733 6734
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                          bool do_send_ok)
6735 6736 6737 6738 6739 6740 6741 6742 6743
{
  DBUG_ENTER("mysql_recreate_table");
  LEX *lex= thd->lex;
  HA_CREATE_INFO create_info;
  lex->create_list.empty();
  lex->key_list.empty();
  lex->col_list.empty();
  lex->alter_info.reset();
  bzero((char*) &create_info,sizeof(create_info));
6744
  create_info.db_type= 0;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
6745
  create_info.row_type=ROW_TYPE_NOT_USED;
6746
  create_info.default_table_charset=default_charset_info;
monty@mysql.com's avatar
monty@mysql.com committed
6747
  /* Force alter table to recreate table */
6748
  lex->alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
6749 6750 6751
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
6752
                                0, &lex->alter_info, do_send_ok));
6753 6754 6755
}


6756 6757
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
6758 6759 6760 6761 6762
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
6763
  DBUG_ENTER("mysql_checksum_table");
6764 6765 6766 6767 6768

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
  field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21));
  item->maybe_null= 1;
6769 6770
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
6771
    DBUG_RETURN(TRUE);
6772

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6773
  for (table= tables; table; table= table->next_local)
6774 6775
  {
    char table_name[NAME_LEN*2+2];
6776
    TABLE *t;
6777

6778
    strxmov(table_name, table->db ,".", table->table_name, NullS);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6779

6780
    t= table->table= open_ltable(thd, table, TL_READ);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6781
    thd->clear_error();			// these errors shouldn't get client
6782 6783 6784 6785

    protocol->prepare_for_resend();
    protocol->store(table_name, system_charset_info);

6786
    if (!t)
6787
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6788
      /* Table didn't exist */
6789
      protocol->store_null();
6790
      thd->clear_error();
6791 6792 6793
    }
    else
    {
6794
      t->pos_in_table_list= table;
6795

6796
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
6797 6798
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
6799
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6800
	       (check_opt->flags & T_QUICK))
6801
	protocol->store_null();
6802 6803
      else
      {
6804 6805
	/* calculating table's checksum */
	ha_checksum crc= 0;
6806
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
6807

6808
        t->use_all_columns();
6809

6810
	if (t->file->ha_rnd_init(1))
6811 6812 6813
	  protocol->store_null();
	else
	{
6814
	  for (;;)
6815 6816
	  {
	    ha_checksum row_crc= 0;
6817 6818 6819 6820 6821 6822 6823
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
6824 6825 6826 6827
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
serg@mysql.com's avatar
serg@mysql.com committed
6828 6829 6830
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

6831 6832
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
6833

6834
	    for (uint i= 0; i < t->s->fields; i++ )
6835 6836
	    {
	      Field *f= t->field[i];
6837 6838
	      if ((f->type() == FIELD_TYPE_BLOB) ||
                  (f->type() == MYSQL_TYPE_VARCHAR))
6839 6840 6841 6842 6843 6844 6845
	      {
		String tmp;
		f->val_str(&tmp);
		row_crc= my_checksum(row_crc, (byte*) tmp.ptr(), tmp.length());
	      }
	      else
		row_crc= my_checksum(row_crc, (byte*) f->ptr,
6846
				     f->pack_length());
6847
	    }
6848

6849 6850 6851
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
6852
          t->file->ha_rnd_end();
6853
	}
6854
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6855
      thd->clear_error();
6856 6857 6858 6859 6860 6861 6862 6863
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
6864
  DBUG_RETURN(FALSE);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6865

6866 6867 6868 6869
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
6870
  DBUG_RETURN(TRUE);
6871
}
6872 6873

static bool check_engine(THD *thd, const char *table_name,
6874
                         HA_CREATE_INFO *create_info)
6875
{
6876
  handlerton **new_engine= &create_info->db_type;
6877
  handlerton *req_engine= *new_engine;
6878
  bool no_substitution=
6879
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
6880 6881
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
6882 6883
    return TRUE;

6884
  if (req_engine && req_engine != *new_engine)
6885 6886 6887 6888
  {
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
6889
                       ha_resolve_storage_engine_name(*new_engine),
6890 6891
                       table_name);
  }
6892 6893 6894 6895 6896
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE &&
      ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED))
  {
    if (create_info->used_fields & HA_CREATE_USED_ENGINE)
    {
6897
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
6898
               hton2plugin[(*new_engine)->slot]->name.str, "TEMPORARY");
6899 6900 6901
      *new_engine= 0;
      return TRUE;
    }
6902
    *new_engine= myisam_hton;
6903
  }
6904 6905
  return FALSE;
}