sql_table.cc 221 KB
Newer Older
1
/* Copyright (C) 2000-2004 MySQL AB
unknown's avatar
unknown committed
2 3 4

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6 7 8 9 10 11 12 13 14 15 16 17 18

   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"
19
#include <hash.h>
unknown's avatar
unknown committed
20
#include <myisam.h>
21
#include <my_dir.h>
22 23
#include "sp_head.h"
#include "sql_trigger.h"
24
#include "sql_show.h"
unknown's avatar
unknown committed
25 26 27 28 29

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

unknown's avatar
unknown committed
30 31
int creating_table= 0;        // How many mysql_create_table are running

unknown's avatar
unknown committed
32
const char *primary_key_name="PRIMARY";
unknown's avatar
unknown committed
33 34 35 36

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,
unknown's avatar
unknown committed
37
                                    List<Create_field> &create, bool ignore,
38
				    uint order_num, ORDER *order,
39
				    ha_rows *copied,ha_rows *deleted,
40 41
                                    enum enum_enable_or_disable keys_onoff,
                                    bool error_if_not_empty);
42

unknown's avatar
unknown committed
43
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
unknown's avatar
unknown committed
44
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
45 46 47 48 49 50 51 52
static bool
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
                           Alter_info *alter_info,
                           bool tmp_table,
                           uint *db_options,
                           handler *file, KEY **key_info_buffer,
                           uint *key_count, int select_field_count);
static bool
unknown's avatar
unknown committed
53 54
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
55
                          Alter_info *alter_info);
unknown's avatar
unknown committed
56

57 58 59 60 61 62 63 64 65 66 67 68 69
/*
  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.
*/

70 71
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
72 73 74 75 76 77
  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))
78
  {
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    /* 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.
      */
    }
96
  }
97 98 99

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
100 101 102
}


103 104 105 106 107 108 109 110 111 112 113 114 115
/*
  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.
*/

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

unknown's avatar
unknown committed
122 123
  if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
                                 MYSQL50_TABLE_NAME_PREFIX_LENGTH))
124 125 126
    DBUG_RETURN((uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
                                to_length-1) -
                        (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH)));
127 128 129 130 131 132 133 134
  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;
  }
135 136
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
137 138 139
}


140
/*
141
  Creates path to a file: mysql_data_dir/db/table.ext
142 143

  SYNOPSIS
144
   build_table_filename()
145 146 147 148 149 150 151
     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.
152 153 154 155 156 157

  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".
158 159
    Unless flags indicate a temporary table name.
    'db' is always converted.
160
    'ext' is not converted.
161

162 163 164 165 166
    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.
167

168 169
  RETURN
    path length
170 171 172
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
173
                          const char *table_name, const char *ext, uint flags)
174 175 176 177
{
  uint length;
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
178 179 180 181 182 183 184
  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)));

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


193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
/*
  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
*/

211
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
212
{
213 214
  DBUG_ENTER("build_tmptable_filename");

215 216 217 218
  char *p= strnmov(buff, mysql_tmpdir, bufflen);
  my_snprintf(p, bufflen - (p - buff), "/%s%lx_%lx_%x%s",
              tmp_file_prefix, current_pid,
              thd->thread_id, thd->tmp_table++, reg_ext);
219

220 221 222 223 224 225 226
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }

  uint length= unpack_filename(buff, buff);
227 228
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
229 230
}

unknown's avatar
unknown committed
231 232 233
/*
--------------------------------------------------------------------------

234
   MODULE: DDL log
unknown's avatar
unknown committed
235 236 237 238 239 240 241 242
   -----------------

   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
243
   ddl log while we are executing. These entries are dropped when the
unknown's avatar
unknown committed
244 245 246 247
   operation is completed.

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

248
   There is only one ddl log in the system and it is protected by a mutex
unknown's avatar
unknown committed
249 250 251
   and there is a global struct that contains information about its current
   state.

252 253
   History:
   First version written in 2006 by Mikael Ronstrom
unknown's avatar
unknown committed
254 255 256 257
--------------------------------------------------------------------------
*/


258
typedef struct st_global_ddl_log
unknown's avatar
unknown committed
259
{
260 261 262 263 264 265
  /*
    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.
  */
266
  char file_entry_buf[4*IO_SIZE];
unknown's avatar
unknown committed
267 268
  char file_name_str[FN_REFLEN];
  char *file_name;
269 270 271
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
unknown's avatar
unknown committed
272 273
  File file_id;
  uint name_len;
274
  uint io_size;
275
  bool inited;
276
  bool recovery_phase;
277
} GLOBAL_DDL_LOG;
unknown's avatar
unknown committed
278

279
GLOBAL_DDL_LOG global_ddl_log;
unknown's avatar
unknown committed
280

281
pthread_mutex_t LOCK_gdl;
unknown's avatar
unknown committed
282

283 284 285 286 287
#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
288

289 290
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
291
#define DDL_LOG_IO_SIZE_POS 8
unknown's avatar
unknown committed
292

unknown's avatar
unknown committed
293
/*
294
  Read one entry from ddl log file
unknown's avatar
unknown committed
295
  SYNOPSIS
296
    read_ddl_log_file_entry()
297
    entry_no                     Entry number to read
unknown's avatar
unknown committed
298
  RETURN VALUES
299 300
    TRUE                         Error
    FALSE                        Success
unknown's avatar
unknown committed
301 302
*/

303
static bool read_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
304 305
{
  bool error= FALSE;
306
  File file_id= global_ddl_log.file_id;
307
  uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
308 309
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
unknown's avatar
unknown committed
310

311
  if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
312
               MYF(MY_WME)) != io_size)
unknown's avatar
unknown committed
313 314 315 316 317 318
    error= TRUE;
  DBUG_RETURN(error);
}


/*
319
  Write one entry from ddl log file
unknown's avatar
unknown committed
320
  SYNOPSIS
321
    write_ddl_log_file_entry()
unknown's avatar
unknown committed
322 323 324 325 326 327
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

328
static bool write_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
329 330
{
  bool error= FALSE;
331 332 333
  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");
unknown's avatar
unknown committed
334

335
  if (my_pwrite(file_id, (uchar*)file_entry_buf,
336
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
unknown's avatar
unknown committed
337 338 339 340 341 342
    error= TRUE;
  DBUG_RETURN(error);
}


/*
343
  Write ddl log header
unknown's avatar
unknown committed
344
  SYNOPSIS
345
    write_ddl_log_header()
unknown's avatar
unknown committed
346 347 348 349 350
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

351
static bool write_ddl_log_header()
unknown's avatar
unknown committed
352 353
{
  uint16 const_var;
354
  bool error= FALSE;
355
  DBUG_ENTER("write_ddl_log_header");
unknown's avatar
unknown committed
356

357 358
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
359
  const_var= FN_LEN;
360
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
unknown's avatar
unknown committed
361
            (ulong) const_var);
362
  const_var= IO_SIZE;
363
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
unknown's avatar
unknown committed
364
            (ulong) const_var);
365
  if (write_ddl_log_file_entry(0UL))
366 367 368 369 370
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
371
  DBUG_RETURN(error);
unknown's avatar
unknown committed
372 373 374
}


375
/*
376
  Create ddl log file name
377
  SYNOPSIS
378
    create_ddl_log_file_name()
379 380 381 382 383
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

384
static inline void create_ddl_log_file_name(char *file_name)
385
{
386
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
387 388 389
}


unknown's avatar
unknown committed
390
/*
391
  Read header of ddl log file
unknown's avatar
unknown committed
392
  SYNOPSIS
393
    read_ddl_log_header()
unknown's avatar
unknown committed
394
  RETURN VALUES
395 396
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
unknown's avatar
unknown committed
397
  DESCRIPTION
398 399 400
    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.
unknown's avatar
unknown committed
401 402
*/

403
static uint read_ddl_log_header()
unknown's avatar
unknown committed
404
{
405
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
406
  char file_name[FN_REFLEN];
407
  uint entry_no;
408
  bool successful_open= FALSE;
409
  DBUG_ENTER("read_ddl_log_header");
unknown's avatar
unknown committed
410

411
  create_ddl_log_file_name(file_name);
412 413
  if ((global_ddl_log.file_id= my_open(file_name,
                                        O_RDWR | O_BINARY, MYF(MY_WME))) >= 0)
unknown's avatar
unknown committed
414
  {
415
    if (read_ddl_log_file_entry(0UL))
416
    {
417 418
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
419
    }
420 421
    else
      successful_open= TRUE;
unknown's avatar
unknown committed
422
  }
423 424
  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]);
425 426
  if (successful_open)
  {
427
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
428 429 430
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
431
  else
432 433 434
  {
    entry_no= 0;
  }
435 436 437 438
  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));
unknown's avatar
unknown committed
439
  DBUG_RETURN(entry_no);
unknown's avatar
unknown committed
440 441 442 443
}


/*
444
  Read a ddl log entry
unknown's avatar
unknown committed
445
  SYNOPSIS
446
    read_ddl_log_entry()
unknown's avatar
unknown committed
447 448 449 450 451 452
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
453
    Read a specified entry in the ddl log
unknown's avatar
unknown committed
454 455
*/

456
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
457
{
458
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
459
  uint inx;
460
  uchar single_char;
461
  DBUG_ENTER("read_ddl_log_entry");
462

463
  if (read_ddl_log_file_entry(read_entry))
464 465 466
  {
    DBUG_RETURN(TRUE);
  }
467
  ddl_log_entry->entry_pos= read_entry;
468 469 470 471
  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;
472 473 474 475 476 477 478
  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];
unknown's avatar
unknown committed
479 480 481 482 483
  DBUG_RETURN(FALSE);
}


/*
484
  Initialise ddl log
unknown's avatar
unknown committed
485
  SYNOPSIS
486
    init_ddl_log()
unknown's avatar
unknown committed
487

unknown's avatar
unknown committed
488
  DESCRIPTION
489
    Write the header of the ddl log file and length of names. Also set
unknown's avatar
unknown committed
490
    number of entries to zero.
unknown's avatar
unknown committed
491 492 493 494

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
unknown's avatar
unknown committed
495 496
*/

497
static bool init_ddl_log()
unknown's avatar
unknown committed
498
{
499
  char file_name[FN_REFLEN];
500
  DBUG_ENTER("init_ddl_log");
unknown's avatar
unknown committed
501

502
  if (global_ddl_log.inited)
unknown's avatar
unknown committed
503 504
    goto end;

505
  global_ddl_log.io_size= IO_SIZE;
506
  create_ddl_log_file_name(file_name);
507 508 509 510
  if ((global_ddl_log.file_id= my_create(file_name,
                                         CREATE_MODE,
                                         O_RDWR | O_TRUNC | O_BINARY,
                                         MYF(MY_WME))) < 0)
511
  {
512
    /* Couldn't create ddl log file, this is serious error */
513 514
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
515
  }
unknown's avatar
Fixes  
unknown committed
516
  global_ddl_log.inited= TRUE;
517
  if (write_ddl_log_header())
518
  {
unknown's avatar
unknown committed
519
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
unknown's avatar
Fixes  
unknown committed
520
    global_ddl_log.inited= FALSE;
521
    DBUG_RETURN(TRUE);
522
  }
unknown's avatar
unknown committed
523 524

end:
525
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
526 527 528 529
}


/*
530
  Execute one action in a ddl log entry
unknown's avatar
unknown committed
531
  SYNOPSIS
532 533
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
unknown's avatar
unknown committed
534
  RETURN VALUES
535 536
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
537 538
*/

539
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
540
{
541 542
  bool frm_action= FALSE;
  LEX_STRING handler_name;
543
  handler *file= NULL;
544
  MEM_ROOT mem_root;
545
  int error= TRUE;
546
  char to_path[FN_REFLEN];
547
  char from_path[FN_REFLEN];
548
#ifdef WITH_PARTITION_STORAGE_ENGINE
549
  char *par_ext= (char*)".par";
550
#endif
551
  handlerton *hton;
552
  DBUG_ENTER("execute_ddl_log_action");
553

554
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
555 556 557
  {
    DBUG_RETURN(FALSE);
  }
558 559
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
560
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
561
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
562 563 564
    frm_action= TRUE;
  else
  {
unknown's avatar
unknown committed
565 566
    plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
    if (!plugin)
567 568 569 570
    {
      my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
      goto error;
    }
unknown's avatar
unknown committed
571 572
    hton= plugin_data(plugin, handlerton*);
    file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
573
    if (!file)
574 575
    {
      mem_alloc_error(sizeof(handler));
576
      goto error;
577
    }
578
  }
579
  switch (ddl_log_entry->action_type)
580
  {
581
    case DDL_LOG_REPLACE_ACTION:
582
    case DDL_LOG_DELETE_ACTION:
583
    {
584
      if (ddl_log_entry->phase == 0)
585 586 587
      {
        if (frm_action)
        {
588 589 590
          strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
          if ((error= my_delete(to_path, MYF(MY_WME))))
          {
591
            if (my_errno != ENOENT)
592 593
              break;
          }
594
#ifdef WITH_PARTITION_STORAGE_ENGINE
595 596
          strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
          VOID(my_delete(to_path, MYF(MY_WME)));
597
#endif
598 599 600
        }
        else
        {
601 602 603 604 605
          if ((error= file->delete_table(ddl_log_entry->name)))
          {
            if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
              break;
          }
606
        }
607
        if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
608 609 610
          break;
        VOID(sync_ddl_log());
        error= FALSE;
611 612
        if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
          break;
613
      }
614 615 616 617 618 619
      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.
      */
620
    }
621
    case DDL_LOG_RENAME_ACTION:
622
    {
623 624 625
      error= TRUE;
      if (frm_action)
      {
626
        strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
627
        strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
628
        if (my_rename(from_path, to_path, MYF(MY_WME)))
629
          break;
630
#ifdef WITH_PARTITION_STORAGE_ENGINE
631
        strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
632
        strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
633
        VOID(my_rename(from_path, to_path, MYF(MY_WME)));
634
#endif
635 636 637
      }
      else
      {
638 639
        if (file->rename_table(ddl_log_entry->from_name,
                               ddl_log_entry->name))
640
          break;
641 642
      }
      if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
643 644 645
        break;
      VOID(sync_ddl_log());
      error= FALSE;
646
      break;
647
    }
648 649 650 651 652 653 654 655
    default:
      DBUG_ASSERT(0);
      break;
  }
  delete file;
error:
  free_root(&mem_root, MYF(0)); 
  DBUG_RETURN(error);
unknown's avatar
unknown committed
656 657 658
}


unknown's avatar
unknown committed
659
/*
660
  Get a free entry in the ddl log
unknown's avatar
unknown committed
661
  SYNOPSIS
662 663
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
unknown's avatar
unknown committed
664
  RETURN VALUES
665 666
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
667 668
*/

669 670
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
unknown's avatar
unknown committed
671
{
672 673 674
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
675

676
  if (global_ddl_log.first_free == NULL)
677
  {
678 679
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
680
    {
681
      sql_print_error("Failed to allocate memory for ddl log free list");
682 683
      DBUG_RETURN(TRUE);
    }
684
    global_ddl_log.num_entries++;
685
    used_entry->entry_pos= global_ddl_log.num_entries;
686
    *write_header= TRUE;
687 688 689
  }
  else
  {
690 691
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
692
    *write_header= FALSE;
693 694 695 696 697 698
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
699
  global_ddl_log.first_used= used_entry;
700 701 702 703
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
704
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
705 706 707 708
}


/*
709
  External interface methods for the DDL log Module
710 711 712 713
  ---------------------------------------------------
*/

/*
unknown's avatar
unknown committed
714
  SYNOPSIS
715 716 717
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
718

unknown's avatar
unknown committed
719
  RETURN VALUES
720 721 722 723
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
724
    A careful write of the ddl log is performed to ensure that we can
725
    handle crashes occurring during CREATE and ALTER TABLE processing.
unknown's avatar
unknown committed
726 727
*/

728 729
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
730
{
731
  bool error, write_header;
732 733 734 735 736 737
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
738 739
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
740
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
741
                                    (char)ddl_log_entry->action_type;
742 743 744 745
  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);
746 747
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
748 749
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
unknown's avatar
unknown committed
750
  {
751
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
752 753
    strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
          ddl_log_entry->from_name, FN_LEN - 1);
unknown's avatar
unknown committed
754
  }
755
  else
756 757
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
758 759
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
760
  if (get_free_ddl_log_entry(active_entry, &write_header))
761 762 763 764
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
765
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
766
  {
767
    error= TRUE;
768 769 770
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
771 772
  if (write_header && !error)
  {
773 774
    VOID(sync_ddl_log());
    if (write_ddl_log_header())
775 776
      error= TRUE;
  }
777
  if (error)
778
    release_ddl_log_memory_entry(*active_entry);
779
  DBUG_RETURN(error);
unknown's avatar
unknown committed
780 781 782 783
}


/*
784
  Write final entry in the ddl log
unknown's avatar
unknown committed
785
  SYNOPSIS
786
    write_execute_ddl_log_entry()
787 788 789 790
    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.
791 792 793
    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
794 795 796
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
unknown's avatar
unknown committed
797
  RETURN VALUES
798 799 800 801
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
802
    This is the last write in the ddl log. The previous log entries have
803
    already been written but not yet synched to disk.
804 805 806 807
    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 
808
*/ 
unknown's avatar
unknown committed
809

810 811 812
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
813
{
814
  bool write_header= FALSE;
815 816
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
817

818 819 820 821
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
822 823
  if (!complete)
  {
824 825 826 827 828 829
    /*
      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.
    */
830
    VOID(sync_ddl_log());
831
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
832 833
  }
  else
834
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
835 836 837 838 839 840
  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;
841
  if (!(*active_entry))
842
  {
843
    if (get_free_ddl_log_entry(active_entry, &write_header))
844 845 846
    {
      DBUG_RETURN(TRUE);
    }
847
  }
848
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
849
  {
850
    sql_print_error("Error writing execute entry in ddl log");
851
    release_ddl_log_memory_entry(*active_entry);
852 853
    DBUG_RETURN(TRUE);
  }
854
  VOID(sync_ddl_log());
855 856
  if (write_header)
  {
857
    if (write_ddl_log_header())
858
    {
859
      release_ddl_log_memory_entry(*active_entry);
860 861 862
      DBUG_RETURN(TRUE);
    }
  }
unknown's avatar
unknown committed
863 864 865 866
  DBUG_RETURN(FALSE);
}


867
/*
868
  For complex rename operations we need to deactivate individual entries.
869
  SYNOPSIS
870
    deactivate_ddl_log_entry()
871 872 873 874 875 876 877 878
    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
879
    do in a safe manner unless the ddl log is informed of the phases in
880 881 882 883 884 885 886 887 888 889 890
    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.
*/

891
bool deactivate_ddl_log_entry(uint entry_no)
892
{
893 894
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
895

896
  if (!read_ddl_log_file_entry(entry_no))
897
  {
898
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
899
    {
900 901 902 903 904 905
      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)
906
      {
907 908
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
909 910 911 912 913
      }
      else
      {
        DBUG_ASSERT(0);
      }
914 915 916 917 918 919
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
920 921
    }
  }
922 923 924 925 926 927
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
928 929 930 931
}


/*
932
  Sync ddl log file
933
  SYNOPSIS
934
    sync_ddl_log()
935 936 937 938 939
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

940
bool sync_ddl_log()
941 942
{
  bool error= FALSE;
943
  DBUG_ENTER("sync_ddl_log");
944

945 946
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
947 948 949 950
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
951 952
  {
    /* Write to error log */
953
    sql_print_error("Failed to sync ddl log");
954 955 956 957 958 959
    error= TRUE;
  }
  DBUG_RETURN(error);
}


960 961 962
/*
  Release a log memory entry
  SYNOPSIS
963
    release_ddl_log_memory_entry()
964 965 966 967 968
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

969
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
970
{
971 972 973 974
  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");
975

976
  global_ddl_log.first_free= log_entry;
977 978 979 980 981
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
982
    global_ddl_log.first_used= next_log_entry;
983 984
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
985
  DBUG_VOID_RETURN;
986 987 988
}


unknown's avatar
unknown committed
989
/*
990
  Execute one entry in the ddl log. Executing an entry means executing
unknown's avatar
unknown committed
991 992
  a linked list of actions.
  SYNOPSIS
993
    execute_ddl_log_entry()
unknown's avatar
unknown committed
994 995 996 997 998 999
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1000
bool execute_ddl_log_entry(THD *thd, uint first_entry)
unknown's avatar
unknown committed
1001
{
1002
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
unknown committed
1003
  uint read_entry= first_entry;
1004
  DBUG_ENTER("execute_ddl_log_entry");
unknown's avatar
unknown committed
1005

1006
  pthread_mutex_lock(&LOCK_gdl);
unknown's avatar
unknown committed
1007 1008
  do
  {
1009
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1010 1011
    {
      /* Write to error log and continue with next log entry */
1012 1013
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1014 1015
      break;
    }
1016 1017
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1018

1019
    if (execute_ddl_log_action(thd, &ddl_log_entry))
unknown's avatar
unknown committed
1020
    {
1021
      /* Write to error log and continue with next log entry */
1022 1023
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1024
      break;
unknown's avatar
unknown committed
1025
    }
1026
    read_entry= ddl_log_entry.next_entry;
unknown's avatar
unknown committed
1027
  } while (read_entry);
1028
  pthread_mutex_unlock(&LOCK_gdl);
unknown's avatar
unknown committed
1029 1030 1031
  DBUG_RETURN(FALSE);
}

1032

1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
/*
  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;
}


unknown's avatar
unknown committed
1053
/*
1054
  Execute the ddl log at recovery of MySQL Server
unknown's avatar
unknown committed
1055
  SYNOPSIS
1056
    execute_ddl_log_recovery()
unknown's avatar
unknown committed
1057 1058 1059 1060
  RETURN VALUES
    NONE
*/

1061
void execute_ddl_log_recovery()
unknown's avatar
unknown committed
1062
{
1063
  uint num_entries, i;
unknown's avatar
Fixes  
unknown committed
1064
  THD *thd;
1065
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
Fixes  
unknown committed
1066
  char file_name[FN_REFLEN];
1067
  DBUG_ENTER("execute_ddl_log_recovery");
unknown's avatar
unknown committed
1068

1069 1070 1071 1072 1073 1074 1075
  /*
    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;
unknown's avatar
unknown committed
1076
  global_ddl_log.file_id= (File) -1;
1077

1078 1079 1080 1081 1082 1083 1084 1085
  /*
    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();

1086
  num_entries= read_ddl_log_header();
1087
  for (i= 1; i < num_entries + 1; i++)
unknown's avatar
unknown committed
1088
  {
1089
    if (read_ddl_log_entry(i, &ddl_log_entry))
1090
    {
1091 1092 1093
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1094
    }
1095
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
unknown's avatar
unknown committed
1096
    {
unknown's avatar
Fixes  
unknown committed
1097
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
unknown's avatar
unknown committed
1098
      {
1099 1100
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
unknown's avatar
unknown committed
1101 1102 1103
      }
    }
  }
1104
  close_ddl_log();
1105 1106 1107 1108 1109 1110
  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);
1111
  DBUG_VOID_RETURN;
1112 1113 1114 1115
}


/*
1116
  Release all memory allocated to the ddl log
1117
  SYNOPSIS
1118
    release_ddl_log()
1119 1120 1121 1122
  RETURN VALUES
    NONE
*/

1123
void release_ddl_log()
1124
{
1125 1126 1127
  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");
1128

1129
  pthread_mutex_lock(&LOCK_gdl);
1130 1131
  while (used_list)
  {
1132
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1133
    my_free(used_list, MYF(0));
unknown's avatar
unknown committed
1134
    used_list= tmp;
1135 1136 1137
  }
  while (free_list)
  {
1138
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1139
    my_free(free_list, MYF(0));
unknown's avatar
unknown committed
1140
    free_list= tmp;
1141
  }
1142
  close_ddl_log();
unknown's avatar
unknown committed
1143
  global_ddl_log.inited= 0;
1144
  pthread_mutex_unlock(&LOCK_gdl);
1145
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1146
  DBUG_VOID_RETURN;
1147 1148 1149
}


unknown's avatar
unknown committed
1150 1151 1152
/*
---------------------------------------------------------------------------

1153
  END MODULE DDL log
unknown's avatar
unknown committed
1154 1155 1156 1157 1158
  --------------------

---------------------------------------------------------------------------
*/

unknown's avatar
unknown committed
1159

1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
/**
   @brief construct a temporary shadow file name.

   @details Make a shadow file name used by ALTER TABLE to construct the
   modified table (with keeping the original). The modified table is then
   moved back as original table. The name must start with the temp file
   prefix so it gets filtered out by table files listing routines. 
    
   @param[out] buff      buffer to receive the constructed name
   @param      bufflen   size of buff
   @param      lpt       alter table data structure

   @retval     path length
*/

uint build_table_shadow_filename(char *buff, size_t bufflen, 
                                 ALTER_PARTITION_PARAM_TYPE *lpt)
{
  char tmp_name[FN_REFLEN];
  my_snprintf (tmp_name, sizeof (tmp_name), "%s-%s", tmp_file_prefix,
               lpt->table_name);
  return build_table_filename(buff, bufflen, lpt->db, tmp_name, "", FN_IS_TMP);
}


unknown's avatar
unknown committed
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
/*
  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];
1217 1218
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
unknown's avatar
unknown committed
1219 1220 1221
  char frm_name[FN_REFLEN+1];
  DBUG_ENTER("mysql_write_frm");

1222 1223 1224
  /*
    Build shadow frm file name
  */
1225
  build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
1226
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1227
  if (flags & WFRM_WRITE_SHADOW)
unknown's avatar
unknown committed
1228
  {
1229 1230 1231 1232 1233 1234 1235 1236
    if (mysql_prepare_create_table(lpt->thd, lpt->create_info,
                                   lpt->alter_info,
                                   /*tmp_table*/ 1,
                                   &lpt->db_options,
                                   lpt->table->file,
                                   &lpt->key_info_buffer,
                                   &lpt->key_count,
                                   /*select_field_count*/ 0))
unknown's avatar
unknown committed
1237 1238 1239 1240 1241
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1242 1243 1244 1245 1246
      partition_info *part_info= lpt->table->part_info;
      char *part_syntax_buf;
      uint syntax_len;

      if (part_info)
unknown's avatar
unknown committed
1247
      {
1248
        TABLE_SHARE *share= lpt->table->s;
1249 1250
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1251
                                                         TRUE, TRUE)))
1252 1253 1254 1255
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
1256 1257 1258 1259 1260
        share->partition_info_len= part_info->part_info_len= syntax_len;
        if (share->partition_info_buffer_size < syntax_len + 1)
        {
          share->partition_info_buffer_size= syntax_len+1;
          if (!(share->partition_info=
unknown's avatar
unknown committed
1261
                  (char*) alloc_root(&share->mem_root, syntax_len+1)))
1262 1263 1264 1265
            DBUG_RETURN(TRUE);

        }
        memcpy((char*) share->partition_info, part_syntax_buf, syntax_len + 1);
unknown's avatar
unknown committed
1266 1267 1268
      }
    }
#endif
1269 1270 1271 1272
    /* 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,
1273
                          lpt->alter_info->create_list, lpt->key_count,
1274
                          lpt->key_info_buffer, lpt->table->file)) ||
1275 1276
         lpt->table->file->create_handler_files(shadow_path, NULL,
                                                CHF_CREATE_FLAG,
1277
                                                lpt->create_info))
1278 1279 1280 1281 1282
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
unknown's avatar
unknown committed
1283 1284 1285 1286 1287 1288 1289 1290 1291
  }
  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.
    */
1292 1293
    uchar *data;
    size_t length;
1294
    if (readfrm(shadow_path, &data, &length) ||
unknown's avatar
unknown committed
1295 1296
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
1297 1298
      my_free(data, MYF(MY_ALLOW_ZERO_PTR));
      my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1299 1300 1301 1302
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1303
    error= my_delete(shadow_frm_name, MYF(MY_WME));
unknown's avatar
unknown committed
1304
  }
1305 1306
  if (flags & WFRM_INSTALL_SHADOW)
  {
1307 1308 1309
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1310 1311 1312 1313
    /*
      Build frm file name
    */
    build_table_filename(path, sizeof(path), lpt->db,
1314
                         lpt->table_name, "", 0);
1315 1316 1317 1318
    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.
1319 1320 1321 1322 1323 1324
      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.
1325 1326 1327
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1328
#ifdef WITH_PARTITION_STORAGE_ENGINE
1329
        lpt->table->file->create_handler_files(path, shadow_path,
1330
                                               CHF_DELETE_FLAG, NULL) ||
1331 1332
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1333
#endif
1334
#ifdef WITH_PARTITION_STORAGE_ENGINE
1335
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1336
        lpt->table->file->create_handler_files(path, shadow_path,
1337
                                               CHF_RENAME_FLAG, NULL))
1338 1339 1340
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1341 1342 1343 1344
    {
      error= 1;
    }
    VOID(pthread_mutex_unlock(&LOCK_open));
1345
#ifdef WITH_PARTITION_STORAGE_ENGINE
1346
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1347
    part_info->frm_log_entry= NULL;
1348
    VOID(sync_ddl_log());
1349
#endif
unknown's avatar
unknown committed
1350
  }
1351

unknown's avatar
unknown committed
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384
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);
  }
}

1385

unknown's avatar
unknown committed
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
/*
 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
unknown's avatar
unknown committed
1404 1405
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
1406 1407

*/
unknown's avatar
unknown committed
1408

unknown's avatar
unknown committed
1409 1410
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
1411
{
1412
  bool error= FALSE, need_start_waiters= FALSE;
unknown's avatar
unknown committed
1413 1414 1415 1416
  DBUG_ENTER("mysql_rm_table");

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

1417
  if (!drop_temporary)
unknown's avatar
unknown committed
1418
  {
1419
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1420
    {
1421
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1422
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1423
    }
1424 1425
    else
      need_start_waiters= TRUE;
unknown's avatar
unknown committed
1426
  }
1427 1428 1429 1430 1431 1432

  /*
    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.
  */
unknown's avatar
VIEW  
unknown committed
1433
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1434

1435 1436 1437
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1438
  if (error)
unknown's avatar
unknown committed
1439
    DBUG_RETURN(TRUE);
1440
  send_ok(thd);
unknown's avatar
unknown committed
1441
  DBUG_RETURN(FALSE);
1442 1443
}

1444
/*
1445 1446 1447 1448 1449 1450 1451 1452 1453
  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
unknown's avatar
VIEW  
unknown committed
1454
    drop_view		Allow to delete VIEW .frm
1455 1456
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1457

1458 1459 1460 1461 1462 1463 1464 1465 1466
  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.
1467 1468 1469 1470 1471

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1472
*/
1473 1474

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
unknown's avatar
VIEW  
unknown committed
1475 1476
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1477 1478
{
  TABLE_LIST *table;
1479 1480
  char path[FN_REFLEN], *alias;
  uint path_length;
1481 1482
  String wrong_tables;
  int error;
1483
  int non_temp_tables_count= 0;
unknown's avatar
unknown committed
1484
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1485
  String built_query;
1486 1487
  DBUG_ENTER("mysql_rm_table_part2");

1488 1489 1490
  LINT_INIT(alias);
  LINT_INIT(path_length);

1491
  if (thd->current_stmt_binlog_row_based && !dont_log_query)
1492 1493 1494 1495 1496 1497 1498
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1499 1500 1501

  pthread_mutex_lock(&LOCK_open);

unknown's avatar
unknown committed
1502 1503 1504 1505 1506 1507 1508 1509 1510
  /*
    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;
unknown's avatar
unknown committed
1511
    table->db_type= NULL;
unknown's avatar
unknown committed
1512
    if ((share= get_cached_table_share(table->db, table->table_name)))
unknown's avatar
unknown committed
1513
      table->db_type= share->db_type();
1514 1515 1516

    /* Disable drop of enabled log tables */
    if (share && share->log_table &&
1517 1518
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1519
    {
1520
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1521
      pthread_mutex_unlock(&LOCK_open);
1522 1523
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
1524 1525
  }

1526 1527
  if (!drop_temporary && lock_table_names_exclusively(thd, tables))
  {
1528
    pthread_mutex_unlock(&LOCK_open);
1529
    DBUG_RETURN(1);
1530
  }
1531

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

unknown's avatar
VIEW  
unknown committed
1535
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
1536
  {
1537
    char *db=table->db;
unknown's avatar
unknown committed
1538 1539
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1540

1541
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, 1);
unknown's avatar
unknown committed
1542
    if (!close_temporary_table(thd, table))
unknown's avatar
unknown committed
1543
    {
1544
      tmp_table_deleted=1;
unknown's avatar
unknown committed
1545
      continue;					// removed temporary table
unknown's avatar
unknown committed
1546
    }
unknown's avatar
unknown committed
1547

1548 1549 1550 1551 1552 1553
    /*
      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.
      */
1554
    if (thd->current_stmt_binlog_row_based && !dont_log_query)
1555
    {
1556
      non_temp_tables_count++;
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
      /*
        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("`,");
    }

unknown's avatar
unknown committed
1572
    error=0;
unknown's avatar
unknown committed
1573
    table_type= table->db_type;
1574
    if (!drop_temporary)
unknown's avatar
unknown committed
1575
    {
1576
      TABLE *locked_table;
unknown's avatar
unknown committed
1577 1578 1579 1580
      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);
1581 1582 1583 1584 1585 1586 1587
      /*
        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;

1588
      if (thd->killed)
1589
      {
1590 1591
        error= -1;
        goto err_with_placeholders;
1592
      }
1593
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
unknown's avatar
unknown committed
1594
      /* remove .frm file and engine files */
1595
      path_length= build_table_filename(path, sizeof(path),
1596
                                        db, alias, reg_ext, 0);
unknown's avatar
unknown committed
1597
    }
1598 1599
    if (drop_temporary ||
        (table_type == NULL &&        
unknown's avatar
unknown committed
1600 1601 1602
         (access(path, F_OK) &&
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
unknown's avatar
unknown committed
1603
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
unknown's avatar
unknown committed
1604
    {
1605
      // Table was not found on disk and table can't be created from engine
1606
      if (if_exists)
1607 1608
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
1609
			    table->table_name);
1610
      else
1611
        error= 1;
unknown's avatar
unknown committed
1612 1613 1614
    }
    else
    {
unknown's avatar
unknown committed
1615
      char *end;
unknown's avatar
unknown committed
1616 1617 1618 1619 1620
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
1621 1622
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
unknown's avatar
unknown committed
1623
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
1624
                             !dont_log_query);
1625
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
unknown's avatar
unknown committed
1626
	  (if_exists || table_type == NULL))
1627
	error= 0;
unknown's avatar
unknown committed
1628
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
1629 1630
      {
	/* the table is referenced by a foreign key constraint */
1631
	foreign_key_error=1;
unknown's avatar
unknown committed
1632
      }
1633
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
1634
      {
1635
        int new_error;
unknown's avatar
unknown committed
1636 1637
	/* Delete the table definition file */
	strmov(end,reg_ext);
1638
	if (!(new_error=my_delete(path,MYF(MY_WME))))
1639
        {
unknown's avatar
unknown committed
1640
	  some_tables_deleted=1;
1641 1642
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
1643
        }
1644
        error|= new_error;
1645
      }
unknown's avatar
unknown committed
1646 1647 1648 1649 1650
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
1651
      wrong_tables.append(String(table->table_name,system_charset_info));
unknown's avatar
unknown committed
1652 1653
    }
  }
1654 1655 1656 1657 1658
  /*
    It's safe to unlock LOCK_open: we have an exclusive lock
    on the table name.
  */
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1659
  thd->tmp_table_used= tmp_table_deleted;
1660 1661 1662 1663
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
1664
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
1665
                      wrong_tables.c_ptr());
1666
    else
unknown's avatar
unknown committed
1667
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
1668 1669 1670 1671
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
1672
  {
unknown's avatar
unknown committed
1673
    query_cache_invalidate3(thd, tables, 0);
1674
    if (!dont_log_query)
1675
    {
1676
      if (!thd->current_stmt_binlog_row_based ||
1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
          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);
      }
1688
      else if (thd->current_stmt_binlog_row_based &&
1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
               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.
      */
1716
    }
unknown's avatar
unknown committed
1717
  }
1718 1719
  pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
1720
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
1721
  pthread_mutex_unlock(&LOCK_open);
1722
  thd->no_warnings_for_error= 0;
1723
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1724 1725 1726
}


1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741
/*
  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
*/

unknown's avatar
unknown committed
1742
bool quick_rm_table(handlerton *base,const char *db,
1743
                    const char *table_name, uint flags)
unknown's avatar
unknown committed
1744 1745
{
  char path[FN_REFLEN];
unknown's avatar
unknown committed
1746 1747 1748
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

1749
  uint path_length= build_table_filename(path, sizeof(path),
1750
                                         db, table_name, reg_ext, flags);
unknown's avatar
unknown committed
1751
  if (my_delete(path,MYF(0)))
unknown's avatar
unknown committed
1752
    error= 1; /* purecov: inspected */
1753
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
unknown's avatar
unknown committed
1754 1755
  DBUG_RETURN(ha_delete_table(current_thd, base, path, db, table_name, 0) ||
              error);
unknown's avatar
unknown committed
1756 1757
}

1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775
/*
  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;
1776
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
1777 1778
    {
      /* Sort NOT NULL keys before other keys */
1779
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792
    }
    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;
  }
unknown's avatar
unknown committed
1793
  /*
1794
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
1795 1796 1797 1798 1799
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
1800 1801
}

1802 1803
/*
  Check TYPELIB (set or enum) for duplicates
1804

1805 1806 1807
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
1808 1809
    name	  name of the checked column
    typelib	  list of values for the column
1810
    dup_val_count  returns count of duplicate elements
1811 1812

  DESCRIPTION
1813
    This function prints an warning for each value in list
1814 1815 1816
    which has some duplicates on its right

  RETURN VALUES
1817 1818
    0             ok
    1             Error
1819 1820
*/

1821
bool check_duplicates_in_interval(const char *set_or_name,
1822
                                  const char *name, TYPELIB *typelib,
1823
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
1824
{
1825
  TYPELIB tmp= *typelib;
1826
  const char **cur_value= typelib->type_names;
1827
  unsigned int *cur_length= typelib->type_lengths;
1828
  *dup_val_count= 0;  
1829 1830
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
1831
  {
1832 1833 1834 1835
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
1836
    {
1837 1838 1839 1840 1841 1842 1843
      if ((current_thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
                 name,*cur_value,set_or_name);
        return 1;
      }
unknown's avatar
unknown committed
1844
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
1845 1846 1847
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
1848
      (*dup_val_count)++;
1849 1850
    }
  }
1851
  return 0;
1852
}
1853

1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888

/*
  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);
  }
}


unknown's avatar
unknown committed
1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
/*
  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
unknown's avatar
unknown committed
1900
    This function prepares a Create_field instance.
unknown's avatar
unknown committed
1901 1902 1903 1904 1905 1906 1907
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

unknown's avatar
unknown committed
1908
int prepare_create_field(Create_field *sql_field, 
unknown's avatar
unknown committed
1909 1910
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
1911
			 longlong table_flags)
unknown's avatar
unknown committed
1912
{
1913
  unsigned int dup_val_count;
unknown's avatar
unknown committed
1914
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
1915 1916

  /*
1917
    This code came from mysql_prepare_create_table.
unknown's avatar
unknown committed
1918 1919 1920 1921 1922
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
1923 1924 1925 1926
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
1927 1928 1929 1930 1931 1932 1933
    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;
1934
    (*blob_columns)++;
unknown's avatar
unknown committed
1935
    break;
1936
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
1937
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
1938 1939 1940 1941
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
unknown's avatar
unknown committed
1942
      DBUG_RETURN(1);
unknown's avatar
unknown committed
1943 1944 1945 1946 1947 1948 1949 1950
    }
    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;
1951
    (*blob_columns)++;
unknown's avatar
unknown committed
1952 1953 1954 1955 1956
    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);
unknown's avatar
unknown committed
1957
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
1958
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
1959
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
1960 1961 1962 1963 1964 1965 1966 1967
    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)
unknown's avatar
unknown committed
1968
      {
unknown's avatar
unknown committed
1969 1970
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
unknown's avatar
unknown committed
1971 1972
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
1973 1974 1975
    }
#endif
    /* fall through */
1976
  case MYSQL_TYPE_STRING:
unknown's avatar
unknown committed
1977 1978 1979 1980
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
1981
  case MYSQL_TYPE_ENUM:
unknown's avatar
unknown committed
1982 1983 1984 1985 1986
    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;
1987 1988 1989 1990
    if (check_duplicates_in_interval("ENUM",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
unknown's avatar
unknown committed
1991
    break;
1992
  case MYSQL_TYPE_SET:
unknown's avatar
unknown committed
1993 1994 1995 1996 1997
    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;
1998 1999 2000 2001
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2002 2003 2004 2005 2006 2007
    /* Check that count of unique members is not more then 64 */
    if (sql_field->interval->count -  dup_val_count > sizeof(longlong)*8)
    {
       my_error(ER_TOO_BIG_SET, MYF(0), sql_field->field_name);
       DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
2008
    break;
2009 2010 2011 2012 2013
  case MYSQL_TYPE_DATE:			// Rest of string types
  case MYSQL_TYPE_NEWDATE:
  case MYSQL_TYPE_TIME:
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_NULL:
unknown's avatar
unknown committed
2014 2015
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2016
  case MYSQL_TYPE_BIT:
unknown's avatar
unknown committed
2017
    /* 
2018 2019
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
unknown's avatar
unknown committed
2020
    */
unknown's avatar
unknown committed
2021
    break;
2022
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
2023 2024 2025 2026 2027 2028 2029
    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;
2030
  case MYSQL_TYPE_TIMESTAMP:
unknown's avatar
unknown committed
2031 2032 2033
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2034
      if (!*timestamps)
unknown's avatar
unknown committed
2035
      {
unknown's avatar
unknown committed
2036
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2037
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2038
      }
unknown's avatar
unknown committed
2039 2040 2041 2042
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2043
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2044

2045
    (*timestamps)++;
unknown's avatar
unknown committed
2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060
    /* 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;
unknown's avatar
unknown committed
2061 2062 2063
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2064
/*
2065
  Preparation for table creation
unknown's avatar
unknown committed
2066 2067

  SYNOPSIS
2068
    mysql_prepare_create_table()
2069 2070
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2071
      alter_info                List of columns and indexes to create
2072 2073 2074 2075 2076 2077
      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.
unknown's avatar
unknown committed
2078

2079
  DESCRIPTION
2080
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
2081

2082
  NOTES
2083
    sets create_info->varchar if the table has a varchar
2084

unknown's avatar
unknown committed
2085
  RETURN VALUES
2086 2087
    FALSE    OK
    TRUE     error
unknown's avatar
unknown committed
2088
*/
unknown's avatar
unknown committed
2089

2090 2091 2092 2093 2094 2095 2096
static bool
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
                           Alter_info *alter_info,
                           bool tmp_table,
                           uint *db_options,
                           handler *file, KEY **key_info_buffer,
                           uint *key_count, int select_field_count)
unknown's avatar
unknown committed
2097
{
2098
  const char	*key_name;
unknown's avatar
unknown committed
2099
  Create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
2100
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
2101
  ulong		record_offset= 0;
2102
  KEY		*key_info;
unknown's avatar
unknown committed
2103
  KEY_PART_INFO *key_part_info;
2104 2105 2106
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
unknown's avatar
unknown committed
2107 2108
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
unknown's avatar
unknown committed
2109
  uint total_uneven_bit_length= 0;
2110
  DBUG_ENTER("mysql_prepare_create_table");
unknown's avatar
unknown committed
2111

2112
  select_field_pos= alter_info->create_list.elements - select_field_count;
unknown's avatar
unknown committed
2113
  null_fields=blob_columns=0;
2114
  create_info->varchar= 0;
unknown's avatar
unknown committed
2115
  max_key_length= file->max_key_length();
2116

2117
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
2118
  {
2119 2120
    CHARSET_INFO *save_cs;

2121 2122 2123 2124 2125 2126
    /*
      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;
2127
    if (!sql_field->charset)
2128 2129 2130
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2131 2132 2133
      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.
2134
    */
2135
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2136
      sql_field->charset= create_info->table_charset;
2137

2138
    save_cs= sql_field->charset;
2139 2140 2141 2142 2143
    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];
2144 2145
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2146
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2147
      DBUG_RETURN(TRUE);
2148
    }
2149

2150
    /*
2151
      Convert the default value from client character
2152 2153 2154
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2155
        save_cs != sql_field->def->collation.collation &&
2156 2157 2158 2159
        (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
         sql_field->sql_type == MYSQL_TYPE_STRING ||
         sql_field->sql_type == MYSQL_TYPE_SET ||
         sql_field->sql_type == MYSQL_TYPE_ENUM))
2160
    {
2161
      /*
unknown's avatar
unknown committed
2162
        Starting from 5.1 we work here with a copy of Create_field
2163 2164 2165 2166 2167 2168 2169 2170
        created by the caller, not with the instance that was
        originally created during parsing. It's OK to create
        a temporary item and initialize with it a member of the
        copy -- this item will be thrown away along with the copy
        at the end of execution, and thus not introduce a dangling
        pointer in the parsed tree of a prepared statement or a
        stored procedure statement.
      */
2171
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2172 2173 2174 2175 2176

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

2181 2182
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2183 2184 2185
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2186
      TYPELIB *interval= sql_field->interval;
2187 2188 2189 2190 2191 2192

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2193
      if (!interval)
2194
      {
2195
        /*
2196 2197 2198
          Create the typelib in runtime memory - we will free the
          occupied memory at the same time when we free this
          sql_field -- at the end of execution.
2199
        */
2200
        interval= sql_field->interval= typelib(thd->mem_root,
2201
                                               sql_field->interval_list);
2202
        List_iterator<String> int_it(sql_field->interval_list);
2203
        String conv, *tmp;
2204 2205 2206 2207 2208
        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);
2209
        for (uint i= 0; (tmp= int_it++); i++)
2210
        {
unknown's avatar
unknown committed
2211
          uint lengthsp;
2212 2213 2214 2215 2216
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2217
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
unknown's avatar
unknown committed
2218
                                                  conv.length());
2219 2220
            interval->type_lengths[i]= conv.length();
          }
2221

2222
          // Strip trailing spaces.
unknown's avatar
unknown committed
2223 2224
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2225 2226
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2227
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2228 2229 2230 2231 2232
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
unknown's avatar
unknown committed
2233
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2234
              DBUG_RETURN(TRUE);
2235 2236
            }
          }
2237
        }
2238
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2239 2240
      }

2241
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2242
      {
2243
        uint32 field_length;
2244
        if (sql_field->def != NULL)
2245 2246 2247 2248 2249
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2250 2251 2252 2253 2254
          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);
2255
              DBUG_RETURN(TRUE);
2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267
            }

            /* 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);
          }

2268 2269 2270
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2271
            DBUG_RETURN(TRUE);
2272 2273
          }
        }
2274 2275
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2276
      }
2277
      else  /* MYSQL_TYPE_ENUM */
2278
      {
2279
        uint32 field_length;
2280
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2281
        if (sql_field->def != NULL)
2282 2283
        {
          String str, *def= sql_field->def->val_str(&str);
2284
          if (def == NULL) /* SQL "NULL" maps to NULL */
2285
          {
2286 2287 2288
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2289
              DBUG_RETURN(TRUE);
2290 2291 2292 2293 2294 2295 2296 2297 2298 2299
            }

            /* 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);
2300
              DBUG_RETURN(TRUE);
2301
            }
2302 2303
          }
        }
2304 2305
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2306 2307 2308 2309
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2310
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2311
    { 
unknown's avatar
unknown committed
2312
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2313
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2314 2315 2316 2317 2318
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2319
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
2320
    if (prepare_blob_field(thd, sql_field))
2321
      DBUG_RETURN(TRUE);
2322

unknown's avatar
unknown committed
2323 2324
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
2325

unknown's avatar
unknown committed
2326 2327
    if (check_column_name(sql_field->field_name))
    {
2328
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2329
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2330
    }
unknown's avatar
unknown committed
2331

2332 2333
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
unknown's avatar
unknown committed
2334
    {
2335
      if (my_strcasecmp(system_charset_info,
2336 2337
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
2338
      {
2339 2340 2341 2342
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2343 2344
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2345
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2346
	  DBUG_RETURN(TRUE);
2347 2348 2349
	}
	else
	{
2350
	  /* Field redefined */
2351
	  sql_field->def=		dup_field->def;
2352
	  sql_field->sql_type=		dup_field->sql_type;
2353 2354 2355
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2356
	  sql_field->length=		dup_field->char_length;
2357
          sql_field->pack_length=	dup_field->pack_length;
2358
          sql_field->key_length=	dup_field->key_length;
2359
	  sql_field->create_length_to_internal_length();
2360 2361
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->unireg_check=	dup_field->unireg_check;
2362 2363 2364 2365 2366 2367 2368 2369
          /* 
            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;
unknown's avatar
unknown committed
2370
          sql_field->interval=          dup_field->interval;
2371 2372 2373
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2374
	}
unknown's avatar
unknown committed
2375 2376
      }
    }
2377 2378 2379 2380
    /* 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)
2381
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
2382 2383
    it2.rewind();
  }
2384 2385 2386

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
2387
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
2388 2389 2390 2391

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

unknown's avatar
unknown committed
2394 2395
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2396
			     file->ha_table_flags()))
2397
      DBUG_RETURN(TRUE);
2398
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2399
      create_info->varchar= TRUE;
2400
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
2401 2402
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2403
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
2404
  }
2405 2406
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
2407 2408
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2409
    DBUG_RETURN(TRUE);
2410
  }
unknown's avatar
unknown committed
2411 2412
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
2413
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2414
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2415 2416
  }
  if (auto_increment &&
2417
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
2418
  {
unknown's avatar
unknown committed
2419 2420
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2421
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2422 2423
  }

2424
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
2425
  {
unknown's avatar
unknown committed
2426 2427
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2428
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2429 2430 2431
  }

  /* Create keys */
2432

2433 2434
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2435
  uint key_parts=0, fk_key_count=0;
2436
  bool primary_key=0,unique_key=0;
2437
  Key *key, *key2;
unknown's avatar
unknown committed
2438
  uint tmp, key_number;
2439 2440
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2441

2442
  /* Calculate number of key segements */
2443
  *key_count= 0;
2444

unknown's avatar
unknown committed
2445 2446
  while ((key=key_iterator++))
  {
2447 2448
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2449
    LEX_STRING key_name_str;
2450 2451 2452
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
unknown's avatar
unknown committed
2453
      Foreign_key *fk_key= (Foreign_key*) key;
2454 2455 2456
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2457 2458 2459
        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));
2460
	DBUG_RETURN(TRUE);
2461 2462 2463
      }
      continue;
    }
2464
    (*key_count)++;
unknown's avatar
unknown committed
2465
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
2466 2467 2468
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2469
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2470
    }
2471 2472 2473 2474
    key_name_str.str= (char*) key->name;
    key_name_str.length= key->name ? strlen(key->name) : 0;
    if (check_string_char_length(&key_name_str, "", NAME_CHAR_LEN,
                                 system_charset_info, 1))
unknown's avatar
unknown committed
2475
    {
2476
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
2477
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2478
    }
2479
    key_iterator2.rewind ();
2480
    if (key->type != Key::FOREIGN_KEY)
2481
    {
2482
      while ((key2 = key_iterator2++) != key)
2483
      {
unknown's avatar
unknown committed
2484
	/*
2485 2486 2487
          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.
unknown's avatar
unknown committed
2488
        */
2489 2490 2491
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2492
        {
2493
          /* TODO: issue warning message */
2494 2495 2496 2497 2498 2499 2500
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2501 2502 2503
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2504 2505 2506
          }
          break;
        }
2507 2508 2509 2510 2511 2512
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2513
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2514 2515 2516
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
2517
      DBUG_RETURN(TRUE);
2518
    }
2519
  }
unknown's avatar
unknown committed
2520
  tmp=file->max_keys();
2521
  if (*key_count > tmp)
2522 2523
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
2524
    DBUG_RETURN(TRUE);
2525
  }
2526

2527
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
2528
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
2529
  if (!*key_info_buffer || ! key_part_info)
2530
    DBUG_RETURN(TRUE);				// Out of memory
2531

2532
  key_iterator.rewind();
unknown's avatar
unknown committed
2533
  key_number=0;
unknown's avatar
unknown committed
2534
  for (; (key=key_iterator++) ; key_number++)
2535 2536
  {
    uint key_length=0;
unknown's avatar
unknown committed
2537
    Key_part_spec *column;
2538

2539 2540 2541 2542 2543 2544 2545 2546 2547 2548
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

2549
    switch (key->type) {
unknown's avatar
unknown committed
2550
    case Key::MULTIPLE:
2551
	key_info->flags= 0;
2552
	break;
unknown's avatar
unknown committed
2553
    case Key::FULLTEXT:
2554
	key_info->flags= HA_FULLTEXT;
unknown's avatar
unknown committed
2555
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
2556
          key_info->flags|= HA_USES_PARSER;
2557 2558
        else
          key_info->parser_name= 0;
2559
	break;
unknown's avatar
unknown committed
2560
    case Key::SPATIAL:
unknown's avatar
unknown committed
2561
#ifdef HAVE_SPATIAL
2562
	key_info->flags= HA_SPATIAL;
2563
	break;
unknown's avatar
unknown committed
2564
#else
2565 2566
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
2567
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2568
#endif
2569 2570 2571 2572
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
2573 2574
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
2575
    }
2576 2577
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
2578

unknown's avatar
unknown committed
2579 2580
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
2581
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
2582
    key_info->algorithm= key->key_create_info.algorithm;
unknown's avatar
unknown committed
2583

2584 2585
    if (key->type == Key::FULLTEXT)
    {
2586
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
2587
      {
unknown's avatar
unknown committed
2588 2589
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
2590
	DBUG_RETURN(TRUE);
2591 2592
      }
    }
unknown's avatar
unknown committed
2593 2594 2595
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
2596
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
2597 2598 2599
       in near future when new frm file is ready
       checking for proper key parts number:
    */
2600

2601
    /* TODO: Add proper checks if handler supports key_type and algorithm */
2602
    if (key_info->flags & HA_SPATIAL)
2603
    {
2604
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
2605 2606 2607
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
2608
        DBUG_RETURN(TRUE);
2609
      }
2610 2611
      if (key_info->key_parts != 1)
      {
2612
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
2613
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2614
      }
2615
    }
unknown's avatar
unknown committed
2616
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
2617
    {
unknown's avatar
unknown committed
2618
#ifdef HAVE_RTREE_KEYS
2619 2620
      if ((key_info->key_parts & 1) == 1)
      {
2621
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
2622
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2623
      }
2624
      /* TODO: To be deleted */
2625
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
2626
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2627
#else
2628 2629
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
2630
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2631
#endif
unknown's avatar
unknown committed
2632
    }
2633

2634 2635 2636 2637 2638
    /* 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
    */
unknown's avatar
unknown committed
2639 2640
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2641 2642 2643 2644 2645
                           create_info->key_block_size);

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

unknown's avatar
unknown committed
2646
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
2647
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
2648 2649
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2650
      uint length;
unknown's avatar
unknown committed
2651
      Key_part_spec *dup_column;
unknown's avatar
unknown committed
2652

unknown's avatar
unknown committed
2653 2654 2655
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
2656
	     my_strcasecmp(system_charset_info,
2657 2658
			   column->field_name,
			   sql_field->field_name))
unknown's avatar
unknown committed
2659 2660 2661
	field++;
      if (!sql_field)
      {
2662
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
2663
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2664
      }
unknown's avatar
unknown committed
2665
      while ((dup_column= cols2++) != column)
2666 2667 2668 2669 2670 2671 2672
      {
        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);
2673
	  DBUG_RETURN(TRUE);
2674 2675 2676
	}
      }
      cols2.rewind();
2677
      if (key->type == Key::FULLTEXT)
2678
      {
2679 2680
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
2681 2682
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
2683
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
2684 2685
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
2686
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697
	    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));
2698
      }
2699
      else
2700
      {
2701 2702
	column->length*= sql_field->charset->mbmaxlen;

unknown's avatar
unknown committed
2703 2704 2705
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
2706
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2707 2708
	}

2709 2710
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
2711
	{
2712
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
2713
	  {
2714
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
2715
	    DBUG_RETURN(TRUE);
2716
	  }
2717 2718 2719
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
            column->length= 21;
2720 2721
	  if (!column->length)
	  {
2722
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
2723
	    DBUG_RETURN(TRUE);
2724 2725
	  }
	}
unknown's avatar
unknown committed
2726
#ifdef HAVE_SPATIAL
2727
	if (key->type == Key::SPATIAL)
2728
	{
2729
	  if (!column->length)
2730 2731
	  {
	    /*
2732 2733
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
2734
	    */
2735
	    column->length= 4*sizeof(double);
2736 2737
	  }
	}
unknown's avatar
unknown committed
2738
#endif
2739 2740 2741 2742 2743 2744 2745
	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;
unknown's avatar
unknown committed
2746
            null_fields--;
2747 2748
	  }
	  else
2749 2750 2751 2752 2753
          {
            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);
2754
              DBUG_RETURN(TRUE);
2755 2756 2757 2758 2759
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
2760
              DBUG_RETURN(TRUE);
2761 2762
            }
          }
2763 2764 2765
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
2766
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
2767 2768
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
2769
      }
2770

unknown's avatar
unknown committed
2771 2772 2773
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
2774 2775
      length= sql_field->key_length;

unknown's avatar
unknown committed
2776 2777 2778 2779
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
2780
	  if ((length=column->length) > max_key_length ||
2781
	      length > file->max_key_part_length())
2782
	  {
unknown's avatar
unknown committed
2783
	    length=min(max_key_length, file->max_key_part_length());
2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795
	    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);
2796
	      DBUG_RETURN(TRUE);
2797 2798
	    }
	  }
unknown's avatar
unknown committed
2799
	}
2800
	else if (!f_is_geom(sql_field->pack_flag) &&
2801
		  (column->length > length ||
unknown's avatar
unknown committed
2802
                   !Field::type_can_have_key_part (sql_field->sql_type) ||
2803
		   ((f_is_packed(sql_field->pack_flag) ||
2804
		     ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
2805 2806 2807
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
unknown's avatar
unknown committed
2808
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
2809
	  DBUG_RETURN(TRUE);
2810
	}
2811
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
2812
	  length=column->length;
unknown's avatar
unknown committed
2813 2814 2815
      }
      else if (length == 0)
      {
2816
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
2817
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2818
      }
2819
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
2820
      {
2821 2822 2823
        length= file->max_key_part_length();
        /* Align key length to multibyte char boundary */
        length-= length % sql_field->charset->mbmaxlen;
2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835
	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);
2836
	  DBUG_RETURN(TRUE);
2837
	}
2838 2839
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
2840
      /* Use packed keys for long strings on the first column */
2841
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
2842
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
2843 2844
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
2845 2846
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
2847 2848 2849
	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;
unknown's avatar
unknown committed
2850 2851 2852 2853 2854 2855 2856 2857 2858 2859
	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)
2860 2861 2862
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
2863 2864
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
2865
	    DBUG_RETURN(TRUE);
2866 2867 2868 2869
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
2870
	else if (!(key_name = key->name))
unknown's avatar
unknown committed
2871
	  key_name=make_unique_key_name(sql_field->field_name,
2872 2873
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
2874
	{
2875
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
2876
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2877 2878 2879 2880
	}
	key_info->name=(char*) key_name;
      }
    }
2881 2882
    if (!key_info->name || check_column_name(key_info->name))
    {
2883
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
2884
      DBUG_RETURN(TRUE);
2885
    }
2886 2887
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
2888
    key_info->key_length=(uint16) key_length;
2889
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
2890
    {
2891
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
2892
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2893
    }
unknown's avatar
unknown committed
2894
    key_info++;
unknown's avatar
unknown committed
2895
  }
2896
  if (!unique_key && !primary_key &&
2897
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
2898
  {
unknown's avatar
unknown committed
2899
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
2900
    DBUG_RETURN(TRUE);
2901
  }
unknown's avatar
unknown committed
2902 2903
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
2904
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2905
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2906
  }
2907
  /* Sort keys in optimized order */
2908
  qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
2909
	(qsort_cmp) sort_keys);
unknown's avatar
unknown committed
2910
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
2911

2912
  DBUG_RETURN(FALSE);
2913 2914
}

2915

unknown's avatar
unknown committed
2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931
/*
  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)
{
2932 2933 2934 2935 2936
  /*
    If the table character set was not given explicitly,
    let's fetch the database default character set and
    apply it to the table.
  */
unknown's avatar
unknown committed
2937 2938 2939
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
2940 2941 2942

    load_db_opt_by_name(thd, db, &db_info);

unknown's avatar
unknown committed
2943 2944 2945 2946 2947
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


unknown's avatar
unknown committed
2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960
/*
  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
*/

unknown's avatar
unknown committed
2961
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
unknown's avatar
unknown committed
2962 2963 2964 2965 2966 2967 2968 2969 2970
{
  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];

2971 2972
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
2973 2974 2975 2976 2977
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
2978
    sql_field->sql_type= MYSQL_TYPE_BLOB;
unknown's avatar
unknown committed
2979 2980
    sql_field->flags|= BLOB_FLAG;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
2981
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
2982 2983 2984 2985 2986 2987 2988
            (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)
  {
2989
    if (sql_field->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000
    {
      /* 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);
}


3001
/*
unknown's avatar
unknown committed
3002
  Preparation of Create_field for SP function return values.
3003 3004
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

unknown's avatar
unknown committed
3016
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3017
{
3018 3019
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3020 3021
  {
    uint32 field_length, dummy;
3022
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3023 3024 3025 3026 3027 3028 3029
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3030
    else /* MYSQL_TYPE_ENUM */
3031 3032 3033 3034 3035 3036 3037 3038 3039
    {
      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);
  }

3040
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3041 3042 3043 3044 3045
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
3046 3047 3048 3049
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3050 3051


3052 3053 3054 3055
/*
  Create a table

  SYNOPSIS
unknown's avatar
unknown committed
3056
    mysql_create_table_no_lock()
3057 3058 3059
    thd			Thread object
    db			Database
    table_name		Table name
3060
    create_info	        Create information (like MAX_ROWS)
3061 3062
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
3063
    internal_tmp_table  Set to 1 if this is an internal temporary table
3064
			(From ALTER TABLE)
3065
    select_field_count
3066 3067

  DESCRIPTION
3068
    If one creates a temporary table, this is automatically opened
3069

unknown's avatar
unknown committed
3070 3071 3072 3073 3074
    Note that this function assumes that caller already have taken
    name-lock on table being created or used some other way to ensure
    that concurrent operations won't intervene. mysql_create_table()
    is a wrapper that can be used for this.

3075 3076 3077 3078 3079 3080
    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
unknown's avatar
unknown committed
3081 3082
    FALSE OK
    TRUE  error
3083 3084
*/

unknown's avatar
unknown committed
3085
bool mysql_create_table_no_lock(THD *thd,
unknown's avatar
unknown committed
3086
                                const char *db, const char *table_name,
3087 3088 3089 3090
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3091
{
3092
  char		path[FN_REFLEN];
3093
  uint          path_length;
3094 3095 3096 3097
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
3098
  bool		error= TRUE;
unknown's avatar
unknown committed
3099
  DBUG_ENTER("mysql_create_table_no_lock");
3100 3101
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3102

3103

3104
  /* Check for duplicate fields and check type of table to create */
3105
  if (!alter_info->create_list.elements)
3106
  {
unknown's avatar
unknown committed
3107 3108
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
3109
    DBUG_RETURN(TRUE);
3110
  }
3111
  if (check_engine(thd, table_name, create_info))
3112
    DBUG_RETURN(TRUE);
3113
  db_options= create_info->table_options;
3114 3115 3116
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3117 3118
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3119
  {
unknown's avatar
unknown committed
3120
    mem_alloc_error(sizeof(handler));
3121 3122
    DBUG_RETURN(TRUE);
  }
3123
#ifdef WITH_PARTITION_STORAGE_ENGINE
3124 3125
  partition_info *part_info= thd->work_part_info;

unknown's avatar
unknown committed
3126 3127 3128 3129 3130 3131 3132 3133
  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.
    */
unknown's avatar
unknown committed
3134
    thd->work_part_info= part_info= new partition_info();
unknown's avatar
unknown committed
3135 3136 3137 3138 3139 3140
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
unknown's avatar
unknown committed
3141
    part_info->default_engine_type= create_info->db_type;
3142
    part_info->is_auto_partitioned= TRUE;
unknown's avatar
unknown committed
3143
  }
3144 3145 3146
  if (part_info)
  {
    /*
unknown's avatar
unknown committed
3147 3148 3149 3150 3151 3152 3153 3154 3155
      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.
3156
    */
3157
    List_iterator<Key> key_iterator(alter_info->key_list);
unknown's avatar
unknown committed
3158
    Key *key;
unknown's avatar
unknown committed
3159
    handlerton *part_engine_type= create_info->db_type;
3160 3161
    char *part_syntax_buf;
    uint syntax_len;
unknown's avatar
unknown committed
3162
    handlerton *engine_type;
3163 3164 3165 3166 3167
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
unknown's avatar
unknown committed
3168 3169
    while ((key= key_iterator++))
    {
3170 3171
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
unknown's avatar
unknown committed
3172 3173 3174 3175 3176
      {
        my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
        goto err;
      }
    }
3177
    if ((part_engine_type == partition_hton) &&
3178
        part_info->default_engine_type)
3179 3180 3181 3182 3183 3184
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
unknown's avatar
unknown committed
3185
      ;
3186
    }
3187 3188
    else
    {
unknown's avatar
unknown committed
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200
      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);
        }
      }
3201
    }
unknown's avatar
unknown committed
3202 3203
    DBUG_PRINT("info", ("db_type = %d",
                         ha_legacy_type(part_info->default_engine_type)));
3204
    if (part_info->check_partition_info(thd, &engine_type, file,
3205
                                        create_info, TRUE))
unknown's avatar
unknown committed
3206
      goto err;
unknown's avatar
unknown committed
3207
    part_info->default_engine_type= engine_type;
unknown's avatar
unknown committed
3208

3209 3210 3211 3212 3213 3214
    /*
      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,
3215
                                                     TRUE, TRUE)))
unknown's avatar
unknown committed
3216
      goto err;
3217 3218
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
3219 3220
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3221
        create_info->db_type == partition_hton)
3222 3223 3224 3225 3226
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
unknown's avatar
unknown committed
3227 3228
      DBUG_PRINT("info", ("db_type: %d",
                          ha_legacy_type(create_info->db_type)));
3229
      delete file;
3230
      create_info->db_type= partition_hton;
3231 3232 3233 3234
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3235 3236 3237 3238 3239 3240 3241 3242
      /*
        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 &&
3243 3244
          (int)part_info->no_parts !=
          file->get_default_no_partitions(create_info))
3245
      {
3246
        uint i;
3247
        List_iterator<partition_element> part_it(part_info->partitions);
3248 3249 3250 3251
        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;
3252 3253 3254 3255
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3256
               (int)part_info->no_subparts !=
3257
                 file->get_default_no_partitions(create_info))
3258
      {
3259
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3260
        part_info->no_subparts= file->get_default_no_partitions(create_info);
3261 3262 3263 3264
      }
    }
    else if (create_info->db_type != engine_type)
    {
3265 3266 3267 3268 3269 3270
      /*
        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.
      */
3271
      delete file;
3272 3273
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3274 3275 3276 3277
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3278 3279 3280
    }
  }
#endif
3281

unknown's avatar
unknown committed
3282
  set_table_default_charset(thd, create_info, (char*) db);
3283

3284 3285 3286 3287 3288
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3289
    goto err;
unknown's avatar
unknown committed
3290 3291 3292 3293

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3294
    path_length= build_tmptable_filename(thd, path, sizeof(path));
unknown's avatar
unknown committed
3295 3296
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3297 3298
  else  
  {
3299 3300
 #ifdef FN_DEVCHAR
    /* check if the table name contains FN_DEVCHAR when defined */
unknown's avatar
unknown committed
3301
    if (strchr(alias, FN_DEVCHAR))
3302
    {
unknown's avatar
unknown committed
3303 3304 3305
      my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
      DBUG_RETURN(TRUE);
    }
3306
#endif
3307 3308
    path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
                                      internal_tmp_table ? FN_IS_TMP : 0);
3309
  }
3310

unknown's avatar
unknown committed
3311
  /* Check if table already exists */
unknown's avatar
unknown committed
3312 3313
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
unknown's avatar
unknown committed
3314
  {
3315
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3316 3317
    {
      create_info->table_existed= 1;		// Mark that table existed
3318 3319 3320
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
unknown's avatar
unknown committed
3321 3322
      error= 0;
      goto err;
3323
    }
unknown's avatar
unknown committed
3324
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3325
    goto err;
unknown's avatar
unknown committed
3326
  }
3327

unknown's avatar
unknown committed
3328
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
3329
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
3330 3331 3332 3333
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3334 3335
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
3336
      goto unlock_and_end;
unknown's avatar
unknown committed
3337
    }
3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350
    /*
      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;
    }
unknown's avatar
unknown committed
3351 3352
  }

unknown's avatar
unknown committed
3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365
  /*
    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;
3366 3367 3368
    int retcode = ha_table_exists_in_engine(thd, db, table_name);
    DBUG_PRINT("info", ("exists_in_engine: %u",retcode));
    switch (retcode)
unknown's avatar
unknown committed
3369
    {
3370 3371 3372 3373 3374
      case HA_ERR_NO_SUCH_TABLE:
        /* Normal case, no table exists. we can go and create it */
        break;
      case HA_ERR_TABLE_EXIST:
        DBUG_PRINT("info", ("Table existed in handler"));
unknown's avatar
unknown committed
3375

3376 3377 3378 3379 3380 3381 3382 3383 3384
        if (create_if_not_exists)
          goto warn;
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        goto unlock_and_end;
        break;
      default:
        DBUG_PRINT("info", ("error: %u from storage engine", retcode));
        my_error(retcode, MYF(0),table_name);
        goto unlock_and_end;
unknown's avatar
unknown committed
3385 3386 3387 3388
    }
  }

  thd->proc_info="creating table";
3389
  create_info->table_existed= 0;		// Mark that table is created
unknown's avatar
unknown committed
3390

unknown's avatar
unknown committed
3391
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
3392
    create_info->data_file_name= create_info->index_file_name= 0;
unknown's avatar
unknown committed
3393
  create_info->table_options=db_options;
3394

3395
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3396 3397
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
unknown's avatar
unknown committed
3398
                       key_count, key_info_buffer, file))
unknown's avatar
unknown committed
3399
    goto unlock_and_end;
unknown's avatar
unknown committed
3400

unknown's avatar
unknown committed
3401 3402 3403 3404 3405 3406
  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);
unknown's avatar
unknown committed
3407
      goto unlock_and_end;
unknown's avatar
unknown committed
3408
    }
unknown's avatar
unknown committed
3409
    thd->tmp_table_used= 1;
unknown's avatar
unknown committed
3410
  }
unknown's avatar
unknown committed
3411

3412 3413 3414 3415 3416
  /*
    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.
3417
    Otherwise, the statement shall be binlogged.
3418 3419
   */
  if (!internal_tmp_table &&
3420 3421
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
3422
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3423
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
unknown's avatar
unknown committed
3424
  error= FALSE;
unknown's avatar
unknown committed
3425
unlock_and_end:
unknown's avatar
unknown committed
3426
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
3427 3428

err:
unknown's avatar
unknown committed
3429
  thd->proc_info="After create";
unknown's avatar
unknown committed
3430
  delete file;
unknown's avatar
unknown committed
3431
  DBUG_RETURN(error);
3432 3433

warn:
3434
  error= FALSE;
3435 3436 3437 3438
  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
unknown's avatar
unknown committed
3439
  goto unlock_and_end;
unknown's avatar
unknown committed
3440 3441
}

unknown's avatar
unknown committed
3442 3443

/*
unknown's avatar
unknown committed
3444
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
unknown's avatar
unknown committed
3445 3446 3447 3448
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
3449 3450 3451
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
unknown's avatar
unknown committed
3452
{
unknown's avatar
unknown committed
3453
  TABLE *name_lock= 0;
unknown's avatar
unknown committed
3454 3455 3456 3457 3458 3459
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
3460
         hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
unknown's avatar
unknown committed
3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473
  {
    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);

unknown's avatar
unknown committed
3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498
  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
    {
      result= TRUE;
      goto unlock;
    }
    if (!name_lock)
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                            ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                            table_name);
        create_info->table_existed= 1;
        result= FALSE;
      }
      else
      {
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        result= TRUE;
      }
      goto unlock;
    }
  }
unknown's avatar
unknown committed
3499

unknown's avatar
unknown committed
3500
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
3501 3502 3503
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
unknown's avatar
unknown committed
3504

unknown's avatar
unknown committed
3505 3506 3507 3508 3509 3510 3511
unlock:
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
3512 3513 3514 3515 3516 3517 3518 3519
  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);
}


unknown's avatar
unknown committed
3520 3521 3522 3523 3524 3525 3526 3527
/*
** 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++)
3528
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
3529 3530 3531 3532 3533 3534 3535 3536 3537 3538
      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;

3539 3540
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
3541
    return (char*) field_name;			// Use fieldname
3542 3543 3544 3545 3546 3547
  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
  */
3548
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
3549
  {
3550 3551
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
3552 3553 3554
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3555
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
3556 3557
}

3558

unknown's avatar
unknown committed
3559 3560 3561 3562
/****************************************************************************
** Alter a table definition
****************************************************************************/

3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576

/*
  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.
3577 3578
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
3579 3580

  RETURN
3581 3582
    FALSE   OK
    TRUE    Error
3583 3584
*/

3585
bool
3586 3587 3588
mysql_rename_table(handlerton *base, const char *old_db,
                   const char *old_name, const char *new_db,
                   const char *new_name, uint flags)
unknown's avatar
unknown committed
3589
{
3590
  THD *thd= current_thd;
3591 3592 3593
  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];
unknown's avatar
unknown committed
3594
  handler *file;
unknown's avatar
unknown committed
3595
  int error=0;
unknown's avatar
unknown committed
3596
  DBUG_ENTER("mysql_rename_table");
3597 3598
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
unknown's avatar
unknown committed
3599

unknown's avatar
unknown committed
3600
  file= (base == NULL ? 0 :
unknown's avatar
unknown committed
3601 3602
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

3603 3604 3605 3606
  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);
3607 3608 3609 3610 3611 3612

  /*
    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.
   */
3613
  if (lower_case_table_names == 2 && file &&
3614
      !(file->ha_table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
3615
  {
3616 3617
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
3618 3619
    build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "",
                         flags & FN_FROM_IS_TMP);
3620
    from_base= lc_from;
unknown's avatar
unknown committed
3621

3622 3623
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
3624 3625
    build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "",
                         flags & FN_TO_IS_TMP);
3626
    to_base= lc_to;
unknown's avatar
unknown committed
3627 3628
  }

3629
  if (!file || !(error=file->rename_table(from_base, to_base)))
3630
  {
3631
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
3632
    {
unknown's avatar
unknown committed
3633
      error=my_errno;
3634
      /* Restore old file name */
3635
      if (file)
3636
        file->rename_table(to_base, from_base);
3637 3638
    }
  }
unknown's avatar
unknown committed
3639
  delete file;
3640 3641 3642
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
3643 3644
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
3645 3646
}

unknown's avatar
unknown committed
3647

unknown's avatar
unknown committed
3648
/*
3649 3650 3651 3652 3653 3654
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
3655
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
3656
			HA_EXTRA_FORCE_REOPEN if table is not be used
3657 3658 3659 3660 3661 3662 3663
  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 !
unknown's avatar
unknown committed
3664 3665
*/

3666 3667
static void wait_while_table_is_used(THD *thd,TABLE *table,
				     enum ha_extra_function function)
unknown's avatar
unknown committed
3668
{
3669
  DBUG_ENTER("wait_while_table_is_used");
unknown's avatar
unknown committed
3670
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %lu",
unknown's avatar
unknown committed
3671 3672
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
unknown's avatar
unknown committed
3673

3674
  VOID(table->file->extra(function));
3675
  /* Mark all tables that are in use as 'old' */
unknown's avatar
unknown committed
3676
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
3677 3678

  /* Wait until all there are no other threads that has this table open */
unknown's avatar
unknown committed
3679 3680 3681
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
3682 3683
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
3684

3685 3686
/*
  Close a cached table
unknown's avatar
unknown committed
3687

3688
  SYNOPSIS
unknown's avatar
unknown committed
3689
    close_cached_table()
3690 3691 3692 3693 3694 3695
    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.
unknown's avatar
unknown committed
3696

3697 3698 3699 3700
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
3701

3702
void close_cached_table(THD *thd, TABLE *table)
3703 3704
{
  DBUG_ENTER("close_cached_table");
3705

3706
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
3707 3708
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
3709
  {
3710 3711
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
3712
  }
3713
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
unknown's avatar
unknown committed
3714
  unlink_open_table(thd, table, TRUE);
3715 3716

  /* When lock on LOCK_open is freed other threads can continue */
3717
  broadcast_refresh();
unknown's avatar
unknown committed
3718
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3719 3720
}

3721
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
3722
			     const char* operator_name, const char* errmsg)
3723

unknown's avatar
unknown committed
3724
{
3725 3726
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3727 3728
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
3729
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3730
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
3731
  thd->clear_error();
3732
  if (protocol->write())
unknown's avatar
unknown committed
3733 3734 3735 3736
    return -1;
  return 1;
}

3737

unknown's avatar
unknown committed
3738
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
3739
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
3740
{
unknown's avatar
unknown committed
3741
  DBUG_ENTER("prepare_for_restore");
3742

unknown's avatar
unknown committed
3743 3744 3745 3746 3747 3748
  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"
				  ));
  }
unknown's avatar
unknown committed
3749
  else
unknown's avatar
unknown committed
3750
  {
3751
    char* backup_dir= thd->lex->backup_dir;
3752
    char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
3753 3754
    char* table_name= table->table_name;
    char* db= table->db;
3755

3756 3757 3758
    VOID(tablename_to_filename(table->table_name, uname, sizeof(uname)));

    if (fn_format_relative_to_data_home(src_path, uname, backup_dir, reg_ext))
unknown's avatar
unknown committed
3759
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
3760

3761 3762
    build_table_filename(dst_path, sizeof(dst_path),
                         db, table_name, reg_ext, 0);
3763

3764
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
3765
      DBUG_RETURN(-1);
3766

3767
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
unknown's avatar
unknown committed
3768
    {
3769
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3770
      unlock_table_name(thd, table);
3771
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3772 3773 3774
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
3775
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
3776
    {
3777
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3778
      unlock_table_name(thd, table);
3779
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3780 3781
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
3782
    }
unknown's avatar
unknown committed
3783
  }
unknown's avatar
unknown committed
3784

3785 3786 3787 3788
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
3789
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3790
  if (reopen_name_locked_table(thd, table, TRUE))
3791
  {
3792
    unlock_table_name(thd, table);
3793
    pthread_mutex_unlock(&LOCK_open);
3794 3795
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
3796
  }
3797
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3798
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3799
}
3800

3801

unknown's avatar
unknown committed
3802
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
3803
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
3804
{
unknown's avatar
unknown committed
3805 3806
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
3807 3808 3809 3810
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
unknown's avatar
unknown committed
3811 3812 3813 3814
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
unknown's avatar
unknown committed
3815 3816

  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
3817
  {
unknown's avatar
unknown committed
3818 3819 3820 3821 3822 3823 3824 3825 3826
    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);
unknown's avatar
unknown committed
3827
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
3828 3829
    }

unknown's avatar
unknown committed
3830
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
unknown's avatar
unknown committed
3831 3832 3833 3834 3835
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
unknown's avatar
unknown committed
3836
    table= &tmp_table;
unknown's avatar
unknown committed
3837
    pthread_mutex_unlock(&LOCK_open);
3838
  }
3839 3840 3841 3842 3843 3844 3845 3846 3847
  /*
    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;
  }
unknown's avatar
unknown committed
3848

unknown's avatar
unknown committed
3849 3850 3851 3852 3853 3854 3855 3856 3857
  /*
    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
  */
unknown's avatar
unknown committed
3858

unknown's avatar
unknown committed
3859 3860
  /*
    Check if this is a table type that stores index and data separately,
3861 3862 3863
    like ISAM or MyISAM. We assume fixed order of engine file name
    extentions array. First element of engine file name extentions array
    is meta/index file extention. Second element - data file extention. 
unknown's avatar
unknown committed
3864
  */
unknown's avatar
unknown committed
3865
  ext= table->file->bas_ext();
unknown's avatar
unknown committed
3866 3867
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
3868

unknown's avatar
unknown committed
3869 3870
  // Name of data file
  strxmov(from, table->s->normalized_path.str, ext[1], NullS);
unknown's avatar
unknown committed
3871 3872
  if (!my_stat(from, &stat_info, MYF(0)))
    goto end;				// Can't use USE_FRM flag
unknown's avatar
unknown committed
3873

3874 3875
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
3876

3877 3878 3879 3880 3881 3882 3883
  /* 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);
  }
unknown's avatar
unknown committed
3884
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
3885
  {
unknown's avatar
unknown committed
3886 3887
    error= -1;
    goto end;
unknown's avatar
unknown committed
3888
  }
unknown's avatar
unknown committed
3889
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
3890
  {
3891
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3892
    unlock_table_name(thd, table_list);
3893
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914
    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;
unknown's avatar
unknown committed
3915 3916
  }

3917 3918 3919 3920
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
3921
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3922
  if (reopen_name_locked_table(thd, table_list, TRUE))
unknown's avatar
unknown committed
3923
  {
unknown's avatar
unknown committed
3924
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
3925
    pthread_mutex_unlock(&LOCK_open);
3926 3927 3928
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
3929
  }
3930
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3931 3932 3933

end:
  if (table == &tmp_table)
unknown's avatar
unknown committed
3934 3935 3936 3937 3938
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
3939
  DBUG_RETURN(error);
unknown's avatar
unknown committed
3940
}
3941

3942

3943

unknown's avatar
unknown committed
3944 3945
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
3946 3947 3948
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
unknown's avatar
unknown committed
3949
*/
unknown's avatar
unknown committed
3950 3951 3952 3953 3954
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,
3955
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
3956 3957 3958
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
3959 3960 3961
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
3962
{
3963
  TABLE_LIST *table;
3964
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
3965
  List<Item> field_list;
3966 3967
  Item *item;
  Protocol *protocol= thd->protocol;
3968
  LEX *lex= thd->lex;
3969
  int result_code, disable_logs= 0;
3970
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
3971

3972 3973
  if (end_active_trans(thd))
    DBUG_RETURN(1);
3974
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
unknown's avatar
unknown committed
3975 3976 3977 3978 3979 3980 3981
  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;
3982 3983
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
3984
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3985

3986
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
unknown's avatar
VIEW  
unknown committed
3987
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
3988 3989
  {
    char table_name[NAME_LEN*2+2];
3990
    char* db = table->db;
3991
    bool fatal_error=0;
unknown's avatar
unknown committed
3992

unknown's avatar
unknown committed
3993
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
3994
    thd->open_options|= extra_open_options;
3995 3996
    table->lock_type= lock_type;
    /* open only one table from local list of command */
3997
    {
3998 3999 4000 4001 4002
      TABLE_LIST *save_next_global, *save_next_local;
      save_next_global= table->next_global;
      table->next_global= 0;
      save_next_local= table->next_local;
      table->next_local= 0;
4003
      select->table_list.first= (uchar*)table;
4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015
      /*
        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;
      lex->query_tables_own_last= 0;
      thd->no_warnings_for_error= no_warnings_for_error;
      if (view_operator_func == NULL)
        table->required_type=FRMTYPE_TABLE;
4016

unknown's avatar
unknown committed
4017 4018 4019 4020 4021
      /*
        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
      */
4022

unknown's avatar
unknown committed
4023 4024 4025 4026 4027 4028 4029 4030 4031
      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);
      }
4032

4033 4034 4035 4036 4037 4038
      open_and_lock_tables(thd, table);
      thd->no_warnings_for_error= 0;
      table->next_global= save_next_global;
      table->next_local= save_next_local;
      thd->open_options&= ~extra_open_options;
    }
unknown's avatar
unknown committed
4039
    if (prepare_func)
4040
    {
unknown's avatar
unknown committed
4041
      switch ((*prepare_func)(thd, table, check_opt)) {
4042
      case  1:           // error, message written to net
4043
        ha_autocommit_or_rollback(thd, 1);
4044 4045 4046 4047 4048 4049
        close_thread_tables(thd);
        continue;
      case -1:           // error, message could be written to net
        goto err;
      default:           // should be 0 otherwise
        ;
unknown's avatar
unknown committed
4050
      }
4051
    }
4052

4053
    /*
unknown's avatar
unknown committed
4054 4055 4056 4057 4058 4059
      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)
4060
    */
unknown's avatar
unknown committed
4061 4062
    if (!table->table)
    {
4063 4064 4065
      if (!thd->warn_list.elements)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4066 4067 4068
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
4069 4070 4071 4072
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
      result_code= HA_ADMIN_CORRUPT;
      goto send_result;
unknown's avatar
unknown committed
4073
    }
4074 4075 4076 4077 4078 4079 4080

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

4081
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
4082
    {
unknown's avatar
unknown committed
4083
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4084
      uint length;
4085
      protocol->prepare_for_resend();
4086 4087
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4088
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4089 4090 4091
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4092
      ha_autocommit_or_rollback(thd, 0);
4093
      close_thread_tables(thd);
4094
      lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
4095
      table->table=0;				// For query cache
4096
      if (protocol->write())
unknown's avatar
unknown committed
4097 4098 4099 4100
	goto err;
      continue;
    }

4101
    /* Close all instances of the table to allow repair to rename files */
4102 4103
    if (lock_type == TL_WRITE && table->table->s->version &&
        !table->table->s->log_table)
4104 4105
    {
      pthread_mutex_lock(&LOCK_open);
4106 4107
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
unknown's avatar
unknown committed
4108
      mysql_lock_abort(thd,table->table, TRUE);
unknown's avatar
unknown committed
4109 4110
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
unknown's avatar
unknown committed
4111 4112
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4113
      thd->exit_cond(old_message);
4114 4115
      if (thd->killed)
	goto err;
4116 4117 4118
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4119 4120
    }

unknown's avatar
unknown committed
4121
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4122 4123 4124 4125
    {
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4126 4127 4128
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4129 4130 4131 4132
      if (protocol->write())
        goto err;
    }

unknown's avatar
unknown committed
4133 4134 4135 4136 4137 4138
    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))
      {
4139
        my_bool save_no_send_ok= thd->net.no_send_ok;
4140
        ha_autocommit_or_rollback(thd, 1);
unknown's avatar
unknown committed
4141 4142
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4143 4144 4145
        thd->net.no_send_ok= TRUE;
        result_code= mysql_recreate_table(thd, table);
        thd->net.no_send_ok= save_no_send_ok;
unknown's avatar
unknown committed
4146 4147 4148 4149 4150 4151
        reenable_binlog(thd);
        goto send_result;
      }

    }

4152 4153 4154 4155
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

4156
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
4157
    thd->clear_error();  // these errors shouldn't get client
4158
    {
4159 4160 4161 4162 4163 4164 4165
      List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
      MYSQL_ERROR *err;
      while ((err= it++))
      {
        protocol->prepare_for_resend();
        protocol->store(table_name, system_charset_info);
        protocol->store((char*) operator_name, system_charset_info);
4166 4167 4168
        protocol->store(warning_level_names[err->level].str,
                        warning_level_names[err->level].length,
                        system_charset_info);
4169 4170 4171 4172 4173
        protocol->store(err->msg, system_charset_info);
        if (protocol->write())
          goto err;
      }
      mysql_reset_errors(thd, true);
4174
    }
4175
    protocol->prepare_for_resend();
4176 4177
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
4178

4179 4180 4181
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4182 4183
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4184
      {
4185 4186
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
4187
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4188
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4189
	protocol->store(buf, length, system_charset_info);
4190
      }
unknown's avatar
unknown committed
4191 4192
      break;

unknown's avatar
unknown committed
4193 4194
    case HA_ADMIN_NOT_BASE_TABLE:
      {
unknown's avatar
unknown committed
4195 4196
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
unknown's avatar
unknown committed
4197
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4198
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4199
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
4200 4201 4202
      }
      break;

4203
    case HA_ADMIN_OK:
4204 4205
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
4206 4207
      break;

4208
    case HA_ADMIN_FAILED:
4209 4210 4211
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
4212 4213
      break;

unknown's avatar
unknown committed
4214
    case HA_ADMIN_REJECT:
4215 4216 4217
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation need committed state"),
                      system_charset_info);
unknown's avatar
unknown committed
4218
      open_for_modify= FALSE;
unknown's avatar
unknown committed
4219 4220
      break;

4221
    case HA_ADMIN_ALREADY_DONE:
4222 4223 4224
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4225 4226
      break;

4227
    case HA_ADMIN_CORRUPT:
4228 4229
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4230
      fatal_error=1;
unknown's avatar
unknown committed
4231 4232
      break;

unknown's avatar
unknown committed
4233
    case HA_ADMIN_INVALID:
4234 4235 4236
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
4237 4238
      break;

4239 4240
    case HA_ADMIN_TRY_ALTER:
    {
4241
      my_bool save_no_send_ok= thd->net.no_send_ok;
4242 4243 4244 4245 4246
      /*
        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.
      */
4247
      ha_autocommit_or_rollback(thd, 0);
4248
      close_thread_tables(thd);
unknown's avatar
VIEW  
unknown committed
4249 4250 4251
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4252
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4253 4254 4255
      thd->net.no_send_ok= TRUE;
      result_code= mysql_recreate_table(thd, table);
      thd->net.no_send_ok= save_no_send_ok;
4256
      reenable_binlog(thd);
4257
      ha_autocommit_or_rollback(thd, 0);
unknown's avatar
unknown committed
4258
      close_thread_tables(thd);
4259 4260 4261 4262 4263 4264
      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
      }
4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276
      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. */
4277
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4278 4279 4280 4281 4282 4283 4284 4285 4286
            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);
          }
        }
      }
4287
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
4288 4289
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4290 4291
      goto send_result_message;
    }
4292 4293
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4294
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4295 4296
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4297 4298
      break;
    }
4299

unknown's avatar
unknown committed
4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312
    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;
    }

4313
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4314 4315 4316 4317 4318
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
4319
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4320 4321 4322 4323
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
4324
    }
unknown's avatar
unknown committed
4325
    if (table->table)
4326
    {
4327
      /* in the below check we do not refresh the log tables */
unknown's avatar
unknown committed
4328 4329
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4330
      else if (open_for_modify && !table->table->s->log_table)
unknown's avatar
unknown committed
4331
      {
unknown's avatar
unknown committed
4332
        if (table->table->s->tmp_table)
unknown's avatar
unknown committed
4333 4334 4335 4336
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4337 4338
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
unknown's avatar
unknown committed
4339 4340 4341
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
4342 4343
        query_cache_invalidate3(thd, table->table, 0);
      }
4344
    }
4345
    ha_autocommit_or_rollback(thd, 0);
unknown's avatar
unknown committed
4346
    close_thread_tables(thd);
unknown's avatar
unknown committed
4347
    table->table=0;				// For query cache
4348
    if (protocol->write())
unknown's avatar
unknown committed
4349 4350 4351
      goto err;
  }

4352
  send_eof(thd);
4353 4354 4355 4356 4357 4358
  if (disable_logs)
  {
    if (logger.reopen_log_tables())
      my_error(ER_CANT_ACTIVATE_LOG, MYF(0));
    logger.unlock();
  }
unknown's avatar
unknown committed
4359
  DBUG_RETURN(FALSE);
4360

unknown's avatar
unknown committed
4361
 err:
4362
  ha_autocommit_or_rollback(thd, 1);
4363
  close_thread_tables(thd);			// Shouldn't be needed
4364 4365 4366 4367 4368 4369 4370
  /* enable logging back if needed */
  if (disable_logs)
  {
    if (logger.reopen_log_tables())
      my_error(ER_CANT_ACTIVATE_LOG, MYF(0));
    logger.unlock();
  }
unknown's avatar
unknown committed
4371 4372
  if (table)
    table->table=0;
unknown's avatar
unknown committed
4373
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4374 4375
}

unknown's avatar
unknown committed
4376

unknown's avatar
unknown committed
4377
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
4378 4379
{
  DBUG_ENTER("mysql_backup_table");
4380 4381
  WARN_DEPRECATED(thd, "5.2", "BACKUP TABLE",
                  "MySQL Administrator (mysqldump, mysql)");
unknown's avatar
unknown committed
4382
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4383
				"backup", TL_READ, 0, 0, 0, 0,
4384
				&handler::backup, 0));
unknown's avatar
unknown committed
4385
}
unknown's avatar
unknown committed
4386

4387

unknown's avatar
unknown committed
4388
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
4389 4390
{
  DBUG_ENTER("mysql_restore_table");
4391 4392
  WARN_DEPRECATED(thd, "5.2", "RESTORE TABLE",
                  "MySQL Administrator (mysqldump, mysql)");
unknown's avatar
unknown committed
4393
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4394
				"restore", TL_WRITE, 1, 1, 0,
4395
				&prepare_for_restore,
4396
				&handler::restore, 0));
unknown's avatar
unknown committed
4397
}
unknown's avatar
unknown committed
4398

4399

unknown's avatar
unknown committed
4400
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4401 4402 4403
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4404 4405 4406
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4407
				&prepare_for_repair,
unknown's avatar
unknown committed
4408
				&handler::ha_repair, 0));
4409 4410
}

4411

unknown's avatar
unknown committed
4412
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4413 4414 4415
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4416
				"optimize", TL_WRITE, 1,0,0,0,
4417
				&handler::optimize, 0));
4418 4419 4420
}


unknown's avatar
unknown committed
4421 4422 4423 4424 4425
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4426 4427
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
4428 4429

  RETURN VALUES
unknown's avatar
unknown committed
4430 4431
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
4432 4433
*/

unknown's avatar
unknown committed
4434
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4435
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
4436
{
4437
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
4438
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
4439
  DBUG_ENTER("mysql_assign_to_keycache");
4440 4441 4442 4443 4444 4445 4446

  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);
unknown's avatar
unknown committed
4447
    DBUG_RETURN(TRUE);
4448 4449 4450 4451
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4452
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4453
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
4454 4455
}

unknown's avatar
unknown committed
4456 4457 4458 4459 4460 4461

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

  SYNOPSIS
    reassign_keycache_tables()
4462 4463 4464
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
4465

4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478
  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
unknown's avatar
unknown committed
4479 4480 4481
    0	  ok
*/

4482 4483
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
4484 4485 4486
{
  DBUG_ENTER("reassign_keycache_tables");

4487 4488
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
unknown's avatar
unknown committed
4489
  src_cache->param_buff_size= 0;		// Free key cache
4490 4491
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4492
  DBUG_RETURN(0);
unknown's avatar
unknown committed
4493 4494 4495
}


unknown's avatar
unknown committed
4496 4497 4498 4499 4500
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
4501 4502
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
4503 4504

  RETURN VALUES
unknown's avatar
unknown committed
4505 4506
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
4507 4508
*/

unknown's avatar
unknown committed
4509
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
4510 4511
{
  DBUG_ENTER("mysql_preload_keys");
4512 4513 4514 4515 4516
  /*
    We cannot allow concurrent inserts. The storage engine reads
    directly from the index file, bypassing the cache. It could read
    outdated information if parallel inserts into cache blocks happen.
  */
unknown's avatar
unknown committed
4517
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
4518
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
4519
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
4520 4521 4522
}


unknown's avatar
unknown committed
4523 4524 4525 4526 4527
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
4528
    thd		Thread object
unknown's avatar
unknown committed
4529 4530
    table       Table list element for target table
    src_table   Table list element for source table
unknown's avatar
unknown committed
4531 4532 4533
    create_info Create info

  RETURN VALUES
unknown's avatar
unknown committed
4534 4535
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
4536 4537
*/

unknown's avatar
unknown committed
4538
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
4539
                             HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
4540
{
unknown's avatar
unknown committed
4541
  TABLE *name_lock= 0;
4542
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
4543
  uint dst_path_length;
unknown's avatar
unknown committed
4544
  char *db= table->db;
4545
  char *table_name= table->table_name;
unknown's avatar
unknown committed
4546
  int  err;
unknown's avatar
unknown committed
4547
  bool res= TRUE;
unknown's avatar
unknown committed
4548
  uint not_used;
4549 4550 4551
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
4552
  char ts_name[FN_LEN];
unknown's avatar
unknown committed
4553
  DBUG_ENTER("mysql_create_like_table");
4554

4555

unknown's avatar
unknown committed
4556 4557
  /* CREATE TABLE ... LIKE is not allowed for views. */
  src_table->required_type= FRMTYPE_TABLE;
unknown's avatar
unknown committed
4558

unknown's avatar
unknown committed
4559 4560 4561 4562 4563 4564 4565 4566
  /*
    By opening source table we guarantee that it exists and no concurrent
    DDL operation will mess with it. Later we also take an exclusive
    name-lock on target table name, which makes copying of .frm file,
    call to ha_create_table() and binlogging atomic against concurrent DML
    and DDL operations on target table. Thus by holding both these "locks"
    we ensure that our statement is properly isolated from all concurrent
    operations which matter.
4567
  */
unknown's avatar
unknown committed
4568
  if (open_tables(thd, &src_table, &not_used, 0))
4569 4570
    DBUG_RETURN(TRUE);

4571 4572 4573 4574 4575 4576
  /*
    For bug#25875, Newly created table through CREATE TABLE .. LIKE
                   has no ndb_dd attributes;
    Add something to get possible tablespace info from src table,
    it can get valid tablespace name only for disk-base ndb table
  */
unknown's avatar
unknown committed
4577
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
4578 4579 4580 4581 4582
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

unknown's avatar
unknown committed
4583
  strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
unknown's avatar
unknown committed
4584

unknown's avatar
unknown committed
4585 4586 4587 4588 4589
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););

  /*
    Check that destination tables does not exist. Note that its name
    was already checked when it was added to the table list.
unknown's avatar
unknown committed
4590 4591 4592 4593 4594
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
4595
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
unknown's avatar
unknown committed
4596 4597 4598 4599
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
unknown's avatar
unknown committed
4600 4601 4602 4603
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
      goto err;
    if (!name_lock)
      goto table_exists;
4604
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
4605
                                          db, table_name, reg_ext, 0);
unknown's avatar
unknown committed
4606 4607 4608 4609
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

unknown's avatar
unknown committed
4610 4611
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

4612
  /*
unknown's avatar
unknown committed
4613
    Create a new table by copying from source table
unknown's avatar
unknown committed
4614 4615 4616 4617 4618 4619 4620 4621 4622 4623

    Altough exclusive name-lock on target table protects us from concurrent
    DML and DDL operations on it we still want to wrap .FRM creation and call
    to ha_create_table() in critical section protected by LOCK_open in order
    to provide minimal atomicity against operations which disregard name-locks,
    like I_S implementation, for example. This is a temporary and should not
    be copied. Instead we should fix our code to always honor name-locks.

    Also some engines (e.g. NDB cluster) require that LOCK_open should be held
    during the call to ha_create_table(). See bug #28614 for more info.
4624
  */
unknown's avatar
unknown committed
4625
  VOID(pthread_mutex_lock(&LOCK_open));
4626 4627 4628 4629 4630 4631
  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);
unknown's avatar
unknown committed
4632
    VOID(pthread_mutex_unlock(&LOCK_open));
4633
    goto err;
4634
  }
unknown's avatar
unknown committed
4635 4636

  /*
4637 4638
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
unknown's avatar
unknown committed
4639 4640
    and temporary tables).
  */
unknown's avatar
unknown committed
4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653
#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
unknown's avatar
unknown committed
4654 4655 4656

  DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000););

4657
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
4658 4659
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
unknown's avatar
unknown committed
4660
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
unknown's avatar
unknown committed
4661
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
4662

unknown's avatar
unknown committed
4663 4664 4665 4666
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
4667 4668
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
4669
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
4670 4671 4672 4673
    }
  }
  else if (err)
  {
4674
    (void) quick_rm_table(create_info->db_type, db,
4675
			  table_name, 0); /* purecov: inspected */
4676
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
4677
  }
4678

unknown's avatar
unknown committed
4679 4680
  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););

4681 4682 4683
  /*
    We have to write the query before we unlock the tables.
  */
4684
  if (thd->current_stmt_binlog_row_based)
4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702
  {
    /*
       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
           ==== ========= ========= ==============================
    */
    if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    {
unknown's avatar
unknown committed
4703
      if (src_table->table->s->tmp_table)               // Case 2
4704 4705 4706 4707 4708 4709
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't

        /*
unknown's avatar
unknown committed
4710 4711 4712 4713
          Here we open the destination table, on which we already have
          name-lock. This is needed for store_create_info() to work.
          The table will be closed by unlink_open_table() at the end
          of this function.
4714
        */
unknown's avatar
unknown committed
4715 4716 4717 4718 4719
        table->table= name_lock;
        VOID(pthread_mutex_lock(&LOCK_open));
        if (reopen_name_locked_table(thd, table, FALSE))
        {
          VOID(pthread_mutex_unlock(&LOCK_open));
4720
          goto err;
unknown's avatar
unknown committed
4721 4722
        }
        VOID(pthread_mutex_unlock(&LOCK_open));
4723

unknown's avatar
unknown committed
4724 4725
        IF_DBUG(int result=) store_create_info(thd, table, &query,
                                               create_info);
4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736

        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
    */
  }
4737
  else
4738 4739
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

unknown's avatar
unknown committed
4740
  res= FALSE;
4741
  goto err;
4742

unknown's avatar
unknown committed
4743 4744 4745 4746
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
4747 4748
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
4749
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4750
		 ER_TABLE_EXISTS_ERROR,warn_buff);
unknown's avatar
unknown committed
4751
    res= FALSE;
unknown's avatar
unknown committed
4752
  }
4753 4754 4755 4756
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
unknown's avatar
unknown committed
4757
  if (name_lock)
4758 4759
  {
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4760
    unlink_open_table(thd, name_lock, FALSE);
4761 4762
    pthread_mutex_unlock(&LOCK_open);
  }
4763
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4764 4765 4766
}


unknown's avatar
unknown committed
4767
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4768
{
unknown's avatar
unknown committed
4769 4770
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4771 4772
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4773
				"analyze", lock_type, 1, 0, 0, 0,
4774
				&handler::analyze, 0));
4775 4776 4777
}


unknown's avatar
unknown committed
4778
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
4779
{
unknown's avatar
unknown committed
4780 4781
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4782 4783
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
4784
				"check", lock_type,
4785
				0, 0, HA_OPEN_FOR_REPAIR, 0,
unknown's avatar
unknown committed
4786
				&handler::ha_check, &view_checksum));
4787 4788
}

unknown's avatar
unknown committed
4789

unknown's avatar
unknown committed
4790
/* table_list should contain just one table */
unknown's avatar
unknown committed
4791 4792 4793 4794
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
4795 4796 4797 4798 4799 4800
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
4801 4802 4803 4804
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
4805 4806 4807

  thd->proc_info="discard_or_import_tablespace";

unknown's avatar
unknown committed
4808
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
4809

unknown's avatar
unknown committed
4810 4811 4812 4813 4814
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
unknown's avatar
unknown committed
4815 4816 4817 4818 4819
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
4820

unknown's avatar
unknown committed
4821 4822 4823 4824 4825 4826 4827
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

unknown's avatar
unknown committed
4828 4829 4830 4831
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
unknown's avatar
unknown committed
4832 4833 4834 4835 4836 4837 4838 4839
  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;
4840
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
4841

unknown's avatar
unknown committed
4842
err:
4843
  ha_autocommit_or_rollback(thd, error);
unknown's avatar
unknown committed
4844
  close_thread_tables(thd);
unknown's avatar
unknown committed
4845
  thd->tablespace_op=FALSE;
4846
  
unknown's avatar
unknown committed
4847 4848
  if (error == 0)
  {
unknown's avatar
unknown committed
4849
    send_ok(thd);
unknown's avatar
unknown committed
4850
    DBUG_RETURN(0);
unknown's avatar
unknown committed
4851
  }
unknown's avatar
unknown committed
4852

4853 4854
  table->file->print_error(error, MYF(0));
    
unknown's avatar
unknown committed
4855
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
4856
}
unknown's avatar
unknown committed
4857

4858

unknown's avatar
unknown committed
4859 4860
/*
  SYNOPSIS
4861 4862
    compare_tables()
      table                     The original table.
4863 4864
      alter_info                Alter options, fields and keys for the new
                                table.
4865 4866
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
4867 4868 4869 4870 4871 4872 4873 4874
      need_copy_table     OUT   Result of the comparison. Undefined if error.
                                Otherwise is one of:
                                ALTER_TABLE_METADATA_ONLY  No copy needed
                                ALTER_TABLE_DATA_CHANGED   Data changes,
                                                           copy needed
                                ALTER_TABLE_INDEX_CHANGED  Index changes,
                                                           copy might be needed
      key_info_buffer     OUT   An array of KEY structs for new indexes
4875 4876 4877 4878
      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.
unknown's avatar
unknown committed
4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889

  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.

4890 4891 4892 4893 4894
    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.

unknown's avatar
unknown committed
4895
  RETURN VALUES
4896 4897
    TRUE   error
    FALSE  success
unknown's avatar
unknown committed
4898 4899
*/

4900 4901 4902 4903 4904 4905
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
unknown's avatar
unknown committed
4906
               enum_alter_table_change_level *need_copy_table,
4907 4908 4909
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
               uint **index_add_buffer, uint *index_add_count)
unknown's avatar
unknown committed
4910 4911 4912
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
4913
  uint key_count;
unknown's avatar
unknown committed
4914 4915
  List_iterator_fast<Create_field> new_field_it(alter_info->create_list);
  Create_field *new_field;
4916 4917
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
4918 4919 4920 4921 4922
  /*
    Remember if the new definition has new VARCHAR column;
    create_info->varchar will be reset in mysql_prepare_create_table.
  */
  bool varchar= create_info->varchar;
4923
  DBUG_ENTER("compare_tables");
unknown's avatar
unknown committed
4924

4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958
  {
    THD *thd= table->in_use;
    /*
      Create a copy of alter_info.
      To compare the new and old table definitions, we need to "prepare"
      the new definition - transform it from parser output to a format
      that describes the final table layout (all column defaults are
      initialized, duplicate columns are removed). This is done by
      mysql_prepare_create_table.  Unfortunately,
      mysql_prepare_create_table performs its transformations
      "in-place", that is, modifies the argument.  Since we would
      like to keep compare_tables() idempotent (not altering any
      of the arguments) we create a copy of alter_info here and
      pass it to mysql_prepare_create_table, then use the result
      to evaluate possibility of fast ALTER TABLE, and then
      destroy the copy.
    */
    Alter_info tmp_alter_info(*alter_info, thd->mem_root);
    uint db_options= 0; /* not used */
    /* Create the prepared information. */
    if (mysql_prepare_create_table(thd, create_info,
                                   &tmp_alter_info,
                                   (table->s->tmp_table != NO_TMP_TABLE),
                                   &db_options,
                                   table->file, key_info_buffer,
                                   &key_count, 0))
      DBUG_RETURN(1);
    /* Allocate result buffers. */
    if (! (*index_drop_buffer=
           (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
        ! (*index_add_buffer=
           (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
      DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975
  /*
    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.
unknown's avatar
unknown committed
4976 4977 4978 4979 4980 4981 4982 4983

    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.
unknown's avatar
unknown committed
4984
  */
4985
  if (table->s->fields != alter_info->create_list.elements ||
unknown's avatar
unknown committed
4986
      table->s->db_type() != create_info->db_type ||
unknown's avatar
unknown committed
4987 4988 4989 4990
      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 ||
4991
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
4992
      order_num ||
unknown's avatar
unknown committed
4993
      !table->s->mysql_version ||
4994
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
4995 4996 4997 4998
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009

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

unknown's avatar
unknown committed
5011 5012 5013
    /* Check that NULL behavior is same for old and new fields */
    if ((new_field->flags & NOT_NULL_FLAG) !=
	(uint) (field->flags & NOT_NULL_FLAG))
5014 5015 5016 5017
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
5018 5019 5020 5021 5022 5023 5024 5025

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

5026
    /* Check if field was renamed */
5027
    field->flags&= ~FIELD_IS_RENAMED;
5028 5029 5030 5031
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
		      new_field->field_name))
      field->flags|= FIELD_IS_RENAMED;      
5032

unknown's avatar
unknown committed
5033 5034
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
    if (!(tmp= field->is_equal(new_field)))
5035 5036 5037 5038
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5039
    // Clear indexed marker
5040
    field->flags&= ~FIELD_IN_ADD_INDEX;
unknown's avatar
unknown committed
5041 5042 5043 5044 5045 5046 5047
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5048 5049 5050
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5051
  KEY *new_key_end= *key_info_buffer + key_count;
unknown's avatar
unknown committed
5052

5053 5054 5055 5056 5057 5058 5059 5060
  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++)
unknown's avatar
unknown committed
5061
  {
5062 5063 5064 5065 5066
    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. */
5067
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5068 5069 5070 5071 5072 5073 5074
    {
      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. */
5075
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5076 5077 5078 5079 5080 5081 5082 5083 5084 5085
      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;
unknown's avatar
unknown committed
5086 5087 5088 5089 5090

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5091 5092 5093
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
unknown's avatar
unknown committed
5094 5095 5096
    {
      /*
	Key definition has changed if we are using a different field or
5097 5098
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
unknown's avatar
unknown committed
5099
      */
5100 5101 5102
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
unknown's avatar
unknown committed
5103
    }
5104 5105 5106 5107
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5108 5109
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5110 5111 5112 5113 5114 5115
    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];
5116
      field->flags|= FIELD_IN_ADD_INDEX;
5117
    }
5118
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
unknown's avatar
unknown committed
5119
  }
5120
  /*end of for (; table_key < table_key_end;) */
unknown's avatar
unknown committed
5121

5122 5123 5124
  /*
    Step through all keys of the new table and find matching old keys.
  */
5125
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5126 5127 5128 5129 5130 5131 5132 5133 5134 5135
  {
    /* 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. */
5136
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5137 5138 5139 5140 5141 5142
      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];
5143
        field->flags|= FIELD_IN_ADD_INDEX;
5144
      }
unknown's avatar
unknown committed
5145
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5146 5147
    }
  }
5148 5149 5150

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5151 5152 5153 5154
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5155

5156
  if (*index_drop_count || *index_add_count)
5157 5158 5159 5160
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5161

5162 5163
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5164 5165 5166
}


unknown's avatar
unknown committed
5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205
/*
  Manages enabling/disabling of indexes for ALTER TABLE

  SYNOPSIS
    alter_table_manage_keys()
      table                  Target table
      indexes_were_disabled  Whether the indexes of the from table
                             were disabled
      keys_onoff             ENABLE | DISABLE | LEAVE_AS_IS

  RETURN VALUES
    FALSE  OK
    TRUE   Error
*/

static
bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
                             enum enum_enable_or_disable keys_onoff)
{
  int error= 0;
  DBUG_ENTER("alter_table_manage_keys");
  DBUG_PRINT("enter", ("table=%p were_disabled=%d on_off=%d",
             table, indexes_were_disabled, keys_onoff));

  switch (keys_onoff) {
  case ENABLE:
    error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
    error= table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5206
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->s->table_name);
unknown's avatar
unknown committed
5207 5208 5209 5210 5211 5212 5213 5214
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253
/**
  Prepare column and key definitions for CREATE TABLE in ALTER TABLE.

  This function transforms parse output of ALTER TABLE - lists of
  columns and keys to add, drop or modify into, essentially,
  CREATE TABLE definition - a list of columns and keys of the new
  table. While doing so, it also performs some (bug not all)
  semantic checks.

  This function is invoked when we know that we're going to
  perform ALTER TABLE via a temporary table -- i.e. fast ALTER TABLE
  is not possible, perhaps because the ALTER statement contains
  instructions that require change in table data, not only in
  table definition or indexes.

  @param[in,out]  thd         thread handle. Used as a memory pool
                              and source of environment information.
  @param[in]      table       the source table, open and locked
                              Used as an interface to the storage engine
                              to acquire additional information about
                              the original table.
  @param[in,out]  create_info A blob with CREATE/ALTER TABLE
                              parameters
  @param[in,out]  alter_info  Another blob with ALTER/CREATE parameters.
                              Originally create_info was used only in
                              CREATE TABLE and alter_info only in ALTER TABLE.
                              But since ALTER might end-up doing CREATE,
                              this distinction is gone and we just carry
                              around two structures.

  @return
    Fills various create_info members based on information retrieved
    from the storage engine.
    Sets create_info->varchar if the table has a VARCHAR column.
    Prepares alter_info->create_list and alter_info->key_list with
    columns and keys of the new table.
  @retval TRUE   error, out of memory or a semantical error in ALTER
                 TABLE instructions
  @retval FALSE  success
5254
*/
5255

5256 5257 5258 5259
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
unknown's avatar
unknown committed
5260
{
5261
  /* New column definitions are added here */
unknown's avatar
unknown committed
5262
  List<Create_field> new_create_list;
5263 5264 5265
  /* New key definitions are added here */
  List<Key> new_key_list;
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
5266
  List_iterator<Create_field> def_it(alter_info->create_list);
5267 5268
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
unknown's avatar
unknown committed
5269 5270 5271
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5272 5273 5274 5275 5276
  uint db_create_options= (table->s->db_create_options
                           & ~(HA_OPTION_PACK_RECORD));
  uint used_fields= create_info->used_fields;
  KEY *key_info=table->key_info;
  bool rc= TRUE;
unknown's avatar
unknown committed
5277

5278
  DBUG_ENTER("mysql_prepare_alter_table");
5279

5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290
  create_info->varchar= FALSE;
  /* Let new create options override the old ones */
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
    create_info->min_rows= table->s->min_rows;
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
    create_info->max_rows= table->s->max_rows;
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
    create_info->avg_row_length= table->s->avg_row_length;
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
    create_info->default_table_charset= table->s->table_charset;
  if (!(used_fields & HA_CREATE_USED_AUTO) && table->found_next_number_field)
5291
  {
5292 5293 5294 5295 5296 5297
    /* Table has an autoincrement, copy value to new table */
    table->file->info(HA_STATUS_AUTO);
    create_info->auto_increment_value= table->file->stats.auto_increment_value;
  }
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5298

5299
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
5300
  {
unknown's avatar
unknown committed
5301
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN));
5302
    /*
5303 5304
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
5305
    */
5306 5307 5308 5309 5310
    if (tablespace &&
        (table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
      create_info->tablespace= tablespace;
  }
  restore_record(table, s->default_values);     // Empty record for DEFAULT
unknown's avatar
unknown committed
5311
  Create_field *def;
5312

5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324
  /*
    First collect all fields from table which isn't in drop_list
  */
  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
    if (field->type() == MYSQL_TYPE_STRING)
      create_info->varchar= TRUE;
    /* Check if field should be dropped */
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
5325
    {
5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337
      if (drop->type == Alter_drop::COLUMN &&
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
      {
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
	    !(used_fields & HA_CREATE_USED_AUTO))
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
	break;
      }
5338
    }
5339
    if (drop)
5340
    {
5341 5342
      drop_it.remove();
      continue;
5343
    }
5344 5345 5346
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
5347
    {
5348 5349 5350
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
5351
    }
5352 5353 5354 5355
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
unknown's avatar
unknown committed
5356
      {
5357 5358
	new_create_list.push_back(def);
	def_it.remove();
unknown's avatar
unknown committed
5359 5360
      }
    }
5361
    else
5362 5363
    {
      /*
5364 5365
        This field was not dropped and not changed, add it to the list
        for the new table.
5366
      */
unknown's avatar
unknown committed
5367
      def= new Create_field(field, field);
5368 5369 5370 5371
      new_create_list.push_back(def);
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
unknown's avatar
unknown committed
5372
      {
5373 5374
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
5375
      }
5376
      if (alter)
unknown's avatar
unknown committed
5377
      {
5378
	if (def->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
5379
	{
5380
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
5381
          goto err;
unknown's avatar
unknown committed
5382
	}
5383 5384 5385 5386 5387
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
	alter_it.remove();
unknown's avatar
unknown committed
5388 5389 5390
      }
    }
  }
5391 5392
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
5393
  {
5394
    if (def->change && ! def->field)
5395
    {
5396 5397
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name);
      goto err;
5398
    }
unknown's avatar
unknown committed
5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415
    /*
      Check that the DATE/DATETIME not null field we are going to add is
      either has a default value or the '0000-00-00' is allowed by the
      set sql mode.
      If the '0000-00-00' value isn't allowed then raise the error_if_not_empty
      flag to allow ALTER TABLE only if the table to be altered is empty.
    */
    if ((def->sql_type == MYSQL_TYPE_DATE ||
         def->sql_type == MYSQL_TYPE_NEWDATE ||
         def->sql_type == MYSQL_TYPE_DATETIME) &&
         !alter_info->datetime_field &&
         !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) &&
         thd->variables.sql_mode & MODE_NO_ZERO_DATE)
    {
        alter_info->datetime_field= def;
        alter_info->error_if_not_empty= TRUE;
    }
5416 5417 5418 5419
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
5420
    else
5421
    {
unknown's avatar
unknown committed
5422
      Create_field *find;
5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name);
        goto err;
      }
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
5435
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
5436
    }
5437
  }
5438
  if (alter_info->alter_list.elements)
5439
  {
5440 5441
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
             alter_info->alter_list.head()->name, table->s->table_name);
unknown's avatar
unknown committed
5442
    goto err;
5443
  }
5444
  if (!new_create_list.elements)
unknown's avatar
unknown committed
5445
  {
unknown's avatar
unknown committed
5446 5447
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
5448
    goto err;
unknown's avatar
unknown committed
5449 5450 5451
  }

  /*
5452 5453
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
5454 5455
  */

5456
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
5457
  {
5458
    char *key_name= key_info->name;
unknown's avatar
unknown committed
5459 5460 5461 5462 5463
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
5464
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479
	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;
unknown's avatar
unknown committed
5480
      Create_field *cfield;
unknown's avatar
unknown committed
5481 5482 5483 5484 5485
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
5486 5487
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
5488 5489
	    break;
	}
5490
	else if (!my_strcasecmp(system_charset_info,
5491
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
5492
	  break;
unknown's avatar
unknown committed
5493 5494 5495 5496 5497
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
5498 5499 5500 5501 5502 5503 5504
      {
        /*
          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.

5505 5506 5507
          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.

5508 5509 5510 5511
          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()) ||
unknown's avatar
unknown committed
5512
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
5513 5514
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
unknown's avatar
unknown committed
5515 5516
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
5517 5518 5519
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
5520
      }
5521
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
5522
      key_parts.push_back(new Key_part_spec(cfield->field_name,
unknown's avatar
unknown committed
5523 5524 5525
					    key_part_length));
    }
    if (key_parts.elements)
5526 5527
    {
      KEY_CREATE_INFO key_create_info;
5528 5529
      Key *key;
      enum Key::Keytype key_type;
5530 5531 5532 5533 5534 5535 5536 5537
      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;

5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672
      if (key_info->flags & HA_SPATIAL)
        key_type= Key::SPATIAL;
      else if (key_info->flags & HA_NOSAME)
      {
        if (! my_strcasecmp(system_charset_info, key_name, primary_key_name))
          key_type= Key::PRIMARY;
        else
          key_type= Key::UNIQUE;
      }
      else if (key_info->flags & HA_FULLTEXT)
        key_type= Key::FULLTEXT;
      else
        key_type= Key::MULTIPLE;

      key= new Key(key_type, key_name,
                   &key_create_info,
                   test(key_info->flags & HA_GENERATED_KEY),
                   key_parts);
      new_key_list.push_back(key);
    }
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
    {
      if (key->type != Key::FOREIGN_KEY)
        new_key_list.push_back(key);
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
        goto err;
      }
    }
  }

  if (alter_info->drop_list.elements)
  {
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
    goto err;
  }
  if (alter_info->alter_list.elements)
  {
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
    goto err;
  }

  if (!create_info->comment.str)
  {
    create_info->comment.str= table->s->comment.str;
    create_info->comment.length= table->s->comment.length;
  }

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

  if (table->s->tmp_table)
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

  rc= FALSE;
  alter_info->create_list.swap(new_create_list);
  alter_info->key_list.swap(new_key_list);
err:
  DBUG_RETURN(rc);
}


/*
  Alter table

  SYNOPSIS
    mysql_alter_table()
      thd              Thread handle
      new_db           If there is a RENAME clause
      new_name         If there is a RENAME clause
      create_info      Information from the parsing phase about new
                       table properties.
      table_list       The table to change.
      alter_info       Lists of fields, keys to be changed, added
                       or dropped.
      order_num        How many ORDER BY fields has been specified.
      order            List of fields to ORDER BY.
      ignore           Whether we have ALTER IGNORE TABLE

  DESCRIPTION
    This is a veery long function and is everything but the kitchen sink :)
    It is used to alter a table and not only by ALTER TABLE but also
    CREATE|DROP INDEX are mapped on this function.

    When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
    or both, then this function short cuts its operation by renaming
    the table and/or enabling/disabling the keys. In this case, the FRM is
    not changed, directly by mysql_alter_table. However, if there is a
    RENAME + change of a field, or an index, the short cut is not used.
    See how `create_list` is used to generate the new FRM regarding the
    structure of the fields. The same is done for the indices of the table.

    Important is the fact, that this function tries to do as little work as
    possible, by finding out whether a intermediate table is needed to copy
    data into and when finishing the altering to use it as the original table.
    For this reason the function compare_tables() is called, which decides
    based on all kind of data how similar are the new and the original
    tables.

  RETURN VALUES
    FALSE  OK
    TRUE   Error
*/

bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
                       HA_CREATE_INFO *create_info,
                       TABLE_LIST *table_list,
                       Alter_info *alter_info,
                       uint order_num, ORDER *order, bool ignore)
{
  TABLE *table, *new_table= 0, *name_lock= 0;
  int error= 0;
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
  char path[FN_REFLEN];
  char reg_path[FN_REFLEN+1];
unknown's avatar
unknown committed
5673
  ha_rows copied,deleted;
5674
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
5675
  legacy_db_type table_type;
5676
  frm_type_enum frm_type;
unknown's avatar
unknown committed
5677
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
5678
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
5679
  uint fast_alter_partition= 0;
5680 5681
  bool partition_changed= FALSE;
#endif
5682 5683 5684 5685 5686 5687
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
  uint index_drop_count;
  uint *index_drop_buffer;
  uint index_add_count;
  uint *index_add_buffer;
unknown's avatar
unknown committed
5688
  bool committed= 0;
unknown's avatar
unknown committed
5689 5690
  DBUG_ENTER("mysql_alter_table");

5691 5692 5693 5694 5695
  LINT_INIT(index_add_count);
  LINT_INIT(index_drop_count);
  LINT_INIT(index_add_buffer);
  LINT_INIT(index_drop_buffer);

5696 5697 5698 5699 5700 5701
  /*
    Check if we attempt to alter mysql.slow_log or
    mysql.general_log table and return an error if
    it is the case.
    TODO: this design is obsolete and will be removed.
  */
5702
  if (table_list && table_list->db && table_list->table_name)
5703
  {
5704
    int table_kind= 0;
5705

5706 5707 5708
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
5709

5710
    if (table_kind)
5711
    {
5712 5713 5714 5715 5716 5717
      /* Disable alter of enabled log tables */
      if (logger.is_log_table_enabled(table_kind))
      {
        my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER");
        DBUG_RETURN(TRUE);
      }
5718

5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734
      /* Disable alter of log tables to unsupported engine */
      if ((create_info->used_fields & HA_CREATE_USED_ENGINE) &&
          (!create_info->db_type || /* unknown engine */
           !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
      {
        my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0));
        DBUG_RETURN(TRUE);
      }

#ifdef WITH_PARTITION_STORAGE_ENGINE
      if (alter_info->flags & ALTER_PARTITION)
      {
	my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
        DBUG_RETURN(TRUE);
      }
#endif
5735 5736 5737
    }
  }

5738 5739 5740 5741 5742
  /*
    Assign variables table_name, new_name, db, new_db, path, reg_path
    to simplify further comparisions: we want to see if it's a RENAME
    later just by comparing the pointers, avoiding the need for strcmp.
  */
unknown's avatar
unknown committed
5743
  thd->proc_info="init";
5744
  table_name=table_list->table_name;
unknown's avatar
unknown committed
5745
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
unknown's avatar
unknown committed
5746
  db=table_list->db;
unknown's avatar
unknown committed
5747
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
5748
    new_db= db;
5749 5750
  build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext, 0);
  build_table_filename(path, sizeof(path), db, table_name, "", 0);
5751 5752


5753
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
unknown's avatar
unknown committed
5754

unknown's avatar
unknown committed
5755
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
5756
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
5757
    /* Conditionally writes to binlog. */
unknown's avatar
unknown committed
5758
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
5759
						   alter_info->tablespace_op));
5760 5761 5762
  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);
5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775
  /*
    If this is just a rename of a view, short cut to the
    following scenario: 1) lock LOCK_open 2) do a RENAME
    2) unlock LOCK_open.
    This is a copy-paste added to make sure
    ALTER (sic:) TABLE .. RENAME works for views. ALTER VIEW is handled
    as an independent branch in mysql_execute_command. The need
    for a copy-paste arose because the main code flow of ALTER TABLE
    ... RENAME tries to use open_ltable, which does not work for views
    (open_ltable was never modified to merge table lists of child tables
    into the main table list, like open_tables does).
    This code is wrong and will be removed, please do not copy.
  */
5776 5777
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
5778
  /* Sic: there is a race here */
5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789
  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));
5790
      DBUG_RETURN(TRUE);
5791 5792 5793
    }

    if (wait_if_global_read_lock(thd,0,1))
5794
      DBUG_RETURN(TRUE);
5795 5796
    VOID(pthread_mutex_lock(&LOCK_open));
    if (lock_table_names(thd, table_list))
5797
    {
unknown's avatar
unknown committed
5798
      error= 1;
5799
      goto view_err;
5800
    }
5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819
    
    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);
  }
unknown's avatar
unknown committed
5820
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
unknown's avatar
unknown committed
5821
    DBUG_RETURN(TRUE);
5822
  table->use_all_columns();
unknown's avatar
unknown committed
5823 5824 5825 5826

  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
5827
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
unknown's avatar
unknown committed
5828
    strmov(new_name_buff,new_name);
5829
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
5830
    if (lower_case_table_names)
unknown's avatar
unknown committed
5831 5832 5833
    {
      if (lower_case_table_names != 2)
      {
5834
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
5835 5836
	new_alias= new_name;			// Create lower case table name
      }
5837
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
5838
    }
5839
    if (new_db == db &&
unknown's avatar
unknown committed
5840
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
5841 5842
    {
      /*
5843 5844
	Source and destination table names are equal: make later check
	easier.
5845
      */
unknown's avatar
unknown committed
5846
      new_alias= new_name= table_name;
5847
    }
unknown's avatar
unknown committed
5848 5849
    else
    {
unknown's avatar
unknown committed
5850
      if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
5851 5852 5853
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
5854
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
5855
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5856 5857 5858 5859
	}
      }
      else
      {
unknown's avatar
unknown committed
5860 5861 5862 5863 5864 5865 5866 5867
        if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
          DBUG_RETURN(TRUE);
        if (!name_lock)
        {
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
	  DBUG_RETURN(TRUE);
        }

5868 5869 5870
        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))
unknown's avatar
unknown committed
5871 5872
	{
	  /* Table will be closed in do_command() */
5873
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
5874
          goto err;
unknown's avatar
unknown committed
5875 5876 5877 5878 5879
	}
      }
    }
  }
  else
5880 5881 5882 5883
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
5884

unknown's avatar
unknown committed
5885
  old_db_type= table->s->db_type();
unknown's avatar
unknown committed
5886
  if (!create_info->db_type)
5887
  {
5888
#ifdef WITH_PARTITION_STORAGE_ENGINE
5889 5890
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
5891 5892 5893 5894
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
5895 5896 5897
        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.
5898
      */
5899
      create_info->db_type= table->part_info->default_engine_type;
5900
    }
5901
    else
5902
#endif
5903
      create_info->db_type= old_db_type;
5904
  }
unknown's avatar
unknown committed
5905

5906
  if (check_engine(thd, new_name, create_info))
unknown's avatar
unknown committed
5907
    goto err;
5908
  new_db_type= create_info->db_type;
5909 5910 5911

  if (new_db_type != old_db_type &&
      !table->file->can_switch_engines())
unknown's avatar
unknown committed
5912
  {
5913
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
unknown's avatar
unknown committed
5914 5915 5916
    goto err;
  }

5917
  if (create_info->row_type == ROW_TYPE_NOT_USED)
5918
    create_info->row_type= table->s->row_type;
unknown's avatar
unknown committed
5919

5920 5921 5922
  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)));
unknown's avatar
unknown committed
5923
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
5924
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
5925 5926 5927
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
5928
    goto err;
5929 5930
  }
  
unknown's avatar
unknown committed
5931
  thd->proc_info="setup";
5932
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
5933
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
5934
  {
5935 5936 5937 5938
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
5939 5940 5941 5942 5943 5944 5945 5946 5947 5948
      /*
        wait_while_table_is_used() ensures that table being altered is
        opened only by this thread and that TABLE::TABLE_SHARE::version
        of TABLE object corresponding to this table is 0.
        The latter guarantees that no DML statement will open this table
        until ALTER TABLE finishes (i.e. until close_thread_tables())
        while the fact that the table is still open gives us protection
        from concurrent DDL statements.
      */
      VOID(pthread_mutex_lock(&LOCK_open));
5949
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
5950
      VOID(pthread_mutex_unlock(&LOCK_open));
5951
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
5952 5953 5954 5955
      error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
5956
      VOID(pthread_mutex_lock(&LOCK_open));
5957
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
5958
      VOID(pthread_mutex_unlock(&LOCK_open));
5959 5960 5961
      error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
5962 5963 5964 5965
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
5966 5967 5968
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
unknown's avatar
unknown committed
5969
      error= 0;
5970 5971 5972 5973 5974
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
    }

5975 5976 5977 5978 5979 5980 5981 5982 5983 5984
    VOID(pthread_mutex_lock(&LOCK_open));
    /*
      Unlike to the above case close_cached_table() below will remove ALL
      instances of TABLE from table cache (it will also remove table lock
      held by this thread). So to make actual table renaming and writing
      to binlog atomic we have to put them into the same critical section
      protected by LOCK_open mutex. This also removes gap for races between
      access() and mysql_rename_table() calls.
    */

5985
    if (!error && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
5986
    {
5987
      thd->proc_info="rename";
unknown's avatar
unknown committed
5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000
      /*
        Then do a 'simple' rename of the table. First we need to close all
        instances of 'source' table.
      */
      close_cached_table(thd, table);
      /*
        Then, we want check once again that target table does not exist.
        Actually the order of these two steps does not matter since
        earlier we took name-lock on the target table, so we do them
        in this particular order only to be consistent with 5.0, in which
        we don't take this name-lock and where this order really matters.
        TODO: Investigate if we need this access() check at all.
      */
6001 6002
      if (!access(new_name_buff,F_OK))
      {
6003
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
6004
	error= -1;
6005 6006 6007
      }
      else
      {
6008 6009 6010 6011 6012 6013 6014 6015 6016 6017
	*fn_ext(new_name)=0;
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
	  error= -1;
        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,
                                  table_name, 0));
          error= -1;
        }
unknown's avatar
unknown committed
6018 6019
      }
    }
6020

6021 6022 6023 6024 6025 6026
    if (error == HA_ERR_WRONG_COMMAND)
    {
      error= 0;
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
6027
    }
6028

6029 6030 6031 6032
    if (!error)
    {
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
      send_ok(thd);
6033
    }
6034
    else if (error > 0)
6035
    {
6036 6037
      table->file->print_error(error, MYF(0));
      error= -1;
6038
    }
6039 6040 6041 6042 6043 6044
    if (name_lock)
      unlink_open_table(thd, name_lock, FALSE);
    VOID(pthread_mutex_unlock(&LOCK_open));
    table_list->table= NULL;                    // For query cache
    query_cache_invalidate3(thd, table_list, 0);
    DBUG_RETURN(error);
unknown's avatar
unknown committed
6045 6046
  }

6047
  /* We have to do full alter table. */
unknown's avatar
unknown committed
6048

6049 6050 6051
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6052
    goto err;
6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065
#endif
  /*
    If the old table had partitions and we are doing ALTER TABLE ...
    engine= <new_engine>, the new table must preserve the original
    partitioning. That means that the new engine is still the
    partitioning engine, not the engine specified in the parser.
    This is discovered  in prep_alter_part_table, which in such case
    updates create_info->db_type.
    Now we need to update the stack copy of create_info->db_type,
    as otherwise we won't be able to correctly move the files of the
    temporary table to the result table files.
  */
  new_db_type= create_info->db_type;
unknown's avatar
unknown committed
6066

6067 6068
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
unknown's avatar
unknown committed
6069 6070
  
  need_copy_table= alter_info->change_level;
unknown's avatar
unknown committed
6071

unknown's avatar
unknown committed
6072 6073
  set_table_default_charset(thd, create_info, db);

6074
  if (thd->variables.old_alter_table
unknown's avatar
unknown committed
6075
      || (table->s->db_type() != create_info->db_type)
6076
#ifdef WITH_PARTITION_STORAGE_ENGINE
6077
      || partition_changed
6078
#endif
6079
     )
6080
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
unknown's avatar
unknown committed
6081
  else
6082
  {
unknown's avatar
unknown committed
6083
    enum_alter_table_change_level need_copy_table_res;
6084
    /* Check how much the tables differ. */
6085 6086
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6087
                       &need_copy_table_res,
6088 6089 6090 6091
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
                       &index_add_buffer, &index_add_count))
      goto err;
6092 6093 6094
   
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6095 6096 6097 6098 6099 6100 6101 6102 6103 6104
  }

  /*
    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;
unknown's avatar
unknown committed
6105
    ulong alter_flags= 0;
6106 6107 6108 6109 6110 6111
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

unknown's avatar
unknown committed
6112 6113
    if (table->s->db_type()->alter_table_flags)
      alter_flags= table->s->db_type()->alter_table_flags(alter_info->flags);
unknown's avatar
unknown committed
6114
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 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 6183 6184 6185 6186 6187 6188 6189 6190 6191
    /* 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. */
6192
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6193 6194 6195 6196 6197
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
6198
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6199 6200 6201 6202 6203
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
unknown's avatar
unknown committed
6204

6205 6206
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
6207
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
6208 6209
    so that ALTER TABLE won't break when somebody will add new flag
  */
6210
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
6211
    create_info->frm_only= 1;
6212

6213
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6214
  if (fast_alter_partition)
unknown's avatar
unknown committed
6215
  {
unknown's avatar
unknown committed
6216
    DBUG_ASSERT(!name_lock);
unknown's avatar
unknown committed
6217 6218 6219 6220
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           db, table_name,
                                           fast_alter_partition));
unknown's avatar
unknown committed
6221
  }
6222
#endif
unknown's avatar
unknown committed
6223

6224 6225 6226 6227 6228 6229
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
  /* Safety fix for innodb */
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, tmp_name);

6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244
  /*
    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.
6245 6246
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272
      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);
    }
  }
6273 6274
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
6275

6276 6277 6278 6279 6280 6281
  /*
    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);
unknown's avatar
unknown committed
6282
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
6283 6284 6285
                                    create_info,
                                    alter_info,
                                    1, 0);
6286 6287
  reenable_binlog(thd);
  if (error)
unknown's avatar
unknown committed
6288
    goto err;
6289 6290

  /* Open the table if we need to copy the data. */
6291
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
6292
  {
6293
    if (table->s->tmp_table)
6294 6295 6296 6297
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
6298
      tbl.table_name= tbl.alias= tmp_name;
unknown's avatar
unknown committed
6299
      /* Table is in thd->temporary_tables */
6300 6301
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
6302 6303 6304 6305
    }
    else
    {
      char path[FN_REFLEN];
unknown's avatar
unknown committed
6306
      /* table is a normal table: Create temporary table in same directory */
6307 6308
      build_table_filename(path, sizeof(path), new_db, tmp_name, "",
                           FN_IS_TMP);
6309
      /* Open our intermediate table */
6310 6311 6312
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
6313
      goto err1;
unknown's avatar
unknown committed
6314 6315
  }

6316
  /* Copy the data if necessary. */
6317
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
6318 6319
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
unknown's avatar
unknown committed
6320
  copied=deleted=0;
6321
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
6322
  {
6323
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
unknown's avatar
unknown committed
6324
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
6325
    new_table->next_number_field=new_table->found_next_number_field;
6326 6327 6328
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
6329
                                    alter_info->keys_onoff,
unknown's avatar
unknown committed
6330
                                    alter_info->error_if_not_empty);
6331
  }
6332
  else
6333 6334 6335
  {
    VOID(pthread_mutex_lock(&LOCK_open));
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6336
    VOID(pthread_mutex_unlock(&LOCK_open));
6337 6338
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
6339 6340 6341
    error= ha_commit_stmt(thd);
    if (ha_commit(thd))
      error= 1;
6342
  }
6343
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
6344

6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357
  /* 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"));

6358
    table->file->prepare_for_alter();
6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385
    if (index_add_count)
    {
      /* 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;
6386
        goto err1;
6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408
      }
    }
    /*end of if (index_add_count)*/

    if (index_drop_count)
    {
      /* 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));
6409
        goto err1;
6410 6411 6412 6413 6414 6415
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
6416
        goto err1;
6417 6418 6419 6420
      }
    }
    /*end of if (index_drop_count)*/

6421 6422 6423 6424
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
6425

6426 6427 6428
    /* 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))
6429
      goto err1;
6430
    committed= 1;
6431 6432 6433
  }
  /*end of if (! new_table) for add/drop index*/

unknown's avatar
unknown committed
6434
  if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6435 6436 6437
  {
    /* We changed a temporary table */
    if (error)
6438
      goto err1;
6439 6440 6441 6442 6443 6444
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
unknown's avatar
unknown committed
6445
    /* Remove link to old table and rename the new one */
unknown's avatar
unknown committed
6446
    close_temporary_table(thd, table, 1, 1);
6447 6448
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
6449
      goto err1;
6450
    /* We don't replicate alter table statement on temporary tables */
6451
    if (!thd->current_stmt_binlog_row_based)
6452
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
unknown's avatar
unknown committed
6453 6454 6455
    goto end_temporary;
  }

6456 6457
  if (new_table)
  {
6458
    /* Close the intermediate table that will be the new table */
unknown's avatar
unknown committed
6459
    intern_close_table(new_table);
6460
    my_free(new_table,MYF(0));
6461
  }
unknown's avatar
unknown committed
6462 6463 6464
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
6465
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
unknown's avatar
unknown committed
6466 6467 6468
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6469

unknown's avatar
unknown committed
6470
  /*
6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483
    Data is copied. Now we:
    1) Wait until all other threads close old version of table.
    2) Close instances of table open by this thread and replace them
       with exclusive name-locks.
    3) Rename the old table to a temp name, rename the new one to the
       old name.
    4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
       we reopen new version of table.
    5) Write statement to the binary log.
    6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
       remove name-locks from list of open tables and table cache.
    7) If we are not not under LOCK TABLES we rely on close_thread_tables()
       call to remove name-locks from table cache and list of open table.
unknown's avatar
unknown committed
6484 6485 6486
  */

  thd->proc_info="rename result table";
6487 6488
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
6489 6490
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
unknown's avatar
unknown committed
6491

6492 6493
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
  close_data_files_and_morph_locks(thd, db, table_name);
unknown's avatar
unknown committed
6494

unknown's avatar
unknown committed
6495
  error=0;
6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510
  save_old_db_type= old_db_type;

  /*
    This leads to the storage engine (SE) not being notified for renames in
    mysql_rename_table(), because we just juggle with the FRM and nothing
    more. If we have an intermediate table, then we notify the SE that
    it should become the actual table. Later, we will recycle the old table.
    However, in case of ALTER TABLE RENAME there might be no intermediate
    table. This is when the old and new tables are compatible, according to
    compare_table(). Then, we need one additional call to
    mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but
    actual rename in the SE and the FRM is not touched. Note that, if the
    table is renamed and the SE is also changed, then an intermediate table
    is created and the additional call will not take place.
  */
6511 6512 6513 6514 6515 6516
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
  {
    DBUG_ASSERT(new_db_type == old_db_type);
    /* This type cannot happen in regular ALTER. */
    new_db_type= old_db_type= NULL;
  }
6517 6518
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
unknown's avatar
unknown committed
6519 6520
  {
    error=1;
6521
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
unknown's avatar
unknown committed
6522
  }
6523
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
6524
                              new_alias, FN_FROM_IS_TMP) ||
6525
           (new_name != table_name || new_db != db) && // we also do rename
6526
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
6527 6528
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
6529 6530
           Table_triggers_list::change_table_name(thd, db, table_name,
                                                  new_db, new_alias))
6531 6532
  {
    /* Try to get everything back. */
unknown's avatar
unknown committed
6533
    error=1;
6534 6535 6536 6537
    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));
unknown's avatar
unknown committed
6538
  }
6539

unknown's avatar
unknown committed
6540 6541
  if (error)
  {
6542 6543
    /* This shouldn't happen. But let us play it safe. */
    goto err_with_placeholders;
unknown's avatar
unknown committed
6544
  }
6545

6546
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
6547
  {
6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559
    /*
      Now we have to inform handler that new .FRM file is in place.
      To do this we need to obtain a handler object for it.
    */
    TABLE *t_table;
    if (new_name != table_name || new_db != db)
    {
      table_list->alias= new_name;
      table_list->table_name= new_name;
      table_list->table_name_length= strlen(new_name);
      table_list->db= new_db;
      table_list->db_length= strlen(new_db);
6560 6561
      table_list->table= name_lock;
      if (reopen_name_locked_table(thd, table_list, FALSE))
6562 6563
        goto err_with_placeholders;
      t_table= table_list->table;
6564
    }
6565
    else
6566
    {
6567 6568 6569
      if (reopen_table(table))
        goto err_with_placeholders;
      t_table= table;
6570
    }
6571 6572 6573 6574 6575
    /* Tell the handler that a new frm file is in place. */
    if (t_table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                            create_info))
      goto err_with_placeholders;
    if (thd->locked_tables && new_name == table_name && new_db == db)
6576
    {
6577 6578 6579 6580 6581 6582 6583 6584
      /*
        We are going to reopen table down on the road, so we have to restore
        state of the TABLE object which we used for obtaining of handler
        object to make it suitable for reopening.
      */
      DBUG_ASSERT(t_table == table);
      table->open_placeholder= 1;
      close_handle_and_leave_table_as_lock(table);
6585
    }
6586
  }
6587

6588 6589 6590
  VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));

  if (thd->locked_tables && new_name == table_name && new_db == db)
unknown's avatar
unknown committed
6591
  {
6592 6593 6594 6595 6596
    thd->in_lock_tables= 1;
    error= reopen_tables(thd, 1, 0);
    thd->in_lock_tables= 0;
    if (error)
      goto err_with_placeholders;
unknown's avatar
unknown committed
6597
  }
6598
  VOID(pthread_mutex_unlock(&LOCK_open));
6599

unknown's avatar
unknown committed
6600
  thd->proc_info="end";
6601

6602 6603
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

6604 6605 6606 6607
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
                      thd->query, thd->query_length,
                      db, table_name);

unknown's avatar
unknown committed
6608 6609
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
                thd->current_stmt_binlog_row_based &&
6610 6611
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
6612

6613
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
6614
  {
unknown's avatar
unknown committed
6615 6616 6617 6618 6619
    /*
      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.
    */
6620
    char path[FN_REFLEN];
6621
    TABLE *t_table;
6622
    build_table_filename(path, sizeof(path), new_db, table_name, "", 0);
6623 6624
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
unknown's avatar
unknown committed
6625
    {
6626
      intern_close_table(t_table);
6627
      my_free(t_table, MYF(0));
unknown's avatar
unknown committed
6628
    }
6629
    else
6630
      sql_print_warning("Could not open table %s.%s after rename\n",
unknown's avatar
unknown committed
6631
                        new_db,table_name);
6632
    ha_flush_logs(old_db_type);
6633
  }
unknown's avatar
unknown committed
6634
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
6635
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
6636

6637
  if (thd->locked_tables && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6638
  {
6639 6640 6641 6642 6643 6644
    /*
      If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
      to remove placeholders for the old table and for the target table
      from the list of open tables and table cache. If we are not under
      LOCK TABLES we can rely on close_thread_tables() doing this job.
    */
unknown's avatar
unknown committed
6645
    pthread_mutex_lock(&LOCK_open);
6646
    unlink_open_table(thd, table, FALSE);
unknown's avatar
unknown committed
6647 6648 6649 6650
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }

unknown's avatar
unknown committed
6651
end_temporary:
6652 6653 6654
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
6655
  send_ok(thd, copied + deleted, 0L, tmp_name);
unknown's avatar
unknown committed
6656
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
6657
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
6658

6659
err1:
6660 6661 6662 6663 6664 6665
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
6666
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
6667

6668
err:
6669 6670 6671 6672 6673 6674
  /*
    No default value was provided for a DATE/DATETIME field, the
    current sql_mode doesn't allow the '0000-00-00' value and
    the table to be altered isn't empty.
    Report error here.
  */
unknown's avatar
unknown committed
6675
  if (alter_info->error_if_not_empty && thd->row_count)
6676
  {
6677 6678
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
unknown's avatar
unknown committed
6679
    switch (alter_info->datetime_field->sql_type)
6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695
    {
      case MYSQL_TYPE_DATE:
      case MYSQL_TYPE_NEWDATE:
        f_val= "0000-00-00";
        t_type= MYSQL_TIMESTAMP_DATE;
        break;
      case MYSQL_TYPE_DATETIME:
        f_val= "0000-00-00 00:00:00";
        t_type= MYSQL_TIMESTAMP_DATETIME;
        break;
      default:
        /* Shouldn't get here. */
        DBUG_ASSERT(0);
    }
    bool save_abort_on_warning= thd->abort_on_warning;
    thd->abort_on_warning= TRUE;
unknown's avatar
unknown committed
6696 6697
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                                 f_val, strlength(f_val), t_type,
unknown's avatar
unknown committed
6698
                                 alter_info->datetime_field->field_name);
6699 6700
    thd->abort_on_warning= save_abort_on_warning;
  }
unknown's avatar
unknown committed
6701 6702 6703 6704 6705 6706
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
6707
  DBUG_RETURN(TRUE);
6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719

err_with_placeholders:
  /*
    An error happened while we were holding exclusive name-lock on table
    being altered. To be safe under LOCK TABLES we should remove placeholders
    from list of open tables list and table cache.
  */
  unlink_open_table(thd, table, FALSE);
  if (name_lock)
    unlink_open_table(thd, name_lock, FALSE);
  VOID(pthread_mutex_unlock(&LOCK_open));
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6720
}
unknown's avatar
unknown committed
6721
/* mysql_alter_table */
unknown's avatar
unknown committed
6722 6723

static int
unknown's avatar
unknown committed
6724
copy_data_between_tables(TABLE *from,TABLE *to,
unknown's avatar
unknown committed
6725
			 List<Create_field> &create,
6726
                         bool ignore,
6727
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
6728
			 ha_rows *copied,
unknown's avatar
unknown committed
6729
			 ha_rows *deleted,
6730 6731
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
unknown's avatar
unknown committed
6732 6733 6734 6735 6736
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
6737
  uint length= 0;
unknown's avatar
unknown committed
6738 6739 6740 6741 6742
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
6743
  ha_rows examined_rows;
6744
  bool auto_increment_field_copied= 0;
6745
  ulong save_sql_mode;
6746
  ulonglong prev_insert_id;
unknown's avatar
unknown committed
6747 6748
  DBUG_ENTER("copy_data_between_tables");

6749 6750 6751 6752 6753 6754
  /*
    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
  */
6755
  error= ha_enable_transaction(thd, FALSE);
6756 6757
  if (error)
    DBUG_RETURN(-1);
6758
  
6759
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
6760 6761
    DBUG_RETURN(-1);				/* purecov: inspected */

6762
  if (to->file->ha_external_lock(thd, F_WRLCK))
6763
    DBUG_RETURN(-1);
6764

unknown's avatar
unknown committed
6765 6766 6767
  /* We need external lock before we can disable/enable keys */
  alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff);

6768
  /* We can abort alter table for any table type */
6769
  thd->no_trans_update.stmt= FALSE;
6770 6771 6772 6773
  thd->abort_on_warning= !ignore && test(thd->variables.sql_mode &
                                         (MODE_STRICT_TRANS_TABLES |
                                          MODE_STRICT_ALL_TABLES));

6774
  from->file->info(HA_STATUS_VARIABLE);
6775
  to->file->ha_start_bulk_insert(from->file->stats.records);
unknown's avatar
unknown committed
6776

6777 6778
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
6779 6780
  List_iterator<Create_field> it(create);
  Create_field *def;
unknown's avatar
unknown committed
6781 6782 6783 6784 6785
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
6786 6787
    {
      if (*ptr == to->next_number_field)
6788
      {
6789
        auto_increment_field_copied= TRUE;
6790 6791 6792 6793 6794 6795 6796 6797 6798
        /*
          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;
      }
unknown's avatar
unknown committed
6799
      (copy_end++)->set(*ptr,def->field,0);
6800 6801
    }

unknown's avatar
unknown committed
6802 6803
  }

6804 6805
  found_count=delete_count=0;

unknown's avatar
unknown committed
6806 6807
  if (order)
  {
unknown's avatar
unknown committed
6808
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
6809
					      MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
6810
    bzero((char*) &tables,sizeof(tables));
6811
    tables.table= from;
unknown's avatar
unknown committed
6812 6813
    tables.alias= tables.table_name= from->s->table_name.str;
    tables.db=    from->s->db.str;
unknown's avatar
unknown committed
6814 6815
    error=1;

unknown's avatar
unknown committed
6816
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
unknown's avatar
unknown committed
6817
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
6818
		    &tables, fields, all_fields, order) ||
6819
	!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
6820
	(from->sort.found_records = filesort(thd, from, sortorder, length,
6821
					     (SQL_SELECT *) 0, HA_POS_ERROR, 1,
unknown's avatar
unknown committed
6822 6823
					     &examined_rows)) ==
	HA_POS_ERROR)
unknown's avatar
unknown committed
6824 6825 6826
      goto err;
  };

6827 6828
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
unknown's avatar
unknown committed
6829
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
6830
  if (ignore)
unknown's avatar
unknown committed
6831
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
6832
  thd->row_count= 0;
6833
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
6834 6835 6836 6837
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
6838
      thd->send_kill_message();
unknown's avatar
unknown committed
6839 6840 6841
      error= 1;
      break;
    }
6842
    thd->row_count++;
6843 6844 6845 6846 6847 6848
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
6849 6850
    if (to->next_number_field)
    {
6851
      if (auto_increment_field_copied)
6852
        to->auto_increment_field_not_null= TRUE;
6853 6854 6855
      else
        to->next_number_field->reset();
    }
6856
    
unknown's avatar
unknown committed
6857
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
6858
    {
unknown's avatar
unknown committed
6859
      copy_ptr->do_copy(copy_ptr);
6860
    }
6861
    prev_insert_id= to->file->next_insert_id;
6862
    error=to->file->write_row(to->record[0]);
6863 6864
    to->auto_increment_field_not_null= FALSE;
    if (error)
unknown's avatar
unknown committed
6865
    {
6866
      if (!ignore ||
6867
          to->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
6868
      {
6869
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
6870 6871 6872 6873
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
unknown's avatar
unknown committed
6874
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
6875
             if (key_nr == 0 &&
unknown's avatar
unknown committed
6876 6877
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
6878
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
unknown's avatar
unknown committed
6879
             to->file->print_keydup_error(key_nr, err_msg);
6880 6881 6882 6883
             break;
           }
         }

unknown's avatar
unknown committed
6884 6885 6886
	to->file->print_error(error,MYF(0));
	break;
      }
6887
      to->file->restore_auto_increment(prev_insert_id);
unknown's avatar
unknown committed
6888 6889 6890
      delete_count++;
    }
    else
6891
      found_count++;
unknown's avatar
unknown committed
6892 6893
  }
  end_read_record(&info);
6894
  free_io_cache(from);
6895
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
6896

6897
  if (to->file->ha_end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
6898
  {
unknown's avatar
unknown committed
6899
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
6900 6901
    error=1;
  }
unknown's avatar
unknown committed
6902
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
6903

6904 6905 6906 6907 6908 6909
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
6910 6911 6912 6913 6914 6915 6916 6917
  /*
    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;
6918

unknown's avatar
unknown committed
6919
 err:
6920
  thd->variables.sql_mode= save_sql_mode;
6921
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
6922
  free_io_cache(from);
unknown's avatar
unknown committed
6923 6924
  *copied= found_count;
  *deleted=delete_count;
6925
  to->file->ha_release_auto_increment();
6926
  if (to->file->ha_external_lock(thd,F_UNLCK))
6927
    error=1;
unknown's avatar
unknown committed
6928 6929
  DBUG_RETURN(error > 0 ? -1 : 0);
}
6930

unknown's avatar
unknown committed
6931

6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
6943
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
6944 6945
{
  HA_CREATE_INFO create_info;
6946 6947 6948 6949 6950
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");

  bzero((char*) &create_info, sizeof(create_info));
unknown's avatar
unknown committed
6951
  create_info.db_type= 0;
unknown's avatar
unknown committed
6952
  create_info.row_type=ROW_TYPE_NOT_USED;
6953
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
6954
  /* Force alter table to recreate table */
6955
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
6956
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
6957 6958
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
6959 6960 6961
}


6962 6963
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
6964 6965 6966 6967 6968
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
6969
  DBUG_ENTER("mysql_checksum_table");
6970 6971 6972

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
6973 6974
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
6975
  item->maybe_null= 1;
6976 6977
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
6978
    DBUG_RETURN(TRUE);
6979

unknown's avatar
VIEW  
unknown committed
6980
  for (table= tables; table; table= table->next_local)
6981 6982
  {
    char table_name[NAME_LEN*2+2];
6983
    TABLE *t;
6984

6985
    strxmov(table_name, table->db ,".", table->table_name, NullS);
unknown's avatar
unknown committed
6986

6987
    t= table->table= open_ltable(thd, table, TL_READ);
unknown's avatar
unknown committed
6988
    thd->clear_error();			// these errors shouldn't get client
6989 6990 6991 6992

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

6993
    if (!t)
6994
    {
unknown's avatar
unknown committed
6995
      /* Table didn't exist */
6996
      protocol->store_null();
unknown's avatar
unknown committed
6997
      thd->clear_error();
6998 6999 7000
    }
    else
    {
7001
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7002 7003
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7004
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
7005
	       (check_opt->flags & T_QUICK))
7006
	protocol->store_null();
7007 7008
      else
      {
7009 7010
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
7011
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7012

7013
        t->use_all_columns();
7014

unknown's avatar
unknown committed
7015
	if (t->file->ha_rnd_init(1))
7016 7017 7018
	  protocol->store_null();
	else
	{
7019
	  for (;;)
7020 7021
	  {
	    ha_checksum row_crc= 0;
7022 7023 7024 7025 7026 7027 7028
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
unknown's avatar
unknown committed
7029 7030 7031 7032
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
7033 7034 7035
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
7036 7037
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7038

7039
	    for (uint i= 0; i < t->s->fields; i++ )
7040 7041
	    {
	      Field *f= t->field[i];
7042
	      if ((f->type() == MYSQL_TYPE_BLOB) ||
7043
                  (f->type() == MYSQL_TYPE_VARCHAR))
7044 7045 7046
	      {
		String tmp;
		f->val_str(&tmp);
7047
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7048 7049
	      }
	      else
7050
		row_crc= my_checksum(row_crc, f->ptr,
unknown's avatar
unknown committed
7051
				     f->pack_length());
7052
	    }
7053

7054 7055 7056
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
7057
          t->file->ha_rnd_end();
7058
	}
7059
      }
unknown's avatar
unknown committed
7060
      thd->clear_error();
7061 7062 7063 7064 7065 7066 7067 7068
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
unknown's avatar
unknown committed
7069
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7070

7071 7072 7073 7074
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
7075
  DBUG_RETURN(TRUE);
7076
}
7077 7078

static bool check_engine(THD *thd, const char *table_name,
7079
                         HA_CREATE_INFO *create_info)
7080
{
7081
  handlerton **new_engine= &create_info->db_type;
unknown's avatar
unknown committed
7082
  handlerton *req_engine= *new_engine;
unknown's avatar
unknown committed
7083
  bool no_substitution=
7084
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
7085 7086
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
7087 7088
    return TRUE;

unknown's avatar
unknown committed
7089
  if (req_engine && req_engine != *new_engine)
7090
  {
unknown's avatar
unknown committed
7091
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
7092 7093
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
unknown's avatar
unknown committed
7094
                       ha_resolve_storage_engine_name(*new_engine),
7095 7096
                       table_name);
  }
7097 7098 7099 7100 7101
  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)
    {
unknown's avatar
unknown committed
7102
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
unknown's avatar
unknown committed
7103
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
7104 7105 7106
      *new_engine= 0;
      return TRUE;
    }
7107
    *new_engine= myisam_hton;
7108
  }
7109 7110
  return FALSE;
}