sql_table.cc 234 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 *);
unknown's avatar
unknown committed
45
static int
46 47 48 49 50 51 52
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
#ifndef DBUG_OFF
unknown's avatar
unknown committed
58

59 60 61 62 63 64 65 66 67 68
/* Wait until we get a 'mysql_kill' signal */

static void wait_for_kill_signal(THD *thd)
{
  while (thd->killed == 0)
    sleep(1);
  // Reset signal and continue as if nothing happend
  thd->killed= THD::NOT_KILLED;
}
#endif
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83

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

84 85
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
86 87 88 89 90 91
  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))
92
  {
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    /* 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.
      */
    }
110
  }
111 112 113

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
114 115 116
}


117 118 119 120 121 122 123 124 125 126 127 128 129
/*
  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.
*/

130 131
uint tablename_to_filename(const char *from, char *to, uint to_length)
{
132
  uint errors, length;
133 134 135
  DBUG_ENTER("tablename_to_filename");
  DBUG_PRINT("enter", ("from '%s'", from));

unknown's avatar
unknown committed
136 137
  if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
                                 MYSQL50_TABLE_NAME_PREFIX_LENGTH))
138 139 140
    DBUG_RETURN((uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
                                to_length-1) -
                        (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH)));
141 142 143 144 145 146 147 148
  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;
  }
149 150
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
151 152 153
}


154
/*
155
  Creates path to a file: mysql_data_dir/db/table.ext
156 157

  SYNOPSIS
158
   build_table_filename()
159
     buff                       Where to write result in my_charset_filename.
160
                                This may be the same as table_name.
161 162 163 164 165 166
     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.
167 168 169 170 171 172

  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".
173 174
    Unless flags indicate a temporary table name.
    'db' is always converted.
175
    'ext' is not converted.
176

177 178 179 180 181
    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.
182

183 184
  RETURN
    path length
185 186 187
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
188
                          const char *table_name, const char *ext, uint flags)
189 190 191
{
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
192
  DBUG_ENTER("build_table_filename");
193 194
  DBUG_PRINT("enter", ("db: '%s'  table_name: '%s'  ext: '%s'  flags: %x",
                       db, table_name, ext, flags));
195 196 197 198 199 200

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

201
  VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
202 203 204 205 206 207 208 209

  char *end = buff + bufflen;
  /* Don't add FN_ROOTDIR if mysql_data_home already includes it */
  char *pos = strnmov(buff, mysql_data_home, bufflen);
  int rootdir_len= strlen(FN_ROOTDIR);
  if (pos - rootdir_len >= buff &&
      memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0)
    pos= strnmov(pos, FN_ROOTDIR, end - pos);
210 211 212 213 214 215
  pos= strxnmov(pos, end - pos, dbbuff, FN_ROOTDIR, NullS);
#ifdef USE_SYMDIR
  unpack_dirname(buff, buff);
  pos= strend(buff);
#endif
  pos= strxnmov(pos, end - pos, tbbuff, ext, NullS);
216

217
  DBUG_PRINT("exit", ("buff: '%s'", buff));
218
  DBUG_RETURN(pos - buff);
219 220 221
}


222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
/*
  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
*/

240
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
241
{
242 243
  DBUG_ENTER("build_tmptable_filename");

244 245 246 247
  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);
248

249 250 251 252 253
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }
254

255
  uint length= unpack_filename(buff, buff);
256 257
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
258 259
}

unknown's avatar
unknown committed
260 261 262
/*
--------------------------------------------------------------------------

263
   MODULE: DDL log
unknown's avatar
unknown committed
264 265 266 267 268 269 270 271
   -----------------

   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
272
   ddl log while we are executing. These entries are dropped when the
unknown's avatar
unknown committed
273 274 275 276
   operation is completed.

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

277
   There is only one ddl log in the system and it is protected by a mutex
unknown's avatar
unknown committed
278 279 280
   and there is a global struct that contains information about its current
   state.

281 282
   History:
   First version written in 2006 by Mikael Ronstrom
unknown's avatar
unknown committed
283 284 285 286
--------------------------------------------------------------------------
*/


287
struct st_global_ddl_log
unknown's avatar
unknown committed
288
{
289 290 291 292 293 294
  /*
    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.
  */
295
  char file_entry_buf[4*IO_SIZE];
unknown's avatar
unknown committed
296 297
  char file_name_str[FN_REFLEN];
  char *file_name;
298 299 300
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
unknown's avatar
unknown committed
301 302
  File file_id;
  uint name_len;
303
  uint io_size;
304
  bool inited;
305
  bool do_release;
306
  bool recovery_phase;
307 308
  st_global_ddl_log() : inited(false), do_release(false) {}
};
unknown's avatar
unknown committed
309

310
st_global_ddl_log global_ddl_log;
unknown's avatar
unknown committed
311

312
pthread_mutex_t LOCK_gdl;
unknown's avatar
unknown committed
313

314 315 316 317 318
#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
319

320 321
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
322
#define DDL_LOG_IO_SIZE_POS 8
unknown's avatar
unknown committed
323

unknown's avatar
unknown committed
324
/*
325
  Read one entry from ddl log file
unknown's avatar
unknown committed
326
  SYNOPSIS
327
    read_ddl_log_file_entry()
328
    entry_no                     Entry number to read
unknown's avatar
unknown committed
329
  RETURN VALUES
330 331
    TRUE                         Error
    FALSE                        Success
unknown's avatar
unknown committed
332 333
*/

334
static bool read_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
335 336
{
  bool error= FALSE;
337
  File file_id= global_ddl_log.file_id;
338
  uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
339 340
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
unknown's avatar
unknown committed
341

342
  if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
343
               MYF(MY_WME)) != io_size)
unknown's avatar
unknown committed
344 345 346 347 348 349
    error= TRUE;
  DBUG_RETURN(error);
}


/*
350
  Write one entry from ddl log file
unknown's avatar
unknown committed
351
  SYNOPSIS
352
    write_ddl_log_file_entry()
unknown's avatar
unknown committed
353 354 355 356 357 358
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

359
static bool write_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
360 361
{
  bool error= FALSE;
362 363 364
  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
365

366
  if (my_pwrite(file_id, (uchar*)file_entry_buf,
367
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
unknown's avatar
unknown committed
368 369 370 371 372 373
    error= TRUE;
  DBUG_RETURN(error);
}


/*
374
  Write ddl log header
unknown's avatar
unknown committed
375
  SYNOPSIS
376
    write_ddl_log_header()
unknown's avatar
unknown committed
377 378 379 380 381
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

382
static bool write_ddl_log_header()
unknown's avatar
unknown committed
383 384
{
  uint16 const_var;
385
  bool error= FALSE;
386
  DBUG_ENTER("write_ddl_log_header");
unknown's avatar
unknown committed
387

388 389
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
390
  const_var= FN_LEN;
391
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
unknown's avatar
unknown committed
392
            (ulong) const_var);
393
  const_var= IO_SIZE;
394
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
unknown's avatar
unknown committed
395
            (ulong) const_var);
396
  if (write_ddl_log_file_entry(0UL))
397 398 399 400 401
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
402
  DBUG_RETURN(error);
unknown's avatar
unknown committed
403 404 405
}


406
/*
407
  Create ddl log file name
408
  SYNOPSIS
409
    create_ddl_log_file_name()
410 411 412 413 414
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

415
static inline void create_ddl_log_file_name(char *file_name)
416
{
417
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
418 419 420
}


unknown's avatar
unknown committed
421
/*
422
  Read header of ddl log file
unknown's avatar
unknown committed
423
  SYNOPSIS
424
    read_ddl_log_header()
unknown's avatar
unknown committed
425
  RETURN VALUES
426 427
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
unknown's avatar
unknown committed
428
  DESCRIPTION
429 430 431
    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
432 433
*/

434
static uint read_ddl_log_header()
unknown's avatar
unknown committed
435
{
436
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
437
  char file_name[FN_REFLEN];
438
  uint entry_no;
439
  bool successful_open= FALSE;
440
  DBUG_ENTER("read_ddl_log_header");
unknown's avatar
unknown committed
441

442
  create_ddl_log_file_name(file_name);
443
  if ((global_ddl_log.file_id= my_open(file_name,
444
                                        O_RDWR | O_BINARY, MYF(0))) >= 0)
unknown's avatar
unknown committed
445
  {
446
    if (read_ddl_log_file_entry(0UL))
447
    {
448 449
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
450
    }
451 452
    else
      successful_open= TRUE;
unknown's avatar
unknown committed
453
  }
454 455
  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]);
456 457
  if (successful_open)
  {
458
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
459 460 461
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
462
  else
463 464 465
  {
    entry_no= 0;
  }
466 467 468 469
  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));
470
  global_ddl_log.do_release= true;
unknown's avatar
unknown committed
471
  DBUG_RETURN(entry_no);
unknown's avatar
unknown committed
472 473 474 475
}


/*
476
  Read a ddl log entry
unknown's avatar
unknown committed
477
  SYNOPSIS
478
    read_ddl_log_entry()
unknown's avatar
unknown committed
479 480 481 482 483 484
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
485
    Read a specified entry in the ddl log
unknown's avatar
unknown committed
486 487
*/

488
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
489
{
490
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
491
  uint inx;
492
  uchar single_char;
493
  DBUG_ENTER("read_ddl_log_entry");
494

495
  if (read_ddl_log_file_entry(read_entry))
496 497 498
  {
    DBUG_RETURN(TRUE);
  }
499
  ddl_log_entry->entry_pos= read_entry;
500 501 502 503
  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;
504 505 506 507 508 509 510
  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
511 512 513 514 515
  DBUG_RETURN(FALSE);
}


/*
516
  Initialise ddl log
unknown's avatar
unknown committed
517
  SYNOPSIS
518
    init_ddl_log()
unknown's avatar
unknown committed
519

unknown's avatar
unknown committed
520
  DESCRIPTION
521
    Write the header of the ddl log file and length of names. Also set
unknown's avatar
unknown committed
522
    number of entries to zero.
unknown's avatar
unknown committed
523 524 525 526

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
unknown's avatar
unknown committed
527 528
*/

529
static bool init_ddl_log()
unknown's avatar
unknown committed
530
{
531
  char file_name[FN_REFLEN];
532
  DBUG_ENTER("init_ddl_log");
unknown's avatar
unknown committed
533

534
  if (global_ddl_log.inited)
unknown's avatar
unknown committed
535 536
    goto end;

537
  global_ddl_log.io_size= IO_SIZE;
538
  create_ddl_log_file_name(file_name);
539 540 541 542
  if ((global_ddl_log.file_id= my_create(file_name,
                                         CREATE_MODE,
                                         O_RDWR | O_TRUNC | O_BINARY,
                                         MYF(MY_WME))) < 0)
543
  {
544
    /* Couldn't create ddl log file, this is serious error */
545 546
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
547
  }
unknown's avatar
Fixes  
unknown committed
548
  global_ddl_log.inited= TRUE;
549
  if (write_ddl_log_header())
550
  {
unknown's avatar
unknown committed
551
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
unknown's avatar
Fixes  
unknown committed
552
    global_ddl_log.inited= FALSE;
553
    DBUG_RETURN(TRUE);
554
  }
unknown's avatar
unknown committed
555 556

end:
557
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
558 559 560 561
}


/*
562
  Execute one action in a ddl log entry
unknown's avatar
unknown committed
563
  SYNOPSIS
564 565
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
unknown's avatar
unknown committed
566
  RETURN VALUES
567 568
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
569 570
*/

571
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
572
{
573 574
  bool frm_action= FALSE;
  LEX_STRING handler_name;
575
  handler *file= NULL;
576
  MEM_ROOT mem_root;
577
  int error= TRUE;
578
  char to_path[FN_REFLEN];
579
  char from_path[FN_REFLEN];
580
#ifdef WITH_PARTITION_STORAGE_ENGINE
581
  char *par_ext= (char*)".par";
582
#endif
583
  handlerton *hton;
584
  DBUG_ENTER("execute_ddl_log_action");
585

586
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
587 588 589
  {
    DBUG_RETURN(FALSE);
  }
590 591
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
592
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
593
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
594 595 596
    frm_action= TRUE;
  else
  {
unknown's avatar
unknown committed
597 598
    plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
    if (!plugin)
599 600 601 602
    {
      my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
      goto error;
    }
unknown's avatar
unknown committed
603 604
    hton= plugin_data(plugin, handlerton*);
    file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
605
    if (!file)
606 607
    {
      mem_alloc_error(sizeof(handler));
608
      goto error;
609
    }
610
  }
611
  switch (ddl_log_entry->action_type)
612
  {
613
    case DDL_LOG_REPLACE_ACTION:
614
    case DDL_LOG_DELETE_ACTION:
615
    {
616
      if (ddl_log_entry->phase == 0)
617 618 619
      {
        if (frm_action)
        {
620 621 622
          strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
          if ((error= my_delete(to_path, MYF(MY_WME))))
          {
623
            if (my_errno != ENOENT)
624 625
              break;
          }
626
#ifdef WITH_PARTITION_STORAGE_ENGINE
627 628
          strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
          VOID(my_delete(to_path, MYF(MY_WME)));
629
#endif
630 631 632
        }
        else
        {
633
          if ((error= file->ha_delete_table(ddl_log_entry->name)))
634 635 636 637
          {
            if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
              break;
          }
638
        }
639
        if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
640 641 642
          break;
        VOID(sync_ddl_log());
        error= FALSE;
643 644
        if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
          break;
645
      }
646 647 648 649 650 651
      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.
      */
652
    }
653
    case DDL_LOG_RENAME_ACTION:
654
    {
655 656 657
      error= TRUE;
      if (frm_action)
      {
658
        strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
659
        strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
660
        if (my_rename(from_path, to_path, MYF(MY_WME)))
661
          break;
662
#ifdef WITH_PARTITION_STORAGE_ENGINE
663
        strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
664
        strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
665
        VOID(my_rename(from_path, to_path, MYF(MY_WME)));
666
#endif
667 668 669
      }
      else
      {
670 671
        if (file->ha_rename_table(ddl_log_entry->from_name,
                                  ddl_log_entry->name))
672
          break;
673 674
      }
      if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
675 676 677
        break;
      VOID(sync_ddl_log());
      error= FALSE;
678
      break;
679
    }
680 681 682 683 684 685 686 687
    default:
      DBUG_ASSERT(0);
      break;
  }
  delete file;
error:
  free_root(&mem_root, MYF(0)); 
  DBUG_RETURN(error);
unknown's avatar
unknown committed
688 689 690
}


unknown's avatar
unknown committed
691
/*
692
  Get a free entry in the ddl log
unknown's avatar
unknown committed
693
  SYNOPSIS
694 695
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
unknown's avatar
unknown committed
696
  RETURN VALUES
697 698
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
699 700
*/

701 702
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
unknown's avatar
unknown committed
703
{
704 705 706
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
707

708
  if (global_ddl_log.first_free == NULL)
709
  {
710 711
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
712
    {
713
      sql_print_error("Failed to allocate memory for ddl log free list");
714 715
      DBUG_RETURN(TRUE);
    }
716
    global_ddl_log.num_entries++;
717
    used_entry->entry_pos= global_ddl_log.num_entries;
718
    *write_header= TRUE;
719 720 721
  }
  else
  {
722 723
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
724
    *write_header= FALSE;
725 726 727 728 729 730
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
731
  global_ddl_log.first_used= used_entry;
732 733 734 735
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
736
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
737 738 739 740
}


/*
741
  External interface methods for the DDL log Module
742 743 744 745
  ---------------------------------------------------
*/

/*
unknown's avatar
unknown committed
746
  SYNOPSIS
747 748 749
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
750

unknown's avatar
unknown committed
751
  RETURN VALUES
752 753 754 755
    TRUE                      Error
    FALSE                     Success

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

760 761
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
762
{
763
  bool error, write_header;
764 765 766 767 768 769
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
770 771
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
772
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
773
                                    (char)ddl_log_entry->action_type;
774 775 776 777
  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);
778 779
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
780 781
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
unknown's avatar
unknown committed
782
  {
783
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
784 785
    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
786
  }
787
  else
788 789
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
790 791
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
792
  if (get_free_ddl_log_entry(active_entry, &write_header))
793 794 795 796
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
797
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
798
  {
799
    error= TRUE;
800 801 802
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
803 804
  if (write_header && !error)
  {
805 806
    VOID(sync_ddl_log());
    if (write_ddl_log_header())
807 808
      error= TRUE;
  }
809
  if (error)
810
    release_ddl_log_memory_entry(*active_entry);
811
  DBUG_RETURN(error);
unknown's avatar
unknown committed
812 813 814 815
}


/*
816
  Write final entry in the ddl log
unknown's avatar
unknown committed
817
  SYNOPSIS
818
    write_execute_ddl_log_entry()
819 820 821 822
    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.
823 824 825
    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
826 827 828
                                   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
829
  RETURN VALUES
830 831 832 833
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
834
    This is the last write in the ddl log. The previous log entries have
835
    already been written but not yet synched to disk.
836 837 838 839
    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 
840
*/ 
unknown's avatar
unknown committed
841

842 843 844
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
845
{
846
  bool write_header= FALSE;
847 848
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
849

850 851 852 853
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
854 855
  if (!complete)
  {
856 857 858 859 860 861
    /*
      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.
    */
862
    VOID(sync_ddl_log());
863
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
864 865
  }
  else
866
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
867 868 869 870 871 872
  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;
873
  if (!(*active_entry))
874
  {
875
    if (get_free_ddl_log_entry(active_entry, &write_header))
876 877 878
    {
      DBUG_RETURN(TRUE);
    }
879
  }
880
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
881
  {
882
    sql_print_error("Error writing execute entry in ddl log");
883
    release_ddl_log_memory_entry(*active_entry);
884 885
    DBUG_RETURN(TRUE);
  }
886
  VOID(sync_ddl_log());
887 888
  if (write_header)
  {
889
    if (write_ddl_log_header())
890
    {
891
      release_ddl_log_memory_entry(*active_entry);
892 893 894
      DBUG_RETURN(TRUE);
    }
  }
unknown's avatar
unknown committed
895 896 897 898
  DBUG_RETURN(FALSE);
}


899
/*
900
  For complex rename operations we need to deactivate individual entries.
901
  SYNOPSIS
902
    deactivate_ddl_log_entry()
903 904 905 906 907 908 909 910
    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
911
    do in a safe manner unless the ddl log is informed of the phases in
912 913 914 915 916 917 918 919 920 921 922
    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.
*/

923
bool deactivate_ddl_log_entry(uint entry_no)
924
{
925 926
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
927

928
  if (!read_ddl_log_file_entry(entry_no))
929
  {
930
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
931
    {
932 933 934 935 936 937
      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)
938
      {
939 940
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
941 942 943 944 945
      }
      else
      {
        DBUG_ASSERT(0);
      }
946 947 948 949 950 951
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
952 953
    }
  }
954 955 956 957 958 959
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
960 961 962 963
}


/*
964
  Sync ddl log file
965
  SYNOPSIS
966
    sync_ddl_log()
967 968 969 970 971
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

972
bool sync_ddl_log()
973 974
{
  bool error= FALSE;
975
  DBUG_ENTER("sync_ddl_log");
976

977 978
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
979 980 981 982
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
983 984
  {
    /* Write to error log */
985
    sql_print_error("Failed to sync ddl log");
986 987 988 989 990 991
    error= TRUE;
  }
  DBUG_RETURN(error);
}


992 993 994
/*
  Release a log memory entry
  SYNOPSIS
995
    release_ddl_log_memory_entry()
996 997 998 999 1000
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

1001
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
1002
{
1003 1004 1005 1006
  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");
1007

1008
  global_ddl_log.first_free= log_entry;
1009 1010 1011 1012 1013
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1014
    global_ddl_log.first_used= next_log_entry;
1015 1016
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1017
  DBUG_VOID_RETURN;
1018 1019 1020
}


unknown's avatar
unknown committed
1021
/*
1022
  Execute one entry in the ddl log. Executing an entry means executing
unknown's avatar
unknown committed
1023 1024
  a linked list of actions.
  SYNOPSIS
1025
    execute_ddl_log_entry()
unknown's avatar
unknown committed
1026 1027 1028 1029 1030 1031
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1032
bool execute_ddl_log_entry(THD *thd, uint first_entry)
unknown's avatar
unknown committed
1033
{
1034
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
unknown committed
1035
  uint read_entry= first_entry;
1036
  DBUG_ENTER("execute_ddl_log_entry");
unknown's avatar
unknown committed
1037

1038
  pthread_mutex_lock(&LOCK_gdl);
unknown's avatar
unknown committed
1039 1040
  do
  {
1041
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1042 1043
    {
      /* Write to error log and continue with next log entry */
1044 1045
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1046 1047
      break;
    }
1048 1049
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1050

1051
    if (execute_ddl_log_action(thd, &ddl_log_entry))
unknown's avatar
unknown committed
1052
    {
1053
      /* Write to error log and continue with next log entry */
1054 1055
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1056
      break;
unknown's avatar
unknown committed
1057
    }
1058
    read_entry= ddl_log_entry.next_entry;
unknown's avatar
unknown committed
1059
  } while (read_entry);
1060
  pthread_mutex_unlock(&LOCK_gdl);
unknown's avatar
unknown committed
1061 1062 1063
  DBUG_RETURN(FALSE);
}

1064

1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
/*
  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
1085
/*
1086
  Execute the ddl log at recovery of MySQL Server
unknown's avatar
unknown committed
1087
  SYNOPSIS
1088
    execute_ddl_log_recovery()
unknown's avatar
unknown committed
1089 1090 1091 1092
  RETURN VALUES
    NONE
*/

1093
void execute_ddl_log_recovery()
unknown's avatar
unknown committed
1094
{
1095
  uint num_entries, i;
unknown's avatar
Fixes  
unknown committed
1096
  THD *thd;
1097
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
Fixes  
unknown committed
1098
  char file_name[FN_REFLEN];
1099
  DBUG_ENTER("execute_ddl_log_recovery");
unknown's avatar
unknown committed
1100

1101 1102 1103 1104 1105 1106 1107
  /*
    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
1108
  global_ddl_log.file_id= (File) -1;
1109

1110 1111 1112 1113 1114 1115 1116 1117
  /*
    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();

1118
  num_entries= read_ddl_log_header();
1119
  for (i= 1; i < num_entries + 1; i++)
unknown's avatar
unknown committed
1120
  {
1121
    if (read_ddl_log_entry(i, &ddl_log_entry))
1122
    {
1123 1124 1125
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1126
    }
1127
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
unknown's avatar
unknown committed
1128
    {
unknown's avatar
Fixes  
unknown committed
1129
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
unknown's avatar
unknown committed
1130
      {
1131 1132
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
unknown's avatar
unknown committed
1133 1134 1135
      }
    }
  }
1136
  close_ddl_log();
1137 1138 1139 1140 1141 1142
  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);
1143
  DBUG_VOID_RETURN;
1144 1145 1146 1147
}


/*
1148
  Release all memory allocated to the ddl log
1149
  SYNOPSIS
1150
    release_ddl_log()
1151 1152 1153 1154
  RETURN VALUES
    NONE
*/

1155
void release_ddl_log()
1156
{
1157 1158 1159
  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");
1160

1161 1162 1163
  if (!global_ddl_log.do_release)
    DBUG_VOID_RETURN;

1164
  pthread_mutex_lock(&LOCK_gdl);
1165 1166
  while (used_list)
  {
1167
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1168
    my_free(used_list, MYF(0));
unknown's avatar
unknown committed
1169
    used_list= tmp;
1170 1171 1172
  }
  while (free_list)
  {
1173
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1174
    my_free(free_list, MYF(0));
unknown's avatar
unknown committed
1175
    free_list= tmp;
1176
  }
1177
  close_ddl_log();
unknown's avatar
unknown committed
1178
  global_ddl_log.inited= 0;
1179
  pthread_mutex_unlock(&LOCK_gdl);
1180
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1181
  global_ddl_log.do_release= false;
1182
  DBUG_VOID_RETURN;
1183 1184 1185
}


unknown's avatar
unknown committed
1186 1187 1188
/*
---------------------------------------------------------------------------

1189
  END MODULE DDL log
unknown's avatar
unknown committed
1190 1191 1192 1193 1194
  --------------------

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

unknown's avatar
unknown committed
1195

1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
/**
   @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
1221 1222 1223 1224 1225 1226 1227 1228
/*
  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
1229 1230 1231 1232 1233 1234
      WFRM_INSTALL_SHADOW       If set we should install the new frm
      WFRM_KEEP_SHARE           If set we know that the share is to be
                                retained and thus we should ensure share
                                object is correct, if not set we don't
                                set the new partition syntax string since
                                we know the share object is destroyed.
unknown's avatar
unknown committed
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
      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];
1257 1258
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
unknown's avatar
unknown committed
1259
  char frm_name[FN_REFLEN+1];
1260 1261 1262 1263
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char *part_syntax_buf;
  uint syntax_len;
#endif
unknown's avatar
unknown committed
1264 1265
  DBUG_ENTER("mysql_write_frm");

1266 1267 1268
  /*
    Build shadow frm file name
  */
1269
  build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
1270
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1271
  if (flags & WFRM_WRITE_SHADOW)
unknown's avatar
unknown committed
1272
  {
1273 1274 1275 1276 1277 1278 1279 1280
    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
1281 1282 1283 1284 1285
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1286 1287
      partition_info *part_info= lpt->table->part_info;
      if (part_info)
unknown's avatar
unknown committed
1288
      {
1289 1290
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1291
                                                         TRUE, TRUE)))
1292 1293 1294 1295
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
1296
        part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
1297 1298 1299
      }
    }
#endif
1300 1301 1302 1303
    /* 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,
1304
                          lpt->alter_info->create_list, lpt->key_count,
1305
                          lpt->key_info_buffer, lpt->table->file)) ||
1306 1307 1308
        lpt->table->file->ha_create_handler_files(shadow_path, NULL,
                                                  CHF_CREATE_FLAG,
                                                  lpt->create_info))
1309 1310 1311 1312 1313
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
unknown's avatar
unknown committed
1314 1315 1316 1317 1318 1319 1320 1321 1322
  }
  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.
    */
1323 1324
    uchar *data;
    size_t length;
1325
    if (readfrm(shadow_path, &data, &length) ||
unknown's avatar
unknown committed
1326 1327
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
1328 1329
      my_free(data, MYF(MY_ALLOW_ZERO_PTR));
      my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1330 1331 1332 1333
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1334
    error= my_delete(shadow_frm_name, MYF(MY_WME));
unknown's avatar
unknown committed
1335
  }
1336 1337
  if (flags & WFRM_INSTALL_SHADOW)
  {
1338 1339 1340
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1341 1342 1343 1344
    /*
      Build frm file name
    */
    build_table_filename(path, sizeof(path), lpt->db,
1345
                         lpt->table_name, "", 0);
1346 1347 1348 1349
    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.
1350 1351 1352 1353 1354 1355
      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.
1356 1357 1358
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1359
#ifdef WITH_PARTITION_STORAGE_ENGINE
1360 1361
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_DELETE_FLAG, NULL) ||
1362 1363
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1364
#endif
1365
#ifdef WITH_PARTITION_STORAGE_ENGINE
1366
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1367 1368
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_RENAME_FLAG, NULL))
1369 1370 1371
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1372 1373
    {
      error= 1;
1374
      goto err;
1375
    }
1376
#ifdef WITH_PARTITION_STORAGE_ENGINE
1377
    if (part_info && (flags & WFRM_KEEP_SHARE))
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
    {
      TABLE_SHARE *share= lpt->table->s;
      char *tmp_part_syntax_str;
      if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                       &syntax_len,
                                                       TRUE, TRUE)))
      {
        error= 1;
        goto err;
      }
      if (share->partition_info_buffer_size < syntax_len + 1)
      {
        share->partition_info_buffer_size= syntax_len+1;
        if (!(tmp_part_syntax_str= (char*) strmake_root(&share->mem_root,
                                                        part_syntax_buf,
                                                        syntax_len)))
        {
          error= 1;
          goto err;
        }
        share->partition_info= tmp_part_syntax_str;
      }
      else
        memcpy((char*) share->partition_info, part_syntax_buf, syntax_len + 1);
      share->partition_info_len= part_info->part_info_len= syntax_len;
      part_info->part_info_string= part_syntax_buf;
1404
    }
1405 1406 1407
#endif

err:
1408
    VOID(pthread_mutex_unlock(&LOCK_open));
1409
#ifdef WITH_PARTITION_STORAGE_ENGINE
1410
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1411
    part_info->frm_log_entry= NULL;
1412
    VOID(sync_ddl_log());
1413
#endif
unknown's avatar
unknown committed
1414
  }
1415

unknown's avatar
unknown committed
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
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);
  }
}

1449

unknown's avatar
unknown committed
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
/*
 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
1468 1469
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
1470 1471

*/
unknown's avatar
unknown committed
1472

unknown's avatar
unknown committed
1473 1474
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
1475
{
1476
  bool error= FALSE, need_start_waiters= FALSE;
unknown's avatar
unknown committed
1477 1478 1479 1480
  DBUG_ENTER("mysql_rm_table");

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

1481
  if (!drop_temporary)
unknown's avatar
unknown committed
1482
  {
1483
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1484
    {
1485
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1486
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1487
    }
1488 1489
    else
      need_start_waiters= TRUE;
unknown's avatar
unknown committed
1490
  }
1491 1492 1493 1494 1495 1496

  /*
    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
1497
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1498

1499 1500 1501
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1502
  if (error)
unknown's avatar
unknown committed
1503
    DBUG_RETURN(TRUE);
1504
  my_ok(thd);
unknown's avatar
unknown committed
1505
  DBUG_RETURN(FALSE);
1506 1507
}

1508
/*
1509 1510 1511 1512 1513 1514 1515 1516 1517
  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
1518
    drop_view		Allow to delete VIEW .frm
1519 1520
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1521

1522 1523 1524 1525 1526 1527 1528 1529 1530
  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.
1531 1532 1533 1534 1535

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1536
*/
1537 1538

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
unknown's avatar
VIEW  
unknown committed
1539 1540
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1541 1542
{
  TABLE_LIST *table;
1543 1544
  char path[FN_REFLEN], *alias;
  uint path_length;
1545
  String wrong_tables;
1546
  int error= 0;
1547
  int non_temp_tables_count= 0;
unknown's avatar
unknown committed
1548
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1549
  String built_query;
1550 1551
  DBUG_ENTER("mysql_rm_table_part2");

1552 1553 1554
  LINT_INIT(alias);
  LINT_INIT(path_length);

1555
  if (thd->current_stmt_binlog_row_based && !dont_log_query)
1556 1557 1558 1559 1560 1561 1562
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1563

1564
  mysql_ha_rm_tables(thd, tables, FALSE);
1565

1566 1567
  pthread_mutex_lock(&LOCK_open);

unknown's avatar
unknown committed
1568 1569 1570 1571 1572 1573 1574 1575 1576
  /*
    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
1577
    table->db_type= NULL;
unknown's avatar
unknown committed
1578
    if ((share= get_cached_table_share(table->db, table->table_name)))
unknown's avatar
unknown committed
1579
      table->db_type= share->db_type();
1580 1581

    /* Disable drop of enabled log tables */
1582
    if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) &&
1583 1584
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1585
    {
1586
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1587
      pthread_mutex_unlock(&LOCK_open);
1588 1589
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
1590 1591
  }

1592 1593
  if (!drop_temporary && lock_table_names_exclusively(thd, tables))
  {
1594
    pthread_mutex_unlock(&LOCK_open);
1595
    DBUG_RETURN(1);
1596
  }
1597

1598 1599 1600
  /* 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
1601
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
1602
  {
1603
    char *db=table->db;
unknown's avatar
unknown committed
1604 1605
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1606

1607 1608 1609
    DBUG_PRINT("table", ("table_l: '%s'.'%s'  table: 0x%lx  s: 0x%lx",
                         table->db, table->table_name, (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
1610 1611 1612 1613 1614 1615 1616 1617 1618

    error= drop_temporary_table(thd, table);

    switch (error) {
    case  0:
      // removed temporary table
      tmp_table_deleted= 1;
      continue;
    case -1:
1619
      DBUG_ASSERT(thd->in_sub_stmt);
1620 1621 1622 1623 1624
      error= 1;
      goto err_with_placeholders;
    default:
      // temporary table not found
      error= 0;
unknown's avatar
unknown committed
1625
    }
unknown's avatar
unknown committed
1626

1627 1628 1629 1630 1631 1632
    /*
      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.
      */
1633
    if (thd->current_stmt_binlog_row_based && !dont_log_query)
1634
    {
1635
      non_temp_tables_count++;
1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650
      /*
        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
1651
    table_type= table->db_type;
1652
    if (!drop_temporary)
unknown's avatar
unknown committed
1653
    {
1654
      TABLE *locked_table;
unknown's avatar
unknown committed
1655 1656 1657 1658
      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);
1659 1660 1661 1662 1663 1664 1665
      /*
        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;

1666
      if (thd->killed)
1667
      {
1668 1669
        error= -1;
        goto err_with_placeholders;
1670
      }
1671
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
unknown's avatar
unknown committed
1672
      /* remove .frm file and engine files */
1673 1674 1675
      path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
                                        table->internal_tmp_table ?
                                        FN_IS_TMP : 0);
unknown's avatar
unknown committed
1676
    }
1677 1678
    if (drop_temporary ||
        (table_type == NULL &&        
unknown's avatar
unknown committed
1679 1680 1681
         (access(path, F_OK) &&
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
unknown's avatar
unknown committed
1682
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
unknown's avatar
unknown committed
1683
    {
1684
      // Table was not found on disk and table can't be created from engine
1685
      if (if_exists)
1686 1687
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
1688
			    table->table_name);
1689
      else
1690
        error= 1;
unknown's avatar
unknown committed
1691 1692 1693
    }
    else
    {
unknown's avatar
unknown committed
1694
      char *end;
unknown's avatar
unknown committed
1695 1696 1697 1698 1699
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
1700 1701
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
unknown's avatar
unknown committed
1702
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
1703
                             !dont_log_query);
1704
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
unknown's avatar
unknown committed
1705
	  (if_exists || table_type == NULL))
1706
      {
1707
	error= 0;
1708 1709
        thd->clear_error();
      }
unknown's avatar
unknown committed
1710
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
1711 1712
      {
	/* the table is referenced by a foreign key constraint */
1713
	foreign_key_error=1;
unknown's avatar
unknown committed
1714
      }
1715
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
1716
      {
1717
        int new_error;
unknown's avatar
unknown committed
1718 1719
	/* Delete the table definition file */
	strmov(end,reg_ext);
1720
	if (!(new_error=my_delete(path,MYF(MY_WME))))
1721
        {
unknown's avatar
unknown committed
1722
	  some_tables_deleted=1;
1723 1724
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
1725
        }
1726
        error|= new_error;
1727
      }
unknown's avatar
unknown committed
1728 1729 1730 1731 1732
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
1733
      wrong_tables.append(String(table->table_name,system_charset_info));
unknown's avatar
unknown committed
1734
    }
1735 1736
    DBUG_PRINT("table", ("table: 0x%lx  s: 0x%lx", (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
unknown's avatar
unknown committed
1737
  }
1738 1739 1740 1741 1742
  /*
    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
1743
  thd->thread_specific_used|= tmp_table_deleted;
1744 1745 1746 1747
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
1748
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
1749
                      wrong_tables.c_ptr());
1750
    else
unknown's avatar
unknown committed
1751
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
1752 1753 1754 1755
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
1756
  {
unknown's avatar
unknown committed
1757
    query_cache_invalidate3(thd, tables, 0);
1758
    if (!dont_log_query)
1759
    {
1760
      if (!thd->current_stmt_binlog_row_based ||
1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771
          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);
      }
1772
      else if (thd->current_stmt_binlog_row_based &&
1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799
               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.
      */
1800
    }
unknown's avatar
unknown committed
1801
  }
1802 1803
  pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
1804
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
1805
  pthread_mutex_unlock(&LOCK_open);
1806
  thd->no_warnings_for_error= 0;
1807
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1808 1809 1810
}


1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825
/*
  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
1826
bool quick_rm_table(handlerton *base,const char *db,
1827
                    const char *table_name, uint flags)
unknown's avatar
unknown committed
1828 1829
{
  char path[FN_REFLEN];
unknown's avatar
unknown committed
1830 1831 1832
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

1833
  uint path_length= build_table_filename(path, sizeof(path),
1834
                                         db, table_name, reg_ext, flags);
unknown's avatar
unknown committed
1835
  if (my_delete(path,MYF(0)))
unknown's avatar
unknown committed
1836
    error= 1; /* purecov: inspected */
1837
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
unknown's avatar
unknown committed
1838 1839
  DBUG_RETURN(ha_delete_table(current_thd, base, path, db, table_name, 0) ||
              error);
unknown's avatar
unknown committed
1840 1841
}

1842 1843 1844
/*
  Sort keys in the following order:
  - PRIMARY KEY
1845 1846
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
  - 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)
{
1857 1858 1859
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
1860
  {
1861
    if (!(b_flags & HA_NOSAME))
1862
      return -1;
1863
    if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
1864 1865
    {
      /* Sort NOT NULL keys before other keys */
1866
      return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
1867 1868 1869 1870 1871
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
1872 1873 1874
    /* Sort keys don't containing partial segments before others */
    if ((a_flags ^ b_flags) & HA_KEY_HAS_PART_KEY_SEG)
      return (a_flags & HA_KEY_HAS_PART_KEY_SEG) ? 1 : -1;
1875
  }
1876
  else if (b_flags & HA_NOSAME)
1877 1878
    return 1;					// Prefer b

1879
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
1880
  {
1881
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
1882
  }
unknown's avatar
unknown committed
1883
  /*
1884
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
1885 1886 1887 1888 1889
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
1890 1891
}

1892 1893
/*
  Check TYPELIB (set or enum) for duplicates
1894

1895 1896 1897
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
1898 1899
    name	  name of the checked column
    typelib	  list of values for the column
1900
    dup_val_count  returns count of duplicate elements
1901 1902

  DESCRIPTION
1903
    This function prints an warning for each value in list
1904 1905 1906
    which has some duplicates on its right

  RETURN VALUES
1907 1908
    0             ok
    1             Error
1909 1910
*/

1911
bool check_duplicates_in_interval(const char *set_or_name,
1912
                                  const char *name, TYPELIB *typelib,
1913
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
1914
{
1915
  TYPELIB tmp= *typelib;
1916
  const char **cur_value= typelib->type_names;
1917
  unsigned int *cur_length= typelib->type_lengths;
1918
  *dup_val_count= 0;  
1919 1920
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
1921
  {
1922 1923 1924 1925
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
1926
    {
1927 1928 1929 1930 1931 1932 1933
      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
1934
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
1935 1936 1937
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
1938
      (*dup_val_count)++;
1939 1940
    }
  }
1941
  return 0;
1942
}
1943

1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978

/*
  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
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
/*
  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
1990
    This function prepares a Create_field instance.
unknown's avatar
unknown committed
1991 1992 1993 1994 1995 1996 1997
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

unknown's avatar
unknown committed
1998
int prepare_create_field(Create_field *sql_field, 
unknown's avatar
unknown committed
1999 2000
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2001
			 longlong table_flags)
unknown's avatar
unknown committed
2002
{
2003
  unsigned int dup_val_count;
unknown's avatar
unknown committed
2004
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
2005 2006

  /*
2007
    This code came from mysql_prepare_create_table.
unknown's avatar
unknown committed
2008 2009 2010 2011 2012
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
2013 2014 2015 2016
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
2017 2018 2019 2020 2021 2022 2023
    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;
2024
    (*blob_columns)++;
unknown's avatar
unknown committed
2025
    break;
2026
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
2027
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
2028 2029 2030 2031
    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
2032
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2033 2034 2035 2036 2037 2038 2039 2040
    }
    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;
2041
    (*blob_columns)++;
unknown's avatar
unknown committed
2042 2043 2044 2045 2046
    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
2047
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
2048
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
2049
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
2050 2051 2052 2053 2054 2055 2056 2057
    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
2058
      {
unknown's avatar
unknown committed
2059 2060
        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
2061 2062
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
2063 2064 2065
    }
#endif
    /* fall through */
2066
  case MYSQL_TYPE_STRING:
unknown's avatar
unknown committed
2067 2068 2069 2070
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2071
  case MYSQL_TYPE_ENUM:
unknown's avatar
unknown committed
2072 2073 2074 2075 2076
    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;
2077 2078 2079 2080
    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
2081
    break;
2082
  case MYSQL_TYPE_SET:
unknown's avatar
unknown committed
2083 2084 2085 2086 2087
    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;
2088 2089 2090 2091
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2092 2093 2094 2095 2096 2097
    /* 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
2098
    break;
2099 2100 2101 2102 2103
  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
2104 2105
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2106
  case MYSQL_TYPE_BIT:
unknown's avatar
unknown committed
2107
    /* 
2108 2109
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
unknown's avatar
unknown committed
2110
    */
unknown's avatar
unknown committed
2111
    break;
2112
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
2113 2114 2115 2116 2117 2118 2119
    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;
2120
  case MYSQL_TYPE_TIMESTAMP:
unknown's avatar
unknown committed
2121 2122 2123
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2124
      if (!*timestamps)
unknown's avatar
unknown committed
2125
      {
unknown's avatar
unknown committed
2126
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2127
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2128
      }
unknown's avatar
unknown committed
2129 2130 2131 2132
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2133
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2134

2135
    (*timestamps)++;
unknown's avatar
unknown committed
2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150
    /* 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
2151 2152 2153
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2154
/*
2155
  Preparation for table creation
unknown's avatar
unknown committed
2156 2157

  SYNOPSIS
2158
    mysql_prepare_create_table()
2159 2160
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2161
      alter_info                List of columns and indexes to create
2162 2163 2164 2165 2166 2167
      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
2168

2169
  DESCRIPTION
2170
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
2171

2172
  NOTES
2173
    sets create_info->varchar if the table has a varchar
2174

unknown's avatar
unknown committed
2175
  RETURN VALUES
2176 2177
    FALSE    OK
    TRUE     error
unknown's avatar
unknown committed
2178
*/
unknown's avatar
unknown committed
2179

unknown's avatar
unknown committed
2180
static int
2181 2182 2183 2184 2185 2186
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
2187
{
2188
  const char	*key_name;
unknown's avatar
unknown committed
2189
  Create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
2190
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
2191
  ulong		record_offset= 0;
2192
  KEY		*key_info;
unknown's avatar
unknown committed
2193
  KEY_PART_INFO *key_part_info;
2194 2195 2196
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
unknown's avatar
unknown committed
2197 2198
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
unknown's avatar
unknown committed
2199
  uint total_uneven_bit_length= 0;
2200
  DBUG_ENTER("mysql_prepare_create_table");
unknown's avatar
unknown committed
2201

2202
  select_field_pos= alter_info->create_list.elements - select_field_count;
unknown's avatar
unknown committed
2203
  null_fields=blob_columns=0;
2204
  create_info->varchar= 0;
unknown's avatar
unknown committed
2205
  max_key_length= file->max_key_length();
2206

2207
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
2208
  {
2209 2210
    CHARSET_INFO *save_cs;

2211 2212 2213 2214 2215 2216
    /*
      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;
2217
    if (!sql_field->charset)
2218 2219 2220
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2221 2222 2223
      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.
2224
    */
2225
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2226
      sql_field->charset= create_info->table_charset;
2227

2228
    save_cs= sql_field->charset;
2229 2230 2231 2232 2233
    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];
2234 2235
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2236
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2237
      DBUG_RETURN(TRUE);
2238
    }
2239

2240
    /*
2241
      Convert the default value from client character
2242 2243 2244
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2245
        save_cs != sql_field->def->collation.collation &&
2246 2247 2248 2249
        (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))
2250
    {
2251
      /*
unknown's avatar
unknown committed
2252
        Starting from 5.1 we work here with a copy of Create_field
2253 2254 2255 2256 2257 2258 2259 2260
        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.
      */
2261
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2262 2263 2264 2265 2266

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

2271 2272
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2273 2274 2275
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2276
      TYPELIB *interval= sql_field->interval;
2277 2278 2279 2280 2281 2282

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2283
      if (!interval)
2284
      {
2285
        /*
2286 2287 2288
          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.
2289
        */
2290
        interval= sql_field->interval= typelib(thd->mem_root,
2291
                                               sql_field->interval_list);
2292
        List_iterator<String> int_it(sql_field->interval_list);
2293
        String conv, *tmp;
2294 2295 2296 2297 2298
        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);
2299
        for (uint i= 0; (tmp= int_it++); i++)
2300
        {
unknown's avatar
unknown committed
2301
          uint lengthsp;
2302 2303 2304 2305 2306
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2307
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
unknown's avatar
unknown committed
2308
                                                  conv.length());
2309 2310
            interval->type_lengths[i]= conv.length();
          }
2311

2312
          // Strip trailing spaces.
unknown's avatar
unknown committed
2313 2314
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2315 2316
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2317
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2318 2319 2320 2321 2322
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
unknown's avatar
unknown committed
2323
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2324
              DBUG_RETURN(TRUE);
2325 2326
            }
          }
2327
        }
2328
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2329 2330
      }

2331
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2332
      {
2333
        uint32 field_length;
2334
        if (sql_field->def != NULL)
2335 2336 2337 2338 2339
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2340 2341 2342 2343 2344
          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);
2345
              DBUG_RETURN(TRUE);
2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357
            }

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

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

            /* 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);
2390
              DBUG_RETURN(TRUE);
2391
            }
2392 2393
          }
        }
2394 2395
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2396 2397 2398 2399
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2400
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2401
    { 
unknown's avatar
unknown committed
2402
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2403
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2404 2405 2406 2407 2408
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2409
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
2410
    if (prepare_blob_field(thd, sql_field))
2411
      DBUG_RETURN(TRUE);
2412

unknown's avatar
unknown committed
2413 2414
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
2415

unknown's avatar
unknown committed
2416 2417
    if (check_column_name(sql_field->field_name))
    {
2418
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2419
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2420
    }
unknown's avatar
unknown committed
2421

2422 2423
    /* 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
2424
    {
2425
      if (my_strcasecmp(system_charset_info,
2426 2427
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
2428
      {
2429 2430 2431 2432
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2433 2434
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2435
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2436
	  DBUG_RETURN(TRUE);
2437 2438 2439
	}
	else
	{
2440
	  /* Field redefined */
2441
	  sql_field->def=		dup_field->def;
2442
	  sql_field->sql_type=		dup_field->sql_type;
2443 2444 2445
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2446
	  sql_field->length=		dup_field->char_length;
2447
          sql_field->pack_length=	dup_field->pack_length;
2448
          sql_field->key_length=	dup_field->key_length;
2449
	  sql_field->decimals=		dup_field->decimals;
2450
	  sql_field->create_length_to_internal_length();
2451
	  sql_field->unireg_check=	dup_field->unireg_check;
2452 2453 2454 2455 2456 2457 2458 2459
          /* 
            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
2460
          sql_field->interval=          dup_field->interval;
2461 2462 2463
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2464
	}
unknown's avatar
unknown committed
2465 2466
      }
    }
2467 2468 2469 2470
    /* 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)
2471
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
2472 2473
    it2.rewind();
  }
2474 2475 2476

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
2477
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
2478 2479 2480 2481

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

unknown's avatar
unknown committed
2484 2485
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2486
			     file->ha_table_flags()))
2487
      DBUG_RETURN(TRUE);
2488
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2489
      create_info->varchar= TRUE;
2490
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
2491 2492
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2493
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
2494
  }
2495 2496
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
2497 2498
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2499
    DBUG_RETURN(TRUE);
2500
  }
unknown's avatar
unknown committed
2501 2502
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
2503
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2504
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2505 2506
  }
  if (auto_increment &&
2507
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
2508
  {
unknown's avatar
unknown committed
2509 2510
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2511
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2512 2513
  }

2514
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
2515
  {
unknown's avatar
unknown committed
2516 2517
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2518
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2519 2520 2521
  }

  /* Create keys */
2522

2523 2524
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2525
  uint key_parts=0, fk_key_count=0;
2526
  bool primary_key=0,unique_key=0;
2527
  Key *key, *key2;
unknown's avatar
unknown committed
2528
  uint tmp, key_number;
2529 2530
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2531

2532
  /* Calculate number of key segements */
2533
  *key_count= 0;
2534

unknown's avatar
unknown committed
2535 2536
  while ((key=key_iterator++))
  {
2537 2538
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2539
    LEX_STRING key_name_str;
2540 2541 2542
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
unknown's avatar
unknown committed
2543
      Foreign_key *fk_key= (Foreign_key*) key;
2544 2545 2546
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2547 2548 2549
        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));
2550
	DBUG_RETURN(TRUE);
2551 2552 2553
      }
      continue;
    }
2554
    (*key_count)++;
unknown's avatar
unknown committed
2555
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
2556 2557 2558
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2559
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2560
    }
2561 2562 2563 2564
    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
2565
    {
2566
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
2567
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2568
    }
2569
    key_iterator2.rewind ();
2570
    if (key->type != Key::FOREIGN_KEY)
2571
    {
2572
      while ((key2 = key_iterator2++) != key)
2573
      {
unknown's avatar
unknown committed
2574
	/*
2575 2576 2577
          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
2578
        */
2579 2580 2581
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2582
        {
2583
          /* TODO: issue warning message */
2584 2585 2586 2587 2588 2589 2590
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2591 2592 2593
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2594 2595 2596
          }
          break;
        }
2597 2598 2599 2600 2601 2602
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2603
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2604 2605 2606
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
2607
      DBUG_RETURN(TRUE);
2608
    }
2609
  }
unknown's avatar
unknown committed
2610
  tmp=file->max_keys();
2611
  if (*key_count > tmp)
2612 2613
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
2614
    DBUG_RETURN(TRUE);
2615
  }
2616

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

2622
  key_iterator.rewind();
unknown's avatar
unknown committed
2623
  key_number=0;
unknown's avatar
unknown committed
2624
  for (; (key=key_iterator++) ; key_number++)
2625 2626
  {
    uint key_length=0;
unknown's avatar
unknown committed
2627
    Key_part_spec *column;
2628

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

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

unknown's avatar
unknown committed
2669 2670
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
2671
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
2672
    key_info->algorithm= key->key_create_info.algorithm;
unknown's avatar
unknown committed
2673

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

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

2724 2725 2726 2727 2728
    /* 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
2729 2730
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2731 2732 2733 2734 2735
                           create_info->key_block_size);

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

unknown's avatar
unknown committed
2736
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
2737
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
2738 2739
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2740
      uint length;
unknown's avatar
unknown committed
2741
      Key_part_spec *dup_column;
unknown's avatar
unknown committed
2742

unknown's avatar
unknown committed
2743 2744 2745
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
2746
	     my_strcasecmp(system_charset_info,
2747 2748
			   column->field_name,
			   sql_field->field_name))
unknown's avatar
unknown committed
2749 2750 2751
	field++;
      if (!sql_field)
      {
2752
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
2753
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2754
      }
unknown's avatar
unknown committed
2755
      while ((dup_column= cols2++) != column)
2756 2757 2758 2759 2760 2761 2762
      {
        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);
2763
	  DBUG_RETURN(TRUE);
2764 2765 2766
	}
      }
      cols2.rewind();
2767
      if (key->type == Key::FULLTEXT)
2768
      {
2769 2770
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
2771 2772
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
2773
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
2774 2775
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
2776
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787
	    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));
2788
      }
2789
      else
2790
      {
2791 2792
	column->length*= sql_field->charset->mbmaxlen;

unknown's avatar
unknown committed
2793 2794 2795
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
2796
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2797 2798
	}

2799 2800
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
2801
	{
2802
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
2803
	  {
2804
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
2805
	    DBUG_RETURN(TRUE);
2806
	  }
2807 2808
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
2809
            column->length= 25;
2810 2811
	  if (!column->length)
	  {
2812
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
2813
	    DBUG_RETURN(TRUE);
2814 2815
	  }
	}
unknown's avatar
unknown committed
2816
#ifdef HAVE_SPATIAL
2817
	if (key->type == Key::SPATIAL)
2818
	{
2819
	  if (!column->length)
2820 2821
	  {
	    /*
2822 2823
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
2824
	    */
2825
	    column->length= 4*sizeof(double);
2826 2827
	  }
	}
unknown's avatar
unknown committed
2828
#endif
2829 2830 2831 2832 2833 2834 2835
	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
2836
            null_fields--;
2837 2838
	  }
	  else
2839 2840 2841 2842 2843
          {
            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);
2844
              DBUG_RETURN(TRUE);
2845 2846 2847 2848 2849
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
2850
              DBUG_RETURN(TRUE);
2851 2852
            }
          }
2853 2854 2855
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
2856
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
2857 2858
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
2859
      }
2860

unknown's avatar
unknown committed
2861 2862 2863
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
2864 2865
      length= sql_field->key_length;

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

unknown's avatar
unknown committed
2949 2950 2951 2952 2953 2954 2955
      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)
2956 2957 2958
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
2959 2960
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
2961
	    DBUG_RETURN(TRUE);
2962 2963 2964 2965
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
2966
	else if (!(key_name = key->name))
unknown's avatar
unknown committed
2967
	  key_name=make_unique_key_name(sql_field->field_name,
2968 2969
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
2970
	{
2971
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
2972
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2973 2974 2975 2976
	}
	key_info->name=(char*) key_name;
      }
    }
2977 2978
    if (!key_info->name || check_column_name(key_info->name))
    {
2979
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
2980
      DBUG_RETURN(TRUE);
2981
    }
2982 2983
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
2984
    key_info->key_length=(uint16) key_length;
2985
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
2986
    {
2987
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
2988
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2989
    }
unknown's avatar
unknown committed
2990
    key_info++;
unknown's avatar
unknown committed
2991
  }
2992
  if (!unique_key && !primary_key &&
2993
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
2994
  {
unknown's avatar
unknown committed
2995
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
2996
    DBUG_RETURN(TRUE);
2997
  }
unknown's avatar
unknown committed
2998 2999
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
3000
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3001
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3002
  }
3003
  /* Sort keys in optimized order */
3004 3005
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
unknown's avatar
unknown committed
3006
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
3007

3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038
  /* Check fields. */
  it.rewind();
  while ((sql_field=it++))
  {
    Field::utype type= (Field::utype) MTYP_TYPENR(sql_field->unireg_check);

    if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
        !sql_field->def &&
        sql_field->sql_type == MYSQL_TYPE_TIMESTAMP &&
        (sql_field->flags & NOT_NULL_FLAG) &&
        (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
    {
      /*
        An error should be reported if:
          - NO_ZERO_DATE SQL mode is active;
          - there is no explicit DEFAULT clause (default column value);
          - this is a TIMESTAMP column;
          - the column is not NULL;
          - this is not the DEFAULT CURRENT_TIMESTAMP column.

        In other words, an error should be reported if
          - NO_ZERO_DATE SQL mode is active;
          - the column definition is equivalent to
            'column_name TIMESTAMP DEFAULT 0'.
      */

      my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
      DBUG_RETURN(TRUE);
    }
  }

3039
  DBUG_RETURN(FALSE);
3040 3041
}

3042

unknown's avatar
unknown committed
3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058
/*
  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)
{
3059 3060 3061 3062 3063
  /*
    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
3064 3065 3066
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3067 3068 3069

    load_db_opt_by_name(thd, db, &db_info);

unknown's avatar
unknown committed
3070 3071 3072 3073 3074
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


unknown's avatar
unknown committed
3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087
/*
  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
3088
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
unknown's avatar
unknown committed
3089 3090 3091 3092 3093 3094 3095 3096 3097
{
  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];

3098 3099
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
3100 3101 3102 3103 3104
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3105
    sql_field->sql_type= MYSQL_TYPE_BLOB;
unknown's avatar
unknown committed
3106 3107
    sql_field->flags|= BLOB_FLAG;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
3108
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
3109 3110 3111 3112 3113 3114 3115
            (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)
  {
3116
    if (sql_field->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127
    {
      /* 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);
}


3128
/*
unknown's avatar
unknown committed
3129
  Preparation of Create_field for SP function return values.
3130 3131
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142

  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
3143
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3144
{
3145 3146
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3147 3148
  {
    uint32 field_length, dummy;
3149
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3150 3151 3152 3153 3154 3155 3156
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3157
    else /* MYSQL_TYPE_ENUM */
3158 3159 3160 3161 3162 3163 3164 3165 3166
    {
      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);
  }

3167
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3168 3169 3170 3171 3172
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
3173 3174 3175 3176
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3177 3178


3179 3180 3181 3182
/*
  Create a table

  SYNOPSIS
unknown's avatar
unknown committed
3183
    mysql_create_table_no_lock()
3184 3185 3186
    thd			Thread object
    db			Database
    table_name		Table name
3187
    create_info	        Create information (like MAX_ROWS)
3188 3189
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
3190
    internal_tmp_table  Set to 1 if this is an internal temporary table
3191
			(From ALTER TABLE)
3192
    select_field_count
3193 3194

  DESCRIPTION
3195
    If one creates a temporary table, this is automatically opened
3196

unknown's avatar
unknown committed
3197 3198 3199 3200 3201
    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.

3202 3203 3204 3205 3206 3207
    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
3208 3209
    FALSE OK
    TRUE  error
3210 3211
*/

unknown's avatar
unknown committed
3212
bool mysql_create_table_no_lock(THD *thd,
unknown's avatar
unknown committed
3213
                                const char *db, const char *table_name,
3214 3215 3216 3217
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3218
{
3219
  char		path[FN_REFLEN];
3220
  uint          path_length;
3221 3222 3223 3224
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
3225
  bool		error= TRUE;
unknown's avatar
unknown committed
3226
  DBUG_ENTER("mysql_create_table_no_lock");
3227 3228
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3229

3230

3231
  /* Check for duplicate fields and check type of table to create */
3232
  if (!alter_info->create_list.elements)
3233
  {
unknown's avatar
unknown committed
3234 3235
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
3236
    DBUG_RETURN(TRUE);
3237
  }
3238
  if (check_engine(thd, table_name, create_info))
3239
    DBUG_RETURN(TRUE);
3240
  db_options= create_info->table_options;
3241 3242 3243
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3244 3245
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3246
  {
unknown's avatar
unknown committed
3247
    mem_alloc_error(sizeof(handler));
3248 3249
    DBUG_RETURN(TRUE);
  }
3250
#ifdef WITH_PARTITION_STORAGE_ENGINE
3251 3252
  partition_info *part_info= thd->work_part_info;

unknown's avatar
unknown committed
3253 3254 3255 3256 3257 3258 3259 3260
  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
3261
    thd->work_part_info= part_info= new partition_info();
unknown's avatar
unknown committed
3262 3263 3264 3265 3266 3267
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
unknown's avatar
unknown committed
3268
    part_info->default_engine_type= create_info->db_type;
3269
    part_info->is_auto_partitioned= TRUE;
unknown's avatar
unknown committed
3270
  }
3271 3272 3273
  if (part_info)
  {
    /*
unknown's avatar
unknown committed
3274 3275 3276 3277 3278 3279 3280 3281 3282
      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.
3283
    */
3284
    List_iterator<Key> key_iterator(alter_info->key_list);
unknown's avatar
unknown committed
3285
    Key *key;
unknown's avatar
unknown committed
3286
    handlerton *part_engine_type= create_info->db_type;
3287 3288
    char *part_syntax_buf;
    uint syntax_len;
unknown's avatar
unknown committed
3289
    handlerton *engine_type;
3290 3291 3292 3293 3294
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
unknown's avatar
unknown committed
3295 3296
    while ((key= key_iterator++))
    {
3297 3298
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
unknown's avatar
unknown committed
3299 3300 3301 3302 3303
      {
        my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
        goto err;
      }
    }
3304
    if ((part_engine_type == partition_hton) &&
3305
        part_info->default_engine_type)
3306 3307 3308 3309 3310 3311
    {
      /*
        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
3312
      ;
3313
    }
3314 3315
    else
    {
unknown's avatar
unknown committed
3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327
      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);
        }
      }
3328
    }
unknown's avatar
unknown committed
3329 3330 3331
    DBUG_PRINT("info", ("db_type = %s create_info->db_type = %s",
             ha_resolve_storage_engine_name(part_info->default_engine_type),
             ha_resolve_storage_engine_name(create_info->db_type)));
3332
    if (part_info->check_partition_info(thd, &engine_type, file,
3333
                                        create_info, TRUE))
unknown's avatar
unknown committed
3334
      goto err;
unknown's avatar
unknown committed
3335
    part_info->default_engine_type= engine_type;
unknown's avatar
unknown committed
3336

3337 3338 3339 3340 3341 3342
    /*
      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,
3343
                                                     TRUE, TRUE)))
unknown's avatar
unknown committed
3344
      goto err;
3345 3346
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
3347 3348
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3349
        create_info->db_type == partition_hton)
3350 3351 3352 3353 3354
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
unknown's avatar
unknown committed
3355
      DBUG_PRINT("info", ("db_type: %s",
unknown's avatar
unknown committed
3356
                        ha_resolve_storage_engine_name(create_info->db_type)));
3357
      delete file;
3358
      create_info->db_type= partition_hton;
3359 3360 3361 3362
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3363 3364 3365 3366 3367 3368 3369 3370
      /*
        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 &&
3371 3372
          (int)part_info->no_parts !=
          file->get_default_no_partitions(create_info))
3373
      {
3374
        uint i;
3375
        List_iterator<partition_element> part_it(part_info->partitions);
3376 3377 3378 3379
        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;
3380 3381 3382 3383
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3384
               (int)part_info->no_subparts !=
3385
                 file->get_default_no_partitions(create_info))
3386
      {
3387
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3388
        part_info->no_subparts= file->get_default_no_partitions(create_info);
3389 3390 3391 3392
      }
    }
    else if (create_info->db_type != engine_type)
    {
3393 3394 3395 3396 3397 3398
      /*
        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.
      */
3399
      delete file;
3400 3401
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3402 3403 3404 3405
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3406 3407 3408
    }
  }
#endif
3409

unknown's avatar
unknown committed
3410
  set_table_default_charset(thd, create_info, (char*) db);
3411

3412 3413 3414 3415 3416
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3417
    goto err;
unknown's avatar
unknown committed
3418 3419 3420 3421

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3422
    path_length= build_tmptable_filename(thd, path, sizeof(path));
unknown's avatar
unknown committed
3423 3424
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3425 3426
  else  
  {
3427 3428
 #ifdef FN_DEVCHAR
    /* check if the table name contains FN_DEVCHAR when defined */
unknown's avatar
unknown committed
3429
    if (strchr(alias, FN_DEVCHAR))
3430
    {
unknown's avatar
unknown committed
3431 3432 3433
      my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
      DBUG_RETURN(TRUE);
    }
3434
#endif
3435 3436
    path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
                                      internal_tmp_table ? FN_IS_TMP : 0);
3437
  }
3438

unknown's avatar
unknown committed
3439
  /* Check if table already exists */
unknown's avatar
unknown committed
3440 3441
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
unknown's avatar
unknown committed
3442
  {
3443
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3444 3445
    {
      create_info->table_existed= 1;		// Mark that table existed
3446 3447 3448
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
unknown's avatar
unknown committed
3449 3450
      error= 0;
      goto err;
3451
    }
unknown's avatar
unknown committed
3452
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3453
    goto err;
unknown's avatar
unknown committed
3454
  }
3455

unknown's avatar
unknown committed
3456
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
3457
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
3458 3459 3460 3461
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3462 3463
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
3464
      goto unlock_and_end;
unknown's avatar
unknown committed
3465
    }
3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478
    /*
      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
3479 3480
  }

unknown's avatar
unknown committed
3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493
  /*
    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;
3494 3495 3496
    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
3497
    {
3498 3499 3500 3501 3502
      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
3503

3504 3505 3506 3507 3508 3509 3510 3511 3512
        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
3513 3514 3515
    }
  }

3516
  thd_proc_info(thd, "creating table");
3517
  create_info->table_existed= 0;		// Mark that table is created
unknown's avatar
unknown committed
3518

3519
#ifdef HAVE_READLINK
3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537
  if (test_if_data_home_dir(create_info->data_file_name))
  {
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
    goto unlock_and_end;
  }
  if (test_if_data_home_dir(create_info->index_file_name))
  {
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
    goto unlock_and_end;
  }

#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (check_partition_dirs(thd->lex->part_info))
  {
    goto unlock_and_end;
  }
#endif /* WITH_PARTITION_STORAGE_ENGINE */

3538
  if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
3539
#endif /* HAVE_READLINK */
3540 3541 3542 3543 3544 3545 3546
  {
    if (create_info->data_file_name)
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                   "DATA DIRECTORY option ignored");
    if (create_info->index_file_name)
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                   "INDEX DIRECTORY option ignored");
3547
    create_info->data_file_name= create_info->index_file_name= 0;
3548
  }
unknown's avatar
unknown committed
3549
  create_info->table_options=db_options;
3550

3551
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3552 3553
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
unknown's avatar
unknown committed
3554
                       key_count, key_info_buffer, file))
unknown's avatar
unknown committed
3555
    goto unlock_and_end;
unknown's avatar
unknown committed
3556

unknown's avatar
unknown committed
3557 3558 3559 3560 3561 3562
  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
3563
      goto unlock_and_end;
unknown's avatar
unknown committed
3564
    }
unknown's avatar
unknown committed
3565
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
3566
  }
unknown's avatar
unknown committed
3567

3568 3569 3570 3571 3572
  /*
    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.
3573
    Otherwise, the statement shall be binlogged.
3574 3575
   */
  if (!internal_tmp_table &&
3576 3577
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
3578
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3579
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
unknown's avatar
unknown committed
3580
  error= FALSE;
unknown's avatar
unknown committed
3581
unlock_and_end:
unknown's avatar
unknown committed
3582
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
3583 3584

err:
3585
  thd_proc_info(thd, "After create");
unknown's avatar
unknown committed
3586
  delete file;
unknown's avatar
unknown committed
3587
  DBUG_RETURN(error);
3588 3589

warn:
3590
  error= FALSE;
3591 3592 3593 3594
  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
3595
  goto unlock_and_end;
unknown's avatar
unknown committed
3596 3597
}

unknown's avatar
unknown committed
3598 3599

/*
unknown's avatar
unknown committed
3600
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
unknown's avatar
unknown committed
3601 3602 3603 3604
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
3605 3606 3607
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
unknown's avatar
unknown committed
3608
{
unknown's avatar
unknown committed
3609
  TABLE *name_lock= 0;
unknown's avatar
unknown committed
3610 3611 3612 3613 3614 3615
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
3616
         hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
unknown's avatar
unknown committed
3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629
  {
    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
3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654
  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
3655

unknown's avatar
unknown committed
3656
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
3657 3658 3659
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
unknown's avatar
unknown committed
3660

unknown's avatar
unknown committed
3661 3662 3663 3664 3665 3666 3667
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
3668 3669 3670 3671 3672 3673 3674 3675
  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
3676 3677 3678 3679 3680 3681 3682 3683
/*
** 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++)
3684
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
3685 3686 3687 3688 3689 3690 3691 3692 3693 3694
      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;

3695 3696
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
3697
    return (char*) field_name;			// Use fieldname
3698 3699 3700 3701 3702 3703
  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
  */
3704
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
3705
  {
3706 3707
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
3708 3709 3710
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3711
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
3712 3713
}

3714

unknown's avatar
unknown committed
3715 3716 3717 3718
/****************************************************************************
** Alter a table definition
****************************************************************************/

3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732

/*
  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.
3733 3734
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
3735 3736

  RETURN
3737 3738
    FALSE   OK
    TRUE    Error
3739 3740
*/

3741
bool
3742 3743 3744
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
3745
{
3746
  THD *thd= current_thd;
3747 3748 3749
  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
3750
  handler *file;
unknown's avatar
unknown committed
3751
  int error=0;
unknown's avatar
unknown committed
3752
  DBUG_ENTER("mysql_rename_table");
3753 3754
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
unknown's avatar
unknown committed
3755

unknown's avatar
unknown committed
3756
  file= (base == NULL ? 0 :
unknown's avatar
unknown committed
3757 3758
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

3759 3760 3761 3762
  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);
3763 3764 3765 3766 3767 3768

  /*
    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.
   */
3769
  if (lower_case_table_names == 2 && file &&
3770
      !(file->ha_table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
3771
  {
3772 3773
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
3774 3775
    build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "",
                         flags & FN_FROM_IS_TMP);
3776
    from_base= lc_from;
unknown's avatar
unknown committed
3777

3778 3779
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
3780 3781
    build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "",
                         flags & FN_TO_IS_TMP);
3782
    to_base= lc_to;
unknown's avatar
unknown committed
3783 3784
  }

3785
  if (!file || !(error=file->ha_rename_table(from_base, to_base)))
3786
  {
3787
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
3788
    {
unknown's avatar
unknown committed
3789
      error=my_errno;
3790
      /* Restore old file name */
3791
      if (file)
3792
        file->ha_rename_table(to_base, from_base);
3793 3794
    }
  }
unknown's avatar
unknown committed
3795
  delete file;
3796 3797 3798
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
3799 3800
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
3801 3802
}

unknown's avatar
unknown committed
3803

unknown's avatar
unknown committed
3804
/*
3805 3806 3807 3808 3809 3810
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
3811 3812 3813
    function            HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
                        HA_EXTRA_FORCE_REOPEN if table is not be used
                        HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
3814 3815 3816 3817 3818 3819 3820
  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
3821 3822
*/

3823 3824
void wait_while_table_is_used(THD *thd, TABLE *table,
                              enum ha_extra_function function)
unknown's avatar
unknown committed
3825
{
3826
  DBUG_ENTER("wait_while_table_is_used");
unknown's avatar
unknown committed
3827
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %lu",
unknown's avatar
unknown committed
3828 3829
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
unknown's avatar
unknown committed
3830

3831 3832
  safe_mutex_assert_owner(&LOCK_open);

3833
  VOID(table->file->extra(function));
3834
  /* Mark all tables that are in use as 'old' */
unknown's avatar
unknown committed
3835
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
3836 3837

  /* Wait until all there are no other threads that has this table open */
unknown's avatar
unknown committed
3838 3839 3840
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
3841 3842
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
3843

3844 3845
/*
  Close a cached table
unknown's avatar
unknown committed
3846

3847
  SYNOPSIS
unknown's avatar
unknown committed
3848
    close_cached_table()
3849 3850 3851 3852 3853 3854
    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
3855

3856 3857 3858 3859
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
3860

3861
void close_cached_table(THD *thd, TABLE *table)
3862 3863
{
  DBUG_ENTER("close_cached_table");
3864

3865
  wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
3866 3867
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
3868
  {
3869 3870
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
3871
  }
3872
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
unknown's avatar
unknown committed
3873
  unlink_open_table(thd, table, TRUE);
3874 3875

  /* When lock on LOCK_open is freed other threads can continue */
3876
  broadcast_refresh();
unknown's avatar
unknown committed
3877
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3878 3879
}

3880
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
3881
			     const char* operator_name, const char* errmsg)
3882

unknown's avatar
unknown committed
3883
{
3884 3885
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3886 3887
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
3888
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3889
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
3890
  thd->clear_error();
3891
  if (protocol->write())
unknown's avatar
unknown committed
3892 3893 3894 3895
    return -1;
  return 1;
}

3896

unknown's avatar
unknown committed
3897
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
3898
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
3899
{
unknown's avatar
unknown committed
3900
  DBUG_ENTER("prepare_for_restore");
3901

unknown's avatar
unknown committed
3902 3903 3904 3905 3906 3907
  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
3908
  else
unknown's avatar
unknown committed
3909
  {
3910
    char* backup_dir= thd->lex->backup_dir;
3911
    char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
3912 3913
    char* table_name= table->table_name;
    char* db= table->db;
3914

3915 3916 3917
    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
3918
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
3919

3920 3921
    build_table_filename(dst_path, sizeof(dst_path),
                         db, table_name, reg_ext, 0);
3922

3923
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
3924
      DBUG_RETURN(-1);
3925

3926
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
unknown's avatar
unknown committed
3927
    {
3928
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3929
      unlock_table_name(thd, table);
3930
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3931 3932 3933
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
3934
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
3935
    {
3936
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3937
      unlock_table_name(thd, table);
3938
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3939 3940
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
3941
    }
unknown's avatar
unknown committed
3942
  }
unknown's avatar
unknown committed
3943

3944 3945 3946 3947
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
3948
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3949
  if (reopen_name_locked_table(thd, table, TRUE))
3950
  {
3951
    unlock_table_name(thd, table);
3952
    pthread_mutex_unlock(&LOCK_open);
3953 3954
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
3955
  }
3956 3957
  /* A MERGE table must not come here. */
  DBUG_ASSERT(!table->table || !table->table->child_l);
3958
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
3959
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3960
}
3961

3962

unknown's avatar
unknown committed
3963
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
3964
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
3965
{
unknown's avatar
unknown committed
3966 3967
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
3968 3969 3970 3971
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
unknown's avatar
unknown committed
3972 3973 3974 3975
  DBUG_ENTER("prepare_for_repair");

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

  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
3978
  {
unknown's avatar
unknown committed
3979 3980 3981 3982 3983 3984 3985 3986 3987
    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
3988
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
3989 3990
    }

unknown's avatar
unknown committed
3991
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
unknown's avatar
unknown committed
3992 3993 3994 3995 3996
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
unknown's avatar
unknown committed
3997
    table= &tmp_table;
unknown's avatar
unknown committed
3998
    pthread_mutex_unlock(&LOCK_open);
3999
  }
4000 4001 4002 4003

  /* A MERGE table must not come here. */
  DBUG_ASSERT(!table->child_l);

4004 4005 4006 4007 4008 4009 4010 4011 4012
  /*
    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
4013

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

4024 4025 4026
  if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  {
    error= send_check_errmsg(thd, table_list, "repair",
Timothy Smith's avatar
Timothy Smith committed
4027
                             "Failed repairing incompatible .frm file");
4028 4029
    goto end;
  }
4030

unknown's avatar
unknown committed
4031 4032
  /*
    Check if this is a table type that stores index and data separately,
4033 4034 4035
    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
4036
  */
unknown's avatar
unknown committed
4037
  ext= table->file->bas_ext();
unknown's avatar
unknown committed
4038 4039
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
4040

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

4046 4047
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
4048

4049 4050 4051 4052 4053 4054 4055
  /* 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
4056
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
4057
  {
unknown's avatar
unknown committed
4058 4059
    error= -1;
    goto end;
unknown's avatar
unknown committed
4060
  }
unknown's avatar
unknown committed
4061
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
4062
  {
4063
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4064
    unlock_table_name(thd, table_list);
4065
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086
    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
4087 4088
  }

4089 4090 4091 4092
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
4093
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4094
  if (reopen_name_locked_table(thd, table_list, TRUE))
unknown's avatar
unknown committed
4095
  {
unknown's avatar
unknown committed
4096
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
4097
    pthread_mutex_unlock(&LOCK_open);
4098 4099 4100
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
4101
  }
4102
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4103 4104 4105

end:
  if (table == &tmp_table)
unknown's avatar
unknown committed
4106 4107 4108 4109 4110
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
4111
  DBUG_RETURN(error);
unknown's avatar
unknown committed
4112
}
4113

4114

4115

unknown's avatar
unknown committed
4116 4117
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
4118 4119 4120
    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
4121
*/
unknown's avatar
unknown committed
4122 4123 4124 4125 4126
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,
4127
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
4128 4129 4130
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
4131 4132 4133
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
4134
{
4135
  TABLE_LIST *table;
4136
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
4137
  List<Item> field_list;
4138 4139
  Item *item;
  Protocol *protocol= thd->protocol;
4140
  LEX *lex= thd->lex;
4141
  int result_code;
4142
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
4143

4144 4145
  if (end_active_trans(thd))
    DBUG_RETURN(1);
4146
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
unknown's avatar
unknown committed
4147 4148 4149 4150 4151 4152 4153
  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;
4154 4155
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4156
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4157

4158
  mysql_ha_rm_tables(thd, tables, FALSE);
4159

unknown's avatar
VIEW  
unknown committed
4160
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
4161 4162
  {
    char table_name[NAME_LEN*2+2];
4163
    char* db = table->db;
4164
    bool fatal_error=0;
unknown's avatar
unknown committed
4165

4166 4167
    DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
    DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
unknown's avatar
unknown committed
4168
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
4169
    thd->open_options|= extra_open_options;
4170 4171
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4172
    {
4173 4174 4175 4176 4177
      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;
4178
      select->table_list.first= (uchar*)table;
4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190
      /*
        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;
4191

4192 4193 4194 4195 4196
      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;
4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236
#ifdef WITH_PARTITION_STORAGE_ENGINE
      if (table->table && table->table->part_info)
      {
        /*
          Set up which partitions that should be processed
          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
        */
        Alter_info *alter_info= &lex->alter_info;

        if (alter_info->flags & ALTER_ANALYZE_PARTITION ||
            alter_info->flags & ALTER_CHECK_PARTITION ||
            alter_info->flags & ALTER_OPTIMIZE_PARTITION ||
            alter_info->flags & ALTER_REPAIR_PARTITION)
        {
          uint no_parts_found;
          uint no_parts_opt= alter_info->partition_names.elements;
          no_parts_found= set_part_state(alter_info, table->table->part_info,
                                         PART_CHANGED);
          if (no_parts_found != no_parts_opt &&
              (!(alter_info->flags & ALTER_ALL_PARTITION)))
          {
            char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
            uint length;
            DBUG_PRINT("admin", ("sending non existent partition error"));
            protocol->prepare_for_resend();
            protocol->store(table_name, system_charset_info);
            protocol->store(operator_name, system_charset_info);
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
            length= my_snprintf(buff, sizeof(buff),
                                ER(ER_DROP_PARTITION_NON_EXISTENT),
                                table_name);
            protocol->store(buff, length, system_charset_info);
            if(protocol->write())
              goto err;
            my_eof(thd);
            goto err;
          }
        }
      }
#endif
4237
    }
4238 4239
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

unknown's avatar
unknown committed
4240
    if (prepare_func)
4241
    {
4242
      DBUG_PRINT("admin", ("calling prepare_func"));
unknown's avatar
unknown committed
4243
      switch ((*prepare_func)(thd, table, check_opt)) {
4244
      case  1:           // error, message written to net
4245
        ha_autocommit_or_rollback(thd, 1);
4246
        end_trans(thd, ROLLBACK);
4247
        close_thread_tables(thd);
4248
        DBUG_PRINT("admin", ("simple error, admin next table"));
4249 4250
        continue;
      case -1:           // error, message could be written to net
4251 4252
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4253
        goto err;
4254
        /* purecov: end */
4255
      default:           // should be 0 otherwise
4256
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4257
        ;
unknown's avatar
unknown committed
4258
      }
4259
    }
4260

4261
    /*
unknown's avatar
unknown committed
4262 4263 4264 4265 4266 4267
      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)
4268
    */
unknown's avatar
unknown committed
4269 4270
    if (!table->table)
    {
4271
      DBUG_PRINT("admin", ("open table failed"));
4272 4273 4274
      if (!thd->warn_list.elements)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4275 4276 4277
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
4278 4279 4280 4281
        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
4282
    }
4283 4284 4285

    if (table->view)
    {
4286
      DBUG_PRINT("admin", ("calling view_operator_func"));
4287 4288 4289 4290
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4291
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
4292
    {
4293
      /* purecov: begin inspected */
unknown's avatar
unknown committed
4294
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4295
      uint length;
4296
      DBUG_PRINT("admin", ("sending error message"));
4297
      protocol->prepare_for_resend();
4298 4299
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4300
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4301 4302 4303
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4304
      ha_autocommit_or_rollback(thd, 0);
4305
      end_trans(thd, COMMIT);
4306
      close_thread_tables(thd);
4307
      lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
4308
      table->table=0;				// For query cache
4309
      if (protocol->write())
unknown's avatar
unknown committed
4310 4311
	goto err;
      continue;
4312
      /* purecov: end */
unknown's avatar
unknown committed
4313 4314
    }

4315
    /* Close all instances of the table to allow repair to rename files */
4316
    if (lock_type == TL_WRITE && table->table->s->version)
4317
    {
4318
      DBUG_PRINT("admin", ("removing table from cache"));
4319
      pthread_mutex_lock(&LOCK_open);
4320 4321
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
unknown's avatar
unknown committed
4322
      mysql_lock_abort(thd,table->table, TRUE);
unknown's avatar
unknown committed
4323 4324
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
unknown's avatar
unknown committed
4325 4326
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4327
      thd->exit_cond(old_message);
4328
      DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
4329 4330
      if (thd->killed)
	goto err;
4331 4332 4333
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4334 4335
    }

unknown's avatar
unknown committed
4336
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4337
    {
4338 4339
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4340 4341 4342
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4343 4344 4345
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4346 4347
      if (protocol->write())
        goto err;
4348
      /* purecov: end */
4349 4350
    }

4351 4352
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
unknown's avatar
unknown committed
4353 4354 4355 4356 4357
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4358
        DBUG_PRINT("admin", ("recreating table"));
4359
        ha_autocommit_or_rollback(thd, 1);
unknown's avatar
unknown committed
4360 4361
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4362
        result_code= mysql_recreate_table(thd, table);
unknown's avatar
unknown committed
4363
        reenable_binlog(thd);
4364 4365 4366 4367 4368 4369 4370 4371
        /*
          mysql_recreate_table() can push OK or ERROR.
          Clear 'OK' status. If there is an error, keep it:
          we will store the error message in a result set row 
          and then clear.
        */
        if (thd->main_da.is_ok())
          thd->main_da.reset_diagnostics_area();
unknown's avatar
unknown committed
4372 4373 4374 4375
        goto send_result;
      }
    }

4376
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4377
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4378
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4379 4380 4381

send_result:

4382
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
4383
    thd->clear_error();  // these errors shouldn't get client
4384
    {
4385 4386 4387 4388 4389 4390 4391
      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);
4392 4393 4394
        protocol->store(warning_level_names[err->level].str,
                        warning_level_names[err->level].length,
                        system_charset_info);
4395 4396 4397 4398 4399
        protocol->store(err->msg, system_charset_info);
        if (protocol->write())
          goto err;
      }
      mysql_reset_errors(thd, true);
4400
    }
4401
    protocol->prepare_for_resend();
4402 4403
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
4404

4405 4406 4407
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4408 4409
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4410
      {
4411 4412
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
4413
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4414
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4415
	protocol->store(buf, length, system_charset_info);
4416
      }
unknown's avatar
unknown committed
4417 4418
      break;

unknown's avatar
unknown committed
4419 4420
    case HA_ADMIN_NOT_BASE_TABLE:
      {
unknown's avatar
unknown committed
4421 4422
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
unknown's avatar
unknown committed
4423
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4424
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4425
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
4426 4427 4428
      }
      break;

4429
    case HA_ADMIN_OK:
4430 4431
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
4432 4433
      break;

4434
    case HA_ADMIN_FAILED:
4435 4436 4437
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
4438 4439
      break;

unknown's avatar
unknown committed
4440
    case HA_ADMIN_REJECT:
4441 4442 4443
      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
4444
      open_for_modify= FALSE;
unknown's avatar
unknown committed
4445 4446
      break;

4447
    case HA_ADMIN_ALREADY_DONE:
4448 4449 4450
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4451 4452
      break;

4453
    case HA_ADMIN_CORRUPT:
4454 4455
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4456
      fatal_error=1;
unknown's avatar
unknown committed
4457 4458
      break;

unknown's avatar
unknown committed
4459
    case HA_ADMIN_INVALID:
4460 4461 4462
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
4463 4464
      break;

4465 4466 4467 4468 4469 4470
    case HA_ADMIN_TRY_ALTER:
    {
      /*
        This is currently used only by InnoDB. ha_innobase::optimize() answers
        "try with alter", so here we close the table, do an ALTER TABLE,
        reopen the table and do ha_innobase::analyze() on it.
4471
        We have to end the row, so analyze could return more rows.
4472
      */
4473 4474 4475 4476 4477 4478
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
      protocol->store(STRING_WITH_LEN(
          "Table does not support optimize, doing recreate + analyze instead"),
                      system_charset_info);
      if (protocol->write())
        goto err;
4479
      ha_autocommit_or_rollback(thd, 0);
4480
      close_thread_tables(thd);
4481
      DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
unknown's avatar
VIEW  
unknown committed
4482 4483 4484
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4485
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4486
      result_code= mysql_recreate_table(thd, table);
4487
      reenable_binlog(thd);
4488 4489 4490 4491 4492 4493 4494 4495
      /*
        mysql_recreate_table() can push OK or ERROR.
        Clear 'OK' status. If there is an error, keep it:
        we will store the error message in a result set row 
        and then clear.
      */
      if (thd->main_da.is_ok())
        thd->main_da.reset_diagnostics_area();
4496
      ha_autocommit_or_rollback(thd, 0);
unknown's avatar
unknown committed
4497
      close_thread_tables(thd);
4498 4499
      if (!result_code) // recreation went ok
      {
4500
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
4501
            ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
4502 4503
          result_code= 0; // analyze went ok
      }
4504 4505 4506 4507
      /* Start a new row for the final status row */
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4508 4509
      if (result_code) // either mysql_recreate_table or analyze failed
      {
4510 4511
        DBUG_ASSERT(thd->is_error());
        if (thd->is_error())
4512
        {
4513
          const char *err_msg= thd->main_da.message();
4514 4515 4516 4517 4518 4519 4520
          if (!thd->vio_ok())
          {
            sql_print_error(err_msg);
          }
          else
          {
            /* Hijack the row already in-progress. */
4521
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4522
            protocol->store(err_msg, system_charset_info);
4523 4524
            if (protocol->write())
              goto err;
4525 4526 4527 4528 4529
            /* 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);
          }
4530
          thd->clear_error();
4531 4532
        }
      }
4533
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
4534 4535
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4536 4537
      goto send_result_message;
    }
4538 4539
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4540
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4541 4542
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4543 4544
      break;
    }
4545

unknown's avatar
unknown committed
4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558
    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;
    }

4559
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4560 4561 4562 4563 4564
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
4565
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4566 4567 4568 4569
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
4570
    }
unknown's avatar
unknown committed
4571
    if (table->table)
4572
    {
unknown's avatar
unknown committed
4573 4574
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4575
      else if (open_for_modify)
unknown's avatar
unknown committed
4576
      {
unknown's avatar
unknown committed
4577
        if (table->table->s->tmp_table)
unknown's avatar
unknown committed
4578 4579 4580 4581
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4582 4583
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
unknown's avatar
unknown committed
4584 4585 4586
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
4587 4588
        query_cache_invalidate3(thd, table->table, 0);
      }
4589
    }
4590
    ha_autocommit_or_rollback(thd, 0);
4591
    end_trans(thd, COMMIT);
unknown's avatar
unknown committed
4592
    close_thread_tables(thd);
unknown's avatar
unknown committed
4593
    table->table=0;				// For query cache
4594
    if (protocol->write())
unknown's avatar
unknown committed
4595 4596 4597
      goto err;
  }

4598
  my_eof(thd);
unknown's avatar
unknown committed
4599
  DBUG_RETURN(FALSE);
4600

4601
err:
4602
  ha_autocommit_or_rollback(thd, 1);
4603
  end_trans(thd, ROLLBACK);
4604
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
4605 4606
  if (table)
    table->table=0;
unknown's avatar
unknown committed
4607
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4608 4609
}

unknown's avatar
unknown committed
4610

unknown's avatar
unknown committed
4611
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
4612 4613
{
  DBUG_ENTER("mysql_backup_table");
4614 4615
  WARN_DEPRECATED(thd, "5.2", "BACKUP TABLE",
                  "MySQL Administrator (mysqldump, mysql)");
unknown's avatar
unknown committed
4616
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4617
				"backup", TL_READ, 0, 0, 0, 0,
4618
				&handler::ha_backup, 0));
unknown's avatar
unknown committed
4619
}
unknown's avatar
unknown committed
4620

4621

unknown's avatar
unknown committed
4622
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
4623 4624
{
  DBUG_ENTER("mysql_restore_table");
4625 4626
  WARN_DEPRECATED(thd, "5.2", "RESTORE TABLE",
                  "MySQL Administrator (mysqldump, mysql)");
unknown's avatar
unknown committed
4627
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4628
				"restore", TL_WRITE, 1, 1, 0,
4629
				&prepare_for_restore,
4630
				&handler::ha_restore, 0));
unknown's avatar
unknown committed
4631
}
unknown's avatar
unknown committed
4632

4633

unknown's avatar
unknown committed
4634
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4635 4636 4637
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4638 4639 4640
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4641
				&prepare_for_repair,
unknown's avatar
unknown committed
4642
				&handler::ha_repair, 0));
4643 4644
}

4645

unknown's avatar
unknown committed
4646
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4647 4648 4649
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4650
				"optimize", TL_WRITE, 1,0,0,0,
4651
				&handler::ha_optimize, 0));
4652 4653 4654
}


unknown's avatar
unknown committed
4655 4656 4657 4658 4659
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4660 4661
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
4662 4663

  RETURN VALUES
unknown's avatar
unknown committed
4664 4665
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
4666 4667
*/

unknown's avatar
unknown committed
4668
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4669
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
4670
{
4671
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
4672
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
4673
  DBUG_ENTER("mysql_assign_to_keycache");
4674 4675 4676 4677 4678 4679 4680

  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
4681
    DBUG_RETURN(TRUE);
4682 4683 4684 4685
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4686
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4687
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
4688 4689
}

unknown's avatar
unknown committed
4690 4691 4692 4693 4694 4695

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

  SYNOPSIS
    reassign_keycache_tables()
4696 4697 4698
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
4699

4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712
  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
4713 4714 4715
    0	  ok
*/

4716 4717
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
4718 4719 4720
{
  DBUG_ENTER("reassign_keycache_tables");

4721 4722
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
unknown's avatar
unknown committed
4723
  src_cache->param_buff_size= 0;		// Free key cache
4724 4725
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4726
  DBUG_RETURN(0);
unknown's avatar
unknown committed
4727 4728 4729
}


unknown's avatar
unknown committed
4730 4731 4732 4733 4734
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
4735 4736
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
4737 4738

  RETURN VALUES
unknown's avatar
unknown committed
4739 4740
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
4741 4742
*/

unknown's avatar
unknown committed
4743
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
4744 4745
{
  DBUG_ENTER("mysql_preload_keys");
4746 4747 4748 4749 4750
  /*
    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
4751
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
4752
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
4753
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
4754 4755 4756
}


4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805

/**
  @brief          Create frm file based on I_S table

  @param[in]      thd                      thread handler
  @param[in]      schema_table             I_S table           
  @param[in]      dst_path                 path where frm should be created
  @param[in]      create_info              Create info

  @return         Operation status
    @retval       0                        success
    @retval       1                        error
*/


bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
                                  char *dst_path, HA_CREATE_INFO *create_info)
{
  HA_CREATE_INFO local_create_info;
  Alter_info alter_info;
  bool tmp_table= (create_info->options & HA_LEX_CREATE_TMP_TABLE);
  uint keys= schema_table->table->s->keys;
  uint db_options= 0;
  DBUG_ENTER("mysql_create_like_schema_frm");

  bzero((char*) &local_create_info, sizeof(local_create_info));
  local_create_info.db_type= schema_table->table->s->db_type();
  local_create_info.row_type= schema_table->table->s->row_type;
  local_create_info.default_table_charset=default_charset_info;
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
  schema_table->table->use_all_columns();
  if (mysql_prepare_alter_table(thd, schema_table->table,
                                &local_create_info, &alter_info))
    DBUG_RETURN(1);
  if (mysql_prepare_create_table(thd, &local_create_info, &alter_info,
                                 tmp_table, &db_options,
                                 schema_table->table->file,
                                 &schema_table->table->s->key_info, &keys, 0))
    DBUG_RETURN(1);
  local_create_info.max_rows= 0;
  if (mysql_create_frm(thd, dst_path, NullS, NullS,
                       &local_create_info, alter_info.create_list,
                       keys, schema_table->table->s->key_info,
                       schema_table->table->file))
    DBUG_RETURN(1);
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
4806 4807 4808 4809 4810
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
4811
    thd		Thread object
unknown's avatar
unknown committed
4812 4813
    table       Table list element for target table
    src_table   Table list element for source table
unknown's avatar
unknown committed
4814 4815 4816
    create_info Create info

  RETURN VALUES
unknown's avatar
unknown committed
4817 4818
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
4819 4820
*/

unknown's avatar
unknown committed
4821
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
4822
                             HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
4823
{
unknown's avatar
unknown committed
4824
  TABLE *name_lock= 0;
4825
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
4826
  uint dst_path_length;
unknown's avatar
unknown committed
4827
  char *db= table->db;
4828
  char *table_name= table->table_name;
unknown's avatar
unknown committed
4829
  int  err;
unknown's avatar
unknown committed
4830
  bool res= TRUE;
unknown's avatar
unknown committed
4831
  uint not_used;
4832 4833 4834
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
4835
  char ts_name[FN_LEN];
unknown's avatar
unknown committed
4836
  DBUG_ENTER("mysql_create_like_table");
4837

4838

unknown's avatar
unknown committed
4839 4840 4841 4842 4843 4844 4845 4846
  /*
    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.
4847
  */
unknown's avatar
unknown committed
4848
  if (open_tables(thd, &src_table, &not_used, 0))
4849 4850
    DBUG_RETURN(TRUE);

4851 4852 4853 4854 4855 4856
  /*
    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
4857
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
4858 4859 4860 4861 4862
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

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

unknown's avatar
unknown committed
4865
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
unknown's avatar
unknown committed
4866

unknown's avatar
unknown committed
4867 4868 4869
  /*
    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
4870 4871 4872 4873 4874
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
4875
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
unknown's avatar
unknown committed
4876 4877 4878 4879
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
unknown's avatar
unknown committed
4880 4881 4882 4883
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
      goto err;
    if (!name_lock)
      goto table_exists;
4884
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
4885
                                          db, table_name, reg_ext, 0);
unknown's avatar
unknown committed
4886 4887 4888 4889
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

unknown's avatar
unknown committed
4890 4891
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

4892
  /*
unknown's avatar
unknown committed
4893
    Create a new table by copying from source table
unknown's avatar
unknown committed
4894 4895 4896 4897 4898 4899 4900 4901 4902 4903

    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.
4904
  */
unknown's avatar
unknown committed
4905
  VOID(pthread_mutex_lock(&LOCK_open));
4906 4907 4908 4909 4910 4911 4912 4913 4914
  if (src_table->schema_table)
  {
    if (mysql_create_like_schema_frm(thd, src_table, dst_path, create_info))
    {
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
  else if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
4915 4916 4917 4918 4919
  {
    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
4920
    VOID(pthread_mutex_unlock(&LOCK_open));
4921
    goto err;
4922
  }
unknown's avatar
unknown committed
4923 4924

  /*
4925 4926
    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
4927 4928
    and temporary tables).
  */
unknown's avatar
unknown committed
4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941
#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
4942 4943 4944

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

4945
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
4946 4947
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
unknown's avatar
unknown committed
4948
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
unknown's avatar
unknown committed
4949
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
4950

unknown's avatar
unknown committed
4951 4952 4953 4954
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
4955 4956
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
4957
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
4958 4959 4960 4961
    }
  }
  else if (err)
  {
4962
    (void) quick_rm_table(create_info->db_type, db,
4963
			  table_name, 0); /* purecov: inspected */
4964
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
4965
  }
4966

unknown's avatar
unknown committed
4967 4968
  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););

4969 4970 4971
  /*
    We have to write the query before we unlock the tables.
  */
4972
  if (thd->current_stmt_binlog_row_based)
4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990
  {
    /*
       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
4991
      if (src_table->table->s->tmp_table)               // Case 2
4992 4993 4994 4995
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
4996 4997

        /*
unknown's avatar
unknown committed
4998 4999 5000 5001
          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.
5002
        */
unknown's avatar
unknown committed
5003 5004 5005 5006 5007
        table->table= name_lock;
        VOID(pthread_mutex_lock(&LOCK_open));
        if (reopen_name_locked_table(thd, table, FALSE))
        {
          VOID(pthread_mutex_unlock(&LOCK_open));
5008
          goto err;
unknown's avatar
unknown committed
5009 5010
        }
        VOID(pthread_mutex_unlock(&LOCK_open));
5011

5012 5013 5014
        IF_DBUG(int result=)
          store_create_info(thd, table, &query,
                            create_info, FALSE /* show_database */);
5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025

        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
    */
  }
5026
  else
5027 5028
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

unknown's avatar
unknown committed
5029
  res= FALSE;
5030
  goto err;
5031

unknown's avatar
unknown committed
5032 5033 5034 5035
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
5036 5037
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
5038
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5039
		 ER_TABLE_EXISTS_ERROR,warn_buff);
unknown's avatar
unknown committed
5040
    res= FALSE;
unknown's avatar
unknown committed
5041
  }
5042 5043 5044 5045
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
unknown's avatar
unknown committed
5046
  if (name_lock)
5047 5048
  {
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5049
    unlink_open_table(thd, name_lock, FALSE);
5050 5051
    pthread_mutex_unlock(&LOCK_open);
  }
5052
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5053 5054 5055
}


unknown's avatar
unknown committed
5056
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5057
{
unknown's avatar
unknown committed
5058 5059
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5060 5061
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5062
				"analyze", lock_type, 1, 0, 0, 0,
5063
				&handler::ha_analyze, 0));
5064 5065 5066
}


unknown's avatar
unknown committed
5067
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
5068
{
unknown's avatar
unknown committed
5069 5070
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5071 5072
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
5073
				"check", lock_type,
5074
				0, 0, HA_OPEN_FOR_REPAIR, 0,
unknown's avatar
unknown committed
5075
				&handler::ha_check, &view_checksum));
5076 5077
}

unknown's avatar
unknown committed
5078

unknown's avatar
unknown committed
5079
/* table_list should contain just one table */
unknown's avatar
unknown committed
5080 5081 5082 5083
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
5084 5085 5086 5087 5088 5089
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
5090 5091 5092 5093
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
5094

5095
  thd_proc_info(thd, "discard_or_import_tablespace");
unknown's avatar
unknown committed
5096

unknown's avatar
unknown committed
5097
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
5098

unknown's avatar
unknown committed
5099 5100 5101 5102 5103
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
5104
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
unknown's avatar
unknown committed
5105 5106 5107 5108
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
5109

5110
  error= table->file->ha_discard_or_import_tablespace(discard);
unknown's avatar
unknown committed
5111

5112
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
5113 5114 5115 5116

  if (error)
    goto err;

unknown's avatar
unknown committed
5117 5118 5119 5120
  /*
    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
5121 5122 5123
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
5124 5125
  error = ha_autocommit_or_rollback(thd, 0);
  if (end_active_trans(thd))
unknown's avatar
unknown committed
5126 5127 5128
    error=1;
  if (error)
    goto err;
5129
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
5130

unknown's avatar
unknown committed
5131
err:
5132
  ha_autocommit_or_rollback(thd, error);
unknown's avatar
unknown committed
5133
  thd->tablespace_op=FALSE;
5134
  
unknown's avatar
unknown committed
5135 5136
  if (error == 0)
  {
5137
    my_ok(thd);
unknown's avatar
unknown committed
5138
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5139
  }
unknown's avatar
unknown committed
5140

5141 5142
  table->file->print_error(error, MYF(0));
    
unknown's avatar
unknown committed
5143
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
5144
}
unknown's avatar
unknown committed
5145

5146

unknown's avatar
unknown committed
5147 5148
/*
  SYNOPSIS
5149 5150
    compare_tables()
      table                     The original table.
5151 5152
      alter_info                Alter options, fields and keys for the new
                                table.
5153 5154
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
5155 5156 5157 5158 5159 5160 5161 5162
      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
5163 5164 5165 5166
      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
5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177

  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.

5178 5179 5180 5181 5182
    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
5183
  RETURN VALUES
5184 5185
    TRUE   error
    FALSE  success
unknown's avatar
unknown committed
5186 5187
*/

5188 5189 5190 5191 5192 5193
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
unknown's avatar
unknown committed
5194
               enum_alter_table_change_level *need_copy_table,
5195 5196 5197
               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
5198 5199 5200
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
5201
  uint key_count;
5202 5203
  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
  Create_field *new_field, *tmp_new_field;
5204 5205
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5206
  THD *thd= table->in_use;
5207 5208 5209 5210 5211
  /*
    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;
5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229
  /*
    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 */

5230
  DBUG_ENTER("compare_tables");
unknown's avatar
unknown committed
5231

5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246
  /* 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
5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263
  /*
    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
5264 5265 5266 5267 5268 5269 5270 5271

    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
5272
  */
5273
  if (table->s->fields != alter_info->create_list.elements ||
unknown's avatar
unknown committed
5274
      table->s->db_type() != create_info->db_type ||
unknown's avatar
unknown committed
5275 5276 5277 5278
      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 ||
5279
      create_info->used_fields & HA_CREATE_USED_ROW_FORMAT ||
5280
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5281
      order_num ||
unknown's avatar
unknown committed
5282
      !table->s->mysql_version ||
5283
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5284 5285 5286 5287
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5288

5289 5290 5291 5292 5293 5294 5295
  /*
    Use transformed info to evaluate possibility of fast ALTER TABLE
    but use the preserved field to persist modifications.
  */
  new_field_it.init(alter_info->create_list);
  tmp_new_field_it.init(tmp_alter_info.create_list);

unknown's avatar
unknown committed
5296 5297 5298 5299
  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
5300 5301 5302 5303 5304
  for (f_ptr= table->field, new_field= new_field_it++,
       tmp_new_field= tmp_new_field_it++;
       (field= *f_ptr);
       f_ptr++, new_field= new_field_it++,
       tmp_new_field= tmp_new_field_it++)
unknown's avatar
unknown committed
5305 5306 5307 5308
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5309

unknown's avatar
unknown committed
5310
    /* Check that NULL behavior is same for old and new fields */
5311
    if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
unknown's avatar
unknown committed
5312
	(uint) (field->flags & NOT_NULL_FLAG))
5313 5314 5315 5316
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
5317 5318 5319

    /* Don't pack rows in old tables if the user has requested this. */
    if (create_info->row_type == ROW_TYPE_DYNAMIC ||
5320 5321
	(tmp_new_field->flags & BLOB_FLAG) ||
	tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
unknown's avatar
unknown committed
5322 5323 5324
	create_info->row_type != ROW_TYPE_FIXED)
      create_info->table_options|= HA_OPTION_PACK_RECORD;

5325
    /* Check if field was renamed */
5326
    field->flags&= ~FIELD_IS_RENAMED;
5327 5328
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
5329
		      tmp_new_field->field_name))
5330
      field->flags|= FIELD_IS_RENAMED;      
5331

unknown's avatar
unknown committed
5332
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
5333
    if (!(tmp= field->is_equal(tmp_new_field)))
5334 5335 5336 5337
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5338
    // Clear indexed marker
5339
    field->flags&= ~FIELD_IN_ADD_INDEX;
unknown's avatar
unknown committed
5340 5341 5342 5343 5344 5345 5346
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5347 5348 5349
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5350
  KEY *new_key_end= *key_info_buffer + key_count;
unknown's avatar
unknown committed
5351

5352 5353 5354 5355 5356 5357 5358 5359
  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
5360
  {
5361 5362 5363 5364 5365
    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. */
5366
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5367 5368 5369 5370 5371 5372 5373
    {
      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. */
5374
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5375 5376 5377 5378 5379 5380 5381 5382 5383 5384
      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
5385 5386 5387 5388 5389

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5390 5391 5392
    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
5393 5394 5395
    {
      /*
	Key definition has changed if we are using a different field or
5396 5397
	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
5398
      */
5399 5400 5401
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
unknown's avatar
unknown committed
5402
    }
5403 5404 5405 5406
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5407 5408
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5409 5410 5411 5412 5413 5414
    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];
5415
      field->flags|= FIELD_IN_ADD_INDEX;
5416
    }
5417
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
unknown's avatar
unknown committed
5418
  }
5419
  /*end of for (; table_key < table_key_end;) */
unknown's avatar
unknown committed
5420

5421 5422 5423
  /*
    Step through all keys of the new table and find matching old keys.
  */
5424
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5425 5426 5427 5428 5429 5430 5431 5432 5433 5434
  {
    /* 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. */
5435
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5436 5437 5438 5439 5440 5441
      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];
5442
        field->flags|= FIELD_IN_ADD_INDEX;
5443
      }
unknown's avatar
unknown committed
5444
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5445 5446
    }
  }
5447 5448 5449

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5450 5451 5452 5453
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5454

5455
  if (*index_drop_count || *index_add_count)
5456 5457 5458 5459
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5460

5461 5462
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5463 5464 5465
}


unknown's avatar
unknown committed
5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491
/*
  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:
5492
    error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5493 5494 5495 5496 5497 5498
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
5499
    error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5500 5501 5502 5503 5504
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5505 5506
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
unknown's avatar
unknown committed
5507 5508 5509 5510 5511 5512 5513 5514
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553
/**
  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
5554
*/
5555

5556 5557 5558 5559
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
unknown's avatar
unknown committed
5560
{
5561
  /* New column definitions are added here */
unknown's avatar
unknown committed
5562
  List<Create_field> new_create_list;
5563 5564 5565
  /* 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
5566
  List_iterator<Create_field> def_it(alter_info->create_list);
5567 5568
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
unknown's avatar
unknown committed
5569 5570 5571
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5572 5573 5574 5575 5576
  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;
5577

5578
  DBUG_ENTER("mysql_prepare_alter_table");
5579

5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590
  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)
5591
  {
5592 5593 5594
    /* 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;
5595
  }
5596 5597
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5598 5599
  if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
    create_info->transactional= table->s->transactional;
unknown's avatar
unknown committed
5600

5601
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
5602
  {
unknown's avatar
unknown committed
5603
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN));
5604
    /*
5605 5606
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
5607
    */
5608 5609 5610 5611 5612
    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
5613
  Create_field *def;
5614

5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626
  /*
    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++))
5627
    {
5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639
      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;
      }
5640
    }
5641
    if (drop)
5642
    {
5643 5644
      drop_it.remove();
      continue;
5645
    }
5646 5647 5648
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
5649
    {
5650 5651 5652
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
5653
    }
5654 5655 5656 5657
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
unknown's avatar
unknown committed
5658
      {
5659 5660
	new_create_list.push_back(def);
	def_it.remove();
unknown's avatar
unknown committed
5661 5662
      }
    }
5663
    else
5664 5665
    {
      /*
5666 5667
        This field was not dropped and not changed, add it to the list
        for the new table.
5668
      */
unknown's avatar
unknown committed
5669
      def= new Create_field(field, field);
5670 5671 5672 5673
      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
5674
      {
5675 5676
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
5677
      }
5678
      if (alter)
unknown's avatar
unknown committed
5679
      {
5680
	if (def->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
5681
	{
5682
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
5683
          goto err;
unknown's avatar
unknown committed
5684
	}
5685 5686 5687 5688 5689
	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
5690 5691 5692
      }
    }
  }
5693 5694
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
5695
  {
5696
    if (def->change && ! def->field)
5697
    {
5698
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
5699
      goto err;
5700
    }
unknown's avatar
unknown committed
5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717
    /*
      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;
    }
5718 5719 5720 5721
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
5722
    else
5723
    {
unknown's avatar
unknown committed
5724
      Create_field *find;
5725 5726 5727 5728 5729 5730 5731 5732
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
5733
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
5734 5735 5736
        goto err;
      }
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
5737
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
5738
    }
5739
  }
5740
  if (alter_info->alter_list.elements)
5741
  {
5742
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
5743
             alter_info->alter_list.head()->name, table->s->table_name.str);
unknown's avatar
unknown committed
5744
    goto err;
5745
  }
5746
  if (!new_create_list.elements)
unknown's avatar
unknown committed
5747
  {
unknown's avatar
unknown committed
5748 5749
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
5750
    goto err;
unknown's avatar
unknown committed
5751 5752 5753
  }

  /*
5754 5755
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
5756 5757
  */

5758
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
5759
  {
5760
    char *key_name= key_info->name;
unknown's avatar
unknown committed
5761 5762 5763 5764 5765
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
5766
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781
	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
5782
      Create_field *cfield;
unknown's avatar
unknown committed
5783 5784 5785 5786 5787
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
5788 5789
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
5790 5791
	    break;
	}
5792
	else if (!my_strcasecmp(system_charset_info,
5793
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
5794
	  break;
unknown's avatar
unknown committed
5795 5796 5797 5798 5799
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
5800 5801 5802 5803 5804 5805 5806
      {
        /*
          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.

5807 5808 5809
          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.

5810 5811 5812 5813
          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
5814
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
5815 5816
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
unknown's avatar
unknown committed
5817 5818
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
5819 5820 5821
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
5822
      }
5823
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
5824
      key_parts.push_back(new Key_part_spec(cfield->field_name,
unknown's avatar
unknown committed
5825 5826 5827
					    key_part_length));
    }
    if (key_parts.elements)
5828 5829
    {
      KEY_CREATE_INFO key_create_info;
5830 5831
      Key *key;
      enum Key::Keytype key_type;
5832 5833 5834 5835 5836 5837 5838 5839
      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;

5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974
      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
5975
  ha_rows copied,deleted;
5976
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
5977
  legacy_db_type table_type;
5978
  frm_type_enum frm_type;
unknown's avatar
unknown committed
5979
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
5980
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
5981
  uint fast_alter_partition= 0;
5982 5983
  bool partition_changed= FALSE;
#endif
5984 5985 5986 5987 5988 5989
  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
5990
  bool committed= 0;
unknown's avatar
unknown committed
5991 5992
  DBUG_ENTER("mysql_alter_table");

5993 5994 5995 5996 5997
  LINT_INIT(index_add_count);
  LINT_INIT(index_drop_count);
  LINT_INIT(index_add_buffer);
  LINT_INIT(index_drop_buffer);

5998 5999 6000 6001 6002 6003
  /*
    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.
  */
6004
  if (table_list && table_list->db && table_list->table_name)
6005
  {
6006
    int table_kind= 0;
6007

6008 6009 6010
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
6011

6012
    if (table_kind)
6013
    {
6014 6015 6016 6017 6018 6019
      /* 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);
      }
6020

6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032
      /* 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)
      {
6033
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
6034 6035 6036
        DBUG_RETURN(TRUE);
      }
#endif
6037 6038 6039
    }
  }

6040 6041 6042 6043 6044
  /*
    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.
  */
6045
  thd_proc_info(thd, "init");
6046
  table_name=table_list->table_name;
unknown's avatar
unknown committed
6047
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
unknown's avatar
unknown committed
6048
  db=table_list->db;
unknown's avatar
unknown committed
6049
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
6050
    new_db= db;
6051 6052
  build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext, 0);
  build_table_filename(path, sizeof(path), db, table_name, "", 0);
6053

6054
  mysql_ha_rm_tables(thd, table_list, FALSE);
unknown's avatar
unknown committed
6055

unknown's avatar
unknown committed
6056
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
6057
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
6058
    /* Conditionally writes to binlog. */
unknown's avatar
unknown committed
6059
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
6060
						   alter_info->tablespace_op));
6061 6062 6063
  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);
6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076
  /*
    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.
  */
6077 6078
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
6079
  /* Sic: there is a race here */
6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090
  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));
6091
      DBUG_RETURN(TRUE);
6092 6093 6094
    }

    if (wait_if_global_read_lock(thd,0,1))
6095
      DBUG_RETURN(TRUE);
6096 6097
    VOID(pthread_mutex_lock(&LOCK_open));
    if (lock_table_names(thd, table_list))
6098
    {
unknown's avatar
unknown committed
6099
      error= 1;
6100
      goto view_err;
6101
    }
6102 6103 6104 6105 6106 6107 6108 6109 6110
    
    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);
      }
6111
      my_ok(thd);
6112 6113 6114 6115 6116 6117 6118 6119 6120
    }

    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);
  }
6121 6122

  if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
unknown's avatar
unknown committed
6123
    DBUG_RETURN(TRUE);
6124
  table->use_all_columns();
unknown's avatar
unknown committed
6125

6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139
  /*
    Prohibit changing of the UNION list of a non-temporary MERGE table
    under LOCK tables. It would be quite difficult to reuse a shrinked
    set of tables from the old table or to open a new TABLE object for
    an extended list and verify that they belong to locked tables.
  */
  if (thd->locked_tables &&
      (create_info->used_fields & HA_CREATE_USED_UNION) &&
      (table->s->tmp_table == NO_TMP_TABLE))
  {
    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
    DBUG_RETURN(TRUE);
  }

unknown's avatar
unknown committed
6140 6141 6142
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
6143
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
unknown's avatar
unknown committed
6144
    strmov(new_name_buff,new_name);
6145
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
6146
    if (lower_case_table_names)
unknown's avatar
unknown committed
6147 6148 6149
    {
      if (lower_case_table_names != 2)
      {
6150
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
6151 6152
	new_alias= new_name;			// Create lower case table name
      }
6153
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
6154
    }
6155
    if (new_db == db &&
unknown's avatar
unknown committed
6156
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
6157 6158
    {
      /*
6159 6160
	Source and destination table names are equal: make later check
	easier.
6161
      */
unknown's avatar
unknown committed
6162
      new_alias= new_name= table_name;
6163
    }
unknown's avatar
unknown committed
6164 6165
    else
    {
unknown's avatar
unknown committed
6166
      if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6167 6168 6169
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
6170
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
6171
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6172 6173 6174 6175
	}
      }
      else
      {
unknown's avatar
unknown committed
6176 6177 6178 6179 6180 6181 6182 6183
        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);
        }

6184 6185 6186
        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
6187 6188
	{
	  /* Table will be closed in do_command() */
6189
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
6190
          goto err;
unknown's avatar
unknown committed
6191 6192 6193 6194 6195
	}
      }
    }
  }
  else
6196 6197 6198 6199
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
6200

unknown's avatar
unknown committed
6201
  old_db_type= table->s->db_type();
unknown's avatar
unknown committed
6202
  if (!create_info->db_type)
6203
  {
6204
#ifdef WITH_PARTITION_STORAGE_ENGINE
6205 6206
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
6207 6208 6209 6210
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
6211 6212 6213
        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.
6214
      */
6215
      create_info->db_type= table->part_info->default_engine_type;
6216
    }
6217
    else
6218
#endif
6219
      create_info->db_type= old_db_type;
6220
  }
unknown's avatar
unknown committed
6221

6222
  if (check_engine(thd, new_name, create_info))
unknown's avatar
unknown committed
6223
    goto err;
6224
  new_db_type= create_info->db_type;
6225

6226 6227
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
6228
      !table->file->can_switch_engines())
unknown's avatar
unknown committed
6229
  {
6230
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
unknown's avatar
unknown committed
6231 6232 6233
    goto err;
  }

6234
  if (create_info->row_type == ROW_TYPE_NOT_USED)
6235
    create_info->row_type= table->s->row_type;
unknown's avatar
unknown committed
6236

6237 6238 6239
  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
6240
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
6241
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
6242 6243 6244
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
6245
    goto err;
6246 6247
  }
  
6248
  thd_proc_info(thd, "setup");
6249
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
6250
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
6251
  {
6252 6253 6254 6255
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
6256 6257 6258 6259 6260 6261 6262 6263 6264 6265
      /*
        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));
6266
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6267
      VOID(pthread_mutex_unlock(&LOCK_open));
6268
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
6269
      error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6270 6271 6272
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
6273
      VOID(pthread_mutex_lock(&LOCK_open));
6274
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6275
      VOID(pthread_mutex_unlock(&LOCK_open));
6276
      error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6277 6278
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
6279 6280 6281 6282
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
6283 6284 6285
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
unknown's avatar
unknown committed
6286
      error= 0;
6287 6288 6289 6290 6291
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
    }

6292 6293 6294 6295 6296 6297 6298 6299 6300 6301
    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.
    */

6302
    if (!error && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6303
    {
6304
      thd_proc_info(thd, "rename");
unknown's avatar
unknown committed
6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317
      /*
        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.
      */
6318 6319
      if (!access(new_name_buff,F_OK))
      {
6320
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
6321
	error= -1;
6322 6323 6324
      }
      else
      {
6325 6326 6327 6328 6329 6330 6331 6332 6333 6334
	*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
6335 6336
      }
    }
6337

6338 6339 6340 6341 6342 6343
    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);
6344
    }
6345

6346 6347 6348
    if (!error)
    {
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
6349
      my_ok(thd);
6350
    }
6351
    else if (error > 0)
6352
    {
6353 6354
      table->file->print_error(error, MYF(0));
      error= -1;
6355
    }
6356 6357 6358 6359 6360 6361
    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
6362 6363
  }

6364
  /* We have to do full alter table. */
unknown's avatar
unknown committed
6365

6366 6367 6368
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6369
    goto err;
6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382
#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
6383

6384 6385
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
unknown's avatar
unknown committed
6386 6387
  
  need_copy_table= alter_info->change_level;
unknown's avatar
unknown committed
6388

unknown's avatar
unknown committed
6389 6390
  set_table_default_charset(thd, create_info, db);

6391
  if (thd->variables.old_alter_table
unknown's avatar
unknown committed
6392
      || (table->s->db_type() != create_info->db_type)
6393
#ifdef WITH_PARTITION_STORAGE_ENGINE
6394
      || partition_changed
6395
#endif
6396
     )
6397
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
unknown's avatar
unknown committed
6398
  else
6399
  {
unknown's avatar
unknown committed
6400
    enum_alter_table_change_level need_copy_table_res;
6401
    /* Check how much the tables differ. */
6402 6403
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6404
                       &need_copy_table_res,
6405 6406 6407 6408
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
                       &index_add_buffer, &index_add_count))
      goto err;
6409 6410 6411
   
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6412 6413 6414 6415 6416 6417 6418 6419 6420 6421
  }

  /*
    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
6422
    ulong alter_flags= 0;
6423 6424 6425 6426 6427 6428
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

unknown's avatar
unknown committed
6429 6430
    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
6431
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508
    /* 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. */
6509
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6510 6511 6512 6513 6514
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
6515
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6516 6517 6518 6519 6520
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
unknown's avatar
unknown committed
6521

6522 6523
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
6524
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
6525 6526
    so that ALTER TABLE won't break when somebody will add new flag
  */
6527
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
6528
    create_info->frm_only= 1;
6529

6530
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6531
  if (fast_alter_partition)
unknown's avatar
unknown committed
6532
  {
unknown's avatar
unknown committed
6533
    DBUG_ASSERT(!name_lock);
unknown's avatar
unknown committed
6534 6535 6536 6537
    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
6538
  }
6539
#endif
unknown's avatar
unknown committed
6540

6541 6542 6543 6544 6545 6546
  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);

6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561
  /*
    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.
6562 6563
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589
      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);
    }
  }
6590 6591
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
6592

6593 6594 6595 6596 6597 6598
  /*
    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
6599
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
6600 6601 6602
                                    create_info,
                                    alter_info,
                                    1, 0);
6603 6604
  reenable_binlog(thd);
  if (error)
unknown's avatar
unknown committed
6605
    goto err;
6606 6607

  /* Open the table if we need to copy the data. */
6608
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
6609
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
6610
  {
6611
    if (table->s->tmp_table)
6612 6613 6614 6615
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
6616
      tbl.table_name= tbl.alias= tmp_name;
unknown's avatar
unknown committed
6617
      /* Table is in thd->temporary_tables */
6618 6619
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
6620 6621 6622 6623
    }
    else
    {
      char path[FN_REFLEN];
unknown's avatar
unknown committed
6624
      /* table is a normal table: Create temporary table in same directory */
6625 6626
      build_table_filename(path, sizeof(path), new_db, tmp_name, "",
                           FN_IS_TMP);
6627
      /* Open our intermediate table */
6628 6629 6630
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
6631
      goto err1;
6632 6633 6634 6635
    /*
      Note: In case of MERGE table, we do not attach children. We do not
      copy data for MERGE tables. Only the children have data.
    */
unknown's avatar
unknown committed
6636 6637
  }

6638
  /* Copy the data if necessary. */
6639
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
6640
  thd->cuted_fields=0L;
6641
  thd_proc_info(thd, "copy to tmp table");
unknown's avatar
unknown committed
6642
  copied=deleted=0;
6643 6644 6645 6646
  /*
    We do not copy data for MERGE tables. Only the children have data.
    MERGE tables have HA_NO_COPY_ON_ALTER set.
  */
6647
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
6648
  {
6649
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
unknown's avatar
unknown committed
6650
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
6651
    new_table->next_number_field=new_table->found_next_number_field;
6652 6653 6654
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
6655
                                    alter_info->keys_onoff,
unknown's avatar
unknown committed
6656
                                    alter_info->error_if_not_empty);
6657
  }
6658
  else
6659 6660 6661
  {
    VOID(pthread_mutex_lock(&LOCK_open));
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6662
    VOID(pthread_mutex_unlock(&LOCK_open));
6663 6664
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
6665 6666
    error= ha_autocommit_or_rollback(thd, 0);
    if (end_active_trans(thd))
6667
      error= 1;
6668
  }
6669
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
6670

6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683
  /* 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"));

6684
    table->file->ha_prepare_for_alter();
6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711
    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;
6712
        goto err1;
6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734
      }
    }
    /*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));
6735
        goto err1;
6736 6737 6738 6739 6740 6741
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
6742
        goto err1;
6743 6744 6745 6746
      }
    }
    /*end of if (index_drop_count)*/

6747 6748 6749 6750
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
6751

6752 6753
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
6754
    if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
6755
      goto err1;
6756
    committed= 1;
6757 6758 6759
  }
  /*end of if (! new_table) for add/drop index*/

unknown's avatar
unknown committed
6760
  if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6761 6762 6763
  {
    /* We changed a temporary table */
    if (error)
6764
      goto err1;
6765 6766 6767 6768 6769 6770
    /* 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
6771
    /* Remove link to old table and rename the new one */
unknown's avatar
unknown committed
6772
    close_temporary_table(thd, table, 1, 1);
6773 6774
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
6775
      goto err1;
6776
    /* We don't replicate alter table statement on temporary tables */
6777
    if (!thd->current_stmt_binlog_row_based)
6778
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
unknown's avatar
unknown committed
6779 6780 6781
    goto end_temporary;
  }

6782 6783
  if (new_table)
  {
6784 6785 6786 6787
    /*
      Close the intermediate table that will be the new table.
      Note that MERGE tables do not have their children attached here.
    */
unknown's avatar
unknown committed
6788
    intern_close_table(new_table);
6789
    my_free(new_table,MYF(0));
6790
  }
unknown's avatar
unknown committed
6791 6792 6793
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
6794
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
unknown's avatar
unknown committed
6795 6796 6797
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6798

unknown's avatar
unknown committed
6799
  /*
6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812
    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
6813 6814
  */

6815
  thd_proc_info(thd, "rename result table");
6816 6817
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
6818 6819
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
unknown's avatar
unknown committed
6820

6821
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
6822
  close_data_files_and_morph_locks(thd, db, table_name);
unknown's avatar
unknown committed
6823

unknown's avatar
unknown committed
6824
  error=0;
6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839
  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.
  */
6840 6841 6842 6843 6844 6845
  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;
  }
6846 6847
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
unknown's avatar
unknown committed
6848 6849
  {
    error=1;
6850
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
unknown's avatar
unknown committed
6851
  }
6852
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
6853
                              new_alias, FN_FROM_IS_TMP) ||
6854
           (new_name != table_name || new_db != db) && // we also do rename
6855
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
6856 6857
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
6858 6859
           Table_triggers_list::change_table_name(thd, db, table_name,
                                                  new_db, new_alias))
6860 6861
  {
    /* Try to get everything back. */
unknown's avatar
unknown committed
6862
    error=1;
6863 6864 6865 6866
    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
6867
  }
6868

unknown's avatar
unknown committed
6869 6870
  if (error)
  {
6871 6872
    /* This shouldn't happen. But let us play it safe. */
    goto err_with_placeholders;
unknown's avatar
unknown committed
6873
  }
6874

6875
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
6876
  {
unknown's avatar
unknown committed
6877
    /*
6878 6879
      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.
6880
      NO need to tamper with MERGE tables. The real open is done later.
unknown's avatar
unknown committed
6881
    */
6882 6883 6884 6885 6886 6887 6888 6889
    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);
6890 6891
      table_list->table= name_lock;
      if (reopen_name_locked_table(thd, table_list, FALSE))
6892 6893
        goto err_with_placeholders;
      t_table= table_list->table;
unknown's avatar
unknown committed
6894
    }
6895
    else
6896
    {
6897 6898 6899
      if (reopen_table(table))
        goto err_with_placeholders;
      t_table= table;
6900 6901
    }
    /* Tell the handler that a new frm file is in place. */
6902 6903
    if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                               create_info))
6904 6905
      goto err_with_placeholders;
    if (thd->locked_tables && new_name == table_name && new_db == db)
6906
    {
6907 6908 6909 6910 6911 6912 6913 6914
      /*
        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);
6915 6916
    }
  }
6917

6918 6919 6920
  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
6921
  {
6922
    thd->in_lock_tables= 1;
6923
    error= reopen_tables(thd, 1, 1);
6924
    thd->in_lock_tables= 0;
unknown's avatar
unknown committed
6925
    if (error)
6926
      goto err_with_placeholders;
unknown's avatar
unknown committed
6927
  }
6928
  VOID(pthread_mutex_unlock(&LOCK_open));
6929

6930
  thd_proc_info(thd, "end");
6931

6932 6933
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

6934 6935 6936 6937
  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
6938 6939
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
                thd->current_stmt_binlog_row_based &&
6940 6941
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
6942

6943
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
6944
  {
unknown's avatar
unknown committed
6945 6946 6947
    /*
      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
6948
      shutdown. But we do not need to attach MERGE children.
unknown's avatar
unknown committed
6949
    */
6950
    char path[FN_REFLEN];
6951
    TABLE *t_table;
6952
    build_table_filename(path, sizeof(path), new_db, table_name, "", 0);
6953 6954
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
unknown's avatar
unknown committed
6955
    {
6956
      intern_close_table(t_table);
6957
      my_free(t_table, MYF(0));
unknown's avatar
unknown committed
6958
    }
6959
    else
6960
      sql_print_warning("Could not open table %s.%s after rename\n",
unknown's avatar
unknown committed
6961
                        new_db,table_name);
6962
    ha_flush_logs(old_db_type);
6963
  }
unknown's avatar
unknown committed
6964
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
6965
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
6966

6967
  if (thd->locked_tables && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6968
  {
6969 6970 6971 6972 6973 6974
    /*
      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
6975
    pthread_mutex_lock(&LOCK_open);
6976
    unlink_open_table(thd, table, FALSE);
unknown's avatar
unknown committed
6977 6978 6979 6980
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }

unknown's avatar
unknown committed
6981
end_temporary:
6982 6983 6984
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
6985
  my_ok(thd, copied + deleted, 0L, tmp_name);
unknown's avatar
unknown committed
6986
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
6987
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
6988

6989
err1:
6990 6991 6992 6993 6994 6995
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
6996
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
6997

6998
err:
6999 7000 7001 7002 7003 7004
  /*
    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
7005
  if (alter_info->error_if_not_empty && thd->row_count)
7006
  {
7007 7008
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
unknown's avatar
unknown committed
7009
    switch (alter_info->datetime_field->sql_type)
7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025
    {
      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
7026 7027
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                                 f_val, strlength(f_val), t_type,
unknown's avatar
unknown committed
7028
                                 alter_info->datetime_field->field_name);
7029 7030
    thd->abort_on_warning= save_abort_on_warning;
  }
unknown's avatar
unknown committed
7031 7032 7033 7034 7035 7036
  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
7037
  DBUG_RETURN(TRUE);
7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048

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));
unknown's avatar
unknown committed
7049
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7050
}
unknown's avatar
unknown committed
7051
/* mysql_alter_table */
unknown's avatar
unknown committed
7052 7053

static int
unknown's avatar
unknown committed
7054
copy_data_between_tables(TABLE *from,TABLE *to,
unknown's avatar
unknown committed
7055
			 List<Create_field> &create,
7056
                         bool ignore,
7057
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
7058
			 ha_rows *copied,
unknown's avatar
unknown committed
7059
			 ha_rows *deleted,
7060 7061
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
unknown's avatar
unknown committed
7062 7063 7064 7065 7066
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
7067
  uint length= 0;
unknown's avatar
unknown committed
7068 7069 7070 7071 7072
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
7073
  ha_rows examined_rows;
7074
  bool auto_increment_field_copied= 0;
7075
  ulong save_sql_mode;
7076
  ulonglong prev_insert_id;
unknown's avatar
unknown committed
7077 7078
  DBUG_ENTER("copy_data_between_tables");

7079 7080 7081 7082 7083 7084
  /*
    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
  */
7085
  error= ha_enable_transaction(thd, FALSE);
7086 7087
  if (error)
    DBUG_RETURN(-1);
7088
  
7089
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
7090 7091
    DBUG_RETURN(-1);				/* purecov: inspected */

7092
  if (to->file->ha_external_lock(thd, F_WRLCK))
7093
    DBUG_RETURN(-1);
7094

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

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

7103
  from->file->info(HA_STATUS_VARIABLE);
7104
  to->file->ha_start_bulk_insert(from->file->stats.records);
unknown's avatar
unknown committed
7105

7106 7107
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
7108 7109
  List_iterator<Create_field> it(create);
  Create_field *def;
unknown's avatar
unknown committed
7110 7111 7112 7113 7114
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
7115 7116
    {
      if (*ptr == to->next_number_field)
7117
      {
7118
        auto_increment_field_copied= TRUE;
7119 7120 7121 7122 7123 7124 7125 7126 7127
        /*
          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
7128
      (copy_end++)->set(*ptr,def->field,0);
7129 7130
    }

unknown's avatar
unknown committed
7131 7132
  }

7133 7134
  found_count=delete_count=0;

unknown's avatar
unknown committed
7135 7136
  if (order)
  {
7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154
    if (to->s->primary_key != MAX_KEY && to->file->primary_key_is_clustered())
    {
      char warn_buff[MYSQL_ERRMSG_SIZE];
      my_snprintf(warn_buff, sizeof(warn_buff), 
                  "ORDER BY ignored as there is a user-defined clustered index"
                  " in the table '%-.192s'", from->s->table_name.str);
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
                   warn_buff);
    }
    else
    {
      from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
                                                MYF(MY_FAE | MY_ZEROFILL));
      bzero((char *) &tables, sizeof(tables));
      tables.table= from;
      tables.alias= tables.table_name= from->s->table_name.str;
      tables.db= from->s->db.str;
      error= 1;
unknown's avatar
unknown committed
7155

7156 7157 7158 7159 7160 7161 7162 7163 7164 7165
      if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
          setup_order(thd, thd->lex->select_lex.ref_pointer_array,
                      &tables, fields, all_fields, order) ||
          !(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
          (from->sort.found_records= filesort(thd, from, sortorder, length,
                                              (SQL_SELECT *) 0, HA_POS_ERROR,
                                              1, &examined_rows)) ==
          HA_POS_ERROR)
        goto err;
    }
unknown's avatar
unknown committed
7166 7167
  };

7168 7169
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
7170
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
7171
  if (ignore)
unknown's avatar
unknown committed
7172
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
7173
  thd->row_count= 0;
7174
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
7175 7176 7177 7178
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
7179
      thd->send_kill_message();
unknown's avatar
unknown committed
7180 7181 7182
      error= 1;
      break;
    }
7183
    thd->row_count++;
7184 7185 7186 7187 7188 7189
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
7190 7191
    if (to->next_number_field)
    {
7192
      if (auto_increment_field_copied)
7193
        to->auto_increment_field_not_null= TRUE;
7194 7195 7196
      else
        to->next_number_field->reset();
    }
7197
    
unknown's avatar
unknown committed
7198
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
7199
    {
unknown's avatar
unknown committed
7200
      copy_ptr->do_copy(copy_ptr);
7201
    }
7202
    prev_insert_id= to->file->next_insert_id;
7203
    error=to->file->ha_write_row(to->record[0]);
7204 7205
    to->auto_increment_field_not_null= FALSE;
    if (error)
unknown's avatar
unknown committed
7206
    {
7207
      if (!ignore ||
7208
          to->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
7209
      {
7210
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
7211 7212 7213 7214
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
unknown's avatar
unknown committed
7215
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
7216
             if (key_nr == 0 &&
unknown's avatar
unknown committed
7217 7218
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
7219
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
unknown's avatar
unknown committed
7220
             to->file->print_keydup_error(key_nr, err_msg);
7221 7222 7223 7224
             break;
           }
         }

unknown's avatar
unknown committed
7225 7226 7227
	to->file->print_error(error,MYF(0));
	break;
      }
7228
      to->file->restore_auto_increment(prev_insert_id);
unknown's avatar
unknown committed
7229 7230 7231
      delete_count++;
    }
    else
7232
      found_count++;
unknown's avatar
unknown committed
7233 7234
  }
  end_read_record(&info);
7235
  free_io_cache(from);
7236
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
7237

7238
  if (to->file->ha_end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
7239
  {
unknown's avatar
unknown committed
7240
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
7241 7242
    error=1;
  }
unknown's avatar
unknown committed
7243
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7244

7245 7246 7247 7248 7249 7250
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7251 7252 7253 7254
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
7255
  if (ha_autocommit_or_rollback(thd, 0))
7256
    error=1;
7257
  if (end_active_trans(thd))
7258
    error=1;
7259

unknown's avatar
unknown committed
7260
 err:
7261
  thd->variables.sql_mode= save_sql_mode;
7262
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
7263
  free_io_cache(from);
unknown's avatar
unknown committed
7264 7265
  *copied= found_count;
  *deleted=delete_count;
7266
  to->file->ha_release_auto_increment();
7267
  if (to->file->ha_external_lock(thd,F_UNLCK))
7268
    error=1;
unknown's avatar
unknown committed
7269 7270
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7271

unknown's avatar
unknown committed
7272

7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7284
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7285 7286
{
  HA_CREATE_INFO create_info;
7287 7288 7289
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7290 7291 7292 7293 7294 7295
  DBUG_ASSERT(!table_list->next_global);
  /*
    table_list->table has been closed and freed. Do not reference
    uninitialized data. open_tables() could fail.
  */
  table_list->table= NULL;
7296 7297

  bzero((char*) &create_info, sizeof(create_info));
unknown's avatar
unknown committed
7298
  create_info.row_type=ROW_TYPE_NOT_USED;
7299
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
7300
  /* Force alter table to recreate table */
7301
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7302
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7303 7304
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7305 7306 7307
}


7308 7309
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7310 7311 7312 7313 7314
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
7315
  DBUG_ENTER("mysql_checksum_table");
7316 7317 7318

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7319 7320
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7321
  item->maybe_null= 1;
7322 7323
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
7324
    DBUG_RETURN(TRUE);
7325

7326
  /* Open one table after the other to keep lock time as short as possible. */
unknown's avatar
VIEW  
unknown committed
7327
  for (table= tables; table; table= table->next_local)
7328 7329
  {
    char table_name[NAME_LEN*2+2];
7330
    TABLE *t;
7331

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

7334
    t= table->table= open_n_lock_single_table(thd, table, TL_READ);
unknown's avatar
unknown committed
7335
    thd->clear_error();			// these errors shouldn't get client
7336 7337 7338 7339

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

7340
    if (!t)
7341
    {
unknown's avatar
unknown committed
7342
      /* Table didn't exist */
7343
      protocol->store_null();
unknown's avatar
unknown committed
7344
      thd->clear_error();
7345 7346 7347
    }
    else
    {
7348
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7349 7350
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7351
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
7352
	       (check_opt->flags & T_QUICK))
7353
	protocol->store_null();
7354 7355
      else
      {
7356 7357
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
7358
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7359

7360
        t->use_all_columns();
7361

unknown's avatar
unknown committed
7362
	if (t->file->ha_rnd_init(1))
7363 7364 7365
	  protocol->store_null();
	else
	{
7366
	  for (;;)
7367 7368
	  {
	    ha_checksum row_crc= 0;
7369 7370 7371 7372 7373 7374 7375
            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
7376 7377 7378 7379
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
7380 7381 7382
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
7383 7384
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7385

7386
	    for (uint i= 0; i < t->s->fields; i++ )
7387 7388
	    {
	      Field *f= t->field[i];
7389
	      if ((f->type() == MYSQL_TYPE_BLOB) ||
7390
                  (f->type() == MYSQL_TYPE_VARCHAR))
7391 7392 7393
	      {
		String tmp;
		f->val_str(&tmp);
7394
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7395 7396
	      }
	      else
7397
		row_crc= my_checksum(row_crc, f->ptr,
unknown's avatar
unknown committed
7398
				     f->pack_length());
7399
	    }
7400

7401 7402 7403
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
7404
          t->file->ha_rnd_end();
7405
	}
7406
      }
unknown's avatar
unknown committed
7407
      thd->clear_error();
7408 7409 7410 7411 7412 7413 7414
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

7415
  my_eof(thd);
unknown's avatar
unknown committed
7416
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7417

7418 7419 7420 7421
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
7422
  DBUG_RETURN(TRUE);
7423
}
7424 7425

static bool check_engine(THD *thd, const char *table_name,
7426
                         HA_CREATE_INFO *create_info)
7427
{
7428
  handlerton **new_engine= &create_info->db_type;
unknown's avatar
unknown committed
7429
  handlerton *req_engine= *new_engine;
unknown's avatar
unknown committed
7430
  bool no_substitution=
7431
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
7432 7433
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
7434 7435
    return TRUE;

unknown's avatar
unknown committed
7436
  if (req_engine && req_engine != *new_engine)
7437
  {
unknown's avatar
unknown committed
7438
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
7439 7440
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
unknown's avatar
unknown committed
7441
                       ha_resolve_storage_engine_name(*new_engine),
7442 7443
                       table_name);
  }
7444 7445 7446 7447 7448
  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
7449
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
unknown's avatar
unknown committed
7450
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
7451 7452 7453
      *new_engine= 0;
      return TRUE;
    }
7454
    *new_engine= myisam_hton;
7455
  }
7456 7457
  return FALSE;
}