sql_table.cc 246 KB
Newer Older
1
/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
bk@work.mysql.com's avatar
bk@work.mysql.com 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
5
   the Free Software Foundation; version 2 of the License.
bk@work.mysql.com's avatar
bk@work.mysql.com 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>
bk@work.mysql.com's avatar
bk@work.mysql.com 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"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
25 26 27 28 29

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

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

serg@serg.mylan's avatar
serg@serg.mylan committed
32
const char *primary_key_name="PRIMARY";
bk@work.mysql.com's avatar
bk@work.mysql.com 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,
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

43
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
serg@janus.mylan's avatar
serg@janus.mylan committed
44
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
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
53 54
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
55
                          Alter_info *alter_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
56

57
#ifndef DBUG_OFF
bk@work.mysql.com's avatar
bk@work.mysql.com 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
/**
  @brief Helper function for explain_filename
*/
static char* add_identifier(char *to_p, const char * end_p,
                           const char* name, uint name_len, int errcode)
{
  uint res;
  uint errors;
  const char *conv_name;
  char tmp_name[FN_REFLEN];
  char conv_string[FN_REFLEN];

  DBUG_ENTER("add_identifier");
  if (!name[name_len])
    conv_name= name;
  else
  {
    strnmov(tmp_name, name, name_len);
    tmp_name[name_len]= 0;
    conv_name= tmp_name;
  }
  res= strconvert(&my_charset_filename, conv_name, system_charset_info,
                  conv_string, FN_REFLEN, &errors);
  if (!res || errors)
    conv_name= name;
  else
  {
    DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string));
    conv_name= conv_string;
  }

  if (errcode)
    to_p+= my_snprintf(to_p, end_p - to_p, ER(errcode), conv_name);
  else
    to_p+= my_snprintf(to_p, end_p - to_p, "`%s`", conv_name);
  return to_p;
}


/**
  @brief Explain a path name by split it to database, table etc.
  
  @details Break down the path name to its logic parts
  (database, table, partition, subpartition).
  filename_to_tablename cannot be used on partitions, due to the #P# part.
  There can be up to 6 '#', #P# for partition, #SP# for subpartition
  and #TMP# or #REN# for temporary or renamed partitions.
  This should be used when something should be presented to a user in a
  diagnostic, error etc. when it would be useful to know what a particular
  file [and directory] means. Such as SHOW ENGINE STATUS, error messages etc.

   @param      from         Path name in my_charset_filename
                            Null terminated in my_charset_filename, normalized
                            to use '/' as directory separation character.
   @param      to           Explained name in system_charset_info
   @param      to_length    Size of to buffer
   @param      explain_mode Requested output format.
                            EXPLAIN_ALL_VERBOSE ->
                            [Database `db`, ]Table `tbl`[,[ Temporary| Renamed]
                            Partition `p` [, Subpartition `sp`]]
                            EXPLAIN_PARTITIONS_VERBOSE -> `db`.`tbl`
                            [[ Temporary| Renamed] Partition `p`
                            [, Subpartition `sp`]]
                            EXPLAIN_PARTITIONS_AS_COMMENT -> `db`.`tbl` |*
                            [,[ Temporary| Renamed] Partition `p`
                            [, Subpartition `sp`]] *|
                            (| is really a /, and it is all in one line)

   @retval     Length of returned string
*/

uint explain_filename(const char *from,
                      char *to,
                      uint to_length,
                      enum_explain_filename_mode explain_mode)
{
  uint res= 0;
  char *to_p= to;
  char *end_p= to_p + to_length;
  const char *db_name= NULL;
  int  db_name_len= 0;
  const char *table_name;
  int  table_name_len= 0;
  const char *part_name= NULL;
  int  part_name_len= 0;
  const char *subpart_name= NULL;
  int  subpart_name_len= 0;
  enum enum_file_name_type {NORMAL, TEMP, RENAMED} name_type= NORMAL;
  const char *tmp_p;
  DBUG_ENTER("explain_filename");
  DBUG_PRINT("enter", ("from '%s'", from));
  tmp_p= from;
  table_name= from;
  /*
    If '/' then take last directory part as database.
    '/' is the directory separator, not FN_LIB_CHAR
  */
  while ((tmp_p= strchr(tmp_p, '/')))
  {
    db_name= table_name;
    /* calculate the length */
    db_name_len= tmp_p - db_name;
    tmp_p++;
    table_name= tmp_p;
  }
  tmp_p= table_name;
  while (!res && (tmp_p= strchr(tmp_p, '#')))
  {
    tmp_p++;
    switch (tmp_p[0]) {
    case 'P':
    case 'p':
      if (tmp_p[1] == '#')
        part_name= tmp_p + 2;
      else
        res= 1;
      tmp_p+= 2;
      break;
    case 'S':
    case 's':
      if ((tmp_p[1] == 'P' || tmp_p[1] == 'p') && tmp_p[2] == '#')
      {
        part_name_len= tmp_p - part_name - 1;
        subpart_name= tmp_p + 3;
      }
      else
        res= 2;
      tmp_p+= 3;
      break;
    case 'T':
    case 't':
      if ((tmp_p[1] == 'M' || tmp_p[1] == 'm') &&
          (tmp_p[2] == 'P' || tmp_p[2] == 'p') &&
          tmp_p[3] == '#' && !tmp_p[4])
        name_type= TEMP;
      else
        res= 3;
      tmp_p+= 4;
      break;
    case 'R':
    case 'r':
      if ((tmp_p[1] == 'E' || tmp_p[1] == 'e') &&
          (tmp_p[2] == 'N' || tmp_p[2] == 'n') &&
          tmp_p[3] == '#' && !tmp_p[4])
        name_type= RENAMED;
      else
        res= 4;
      tmp_p+= 4;
      break;
    default:
      res= 5;
    }
  }
  if (res)
  {
    /* Better to give something back if we fail parsing, than nothing at all */
    DBUG_PRINT("info", ("Error in explain_filename: %u", res));
    sql_print_warning("Invalid (old?) table or database name '%s'", from);
    DBUG_RETURN(my_snprintf(to, to_length,
                            "<result %u when explaining filename '%s'>",
                            res, from));
  }
  if (part_name)
  {
    table_name_len= part_name - table_name - 3;
    if (subpart_name)
      subpart_name_len= strlen(subpart_name);
    else
      part_name_len= strlen(part_name);
    if (name_type != NORMAL)
    {
      if (subpart_name)
        subpart_name_len-= 5;
      else
        part_name_len-= 5;
    }
  }
  if (db_name)
  {
    if (explain_mode == EXPLAIN_ALL_VERBOSE)
    {
      to_p= add_identifier(to_p, end_p, db_name, db_name_len,
                           ER_DATABASE_NAME);
      to_p= strnmov(to_p, ", ", end_p - to_p);
    }
    else
    {
      to_p= add_identifier(to_p, end_p, db_name, db_name_len, 0);
      to_p= strnmov(to_p, ".", end_p - to_p);
    }
  }
  if (explain_mode == EXPLAIN_ALL_VERBOSE)
    to_p= add_identifier(to_p, end_p, table_name, table_name_len,
                         ER_TABLE_NAME);
  else
    to_p= add_identifier(to_p, end_p, table_name, table_name_len, 0);
  if (part_name)
  {
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
      to_p= strnmov(to_p, " /* ", end_p - to_p);
    else if (explain_mode == EXPLAIN_PARTITIONS_VERBOSE)
      to_p= strnmov(to_p, " ", end_p - to_p);
    else
      to_p= strnmov(to_p, ", ", end_p - to_p);
    if (name_type != NORMAL)
    {
      if (name_type == TEMP)
        to_p= strnmov(to_p, ER(ER_TEMPORARY_NAME), end_p - to_p);
      else
        to_p= strnmov(to_p, ER(ER_RENAMED_NAME), end_p - to_p);
      to_p= strnmov(to_p, " ", end_p - to_p);
    }
    to_p= add_identifier(to_p, end_p, part_name, part_name_len,
                         ER_PARTITION_NAME);
    if (subpart_name)
    {
      to_p= strnmov(to_p, ", ", end_p - to_p);
      to_p= add_identifier(to_p, end_p, subpart_name, subpart_name_len,
                           ER_SUBPARTITION_NAME);
    }
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
      to_p= strnmov(to_p, " */", end_p - to_p);
  }
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(to_p - to);
}


299 300 301 302 303 304 305 306 307 308 309 310 311
/*
  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.
*/

312 313
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
314
  uint errors;
315
  size_t res;
316 317 318 319
  DBUG_ENTER("filename_to_tablename");
  DBUG_PRINT("enter", ("from '%s'", from));

  if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length))
320
  {
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    /* 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.
      */
    }
338
  }
339 340 341

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
342 343 344
}


Ramil Kalimullin's avatar
Ramil Kalimullin committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
/**
  Check if given string begins with "#mysql50#" prefix, cut it if so.
  
  @param   from          string to check and cut 
  @param   to[out]       buffer for result string
  @param   to_length     its size
  
  @retval
    0      no prefix found
  @retval
    non-0  result string length
*/

uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length)
{
  if (from[0] == '#' && 
      !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
               MYSQL50_TABLE_NAME_PREFIX_LENGTH))
    return (uint) (strmake(to, from + MYSQL50_TABLE_NAME_PREFIX_LENGTH,
                           to_length - 1) - to);
  return 0;
}


369 370 371 372 373 374 375 376 377 378 379 380 381
/*
  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.
*/

382 383
uint tablename_to_filename(const char *from, char *to, uint to_length)
{
384
  uint errors, length;
385 386 387
  DBUG_ENTER("tablename_to_filename");
  DBUG_PRINT("enter", ("from '%s'", from));

Ramil Kalimullin's avatar
Ramil Kalimullin committed
388 389
  if ((length= check_n_cut_mysql50_prefix(from, to, to_length)))
    DBUG_RETURN(length);
390 391 392 393 394 395 396 397
  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;
  }
398 399
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
400 401 402
}


403
/*
404
  Creates path to a file: mysql_data_dir/db/table.ext
405 406

  SYNOPSIS
407
   build_table_filename()
408
     buff                       Where to write result in my_charset_filename.
409
                                This may be the same as table_name.
410 411 412 413 414 415
     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.
416 417 418 419 420 421

  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".
422 423
    Unless flags indicate a temporary table name.
    'db' is always converted.
424
    'ext' is not converted.
425

426 427 428 429 430
    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.
431

432 433
  RETURN
    path length
434 435 436
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
437
                          const char *table_name, const char *ext, uint flags)
438 439 440
{
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
441
  DBUG_ENTER("build_table_filename");
442 443
  DBUG_PRINT("enter", ("db: '%s'  table_name: '%s'  ext: '%s'  flags: %x",
                       db, table_name, ext, flags));
444 445 446 447 448 449

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

450
  VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
451 452 453 454

  char *end = buff + bufflen;
  /* Don't add FN_ROOTDIR if mysql_data_home already includes it */
  char *pos = strnmov(buff, mysql_data_home, bufflen);
455
  size_t rootdir_len= strlen(FN_ROOTDIR);
456 457 458
  if (pos - rootdir_len >= buff &&
      memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0)
    pos= strnmov(pos, FN_ROOTDIR, end - pos);
459 460 461 462 463 464
  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);
465

466
  DBUG_PRINT("exit", ("buff: '%s'", buff));
467
  DBUG_RETURN(pos - buff);
468 469 470
}


471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
/*
  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
*/

489
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
490
{
491 492
  DBUG_ENTER("build_tmptable_filename");

493 494 495 496
  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);
497

498 499 500 501 502
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }
503

504
  size_t length= unpack_filename(buff, buff);
505 506
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
507 508
}

509 510 511
/*
--------------------------------------------------------------------------

512
   MODULE: DDL log
513 514 515 516 517 518 519 520
   -----------------

   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
521
   ddl log while we are executing. These entries are dropped when the
522 523 524 525
   operation is completed.

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

526
   There is only one ddl log in the system and it is protected by a mutex
527 528 529
   and there is a global struct that contains information about its current
   state.

530 531
   History:
   First version written in 2006 by Mikael Ronstrom
532 533 534 535
--------------------------------------------------------------------------
*/


536
struct st_global_ddl_log
537
{
538 539 540 541 542 543
  /*
    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.
  */
544
  char file_entry_buf[4*IO_SIZE];
545 546
  char file_name_str[FN_REFLEN];
  char *file_name;
547 548 549
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
550 551
  File file_id;
  uint name_len;
552
  uint io_size;
553
  bool inited;
554
  bool do_release;
555
  bool recovery_phase;
556 557
  st_global_ddl_log() : inited(false), do_release(false) {}
};
558

559
st_global_ddl_log global_ddl_log;
560

561
pthread_mutex_t LOCK_gdl;
562

563 564 565 566 567
#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
568

569 570
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
571
#define DDL_LOG_IO_SIZE_POS 8
572

573
/*
574
  Read one entry from ddl log file
575
  SYNOPSIS
576
    read_ddl_log_file_entry()
577
    entry_no                     Entry number to read
578
  RETURN VALUES
579 580
    TRUE                         Error
    FALSE                        Success
581 582
*/

583
static bool read_ddl_log_file_entry(uint entry_no)
584 585
{
  bool error= FALSE;
586
  File file_id= global_ddl_log.file_id;
587
  uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
588 589
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
590

591
  if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
592
               MYF(MY_WME)) != io_size)
593 594 595 596 597 598
    error= TRUE;
  DBUG_RETURN(error);
}


/*
599
  Write one entry from ddl log file
600
  SYNOPSIS
601
    write_ddl_log_file_entry()
602 603 604 605 606 607
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

608
static bool write_ddl_log_file_entry(uint entry_no)
609 610
{
  bool error= FALSE;
611 612 613
  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");
614

615
  if (my_pwrite(file_id, (uchar*)file_entry_buf,
616
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
617 618 619 620 621 622
    error= TRUE;
  DBUG_RETURN(error);
}


/*
623
  Write ddl log header
624
  SYNOPSIS
625
    write_ddl_log_header()
626 627 628 629 630
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

631
static bool write_ddl_log_header()
632 633
{
  uint16 const_var;
634
  bool error= FALSE;
635
  DBUG_ENTER("write_ddl_log_header");
636

637 638
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
639
  const_var= FN_LEN;
640
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
641
            (ulong) const_var);
642
  const_var= IO_SIZE;
643
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
644
            (ulong) const_var);
645
  if (write_ddl_log_file_entry(0UL))
646 647 648 649 650
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
651
  DBUG_RETURN(error);
652 653 654
}


655
/*
656
  Create ddl log file name
657
  SYNOPSIS
658
    create_ddl_log_file_name()
659 660 661 662 663
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

664
static inline void create_ddl_log_file_name(char *file_name)
665
{
666
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
667 668 669
}


670
/*
671
  Read header of ddl log file
672
  SYNOPSIS
673
    read_ddl_log_header()
674
  RETURN VALUES
675 676
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
677
  DESCRIPTION
678 679 680
    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.
681 682
*/

683
static uint read_ddl_log_header()
684
{
685
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
686
  char file_name[FN_REFLEN];
687
  uint entry_no;
688
  bool successful_open= FALSE;
689
  DBUG_ENTER("read_ddl_log_header");
690

691
  create_ddl_log_file_name(file_name);
692
  if ((global_ddl_log.file_id= my_open(file_name,
693
                                        O_RDWR | O_BINARY, MYF(0))) >= 0)
694
  {
695
    if (read_ddl_log_file_entry(0UL))
696
    {
697 698
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
699
    }
700 701
    else
      successful_open= TRUE;
702
  }
703 704
  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]);
705 706
  if (successful_open)
  {
707
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
708 709 710
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
711
  else
712 713 714
  {
    entry_no= 0;
  }
715 716 717 718
  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));
719
  global_ddl_log.do_release= true;
720
  DBUG_RETURN(entry_no);
721 722 723 724
}


/*
725
  Read a ddl log entry
726
  SYNOPSIS
727
    read_ddl_log_entry()
728 729 730 731 732 733
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
734
    Read a specified entry in the ddl log
735 736
*/

737
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
738
{
739
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
740
  uint inx;
741
  uchar single_char;
742
  DBUG_ENTER("read_ddl_log_entry");
743

744
  if (read_ddl_log_file_entry(read_entry))
745 746 747
  {
    DBUG_RETURN(TRUE);
  }
748
  ddl_log_entry->entry_pos= read_entry;
749 750 751 752
  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;
753 754 755 756 757 758 759
  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];
760 761 762 763 764
  DBUG_RETURN(FALSE);
}


/*
765
  Initialise ddl log
766
  SYNOPSIS
767
    init_ddl_log()
768

769
  DESCRIPTION
770
    Write the header of the ddl log file and length of names. Also set
771
    number of entries to zero.
772 773 774 775

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
776 777
*/

778
static bool init_ddl_log()
779
{
780
  char file_name[FN_REFLEN];
781
  DBUG_ENTER("init_ddl_log");
782

783
  if (global_ddl_log.inited)
784 785
    goto end;

786
  global_ddl_log.io_size= IO_SIZE;
787
  create_ddl_log_file_name(file_name);
788 789 790 791
  if ((global_ddl_log.file_id= my_create(file_name,
                                         CREATE_MODE,
                                         O_RDWR | O_TRUNC | O_BINARY,
                                         MYF(MY_WME))) < 0)
792
  {
793
    /* Couldn't create ddl log file, this is serious error */
794 795
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
796
  }
797
  global_ddl_log.inited= TRUE;
798
  if (write_ddl_log_header())
799
  {
800
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
801
    global_ddl_log.inited= FALSE;
802
    DBUG_RETURN(TRUE);
803
  }
804 805

end:
806
  DBUG_RETURN(FALSE);
807 808 809 810
}


/*
811
  Execute one action in a ddl log entry
812
  SYNOPSIS
813 814
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
815
  RETURN VALUES
816 817
    TRUE                       Error
    FALSE                      Success
818 819
*/

820
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
821
{
822 823
  bool frm_action= FALSE;
  LEX_STRING handler_name;
824
  handler *file= NULL;
825
  MEM_ROOT mem_root;
826
  int error= TRUE;
827
  char to_path[FN_REFLEN];
828
  char from_path[FN_REFLEN];
829
#ifdef WITH_PARTITION_STORAGE_ENGINE
830
  char *par_ext= (char*)".par";
831
#endif
832
  handlerton *hton;
833
  DBUG_ENTER("execute_ddl_log_action");
834

835
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
836 837 838
  {
    DBUG_RETURN(FALSE);
  }
839 840
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
841
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
842
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
843 844 845
    frm_action= TRUE;
  else
  {
antony@ppcg5.local's avatar
antony@ppcg5.local committed
846 847
    plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
    if (!plugin)
848 849 850 851
    {
      my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
      goto error;
    }
antony@ppcg5.local's avatar
antony@ppcg5.local committed
852 853
    hton= plugin_data(plugin, handlerton*);
    file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
854
    if (!file)
855 856
    {
      mem_alloc_error(sizeof(handler));
857
      goto error;
858
    }
859
  }
860
  switch (ddl_log_entry->action_type)
861
  {
862
    case DDL_LOG_REPLACE_ACTION:
863
    case DDL_LOG_DELETE_ACTION:
864
    {
865
      if (ddl_log_entry->phase == 0)
866 867 868
      {
        if (frm_action)
        {
869 870 871
          strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
          if ((error= my_delete(to_path, MYF(MY_WME))))
          {
872
            if (my_errno != ENOENT)
873 874
              break;
          }
875
#ifdef WITH_PARTITION_STORAGE_ENGINE
876 877
          strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
          VOID(my_delete(to_path, MYF(MY_WME)));
878
#endif
879 880 881
        }
        else
        {
882
          if ((error= file->ha_delete_table(ddl_log_entry->name)))
883 884 885 886
          {
            if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
              break;
          }
887
        }
888
        if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
889 890 891
          break;
        VOID(sync_ddl_log());
        error= FALSE;
892 893
        if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
          break;
894
      }
895 896 897 898 899 900
      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.
      */
901
    }
902
    case DDL_LOG_RENAME_ACTION:
903
    {
904 905 906
      error= TRUE;
      if (frm_action)
      {
907
        strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
908
        strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
909
        if (my_rename(from_path, to_path, MYF(MY_WME)))
910
          break;
911
#ifdef WITH_PARTITION_STORAGE_ENGINE
912
        strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
913
        strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
914
        VOID(my_rename(from_path, to_path, MYF(MY_WME)));
915
#endif
916 917 918
      }
      else
      {
919 920
        if (file->ha_rename_table(ddl_log_entry->from_name,
                                  ddl_log_entry->name))
921
          break;
922 923
      }
      if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
924 925 926
        break;
      VOID(sync_ddl_log());
      error= FALSE;
927
      break;
928
    }
929 930 931 932 933 934 935 936
    default:
      DBUG_ASSERT(0);
      break;
  }
  delete file;
error:
  free_root(&mem_root, MYF(0)); 
  DBUG_RETURN(error);
937 938 939
}


940
/*
941
  Get a free entry in the ddl log
942
  SYNOPSIS
943 944
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
945
  RETURN VALUES
946 947
    TRUE                       Error
    FALSE                      Success
948 949
*/

950 951
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
952
{
953 954 955
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
956

957
  if (global_ddl_log.first_free == NULL)
958
  {
959 960
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
961
    {
962
      sql_print_error("Failed to allocate memory for ddl log free list");
963 964
      DBUG_RETURN(TRUE);
    }
965
    global_ddl_log.num_entries++;
966
    used_entry->entry_pos= global_ddl_log.num_entries;
967
    *write_header= TRUE;
968 969 970
  }
  else
  {
971 972
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
973
    *write_header= FALSE;
974 975 976 977 978 979
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
980
  global_ddl_log.first_used= used_entry;
981 982 983 984
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
985
  DBUG_RETURN(FALSE);
986 987 988 989
}


/*
990
  External interface methods for the DDL log Module
991 992 993 994
  ---------------------------------------------------
*/

/*
995
  SYNOPSIS
996 997 998
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
999

1000
  RETURN VALUES
1001 1002 1003 1004
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
1005
    A careful write of the ddl log is performed to ensure that we can
1006
    handle crashes occurring during CREATE and ALTER TABLE processing.
1007 1008
*/

1009 1010
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
1011
{
1012
  bool error, write_header;
1013 1014 1015 1016 1017 1018
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
1019 1020
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
1021
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
1022
                                    (char)ddl_log_entry->action_type;
1023 1024 1025 1026
  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);
1027 1028
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
1029 1030
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
1031
  {
1032
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
1033 1034
    strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
          ddl_log_entry->from_name, FN_LEN - 1);
1035
  }
1036
  else
1037 1038
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
1039 1040
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
1041
  if (get_free_ddl_log_entry(active_entry, &write_header))
1042 1043 1044 1045
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
1046
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
1047
  {
1048
    error= TRUE;
1049 1050 1051
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
1052 1053
  if (write_header && !error)
  {
1054 1055
    VOID(sync_ddl_log());
    if (write_ddl_log_header())
1056 1057
      error= TRUE;
  }
1058
  if (error)
1059
    release_ddl_log_memory_entry(*active_entry);
1060
  DBUG_RETURN(error);
1061 1062 1063 1064
}


/*
1065
  Write final entry in the ddl log
1066
  SYNOPSIS
1067
    write_execute_ddl_log_entry()
1068 1069 1070 1071
    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.
1072 1073 1074
    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
1075 1076 1077
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
1078
  RETURN VALUES
1079 1080 1081 1082
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
1083
    This is the last write in the ddl log. The previous log entries have
1084
    already been written but not yet synched to disk.
1085 1086 1087 1088
    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 
1089
*/ 
1090

1091 1092 1093
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
1094
{
1095
  bool write_header= FALSE;
1096 1097
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
1098

1099 1100 1101 1102
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
1103 1104
  if (!complete)
  {
1105 1106 1107 1108 1109 1110
    /*
      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.
    */
1111
    VOID(sync_ddl_log());
1112
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
1113 1114
  }
  else
1115
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
1116 1117 1118 1119 1120 1121
  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;
1122
  if (!(*active_entry))
1123
  {
1124
    if (get_free_ddl_log_entry(active_entry, &write_header))
1125 1126 1127
    {
      DBUG_RETURN(TRUE);
    }
1128
  }
1129
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
1130
  {
1131
    sql_print_error("Error writing execute entry in ddl log");
1132
    release_ddl_log_memory_entry(*active_entry);
1133 1134
    DBUG_RETURN(TRUE);
  }
1135
  VOID(sync_ddl_log());
1136 1137
  if (write_header)
  {
1138
    if (write_ddl_log_header())
1139
    {
1140
      release_ddl_log_memory_entry(*active_entry);
1141 1142 1143
      DBUG_RETURN(TRUE);
    }
  }
1144 1145 1146 1147
  DBUG_RETURN(FALSE);
}


1148
/*
1149
  For complex rename operations we need to deactivate individual entries.
1150
  SYNOPSIS
1151
    deactivate_ddl_log_entry()
1152 1153 1154 1155 1156 1157 1158 1159
    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
1160
    do in a safe manner unless the ddl log is informed of the phases in
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
    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.
*/

1172
bool deactivate_ddl_log_entry(uint entry_no)
1173
{
1174 1175
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
1176

1177
  if (!read_ddl_log_file_entry(entry_no))
1178
  {
1179
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
1180
    {
1181 1182 1183 1184 1185 1186
      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)
1187
      {
1188 1189
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
1190 1191 1192 1193 1194
      }
      else
      {
        DBUG_ASSERT(0);
      }
1195 1196 1197 1198 1199 1200
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
1201 1202
    }
  }
1203 1204 1205 1206 1207 1208
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
1209 1210 1211 1212
}


/*
1213
  Sync ddl log file
1214
  SYNOPSIS
1215
    sync_ddl_log()
1216 1217 1218 1219 1220
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

1221
bool sync_ddl_log()
1222 1223
{
  bool error= FALSE;
1224
  DBUG_ENTER("sync_ddl_log");
1225

1226 1227
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
1228 1229 1230 1231
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
1232 1233
  {
    /* Write to error log */
1234
    sql_print_error("Failed to sync ddl log");
1235 1236 1237 1238 1239 1240
    error= TRUE;
  }
  DBUG_RETURN(error);
}


1241 1242 1243
/*
  Release a log memory entry
  SYNOPSIS
1244
    release_ddl_log_memory_entry()
1245 1246 1247 1248 1249
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

1250
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
1251
{
1252 1253 1254 1255
  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");
1256

1257
  global_ddl_log.first_free= log_entry;
1258 1259 1260 1261 1262
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1263
    global_ddl_log.first_used= next_log_entry;
1264 1265
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1266
  DBUG_VOID_RETURN;
1267 1268 1269
}


1270
/*
1271
  Execute one entry in the ddl log. Executing an entry means executing
1272 1273
  a linked list of actions.
  SYNOPSIS
1274
    execute_ddl_log_entry()
1275 1276 1277 1278 1279 1280
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1281
bool execute_ddl_log_entry(THD *thd, uint first_entry)
1282
{
1283
  DDL_LOG_ENTRY ddl_log_entry;
1284
  uint read_entry= first_entry;
1285
  DBUG_ENTER("execute_ddl_log_entry");
1286

1287
  pthread_mutex_lock(&LOCK_gdl);
1288 1289
  do
  {
1290
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1291 1292
    {
      /* Write to error log and continue with next log entry */
1293 1294
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1295 1296
      break;
    }
1297 1298
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1299

1300
    if (execute_ddl_log_action(thd, &ddl_log_entry))
1301
    {
1302
      /* Write to error log and continue with next log entry */
1303 1304
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1305
      break;
1306
    }
1307
    read_entry= ddl_log_entry.next_entry;
1308
  } while (read_entry);
1309
  pthread_mutex_unlock(&LOCK_gdl);
1310 1311 1312
  DBUG_RETURN(FALSE);
}

1313

1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
/*
  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;
}


1334
/*
1335
  Execute the ddl log at recovery of MySQL Server
1336
  SYNOPSIS
1337
    execute_ddl_log_recovery()
1338 1339 1340 1341
  RETURN VALUES
    NONE
*/

1342
void execute_ddl_log_recovery()
1343
{
1344
  uint num_entries, i;
1345
  THD *thd;
1346
  DDL_LOG_ENTRY ddl_log_entry;
1347
  char file_name[FN_REFLEN];
1348
  DBUG_ENTER("execute_ddl_log_recovery");
1349

1350 1351 1352 1353 1354 1355 1356
  /*
    Initialise global_ddl_log struct
  */
  bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
  global_ddl_log.inited= FALSE;
  global_ddl_log.recovery_phase= TRUE;
  global_ddl_log.io_size= IO_SIZE;
monty@mysql.com's avatar
monty@mysql.com committed
1357
  global_ddl_log.file_id= (File) -1;
1358

1359 1360 1361 1362 1363 1364 1365 1366
  /*
    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();

1367
  num_entries= read_ddl_log_header();
1368
  for (i= 1; i < num_entries + 1; i++)
1369
  {
1370
    if (read_ddl_log_entry(i, &ddl_log_entry))
1371
    {
1372 1373 1374
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1375
    }
1376
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1377
    {
1378
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1379
      {
1380 1381
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
1382 1383 1384
      }
    }
  }
1385
  close_ddl_log();
1386 1387 1388 1389 1390 1391
  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);
1392
  DBUG_VOID_RETURN;
1393 1394 1395 1396
}


/*
1397
  Release all memory allocated to the ddl log
1398
  SYNOPSIS
1399
    release_ddl_log()
1400 1401 1402 1403
  RETURN VALUES
    NONE
*/

1404
void release_ddl_log()
1405
{
1406 1407 1408
  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");
1409

1410 1411 1412
  if (!global_ddl_log.do_release)
    DBUG_VOID_RETURN;

1413
  pthread_mutex_lock(&LOCK_gdl);
1414 1415
  while (used_list)
  {
1416
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1417
    my_free(used_list, MYF(0));
1418
    used_list= tmp;
1419 1420 1421
  }
  while (free_list)
  {
1422
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1423
    my_free(free_list, MYF(0));
1424
    free_list= tmp;
1425
  }
1426
  close_ddl_log();
monty@mysql.com's avatar
monty@mysql.com committed
1427
  global_ddl_log.inited= 0;
1428
  pthread_mutex_unlock(&LOCK_gdl);
1429
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1430
  global_ddl_log.do_release= false;
1431
  DBUG_VOID_RETURN;
1432 1433 1434
}


1435 1436 1437
/*
---------------------------------------------------------------------------

1438
  END MODULE DDL log
1439 1440 1441 1442 1443
  --------------------

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

1444

1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
/**
   @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);
}


1470 1471 1472 1473 1474 1475 1476 1477
/*
  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
1478 1479 1480 1481 1482 1483
      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.
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
      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];
1506 1507
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
1508
  char frm_name[FN_REFLEN+1];
1509 1510 1511 1512
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char *part_syntax_buf;
  uint syntax_len;
#endif
1513 1514
  DBUG_ENTER("mysql_write_frm");

1515 1516 1517
  /*
    Build shadow frm file name
  */
1518
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
1519
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1520
  if (flags & WFRM_WRITE_SHADOW)
1521
  {
1522 1523 1524 1525 1526 1527 1528 1529
    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))
1530 1531 1532 1533 1534
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1535 1536
      partition_info *part_info= lpt->table->part_info;
      if (part_info)
1537
      {
1538 1539
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1540
                                                         TRUE, TRUE)))
1541 1542 1543 1544
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
1545
        part_info->part_info_len= syntax_len;
1546 1547 1548
      }
    }
#endif
1549 1550 1551 1552
    /* 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,
1553
                          lpt->alter_info->create_list, lpt->key_count,
1554
                          lpt->key_info_buffer, lpt->table->file)) ||
1555 1556 1557
        lpt->table->file->ha_create_handler_files(shadow_path, NULL,
                                                  CHF_CREATE_FLAG,
                                                  lpt->create_info))
1558 1559 1560 1561 1562
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
1563 1564 1565 1566 1567 1568 1569 1570 1571
  }
  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.
    */
1572 1573
    uchar *data;
    size_t length;
1574
    if (readfrm(shadow_path, &data, &length) ||
1575 1576
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
1577 1578
      my_free(data, MYF(MY_ALLOW_ZERO_PTR));
      my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
1579 1580 1581 1582
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1583
    error= my_delete(shadow_frm_name, MYF(MY_WME));
1584
  }
1585 1586
  if (flags & WFRM_INSTALL_SHADOW)
  {
1587 1588 1589
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1590 1591 1592
    /*
      Build frm file name
    */
1593
    build_table_filename(path, sizeof(path) - 1, lpt->db,
1594
                         lpt->table_name, "", 0);
1595 1596 1597 1598
    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.
1599 1600 1601 1602 1603 1604
      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.
1605 1606 1607
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1608
#ifdef WITH_PARTITION_STORAGE_ENGINE
1609 1610
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_DELETE_FLAG, NULL) ||
1611 1612
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1613
#endif
1614
#ifdef WITH_PARTITION_STORAGE_ENGINE
1615
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1616 1617
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_RENAME_FLAG, NULL))
1618 1619 1620
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1621 1622
    {
      error= 1;
1623
      goto err;
1624
    }
1625
#ifdef WITH_PARTITION_STORAGE_ENGINE
1626
    if (part_info && (flags & WFRM_KEEP_SHARE))
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652
    {
      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;
1653
    }
1654 1655 1656
#endif

err:
1657
    VOID(pthread_mutex_unlock(&LOCK_open));
1658
#ifdef WITH_PARTITION_STORAGE_ENGINE
1659
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1660
    part_info->frm_log_entry= NULL;
1661
    VOID(sync_ddl_log());
1662
#endif
1663
  }
1664

1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690
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())
  {
1691
    int errcode= 0;
1692 1693
    if (clear_error)
      thd->clear_error();
1694 1695
    else
      errcode= query_error_code(thd, TRUE);
1696
    thd->binlog_query(THD::STMT_QUERY_TYPE,
1697
                      query, query_length, FALSE, FALSE, errcode);
1698 1699 1700
  }
}

1701

1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719
/*
 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
1720 1721
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
1722 1723

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

1725 1726
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1727
{
1728
  bool error= FALSE, need_start_waiters= FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1729 1730 1731 1732
  DBUG_ENTER("mysql_rm_table");

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

1733
  if (!drop_temporary)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1734
  {
1735
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1736
    {
1737
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1738
      DBUG_RETURN(TRUE);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1739
    }
1740 1741
    else
      need_start_waiters= TRUE;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1742
  }
1743 1744 1745 1746 1747 1748

  /*
    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.
  */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1749
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1750

1751 1752 1753
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1754
  if (error)
1755
    DBUG_RETURN(TRUE);
1756
  my_ok(thd);
1757
  DBUG_RETURN(FALSE);
1758 1759
}

1760
/*
1761 1762 1763 1764 1765 1766 1767 1768 1769
  Execute the drop of a normal or temporary table

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

1774 1775 1776 1777 1778 1779 1780 1781 1782
  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.
1783 1784 1785 1786 1787

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1788
*/
1789 1790

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1791 1792
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1793 1794
{
  TABLE_LIST *table;
1795
  char path[FN_REFLEN + 1], *alias;
1796
  uint path_length;
1797
  String wrong_tables;
1798
  int error= 0;
1799
  int non_temp_tables_count= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1800
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1801
  String built_query;
1802 1803
  DBUG_ENTER("mysql_rm_table_part2");

1804 1805 1806
  LINT_INIT(alias);
  LINT_INIT(path_length);

1807
  if (thd->is_current_stmt_binlog_format_row() && !dont_log_query)
1808 1809 1810 1811 1812 1813 1814
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1815

1816
  mysql_ha_rm_tables(thd, tables, FALSE);
1817

1818 1819
  pthread_mutex_lock(&LOCK_open);

1820 1821 1822 1823 1824 1825 1826 1827 1828
  /*
    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;
1829
    table->db_type= NULL;
1830
    if ((share= get_cached_table_share(table->db, table->table_name)))
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1831
      table->db_type= share->db_type();
1832 1833

    /* Disable drop of enabled log tables */
1834
    if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) &&
1835 1836
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1837
    {
1838
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1839
      pthread_mutex_unlock(&LOCK_open);
1840 1841
      DBUG_RETURN(1);
    }
1842 1843
  }

1844 1845
  if (!drop_temporary && lock_table_names_exclusively(thd, tables))
  {
1846
    pthread_mutex_unlock(&LOCK_open);
1847
    DBUG_RETURN(1);
1848
  }
1849

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1853
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1854
  {
1855
    char *db=table->db;
1856 1857
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1858

1859 1860 1861
    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));
1862 1863 1864 1865 1866 1867 1868 1869 1870

    error= drop_temporary_table(thd, table);

    switch (error) {
    case  0:
      // removed temporary table
      tmp_table_deleted= 1;
      continue;
    case -1:
1871
      DBUG_ASSERT(thd->in_sub_stmt);
1872 1873 1874 1875 1876
      error= 1;
      goto err_with_placeholders;
    default:
      // temporary table not found
      error= 0;
1877
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1878

1879 1880 1881 1882 1883 1884
    /*
      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.
      */
1885
    if (thd->is_current_stmt_binlog_format_row() && !dont_log_query)
1886
    {
1887
      non_temp_tables_count++;
1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902
      /*
        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("`,");
    }

1903
    table_type= table->db_type;
1904
    if (!drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1905
    {
1906
      TABLE *locked_table;
1907 1908 1909 1910
      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);
1911 1912 1913 1914 1915 1916 1917
      /*
        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;

1918
      if (thd->killed)
1919
      {
1920 1921
        error= -1;
        goto err_with_placeholders;
1922
      }
1923
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
1924
      /* remove .frm file and engine files */
1925 1926
      path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
                                        reg_ext,
1927 1928
                                        table->internal_tmp_table ?
                                        FN_IS_TMP : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1929
    }
1930
    if (drop_temporary ||
Staale Smedseng's avatar
Staale Smedseng committed
1931 1932
        ((table_type == NULL &&        
         access(path, F_OK) &&
1933 1934
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
1935
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1936
    {
1937
      // Table was not found on disk and table can't be created from engine
1938
      if (if_exists)
1939 1940
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
1941
			    table->table_name);
1942
      else
1943
        error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1944 1945 1946
    }
    else
    {
1947
      char *end;
1948 1949 1950 1951 1952
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
1953 1954
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
1955
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
1956
                             !dont_log_query);
1957
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
1958
	  (if_exists || table_type == NULL))
1959
      {
1960
	error= 0;
1961 1962
        thd->clear_error();
      }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1963
      if (error == HA_ERR_ROW_IS_REFERENCED)
monty@mysql.com's avatar
monty@mysql.com committed
1964 1965
      {
	/* the table is referenced by a foreign key constraint */
1966
	foreign_key_error=1;
monty@mysql.com's avatar
monty@mysql.com committed
1967
      }
1968
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
1969
      {
1970
        int new_error;
1971 1972
	/* Delete the table definition file */
	strmov(end,reg_ext);
1973
	if (!(new_error=my_delete(path,MYF(MY_WME))))
1974
        {
1975
	  some_tables_deleted=1;
1976 1977
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
1978
        }
1979
        error|= new_error;
1980
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1981 1982 1983 1984 1985
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
1986
      wrong_tables.append(String(table->table_name,system_charset_info));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1987
    }
1988 1989
    DBUG_PRINT("table", ("table: 0x%lx  s: 0x%lx", (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1990
  }
1991 1992 1993 1994 1995
  /*
    It's safe to unlock LOCK_open: we have an exclusive lock
    on the table name.
  */
  pthread_mutex_unlock(&LOCK_open);
1996
  thd->thread_specific_used|= tmp_table_deleted;
1997 1998 1999 2000
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
2001
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
2002
                      wrong_tables.c_ptr());
2003
    else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2004
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
2005 2006 2007 2008
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2009
  {
2010
    query_cache_invalidate3(thd, tables, 0);
2011
    if (!dont_log_query)
2012
    {
2013
      if (!thd->is_current_stmt_binlog_format_row() ||
Staale Smedseng's avatar
Staale Smedseng committed
2014
          (non_temp_tables_count > 0 && !tmp_table_deleted))
2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
      {
        /*
          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);
      }
2025
      else if (thd->is_current_stmt_binlog_format_row() &&
2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
               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.
      */
2053
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2054
  }
2055 2056
  pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
2057
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
2058
  pthread_mutex_unlock(&LOCK_open);
2059
  thd->no_warnings_for_error= 0;
2060
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2061 2062 2063
}


2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078
/*
  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
*/

2079
bool quick_rm_table(handlerton *base,const char *db,
2080
                    const char *table_name, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2081
{
2082
  char path[FN_REFLEN + 1];
2083 2084 2085
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

2086
  uint path_length= build_table_filename(path, sizeof(path) - 1,
2087
                                         db, table_name, reg_ext, flags);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2088
  if (my_delete(path,MYF(0)))
2089
    error= 1; /* purecov: inspected */
2090
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
2091 2092 2093
  if (!(flags & FRM_ONLY))
    error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2094 2095
}

2096 2097 2098
/*
  Sort keys in the following order:
  - PRIMARY KEY
2099 2100
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
  - 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)
{
2111 2112 2113
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
2114
  {
2115
    if (!(b_flags & HA_NOSAME))
2116
      return -1;
2117
    if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
2118 2119
    {
      /* Sort NOT NULL keys before other keys */
2120
      return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
2121 2122 2123 2124 2125
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
2126 2127 2128
    /* 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;
2129
  }
2130
  else if (b_flags & HA_NOSAME)
2131 2132
    return 1;					// Prefer b

2133
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
2134
  {
2135
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
2136
  }
2137
  /*
2138
    Prefer original key order.	usable_key_parts contains here
2139 2140 2141 2142 2143
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
2144 2145
}

2146 2147
/*
  Check TYPELIB (set or enum) for duplicates
2148

2149 2150 2151
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
2152 2153
    name	  name of the checked column
    typelib	  list of values for the column
2154
    dup_val_count  returns count of duplicate elements
2155 2156

  DESCRIPTION
2157
    This function prints an warning for each value in list
2158 2159 2160
    which has some duplicates on its right

  RETURN VALUES
2161 2162
    0             ok
    1             Error
2163 2164
*/

2165
bool check_duplicates_in_interval(const char *set_or_name,
2166
                                  const char *name, TYPELIB *typelib,
2167
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
2168
{
2169
  TYPELIB tmp= *typelib;
2170
  const char **cur_value= typelib->type_names;
2171
  unsigned int *cur_length= typelib->type_lengths;
2172
  *dup_val_count= 0;  
2173 2174
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
2175
  {
2176 2177 2178 2179
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
2180
    {
2181 2182 2183 2184 2185 2186 2187
      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;
      }
monty@mysql.com's avatar
monty@mysql.com committed
2188
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
2189 2190 2191
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
2192
      (*dup_val_count)++;
2193 2194
    }
  }
2195
  return 0;
2196
}
2197

2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225

/*
  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++)
  {
2226
    size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
2227 2228 2229 2230 2231 2232
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243
/*
  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
2244
    This function prepares a Create_field instance.
2245 2246 2247 2248 2249 2250 2251
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

2252
int prepare_create_field(Create_field *sql_field, 
monty@mysql.com's avatar
monty@mysql.com committed
2253 2254
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2255
			 longlong table_flags)
2256
{
2257
  unsigned int dup_val_count;
2258
  DBUG_ENTER("prepare_field");
monty@mysql.com's avatar
monty@mysql.com committed
2259 2260

  /*
2261
    This code came from mysql_prepare_create_table.
monty@mysql.com's avatar
monty@mysql.com committed
2262 2263 2264 2265 2266
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
2267 2268 2269 2270
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
monty@mysql.com's avatar
monty@mysql.com committed
2271 2272 2273 2274 2275 2276 2277
    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;
2278
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
2279
    break;
2280
  case MYSQL_TYPE_GEOMETRY:
2281
#ifdef HAVE_SPATIAL
monty@mysql.com's avatar
monty@mysql.com committed
2282 2283 2284 2285
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
2286
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
2287 2288 2289 2290 2291 2292 2293 2294
    }
    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;
2295
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
2296 2297 2298 2299 2300
    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);
2301
#endif /*HAVE_SPATIAL*/
monty@mysql.com's avatar
monty@mysql.com committed
2302
  case MYSQL_TYPE_VARCHAR:
2303
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
monty@mysql.com's avatar
monty@mysql.com committed
2304 2305 2306 2307 2308 2309 2310 2311
    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)
2312
      {
monty@mysql.com's avatar
monty@mysql.com committed
2313 2314
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
2315 2316
        DBUG_RETURN(1);
      }
monty@mysql.com's avatar
monty@mysql.com committed
2317 2318 2319
    }
#endif
    /* fall through */
2320
  case MYSQL_TYPE_STRING:
monty@mysql.com's avatar
monty@mysql.com committed
2321 2322 2323 2324
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2325
  case MYSQL_TYPE_ENUM:
monty@mysql.com's avatar
monty@mysql.com committed
2326 2327 2328 2329 2330
    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;
2331 2332 2333 2334
    if (check_duplicates_in_interval("ENUM",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
2335
    break;
2336
  case MYSQL_TYPE_SET:
monty@mysql.com's avatar
monty@mysql.com committed
2337 2338 2339 2340 2341
    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;
2342 2343 2344 2345
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2346 2347 2348 2349 2350 2351
    /* 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);
    }
monty@mysql.com's avatar
monty@mysql.com committed
2352
    break;
2353 2354 2355 2356 2357
  case MYSQL_TYPE_DATE:			// Rest of string types
  case MYSQL_TYPE_NEWDATE:
  case MYSQL_TYPE_TIME:
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_NULL:
monty@mysql.com's avatar
monty@mysql.com committed
2358 2359
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2360
  case MYSQL_TYPE_BIT:
ramil@mysql.com's avatar
ramil@mysql.com committed
2361
    /* 
2362 2363
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
ramil@mysql.com's avatar
ramil@mysql.com committed
2364
    */
monty@mysql.com's avatar
monty@mysql.com committed
2365
    break;
2366
  case MYSQL_TYPE_NEWDECIMAL:
monty@mysql.com's avatar
monty@mysql.com committed
2367 2368 2369 2370 2371 2372 2373
    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;
2374
  case MYSQL_TYPE_TIMESTAMP:
monty@mysql.com's avatar
monty@mysql.com committed
2375 2376 2377
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2378
      if (!*timestamps)
2379
      {
monty@mysql.com's avatar
monty@mysql.com committed
2380
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2381
        (*timestamps_with_niladic)++;
2382
      }
monty@mysql.com's avatar
monty@mysql.com committed
2383 2384 2385 2386
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2387
      (*timestamps_with_niladic)++;
monty@mysql.com's avatar
monty@mysql.com committed
2388

2389
    (*timestamps)++;
monty@mysql.com's avatar
monty@mysql.com committed
2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404
    /* 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;
2405 2406 2407
  DBUG_RETURN(0);
}

2408
/*
2409
  Preparation for table creation
2410 2411

  SYNOPSIS
2412
    mysql_prepare_create_table()
2413 2414
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2415
      alter_info                List of columns and indexes to create
2416 2417 2418 2419 2420 2421
      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.
2422

2423
  DESCRIPTION
2424
    Prepares the table and key structures for table creation.
2425

2426
  NOTES
2427
    sets create_info->varchar if the table has a varchar
2428

2429
  RETURN VALUES
2430 2431
    FALSE    OK
    TRUE     error
2432
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2433

2434
static int
2435 2436 2437 2438 2439 2440
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)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2441
{
2442
  const char	*key_name;
2443
  Create_field	*sql_field,*dup_field;
monty@mysql.com's avatar
monty@mysql.com committed
2444
  uint		field,null_fields,blob_columns,max_key_length;
monty@mysql.com's avatar
monty@mysql.com committed
2445
  ulong		record_offset= 0;
2446
  KEY		*key_info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2447
  KEY_PART_INFO *key_part_info;
2448 2449 2450
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
2451 2452
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
2453
  uint total_uneven_bit_length= 0;
2454
  DBUG_ENTER("mysql_prepare_create_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2455

2456
  select_field_pos= alter_info->create_list.elements - select_field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2457
  null_fields=blob_columns=0;
2458
  create_info->varchar= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2459
  max_key_length= file->max_key_length();
2460

2461
  for (field_no=0; (sql_field=it++) ; field_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2462
  {
2463 2464
    CHARSET_INFO *save_cs;

2465 2466 2467 2468 2469 2470
    /*
      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;
2471
    if (!sql_field->charset)
2472 2473 2474
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2475 2476 2477
      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.
2478
    */
2479
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2480
      sql_field->charset= create_info->table_charset;
2481

2482
    save_cs= sql_field->charset;
2483 2484 2485 2486 2487
    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];
2488 2489
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2490
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2491
      DBUG_RETURN(TRUE);
2492
    }
2493

2494
    /*
2495
      Convert the default value from client character
2496 2497 2498
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2499
        save_cs != sql_field->def->collation.collation &&
2500 2501 2502 2503
        (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))
2504
    {
2505
      /*
2506
        Starting from 5.1 we work here with a copy of Create_field
2507 2508 2509 2510 2511 2512 2513 2514
        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.
      */
2515
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2516 2517 2518 2519 2520

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

2525 2526
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2527 2528 2529
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2530
      TYPELIB *interval= sql_field->interval;
2531 2532 2533 2534 2535 2536

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2537
      if (!interval)
2538
      {
2539
        /*
2540 2541 2542
          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.
2543
        */
2544
        interval= sql_field->interval= typelib(thd->mem_root,
2545
                                               sql_field->interval_list);
2546
        List_iterator<String> int_it(sql_field->interval_list);
2547
        String conv, *tmp;
2548 2549 2550 2551 2552
        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);
2553
        for (uint i= 0; (tmp= int_it++); i++)
2554
        {
2555
          size_t lengthsp;
2556 2557 2558 2559 2560
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2561
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
2562
                                                  conv.length());
2563 2564
            interval->type_lengths[i]= conv.length();
          }
2565

2566
          // Strip trailing spaces.
2567 2568
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2569 2570
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2571
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2572 2573 2574 2575 2576
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2577
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2578
              DBUG_RETURN(TRUE);
2579 2580
            }
          }
2581
        }
2582
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2583 2584
      }

2585
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2586
      {
2587
        uint32 field_length;
2588
        if (sql_field->def != NULL)
2589 2590 2591 2592 2593
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2594 2595 2596 2597 2598
          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);
2599
              DBUG_RETURN(TRUE);
2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611
            }

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

2612 2613 2614
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2615
            DBUG_RETURN(TRUE);
2616 2617
          }
        }
2618 2619
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2620
      }
2621
      else  /* MYSQL_TYPE_ENUM */
2622
      {
2623
        uint32 field_length;
2624
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2625
        if (sql_field->def != NULL)
2626 2627
        {
          String str, *def= sql_field->def->val_str(&str);
2628
          if (def == NULL) /* SQL "NULL" maps to NULL */
2629
          {
2630 2631 2632
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2633
              DBUG_RETURN(TRUE);
2634 2635 2636 2637 2638 2639 2640 2641 2642 2643
            }

            /* 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);
2644
              DBUG_RETURN(TRUE);
2645
            }
2646 2647
          }
        }
2648 2649
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2650 2651 2652 2653
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2654
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2655
    { 
ramil@mysql.com's avatar
ramil@mysql.com committed
2656
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2657
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2658 2659 2660 2661 2662
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2663
    sql_field->create_length_to_internal_length();
2664
    if (prepare_blob_field(thd, sql_field))
2665
      DBUG_RETURN(TRUE);
2666

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

2670 2671
    if (check_column_name(sql_field->field_name))
    {
2672
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2673
      DBUG_RETURN(TRUE);
2674
    }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2675

2676 2677
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2678
    {
2679
      if (my_strcasecmp(system_charset_info,
2680 2681
			sql_field->field_name,
			dup_field->field_name) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2682
      {
2683 2684 2685 2686
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2687 2688
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2689
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2690
	  DBUG_RETURN(TRUE);
2691 2692 2693
	}
	else
	{
2694
	  /* Field redefined */
2695
	  sql_field->def=		dup_field->def;
2696
	  sql_field->sql_type=		dup_field->sql_type;
2697 2698 2699
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2700
	  sql_field->length=		dup_field->char_length;
2701
          sql_field->pack_length=	dup_field->pack_length;
2702
          sql_field->key_length=	dup_field->key_length;
2703
	  sql_field->decimals=		dup_field->decimals;
2704
	  sql_field->create_length_to_internal_length();
2705
	  sql_field->unireg_check=	dup_field->unireg_check;
2706 2707 2708 2709 2710 2711 2712 2713
          /* 
            We're making one field from two, the result field will have
            dup_field->flags as flags. If we've incremented null_fields
            because of sql_field->flags, decrement it back.
          */
          if (!(sql_field->flags & NOT_NULL_FLAG))
            null_fields--;
	  sql_field->flags=		dup_field->flags;
andrey@lmy004's avatar
andrey@lmy004 committed
2714
          sql_field->interval=          dup_field->interval;
2715 2716 2717
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2718
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2719 2720
      }
    }
2721 2722
    /* Don't pack rows in old tables if the user has requested this */
    if ((sql_field->flags & BLOB_FLAG) ||
Staale Smedseng's avatar
Staale Smedseng committed
2723 2724
	(sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
2725
      (*db_options)|= HA_OPTION_PACK_RECORD;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2726 2727
    it2.rewind();
  }
2728 2729 2730

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2731
  null_fields+= total_uneven_bit_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2732 2733 2734 2735

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

monty@mysql.com's avatar
monty@mysql.com committed
2738 2739
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2740
			     file->ha_table_flags()))
2741
      DBUG_RETURN(TRUE);
2742
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2743
      create_info->varchar= TRUE;
2744
    sql_field->offset= record_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2745 2746
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2747
    record_offset+= sql_field->pack_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2748
  }
2749 2750
  if (timestamps_with_niladic > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2751 2752
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2753
    DBUG_RETURN(TRUE);
2754
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2755 2756
  if (auto_increment > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2757
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2758
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2759 2760
  }
  if (auto_increment &&
2761
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2762
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2763 2764
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2765
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2766 2767
  }

2768
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2769
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2770 2771
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2772
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2773 2774 2775
  }

  /* Create keys */
2776

2777 2778
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2779
  uint key_parts=0, fk_key_count=0;
2780
  bool primary_key=0,unique_key=0;
2781
  Key *key, *key2;
2782
  uint tmp, key_number;
2783 2784
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2785

2786
  /* Calculate number of key segements */
2787
  *key_count= 0;
2788

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2789 2790
  while ((key=key_iterator++))
  {
2791 2792
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2793
    LEX_STRING key_name_str;
2794 2795 2796
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
2797
      Foreign_key *fk_key= (Foreign_key*) key;
2798 2799 2800
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2801 2802 2803
        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));
2804
	DBUG_RETURN(TRUE);
2805 2806 2807
      }
      continue;
    }
2808
    (*key_count)++;
2809
    tmp=file->max_key_parts();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2810 2811 2812
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2813
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2814
    }
2815 2816 2817 2818
    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))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2819
    {
2820
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
2821
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2822
    }
2823
    key_iterator2.rewind ();
2824
    if (key->type != Key::FOREIGN_KEY)
2825
    {
2826
      while ((key2 = key_iterator2++) != key)
2827
      {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2828
	/*
2829 2830 2831
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2832
        */
2833 2834 2835
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2836
        {
2837
          /* TODO: issue warning message */
2838 2839 2840 2841 2842 2843 2844
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2845 2846 2847
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2848 2849 2850
          }
          break;
        }
2851 2852 2853 2854 2855 2856
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2857
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2858 2859 2860
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
2861
      DBUG_RETURN(TRUE);
2862
    }
2863
  }
2864
  tmp=file->max_keys();
2865
  if (*key_count > tmp)
2866 2867
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
2868
    DBUG_RETURN(TRUE);
2869
  }
2870

2871
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
2872
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
2873
  if (!*key_info_buffer || ! key_part_info)
2874
    DBUG_RETURN(TRUE);				// Out of memory
2875

2876
  key_iterator.rewind();
2877
  key_number=0;
2878
  for (; (key=key_iterator++) ; key_number++)
2879 2880
  {
    uint key_length=0;
2881
    Key_part_spec *column;
2882

2883 2884 2885 2886 2887 2888 2889 2890 2891 2892
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

2893
    switch (key->type) {
2894
    case Key::MULTIPLE:
2895
	key_info->flags= 0;
2896
	break;
2897
    case Key::FULLTEXT:
2898
	key_info->flags= HA_FULLTEXT;
2899
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
2900
          key_info->flags|= HA_USES_PARSER;
2901 2902
        else
          key_info->parser_name= 0;
2903
	break;
2904
    case Key::SPATIAL:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2905
#ifdef HAVE_SPATIAL
2906
	key_info->flags= HA_SPATIAL;
2907
	break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2908
#else
2909 2910
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
2911
	DBUG_RETURN(TRUE);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2912
#endif
2913 2914 2915 2916
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
2917 2918
      key_info->flags = HA_NOSAME;
      break;
2919
    }
2920 2921
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
2922

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2923 2924
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
2925
    key_info->usable_key_parts= key_number;
2926
    key_info->algorithm= key->key_create_info.algorithm;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2927

2928 2929
    if (key->type == Key::FULLTEXT)
    {
2930
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
2931
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2932 2933
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
2934
	DBUG_RETURN(TRUE);
2935 2936
      }
    }
2937 2938 2939
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
2940
       actually should be replaced by special GEOM type
2941 2942 2943
       in near future when new frm file is ready
       checking for proper key parts number:
    */
2944

2945
    /* TODO: Add proper checks if handler supports key_type and algorithm */
2946
    if (key_info->flags & HA_SPATIAL)
2947
    {
2948
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
2949 2950 2951
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
2952
        DBUG_RETURN(TRUE);
2953
      }
2954 2955
      if (key_info->key_parts != 1)
      {
2956
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
2957
	DBUG_RETURN(TRUE);
2958
      }
2959
    }
2960
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
2961
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2962
#ifdef HAVE_RTREE_KEYS
2963 2964
      if ((key_info->key_parts & 1) == 1)
      {
2965
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
2966
	DBUG_RETURN(TRUE);
2967
      }
2968
      /* TODO: To be deleted */
2969
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
2970
      DBUG_RETURN(TRUE);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2971
#else
2972 2973
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
2974
      DBUG_RETURN(TRUE);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2975
#endif
2976
    }
2977

2978 2979 2980 2981 2982
    /* 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
    */
2983 2984
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2985 2986 2987 2988 2989
                           create_info->key_block_size);

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

2990
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
2991
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2992 2993
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2994
      uint length;
2995
      Key_part_spec *dup_column;
2996

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2997 2998 2999
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
3000
	     my_strcasecmp(system_charset_info,
3001 3002
			   column->field_name,
			   sql_field->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3003 3004 3005
	field++;
      if (!sql_field)
      {
3006
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
3007
	DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3008
      }
3009
      while ((dup_column= cols2++) != column)
3010 3011 3012 3013 3014 3015 3016
      {
        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);
3017
	  DBUG_RETURN(TRUE);
3018 3019 3020
	}
      }
      cols2.rewind();
3021
      if (key->type == Key::FULLTEXT)
3022
      {
3023 3024
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
3025 3026
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
3027
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
3028 3029
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
3030
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041
	    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));
3042
      }
3043
      else
3044
      {
3045 3046
	column->length*= sql_field->charset->mbmaxlen;

3047 3048 3049
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
3050
	  DBUG_RETURN(TRUE);
3051 3052
	}

3053 3054
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
3055
	{
3056
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
3057
	  {
3058
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
3059
	    DBUG_RETURN(TRUE);
3060
	  }
3061 3062
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
3063
            column->length= 25;
3064 3065
	  if (!column->length)
	  {
3066
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
3067
	    DBUG_RETURN(TRUE);
3068 3069
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
3070
#ifdef HAVE_SPATIAL
3071
	if (key->type == Key::SPATIAL)
3072
	{
3073
	  if (!column->length)
3074 3075
	  {
	    /*
3076 3077
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
3078
	    */
3079
	    column->length= 4*sizeof(double);
3080 3081
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
3082
#endif
3083 3084 3085 3086 3087 3088 3089
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
monty@mysql.com's avatar
monty@mysql.com committed
3090
            null_fields--;
3091 3092
	  }
	  else
3093 3094 3095 3096 3097
          {
            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);
3098
              DBUG_RETURN(TRUE);
3099 3100 3101 3102 3103
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
3104
              DBUG_RETURN(TRUE);
3105 3106
            }
          }
3107 3108 3109
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
3110
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
3111 3112
	    auto_increment--;			// Field is used
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3113
      }
3114

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3115 3116 3117
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
3118 3119
      length= sql_field->key_length;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3120 3121 3122 3123
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
monty@mysql.com's avatar
monty@mysql.com committed
3124
	  if ((length=column->length) > max_key_length ||
3125
	      length > file->max_key_part_length())
3126
	  {
monty@mysql.com's avatar
monty@mysql.com committed
3127
	    length=min(max_key_length, file->max_key_part_length());
3128 3129 3130 3131 3132 3133 3134 3135
	    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);
3136 3137
              /* Align key length to multibyte char boundary */
              length-= length % sql_field->charset->mbmaxlen;
3138 3139 3140 3141
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
3142
	      DBUG_RETURN(TRUE);
3143 3144
	    }
	  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3145
	}
3146
	else if (!f_is_geom(sql_field->pack_flag) &&
3147
		  (column->length > length ||
gkodinov/kgeorge@magare.gmz's avatar
gkodinov/kgeorge@magare.gmz committed
3148
                   !Field::type_can_have_key_part (sql_field->sql_type) ||
3149
		   ((f_is_packed(sql_field->pack_flag) ||
3150
		     ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
3151 3152 3153
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3154
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
3155
	  DBUG_RETURN(TRUE);
3156
	}
3157
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
3158
	  length=column->length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3159 3160 3161
      }
      else if (length == 0)
      {
3162
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
3163
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3164
      }
3165
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
3166
      {
3167
        length= file->max_key_part_length();
3168 3169 3170 3171 3172 3173 3174 3175
	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);
3176 3177
          /* Align key length to multibyte char boundary */
          length-= length % sql_field->charset->mbmaxlen;
3178 3179 3180 3181
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
3182
	  DBUG_RETURN(TRUE);
3183
	}
3184 3185
      }
      key_part_info->length=(uint16) length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3186
      /* Use packed keys for long strings on the first column */
3187
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3188
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
3189 3190
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3191 3192
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
Staale Smedseng's avatar
Staale Smedseng committed
3193
	if ((column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB)) ||
3194 3195
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3196 3197 3198
	else
	  key_info->flags|= HA_PACK_KEY;
      }
3199 3200 3201 3202
      /* 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;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3203 3204 3205 3206 3207 3208 3209
      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)
3210 3211 3212
	{
	  if (primary_key)
	  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3213 3214
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
3215
	    DBUG_RETURN(TRUE);
3216 3217 3218 3219
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
3220
	else if (!(key_name = key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3221
	  key_name=make_unique_key_name(sql_field->field_name,
3222 3223
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3224
	{
3225
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
3226
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3227 3228 3229 3230
	}
	key_info->name=(char*) key_name;
      }
    }
3231 3232
    if (!key_info->name || check_column_name(key_info->name))
    {
3233
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
3234
      DBUG_RETURN(TRUE);
3235
    }
3236 3237
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3238
    key_info->key_length=(uint16) key_length;
3239
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3240
    {
3241
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
3242
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3243
    }
3244
    key_info++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3245
  }
3246
  if (!unique_key && !primary_key &&
3247
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
3248
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3249
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
3250
    DBUG_RETURN(TRUE);
3251
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3252 3253
  if (auto_increment > 0)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3254
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3255
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3256
  }
3257
  /* Sort keys in optimized order */
3258 3259
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
monty@mysql.com's avatar
monty@mysql.com committed
3260
  create_info->null_bits= null_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3261

3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292
  /* 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);
    }
  }

3293
  DBUG_RETURN(FALSE);
3294 3295
}

3296

3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312
/*
  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)
{
3313 3314 3315 3316 3317
  /*
    If the table character set was not given explicitly,
    let's fetch the database default character set and
    apply it to the table.
  */
3318 3319 3320
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3321 3322 3323

    load_db_opt_by_name(thd, db, &db_info);

3324 3325 3326 3327 3328
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341
/*
  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
*/

3342
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
3343 3344 3345 3346 3347 3348 3349 3350 3351
{
  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];

3352 3353
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
3354 3355 3356 3357 3358
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3359
    sql_field->sql_type= MYSQL_TYPE_BLOB;
3360
    sql_field->flags|= BLOB_FLAG;
Sergei Golubchik's avatar
Sergei Golubchik committed
3361
    my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name,
3362
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
3363 3364 3365 3366
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
3367

3368 3369
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
3370 3371 3372
    if (sql_field->sql_type == FIELD_TYPE_BLOB ||
        sql_field->sql_type == FIELD_TYPE_TINY_BLOB ||
        sql_field->sql_type == FIELD_TYPE_MEDIUM_BLOB)
3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383
    {
      /* 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);
}


3384
/*
3385
  Preparation of Create_field for SP function return values.
3386 3387
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

3399
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3400
{
3401 3402
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3403 3404
  {
    uint32 field_length, dummy;
3405
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3406 3407 3408 3409 3410 3411 3412
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3413
    else /* MYSQL_TYPE_ENUM */
3414 3415 3416 3417 3418 3419 3420 3421 3422
    {
      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);
  }

3423
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3424 3425 3426 3427 3428
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
3429 3430 3431 3432
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3433 3434


3435 3436 3437 3438
/*
  Create a table

  SYNOPSIS
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3439
    mysql_create_table_no_lock()
3440 3441 3442
    thd			Thread object
    db			Database
    table_name		Table name
3443
    create_info	        Create information (like MAX_ROWS)
3444 3445
    fields		List of fields to create
    keys		List of keys to create
3446
    internal_tmp_table  Set to 1 if this is an internal temporary table
3447
			(From ALTER TABLE)
3448
    select_field_count
3449 3450

  DESCRIPTION
3451
    If one creates a temporary table, this is automatically opened
3452

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3453 3454 3455 3456 3457
    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.

3458 3459 3460 3461 3462 3463
    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
3464 3465
    FALSE OK
    TRUE  error
3466 3467
*/

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3468
bool mysql_create_table_no_lock(THD *thd,
3469
                                const char *db, const char *table_name,
3470 3471 3472 3473
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3474
{
3475
  char		path[FN_REFLEN + 1];
3476
  uint          path_length;
3477 3478 3479 3480
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
3481
  bool		error= TRUE;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3482
  DBUG_ENTER("mysql_create_table_no_lock");
3483 3484
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3485

3486

3487
  /* Check for duplicate fields and check type of table to create */
3488
  if (!alter_info->create_list.elements)
3489
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3490 3491
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
3492
    DBUG_RETURN(TRUE);
3493
  }
3494
  if (check_engine(thd, table_name, create_info))
3495
    DBUG_RETURN(TRUE);
3496
  db_options= create_info->table_options;
3497 3498 3499
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3500 3501
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3502
  {
3503
    mem_alloc_error(sizeof(handler));
3504 3505
    DBUG_RETURN(TRUE);
  }
3506
#ifdef WITH_PARTITION_STORAGE_ENGINE
3507 3508
  partition_info *part_info= thd->work_part_info;

3509 3510 3511 3512 3513 3514 3515 3516
  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.
    */
3517
    thd->work_part_info= part_info= new partition_info();
3518 3519 3520 3521 3522 3523
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
3524
    part_info->default_engine_type= create_info->db_type;
3525
    part_info->is_auto_partitioned= TRUE;
3526
  }
3527 3528 3529
  if (part_info)
  {
    /*
3530 3531 3532 3533 3534 3535 3536 3537 3538
      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.
3539
    */
3540
    List_iterator<Key> key_iterator(alter_info->key_list);
3541
    Key *key;
3542
    handlerton *part_engine_type= create_info->db_type;
3543 3544
    char *part_syntax_buf;
    uint syntax_len;
3545
    handlerton *engine_type;
3546 3547 3548 3549 3550
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
3551 3552
    while ((key= key_iterator++))
    {
3553 3554
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
3555
      {
3556
        my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
3557 3558 3559
        goto err;
      }
    }
3560
    if ((part_engine_type == partition_hton) &&
3561
        part_info->default_engine_type)
3562 3563 3564 3565 3566 3567
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
3568
      ;
3569
    }
3570 3571
    else
    {
3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583
      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);
        }
      }
3584
    }
mattiasj@witty's avatar
mattiasj@witty committed
3585 3586 3587
    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)));
3588
    if (part_info->check_partition_info(thd, &engine_type, file,
3589
                                        create_info, TRUE))
3590
      goto err;
3591
    part_info->default_engine_type= engine_type;
3592

3593 3594 3595 3596 3597 3598
    /*
      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,
3599
                                                     TRUE, TRUE)))
3600
      goto err;
3601 3602
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
3603 3604
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3605
        create_info->db_type == partition_hton)
3606 3607 3608 3609 3610
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
mattiasj@witty's avatar
mattiasj@witty committed
3611
      DBUG_PRINT("info", ("db_type: %s",
mattiasj@witty's avatar
mattiasj@witty committed
3612
                        ha_resolve_storage_engine_name(create_info->db_type)));
3613
      delete file;
3614
      create_info->db_type= partition_hton;
3615 3616 3617 3618
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3619 3620 3621 3622 3623 3624 3625 3626
      /*
        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 &&
3627 3628
          (int)part_info->no_parts !=
          file->get_default_no_partitions(create_info))
3629
      {
3630
        uint i;
3631
        List_iterator<partition_element> part_it(part_info->partitions);
3632 3633 3634 3635
        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;
3636 3637 3638 3639
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3640
               (int)part_info->no_subparts !=
3641
                 file->get_default_no_partitions(create_info))
3642
      {
3643
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3644
        part_info->no_subparts= file->get_default_no_partitions(create_info);
3645 3646 3647 3648
      }
    }
    else if (create_info->db_type != engine_type)
    {
3649 3650 3651 3652 3653 3654
      /*
        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.
      */
3655
      delete file;
3656 3657
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3658 3659 3660 3661
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3662 3663 3664
    }
  }
#endif
3665

3666
  set_table_default_charset(thd, create_info, (char*) db);
3667

3668 3669 3670 3671 3672
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3673
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3674 3675 3676 3677

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3678
    path_length= build_tmptable_filename(thd, path, sizeof(path));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3679 3680
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3681 3682
  else  
  {
3683
    path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,
3684
                                      internal_tmp_table ? FN_IS_TMP : 0);
3685
  }
3686

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3687
  /* Check if table already exists */
3688 3689
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3690
  {
3691
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3692 3693
    {
      create_info->table_existed= 1;		// Mark that table existed
3694 3695 3696
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
3697 3698
      error= 0;
      goto err;
3699
    }
monty@mysql.com's avatar
monty@mysql.com committed
3700
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3701
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3702
  }
3703

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3704
  VOID(pthread_mutex_lock(&LOCK_open));
3705
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3706 3707 3708 3709
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3710 3711
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3712
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3713
    }
3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726
    /*
      We don't assert here, but check the result, because the table could be
      in the table definition cache and in the same time the .frm could be
      missing from the disk, in case of manual intervention which deletes
      the .frm file. The user has to use FLUSH TABLES; to clear the cache.
      Then she could create the table. This case is pretty obscure and
      therefore we don't introduce a new error message only for it.
    */
    if (get_cached_table_share(db, alias))
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3727 3728
  }

3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741
  /*
    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;
3742 3743 3744
    int retcode = ha_table_exists_in_engine(thd, db, table_name);
    DBUG_PRINT("info", ("exists_in_engine: %u",retcode));
    switch (retcode)
3745
    {
3746 3747 3748 3749 3750
      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"));
3751

3752 3753 3754 3755 3756 3757 3758 3759 3760
        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;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3761 3762 3763
    }
  }

3764
  thd_proc_info(thd, "creating table");
3765
  create_info->table_existed= 0;		// Mark that table is created
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3766

3767
#ifdef HAVE_READLINK
3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785
  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 */

3786
  if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
3787
#endif /* HAVE_READLINK */
3788 3789
  {
    if (create_info->data_file_name)
3790 3791 3792
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "DATA DIRECTORY");
3793
    if (create_info->index_file_name)
3794 3795 3796
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "INDEX DIRECTORY");
3797
    create_info->data_file_name= create_info->index_file_name= 0;
3798
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3799
  create_info->table_options=db_options;
3800

3801
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3802 3803
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
3804
                       key_count, key_info_buffer, file))
3805
    goto unlock_and_end;
3806

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3807 3808 3809 3810 3811 3812
  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);
3813
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3814
    }
3815
    thd->thread_specific_used= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3816
  }
monty@mysql.com's avatar
monty@mysql.com committed
3817

3818 3819 3820 3821 3822
  /*
    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.
3823
    Otherwise, the statement shall be binlogged.
3824 3825
   */
  if (!internal_tmp_table &&
3826 3827
      (!thd->is_current_stmt_binlog_format_row() ||
       (thd->is_current_stmt_binlog_format_row() &&
3828
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3829
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
3830
  error= FALSE;
3831
unlock_and_end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3832
  VOID(pthread_mutex_unlock(&LOCK_open));
3833 3834

err:
3835
  thd_proc_info(thd, "After create");
3836
  delete file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3837
  DBUG_RETURN(error);
3838 3839

warn:
3840
  error= FALSE;
3841 3842 3843 3844
  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
3845
  goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3846 3847
}

3848 3849

/*
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3850
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
3851 3852 3853 3854
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
3855 3856 3857
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
3858
{
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3859
  TABLE *name_lock= 0;
3860 3861 3862 3863 3864 3865
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
3866
         hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879
  {
    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);

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904
  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;
    }
  }
3905

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3906
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
3907 3908 3909
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
3910

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3911 3912 3913 3914 3915 3916 3917
unlock:
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
3918 3919 3920 3921 3922 3923 3924 3925
  pthread_mutex_lock(&LOCK_lock_db);
  if (!--creating_table && creating_database)
    pthread_cond_signal(&COND_refresh);
  pthread_mutex_unlock(&LOCK_lock_db);
  DBUG_RETURN(result);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3926 3927 3928 3929 3930 3931 3932 3933
/*
** 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++)
3934
    if (!my_strcasecmp(system_charset_info,name,key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3935 3936 3937 3938 3939 3940 3941 3942 3943 3944
      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;

3945 3946
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3947
    return (char*) field_name;			// Use fieldname
3948 3949 3950 3951 3952 3953
  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
  */
3954
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3955
  {
3956 3957
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3958 3959 3960
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3961
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3962 3963
}

3964

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3965 3966 3967 3968
/****************************************************************************
** Alter a table definition
****************************************************************************/

3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982

/*
  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.
3983 3984
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
3985 3986

  RETURN
3987 3988
    FALSE   OK
    TRUE    Error
3989 3990
*/

3991
bool
3992 3993 3994
mysql_rename_table(handlerton *base, const char *old_db,
                   const char *old_name, const char *new_db,
                   const char *new_name, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3995
{
3996
  THD *thd= current_thd;
3997 3998
  char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
    lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
3999 4000
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
4001
  handler *file;
4002
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4003
  DBUG_ENTER("mysql_rename_table");
4004 4005
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
4006

4007
  file= (base == NULL ? 0 :
4008 4009
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

4010
  build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
4011
                       flags & FN_FROM_IS_TMP);
4012
  build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
4013
                       flags & FN_TO_IS_TMP);
4014 4015 4016 4017 4018 4019

  /*
    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.
   */
4020
  if (lower_case_table_names == 2 && file &&
4021
      !(file->ha_table_flags() & HA_FILE_BASED))
4022
  {
4023 4024
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
4025
    build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
4026
                         flags & FN_FROM_IS_TMP);
4027
    from_base= lc_from;
4028

4029 4030
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
4031
    build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
4032
                         flags & FN_TO_IS_TMP);
4033
    to_base= lc_to;
4034 4035
  }

4036
  if (!file || !(error=file->ha_rename_table(from_base, to_base)))
4037
  {
4038
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
4039
    {
4040
      error=my_errno;
4041
      /* Restore old file name */
4042
      if (file)
4043
        file->ha_rename_table(to_base, from_base);
4044 4045
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4046
  delete file;
4047 4048 4049
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
4050 4051
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4052 4053
}

4054

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4055
/*
4056 4057 4058 4059 4060 4061
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
4062 4063 4064
    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
4065 4066 4067 4068 4069 4070 4071
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

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

4074 4075
void wait_while_table_is_used(THD *thd, TABLE *table,
                              enum ha_extra_function function)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4076
{
4077
  DBUG_ENTER("wait_while_table_is_used");
4078
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %lu",
4079 4080
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4081

4082 4083
  safe_mutex_assert_owner(&LOCK_open);

4084
  VOID(table->file->extra(function));
4085
  /* Mark all tables that are in use as 'old' */
4086
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
4087 4088

  /* Wait until all there are no other threads that has this table open */
4089 4090 4091
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
4092 4093
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4094

4095 4096
/*
  Close a cached table
4097

4098
  SYNOPSIS
4099
    close_cached_table()
4100 4101 4102 4103 4104 4105
    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.
4106

4107 4108 4109 4110
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
4111

4112
void close_cached_table(THD *thd, TABLE *table)
4113 4114
{
  DBUG_ENTER("close_cached_table");
4115

4116
  wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
4117 4118
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
4119
  {
4120 4121
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4122
  }
4123
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4124
  unlink_open_table(thd, table, TRUE);
4125 4126

  /* When lock on LOCK_open is freed other threads can continue */
4127
  broadcast_refresh();
monty@mysql.com's avatar
monty@mysql.com committed
4128
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4129 4130
}

4131
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
4132
			     const char* operator_name, const char* errmsg)
4133

4134
{
4135 4136
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
4137 4138
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
4139
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4140
  protocol->store(errmsg, system_charset_info);
4141
  thd->clear_error();
4142
  if (protocol->write())
4143 4144 4145 4146
    return -1;
  return 1;
}

4147

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4148
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
4149
			       HA_CHECK_OPT *check_opt)
4150
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4151
  DBUG_ENTER("prepare_for_restore");
4152

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4153 4154 4155 4156 4157 4158
  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"
				  ));
  }
4159
  else
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4160
  {
4161
    char* backup_dir= thd->lex->backup_dir;
4162
    char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1], uname[FN_REFLEN];
4163 4164
    char* table_name= table->table_name;
    char* db= table->db;
4165

4166
    VOID(tablename_to_filename(table->table_name, uname, sizeof(uname) - 1));
4167 4168

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

4171
    build_table_filename(dst_path, sizeof(dst_path) - 1,
4172
                         db, table_name, reg_ext, 0);
4173

4174
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4175
      DBUG_RETURN(-1);
4176

4177
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4178
    {
4179
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4180
      unlock_table_name(thd, table);
4181
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4182 4183 4184
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
4185
    if (mysql_truncate(thd, table, 1))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4186
    {
4187
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4188
      unlock_table_name(thd, table);
4189
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4190 4191
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
4192
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4193
  }
4194

4195 4196 4197 4198
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
4199
  pthread_mutex_lock(&LOCK_open);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4200
  if (reopen_name_locked_table(thd, table, TRUE))
4201
  {
4202
    unlock_table_name(thd, table);
4203
    pthread_mutex_unlock(&LOCK_open);
4204 4205
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
4206
  }
4207 4208
  /* A MERGE table must not come here. */
  DBUG_ASSERT(!table->table || !table->table->child_l);
4209
  pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4210
  DBUG_RETURN(0);
4211
}
4212

4213

4214
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
4215
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4216
{
4217 4218
  int error= 0;
  TABLE tmp_table, *table;
4219 4220 4221 4222
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4223 4224 4225 4226
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
4227 4228

  if (!(table= table_list->table))		/* if open_ltable failed */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4229
  {
4230 4231 4232 4233 4234 4235 4236 4237 4238
    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);
4239
      DBUG_RETURN(0);				// Can't open frm file
4240 4241
    }

4242
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
4243 4244 4245 4246 4247
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
4248
    table= &tmp_table;
4249
    pthread_mutex_unlock(&LOCK_open);
4250
  }
4251 4252 4253 4254

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

4255 4256 4257 4258 4259 4260 4261 4262 4263
  /*
    REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
  */
  if (table->s->tmp_table)
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Cannot repair temporary table from .frm file");
    goto end;
  }
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4264

4265 4266 4267 4268 4269 4270 4271 4272 4273
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4274

4275 4276 4277
  if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  {
    error= send_check_errmsg(thd, table_list, "repair",
Timothy Smith's avatar
Timothy Smith committed
4278
                             "Failed repairing incompatible .frm file");
4279 4280
    goto end;
  }
4281

4282 4283
  /*
    Check if this is a table type that stores index and data separately,
4284 4285 4286
    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. 
4287
  */
4288
  ext= table->file->bas_ext();
4289 4290
  if (!ext[0] || !ext[1])
    goto end;					// No data file
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4291

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

4297 4298
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
4299

4300 4301 4302 4303 4304 4305 4306
  /* 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);
  }
4307
  if (lock_and_wait_for_table_name(thd,table_list))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4308
  {
4309 4310
    error= -1;
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4311
  }
4312
  if (my_rename(from, tmp, MYF(MY_WME)))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4313
  {
4314
    pthread_mutex_lock(&LOCK_open);
4315
    unlock_table_name(thd, table_list);
4316
    pthread_mutex_unlock(&LOCK_open);
4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4338 4339
  }

4340 4341 4342 4343
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
4344
  pthread_mutex_lock(&LOCK_open);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4345
  if (reopen_name_locked_table(thd, table_list, TRUE))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4346
  {
4347
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4348
    pthread_mutex_unlock(&LOCK_open);
4349 4350 4351
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4352
  }
4353
  pthread_mutex_unlock(&LOCK_open);
4354 4355 4356

end:
  if (table == &tmp_table)
4357 4358 4359 4360 4361
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
4362
  DBUG_RETURN(error);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4363
}
4364

4365

4366

4367 4368
/*
  RETURN VALUES
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
4369 4370 4371
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
4372
*/
4373 4374 4375 4376 4377
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,
4378
                              bool no_warnings_for_error,
4379 4380 4381
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4382 4383 4384
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4385
{
4386
  TABLE_LIST *table;
4387
  SELECT_LEX *select= &thd->lex->select_lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4388
  List<Item> field_list;
4389 4390
  Item *item;
  Protocol *protocol= thd->protocol;
4391
  LEX *lex= thd->lex;
4392
  int result_code;
4393
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4394

4395 4396
  if (end_active_trans(thd))
    DBUG_RETURN(1);
4397
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4398 4399 4400 4401 4402 4403 4404
  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;
4405 4406
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4407
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4408

4409
  mysql_ha_rm_tables(thd, tables, FALSE);
4410

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4411
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4412 4413
  {
    char table_name[NAME_LEN*2+2];
4414
    char* db = table->db;
4415
    bool fatal_error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4416

4417 4418
    DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
    DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
serg@serg.mylan's avatar
serg@serg.mylan committed
4419
    strxmov(table_name, db, ".", table->table_name, NullS);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4420
    thd->open_options|= extra_open_options;
4421 4422
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4423
    {
4424 4425 4426 4427 4428
      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;
4429
      select->table_list.first= (uchar*)table;
4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441
      /*
        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;
4442

4443 4444 4445 4446 4447
      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;
4448
#ifdef WITH_PARTITION_STORAGE_ENGINE
4449
      if (table->table)
4450 4451 4452 4453 4454 4455 4456
      {
        /*
          Set up which partitions that should be processed
          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
        */
        Alter_info *alter_info= &lex->alter_info;

4457
        if (alter_info->flags & ALTER_ADMIN_PARTITION)
4458
        {
4459 4460 4461 4462 4463
          if (!table->table->part_info)
          {
            my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
            DBUG_RETURN(TRUE);
          }
4464 4465 4466 4467 4468 4469 4470 4471
          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];
4472
            size_t length;
4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489
            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
4490
    }
4491 4492
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

4493
    if (prepare_func)
4494
    {
4495
      DBUG_PRINT("admin", ("calling prepare_func"));
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4496
      switch ((*prepare_func)(thd, table, check_opt)) {
4497
      case  1:           // error, message written to net
4498
        ha_autocommit_or_rollback(thd, 1);
4499
        end_trans(thd, ROLLBACK);
4500
        close_thread_tables(thd);
4501
        DBUG_PRINT("admin", ("simple error, admin next table"));
4502 4503
        continue;
      case -1:           // error, message could be written to net
4504 4505
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4506
        goto err;
4507
        /* purecov: end */
4508
      default:           // should be 0 otherwise
4509
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4510
        ;
4511
      }
4512
    }
4513

4514
    /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4515 4516 4517 4518 4519 4520
      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)
4521
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4522 4523
    if (!table->table)
    {
4524
      DBUG_PRINT("admin", ("open table failed"));
4525 4526 4527
      if (!thd->warn_list.elements)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4528 4529 4530
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
4531 4532
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
Staale Smedseng's avatar
Staale Smedseng committed
4533
      if (thd->main_da.is_error() && 
4534 4535
          (thd->main_da.sql_errno() == ER_NO_SUCH_TABLE ||
           thd->main_da.sql_errno() == ER_FILE_NOT_FOUND))
Staale Smedseng's avatar
Staale Smedseng committed
4536 4537 4538 4539 4540
        /* A missing table is just issued as a failed command */
        result_code= HA_ADMIN_FAILED;
      else
        /* Default failure code is corrupt table */
        result_code= HA_ADMIN_CORRUPT;
4541
      goto send_result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4542
    }
4543 4544 4545

    if (table->view)
    {
4546
      DBUG_PRINT("admin", ("calling view_operator_func"));
4547 4548 4549 4550
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4551
    if (table->schema_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4552
    {
4553 4554
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4555 4556
    }

4557
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4558
    {
4559
      /* purecov: begin inspected */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
4560
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4561
      size_t length;
4562
      DBUG_PRINT("admin", ("sending error message"));
4563
      protocol->prepare_for_resend();
4564 4565
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4566
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4567 4568 4569
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4570
      ha_autocommit_or_rollback(thd, 0);
4571
      end_trans(thd, COMMIT);
4572
      close_thread_tables(thd);
4573
      lex->reset_query_tables_list(FALSE);
4574
      table->table=0;				// For query cache
4575
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4576
	goto err;
4577
      thd->main_da.reset_diagnostics_area();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4578
      continue;
4579
      /* purecov: end */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4580 4581
    }

4582
    /* Close all instances of the table to allow repair to rename files */
4583
    if (lock_type == TL_WRITE && table->table->s->version)
4584
    {
4585
      DBUG_PRINT("admin", ("removing table from cache"));
4586
      pthread_mutex_lock(&LOCK_open);
4587 4588
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
4589
      mysql_lock_abort(thd,table->table, TRUE);
4590 4591
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
monty@mysql.com's avatar
monty@mysql.com committed
4592 4593
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4594
      thd->exit_cond(old_message);
4595
      DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
4596 4597
      if (thd->killed)
	goto err;
4598 4599 4600
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4601 4602
    }

4603
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4604
    {
4605 4606
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4607 4608 4609
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4610 4611 4612
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4613 4614
      if (protocol->write())
        goto err;
4615
      /* purecov: end */
4616 4617
    }

4618 4619
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
4620 4621 4622 4623 4624
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4625
        DBUG_PRINT("admin", ("recreating table"));
4626
        ha_autocommit_or_rollback(thd, 1);
4627 4628
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4629
        result_code= mysql_recreate_table(thd, table);
4630
        reenable_binlog(thd);
4631 4632 4633 4634 4635 4636 4637 4638
        /*
          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();
4639 4640 4641 4642
        goto send_result;
      }
    }

4643
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4644
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4645
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4646 4647 4648

send_result:

4649
    lex->cleanup_after_one_table_open();
4650
    thd->clear_error();  // these errors shouldn't get client
4651
    {
4652 4653 4654 4655 4656 4657 4658
      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);
4659 4660 4661
        protocol->store(warning_level_names[err->level].str,
                        warning_level_names[err->level].length,
                        system_charset_info);
4662 4663 4664 4665 4666
        protocol->store(err->msg, system_charset_info);
        if (protocol->write())
          goto err;
      }
      mysql_reset_errors(thd, true);
4667
    }
4668
    protocol->prepare_for_resend();
4669 4670
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4671

4672 4673 4674
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4675 4676
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4677
      {
4678
       char buf[MYSQL_ERRMSG_SIZE];
4679
       size_t length=my_snprintf(buf, sizeof(buf),
4680
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4681
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4682
	protocol->store(buf, length, system_charset_info);
4683
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4684 4685
      break;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4686 4687
    case HA_ADMIN_NOT_BASE_TABLE:
      {
4688
        char buf[MYSQL_ERRMSG_SIZE];
4689
        size_t length= my_snprintf(buf, sizeof(buf),
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4690
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4691
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4692
        protocol->store(buf, length, system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4693 4694 4695
      }
      break;

4696
    case HA_ADMIN_OK:
4697 4698
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4699 4700
      break;

4701
    case HA_ADMIN_FAILED:
4702 4703 4704
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4705 4706
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4707
    case HA_ADMIN_REJECT:
4708 4709 4710
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation need committed state"),
                      system_charset_info);
monty@mysql.com's avatar
monty@mysql.com committed
4711
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4712 4713
      break;

4714
    case HA_ADMIN_ALREADY_DONE:
4715 4716 4717
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4718 4719
      break;

4720
    case HA_ADMIN_CORRUPT:
4721 4722
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4723
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4724 4725
      break;

4726
    case HA_ADMIN_INVALID:
4727 4728 4729
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
4730 4731
      break;

4732 4733 4734 4735 4736 4737
    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.
4738
        We have to end the row, so analyze could return more rows.
4739
      */
4740 4741 4742 4743 4744 4745
      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;
4746
      ha_autocommit_or_rollback(thd, 0);
4747
      close_thread_tables(thd);
4748
      DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4749 4750 4751
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4752
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4753
      result_code= mysql_recreate_table(thd, table);
4754
      reenable_binlog(thd);
4755 4756 4757 4758 4759 4760 4761 4762
      /*
        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();
4763
      ha_autocommit_or_rollback(thd, 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4764
      close_thread_tables(thd);
4765 4766
      if (!result_code) // recreation went ok
      {
4767
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
4768
            ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
4769 4770
          result_code= 0; // analyze went ok
      }
4771 4772 4773 4774
      /* 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);
4775 4776
      if (result_code) // either mysql_recreate_table or analyze failed
      {
4777 4778
        DBUG_ASSERT(thd->is_error());
        if (thd->is_error())
4779
        {
4780
          const char *err_msg= thd->main_da.message();
4781 4782
          if (!thd->vio_ok())
          {
Staale Smedseng's avatar
Staale Smedseng committed
4783
            sql_print_error("%s", err_msg);
4784 4785 4786 4787
          }
          else
          {
            /* Hijack the row already in-progress. */
4788
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4789
            protocol->store(err_msg, system_charset_info);
4790 4791
            if (protocol->write())
              goto err;
4792 4793 4794 4795 4796
            /* 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);
          }
4797
          thd->clear_error();
4798 4799
        }
      }
4800
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4801 4802
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4803 4804
      goto send_result_message;
    }
4805 4806
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4807
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4808 4809
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4810 4811
      break;
    }
4812

4813 4814 4815
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
4816
      char buf[MYSQL_ERRMSG_SIZE];
4817
      size_t length;
4818 4819

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4820 4821
      length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
                         table->table_name);
4822 4823 4824 4825 4826
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

4827
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4828
      {
4829
        char buf[MYSQL_ERRMSG_SIZE];
4830
        size_t length=my_snprintf(buf, sizeof(buf),
4831 4832
                                "Unknown - internal error %d during operation",
                                result_code);
4833
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4834 4835 4836 4837
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4838
    }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4839
    if (table->table)
4840
    {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4841 4842
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4843
      else if (open_for_modify)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4844
      {
holyfoot@deer.(none)'s avatar
holyfoot@deer.(none) committed
4845
        if (table->table->s->tmp_table)
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4846 4847 4848 4849
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
4850 4851
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4852 4853 4854
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4855 4856
        query_cache_invalidate3(thd, table->table, 0);
      }
4857
    }
4858
    ha_autocommit_or_rollback(thd, 0);
4859
    end_trans(thd, COMMIT);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4860
    close_thread_tables(thd);
4861
    table->table=0;				// For query cache
4862
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4863 4864 4865
      goto err;
  }

4866
  my_eof(thd);
4867
  DBUG_RETURN(FALSE);
4868

4869
err:
4870
  ha_autocommit_or_rollback(thd, 1);
4871
  end_trans(thd, ROLLBACK);
4872
  close_thread_tables(thd);			// Shouldn't be needed
4873 4874
  if (table)
    table->table=0;
4875
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4876 4877
}

4878

4879
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
4880 4881
{
  DBUG_ENTER("mysql_backup_table");
4882
  WARN_DEPRECATED(thd, "6.0", "BACKUP TABLE",
4883
                  "MySQL Administrator (mysqldump, mysql)");
4884
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4885
				"backup", TL_READ, 0, 0, 0, 0,
4886
				&handler::ha_backup, 0));
4887
}
4888

4889

4890
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
4891 4892
{
  DBUG_ENTER("mysql_restore_table");
4893
  WARN_DEPRECATED(thd, "6.0", "RESTORE TABLE",
4894
                  "MySQL Administrator (mysqldump, mysql)");
4895
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4896
				"restore", TL_WRITE, 1, 1, 0,
4897
				&prepare_for_restore,
4898
				&handler::ha_restore, 0));
4899
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4900

4901

4902
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4903 4904 4905
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4906 4907 4908
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4909
				&prepare_for_repair,
4910
				&handler::ha_repair, 0));
4911 4912
}

4913

4914
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4915 4916 4917
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4918
				"optimize", TL_WRITE, 1,0,0,0,
4919
				&handler::ha_optimize, 0));
4920 4921 4922
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4923 4924 4925 4926 4927
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4928 4929
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4930 4931

  RETURN VALUES
4932 4933
   FALSE ok
   TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4934 4935
*/

4936
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4937
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4938
{
4939
  HA_CHECK_OPT check_opt;
4940
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4941
  DBUG_ENTER("mysql_assign_to_keycache");
4942 4943 4944 4945 4946 4947 4948

  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);
4949
    DBUG_RETURN(TRUE);
4950 4951 4952 4953
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4954
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4955
				0, 0, &handler::assign_to_keycache, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4956 4957
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4958 4959 4960 4961 4962 4963

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

  SYNOPSIS
    reassign_keycache_tables()
4964 4965 4966
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4967

4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980
  NOTES
    This is called when one sets a key cache size to zero, in which
    case we have to move the tables associated to this key cache to
    the "default" one.

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

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

 RETURN VALUES
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4981 4982 4983
    0	  ok
*/

4984 4985
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4986 4987 4988
{
  DBUG_ENTER("reassign_keycache_tables");

4989 4990
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
4991
  src_cache->param_buff_size= 0;		// Free key cache
4992 4993
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4994
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4995 4996 4997
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4998 4999 5000 5001 5002
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
5003 5004
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5005 5006

  RETURN VALUES
5007 5008
    FALSE ok
    TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5009 5010
*/

5011
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5012 5013
{
  DBUG_ENTER("mysql_preload_keys");
5014 5015 5016 5017 5018
  /*
    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.
  */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5019
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
5020
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
5021
				&handler::preload_keys, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5022 5023 5024
}


5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073

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


venu@myvenu.com's avatar
venu@myvenu.com committed
5074 5075 5076 5077 5078
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
5079
    thd		Thread object
5080 5081
    table       Table list element for target table
    src_table   Table list element for source table
venu@myvenu.com's avatar
venu@myvenu.com committed
5082 5083 5084
    create_info Create info

  RETURN VALUES
5085 5086
    FALSE OK
    TRUE  error
venu@myvenu.com's avatar
venu@myvenu.com committed
5087 5088
*/

5089
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
5090
                             HA_CREATE_INFO *create_info)
venu@myvenu.com's avatar
venu@myvenu.com committed
5091
{
5092
  TABLE *name_lock= 0;
5093
  char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
5094
  uint dst_path_length;
venu@myvenu.com's avatar
venu@myvenu.com committed
5095
  char *db= table->db;
5096
  char *table_name= table->table_name;
5097
  int  err;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5098
  bool res= TRUE;
5099
  uint not_used;
5100 5101 5102
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
5103
  char ts_name[FN_LEN + 1];
venu@myvenu.com's avatar
venu@myvenu.com committed
5104
  DBUG_ENTER("mysql_create_like_table");
5105

5106

5107 5108 5109 5110 5111 5112 5113 5114
  /*
    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.
5115
  */
5116
  if (open_tables(thd, &src_table, &not_used, 0))
5117 5118
    DBUG_RETURN(TRUE);

5119 5120 5121 5122 5123 5124
  /*
    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
  */
5125
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
5126 5127 5128 5129 5130
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

5131
  strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
venu@myvenu.com's avatar
venu@myvenu.com committed
5132

5133
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
venu@myvenu.com's avatar
venu@myvenu.com committed
5134

5135 5136 5137
  /*
    Check that destination tables does not exist. Note that its name
    was already checked when it was added to the table list.
venu@myvenu.com's avatar
venu@myvenu.com committed
5138 5139 5140 5141 5142
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
5143
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
venu@myvenu.com's avatar
venu@myvenu.com committed
5144 5145 5146 5147
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5148 5149 5150 5151
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
      goto err;
    if (!name_lock)
      goto table_exists;
5152
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
5153
                                          db, table_name, reg_ext, 0);
venu@myvenu.com's avatar
venu@myvenu.com committed
5154 5155 5156 5157
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

5158 5159
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

5160
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
5161
    Create a new table by copying from source table
5162 5163 5164 5165 5166 5167 5168 5169 5170 5171

    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.
5172
  */
5173
  VOID(pthread_mutex_lock(&LOCK_open));
5174 5175 5176 5177 5178 5179 5180 5181 5182
  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)))
5183 5184 5185 5186 5187
  {
    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);
5188
    VOID(pthread_mutex_unlock(&LOCK_open));
5189
    goto err;
5190
  }
venu@myvenu.com's avatar
venu@myvenu.com committed
5191 5192

  /*
5193 5194
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
venu@myvenu.com's avatar
venu@myvenu.com committed
5195 5196
    and temporary tables).
  */
5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209
#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
5210 5211 5212

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

5213
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
5214 5215
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
5216
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
5217
  VOID(pthread_mutex_unlock(&LOCK_open));
5218

venu@myvenu.com's avatar
venu@myvenu.com committed
5219 5220 5221 5222
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
5223 5224
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
5225
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
5226
    }
5227
    thd->thread_specific_used= TRUE;
venu@myvenu.com's avatar
venu@myvenu.com committed
5228 5229 5230
  }
  else if (err)
  {
5231
    (void) quick_rm_table(create_info->db_type, db,
5232
			  table_name, 0); /* purecov: inspected */
5233
    goto err;	    /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
5234
  }
5235

5236 5237
  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););

5238 5239 5240
  /*
    We have to write the query before we unlock the tables.
  */
5241
  if (thd->is_current_stmt_binlog_format_row())
5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259
  {
    /*
       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))
    {
5260
      if (src_table->table->s->tmp_table)               // Case 2
5261 5262 5263 5264
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
5265 5266

        /*
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5267 5268 5269 5270
          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.
5271
        */
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5272 5273 5274 5275 5276
        table->table= name_lock;
        VOID(pthread_mutex_lock(&LOCK_open));
        if (reopen_name_locked_table(thd, table, FALSE))
        {
          VOID(pthread_mutex_unlock(&LOCK_open));
5277
          goto err;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5278 5279
        }
        VOID(pthread_mutex_unlock(&LOCK_open));
5280

5281 5282 5283
        IF_DBUG(int result=)
          store_create_info(thd, table, &query,
                            create_info, FALSE /* show_database */);
5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294

        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
    */
  }
5295
  else
5296 5297
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

5298
  res= FALSE;
5299
  goto err;
5300

venu@myvenu.com's avatar
venu@myvenu.com committed
5301 5302 5303 5304
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
5305 5306
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
5307
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5308
		 ER_TABLE_EXISTS_ERROR,warn_buff);
5309
    res= FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
5310
  }
5311 5312 5313 5314
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5315
  if (name_lock)
5316 5317
  {
    pthread_mutex_lock(&LOCK_open);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5318
    unlink_open_table(thd, name_lock, FALSE);
5319 5320
    pthread_mutex_unlock(&LOCK_open);
  }
5321
  DBUG_RETURN(res);
venu@myvenu.com's avatar
venu@myvenu.com committed
5322 5323 5324
}


5325
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5326
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5327 5328
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5329 5330
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5331
				"analyze", lock_type, 1, 0, 0, 0,
5332
				&handler::ha_analyze, 0));
5333 5334 5335
}


5336
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
5337
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5338 5339
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5340 5341
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5342
				"check", lock_type,
5343
				0, 0, HA_OPEN_FOR_REPAIR, 0,
5344
				&handler::ha_check, &view_checksum));
5345 5346
}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5348
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
5349 5350 5351 5352
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5353 5354 5355 5356 5357 5358
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
5359 5360 5361 5362
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5363

5364
  thd_proc_info(thd, "discard_or_import_tablespace");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5365

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

monty@mysql.com's avatar
monty@mysql.com committed
5368 5369 5370 5371 5372
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
5373
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5374 5375 5376 5377
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
5378

5379
  error= table->file->ha_discard_or_import_tablespace(discard);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5380

5381
  thd_proc_info(thd, "end");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5382 5383 5384 5385

  if (error)
    goto err;

monty@mysql.com's avatar
monty@mysql.com committed
5386 5387 5388 5389
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5390 5391 5392
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
5393 5394
  error = ha_autocommit_or_rollback(thd, 0);
  if (end_active_trans(thd))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5395 5396 5397
    error=1;
  if (error)
    goto err;
5398
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
5399

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5400
err:
5401
  ha_autocommit_or_rollback(thd, error);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5402
  thd->tablespace_op=FALSE;
5403
  
monty@mysql.com's avatar
monty@mysql.com committed
5404 5405
  if (error == 0)
  {
5406
    my_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
5407
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5408
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5409

5410 5411
  table->file->print_error(error, MYF(0));
    
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5412
  DBUG_RETURN(-1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5413
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5414

5415

5416 5417
/*
  SYNOPSIS
5418 5419
    compare_tables()
      table                     The original table.
5420 5421
      alter_info                Alter options, fields and keys for the new
                                table.
5422 5423
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
5424 5425 5426 5427 5428 5429 5430 5431
      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
5432 5433 5434 5435
      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.
5436
      candidate_key_count OUT   The number of candidate keys in original table.
5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447

  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.

5448 5449 5450 5451 5452
    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.

5453
  RETURN VALUES
5454 5455
    TRUE   error
    FALSE  success
5456 5457
*/

5458 5459 5460 5461 5462 5463
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
5464
               enum_alter_table_change_level *need_copy_table,
5465 5466
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
5467 5468
               uint **index_add_buffer, uint *index_add_count,
               uint *candidate_key_count)
5469 5470 5471
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
5472
  uint key_count;
5473 5474
  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
  Create_field *new_field, *tmp_new_field;
5475 5476
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5477
  THD *thd= table->in_use;
5478 5479 5480 5481 5482
  /*
    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;
5483 5484 5485
  bool not_nullable= true;
  DBUG_ENTER("compare_tables");

5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504
  /*
    Create a copy of alter_info.
    To compare the new and old table definitions, we need to "prepare"
    the new definition - transform it from parser output to a format
    that describes the final table layout (all column defaults are
    initialized, duplicate columns are removed). This is done by
    mysql_prepare_create_table.  Unfortunately,
    mysql_prepare_create_table performs its transformations
    "in-place", that is, modifies the argument.  Since we would
    like to keep compare_tables() idempotent (not altering any
    of the arguments) we create a copy of alter_info here and
    pass it to mysql_prepare_create_table, then use the result
    to evaluate possibility of fast ALTER TABLE, and then
    destroy the copy.
  */
  Alter_info tmp_alter_info(*alter_info, thd->mem_root);
  uint db_options= 0; /* not used */
  /* Create the prepared information. */
  if (mysql_prepare_create_table(thd, create_info,
5505 5506 5507 5508 5509
                                 &tmp_alter_info,
                                 (table->s->tmp_table != NO_TMP_TABLE),
                                 &db_options,
                                 table->file, key_info_buffer,
                                 &key_count, 0))
5510 5511 5512
    DBUG_RETURN(1);
  /* Allocate result buffers. */
  if (! (*index_drop_buffer=
5513
         (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
5514
      ! (*index_add_buffer=
5515
         (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
5516
    DBUG_RETURN(1);
5517
  
5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534
  /*
    Some very basic checks. If number of fields changes, or the
    handler, we need to run full ALTER TABLE. In the future
    new fields can be added and old dropped without copy, but
    not yet.

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

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

    At the moment we can't handle altering temporary tables without a copy.
    We also test if OPTIMIZE TABLE was given and was mapped to alter table.
    In that case we always do full copy.
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
5535 5536 5537 5538 5539 5540 5541 5542

    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.
5543
  */
5544
  if (table->s->fields != alter_info->create_list.elements ||
antony@ppcg5.local's avatar
antony@ppcg5.local committed
5545
      table->s->db_type() != create_info->db_type ||
5546 5547 5548 5549
      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 ||
5550
      (table->s->row_type != create_info->row_type) ||
5551 5552
      create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
      create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
5553
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5554
      order_num ||
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
5555
      !table->s->mysql_version ||
5556
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5557 5558 5559 5560
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5561

5562 5563 5564 5565 5566 5567 5568
  /*
    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);

5569 5570 5571 5572
  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
5573 5574 5575 5576 5577
  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++)
5578 5579 5580 5581
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5582

5583
    /* Check that NULL behavior is same for old and new fields */
5584
    if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
5585
	(uint) (field->flags & NOT_NULL_FLAG))
5586 5587 5588 5589
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5590 5591 5592

    /* Don't pack rows in old tables if the user has requested this. */
    if (create_info->row_type == ROW_TYPE_DYNAMIC ||
5593
	(tmp_new_field->flags & BLOB_FLAG) ||
Staale Smedseng's avatar
Staale Smedseng committed
5594 5595
	(tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
5596 5597
      create_info->table_options|= HA_OPTION_PACK_RECORD;

5598
    /* Check if field was renamed */
5599
    field->flags&= ~FIELD_IS_RENAMED;
5600 5601
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
5602
		      tmp_new_field->field_name))
5603
      field->flags|= FIELD_IS_RENAMED;      
5604

5605
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
5606
    if (!(tmp= field->is_equal(tmp_new_field)))
5607 5608 5609 5610
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5611
    // Clear indexed marker
5612
    field->flags&= ~FIELD_IN_ADD_INDEX;
5613 5614 5615 5616 5617 5618 5619
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5620 5621 5622
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5623
  KEY *new_key_end= *key_info_buffer + key_count;
5624

5625 5626 5627 5628 5629 5630 5631
  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;
5632
  *candidate_key_count= 0;
5633
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
5634
  {
5635 5636 5637 5638
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654
   /*
      Check if key is a candidate key, i.e. a unique index with no index
      fields nullable, then key is either already primary key or could
      be promoted to primary key if the original primary key is dropped.
      Count all candidate keys.
    */
    not_nullable= true;
    for (table_part= table_key->key_part;
         table_part < table_part_end;
         table_part++)
    {
      not_nullable= not_nullable && (! table_part->field->maybe_null());
    }
    if ((table_key->flags & HA_NOSAME) && not_nullable)
      (*candidate_key_count)++;

5655
    /* Search a new key with the same name. */
5656
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5657 5658 5659 5660 5661 5662 5663
    {
      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. */
5664
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5665 5666 5667 5668 5669 5670 5671 5672 5673 5674
      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;
5675 5676 5677 5678 5679

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5680 5681 5682
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
5683 5684 5685
    {
      /*
	Key definition has changed if we are using a different field or
5686 5687
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
5688
      */
5689 5690 5691
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
5692
    }
5693 5694 5695 5696
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5697 5698
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5699 5700 5701 5702 5703 5704
    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];
5705
      field->flags|= FIELD_IN_ADD_INDEX;
5706
    }
5707
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
5708
  }
5709
  /*end of for (; table_key < table_key_end;) */
5710

5711 5712 5713
  /*
    Step through all keys of the new table and find matching old keys.
  */
5714
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5715 5716 5717 5718 5719 5720 5721 5722 5723 5724
  {
    /* 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. */
5725
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5726 5727 5728 5729 5730 5731
      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];
5732
        field->flags|= FIELD_IN_ADD_INDEX;
5733
      }
marty@linux.site's avatar
marty@linux.site committed
5734
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5735 5736
    }
  }
5737 5738 5739

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5740 5741 5742 5743
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5744

5745
  if (*index_drop_count || *index_add_count)
5746 5747 5748 5749
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5750

5751 5752
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
5753 5754 5755
}


andrey@example.com's avatar
andrey@example.com committed
5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781
/*
  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:
5782
    error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
andrey@example.com's avatar
andrey@example.com committed
5783 5784 5785 5786 5787 5788
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
5789
    error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
andrey@example.com's avatar
andrey@example.com committed
5790 5791 5792 5793 5794
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5795 5796
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
andrey@example.com's avatar
andrey@example.com committed
5797 5798 5799 5800 5801 5802 5803 5804
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843
/**
  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
5844
*/
5845

5846 5847 5848 5849
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5850
{
5851
  /* New column definitions are added here */
5852
  List<Create_field> new_create_list;
5853 5854 5855
  /* New key definitions are added here */
  List<Key> new_key_list;
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
5856
  List_iterator<Create_field> def_it(alter_info->create_list);
5857 5858
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
5859 5860 5861
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5862 5863 5864 5865 5866
  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;
5867

5868
  DBUG_ENTER("mysql_prepare_alter_table");
5869

5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880
  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)
5881
  {
5882 5883 5884
    /* 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;
5885
  }
5886 5887
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5888 5889
  if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
    create_info->transactional= table->s->transactional;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5890

5891
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
5892
  {
5893
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
5894
    /*
5895 5896
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
5897
    */
5898 5899 5900 5901 5902
    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
5903
  Create_field *def;
5904

5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916
  /*
    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++))
5917
    {
5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929
      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;
      }
5930
    }
5931
    if (drop)
5932
    {
5933 5934
      drop_it.remove();
      continue;
5935
    }
5936 5937 5938
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
5939
    {
5940 5941 5942
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
5943
    }
5944 5945 5946 5947
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
5948
      {
5949 5950
	new_create_list.push_back(def);
	def_it.remove();
5951 5952
      }
    }
5953
    else
5954 5955
    {
      /*
5956 5957
        This field was not dropped and not changed, add it to the list
        for the new table.
5958
      */
5959
      def= new Create_field(field, field);
5960 5961 5962 5963
      new_create_list.push_back(def);
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5964
      {
5965 5966
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5967
      }
5968
      if (alter)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5969
      {
5970
	if (def->sql_type == MYSQL_TYPE_BLOB)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5971
	{
5972
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5973
          goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5974
	}
5975 5976 5977 5978 5979
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
	alter_it.remove();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5980 5981 5982
      }
    }
  }
5983 5984
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
5985
  {
5986
    if (def->change && ! def->field)
5987
    {
5988
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
5989
      goto err;
5990
    }
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007
    /*
      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;
    }
6008 6009 6010 6011
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
6012
    else
6013
    {
6014
      Create_field *find;
6015 6016 6017 6018 6019 6020 6021 6022
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
6023
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
6024 6025 6026
        goto err;
      }
      find_it.after(def);			// Put element after this
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6027
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
6028
    }
6029
  }
6030
  if (alter_info->alter_list.elements)
6031
  {
6032
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
6033
             alter_info->alter_list.head()->name, table->s->table_name.str);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6034
    goto err;
6035
  }
6036
  if (!new_create_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6037
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6038 6039
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6040
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6041 6042 6043
  }

  /*
6044 6045
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6046 6047
  */

6048
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6049
  {
6050
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6051 6052 6053 6054 6055
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
6056
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071
	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;
6072
      Create_field *cfield;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6073 6074 6075 6076 6077
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
6078 6079
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6080 6081
	    break;
	}
6082
	else if (!my_strcasecmp(system_charset_info,
6083
				key_part_name, cfield->field_name))
6084
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6085 6086 6087 6088 6089
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
6090 6091 6092 6093 6094 6095 6096
      {
        /*
          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.

6097 6098 6099
          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.

6100 6101 6102 6103
          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()) ||
6104
            !Field::type_can_have_key_part(cfield->sql_type) ||
6105 6106
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
bar@mysql.com's avatar
bar@mysql.com committed
6107 6108
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
6109 6110 6111
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6112
      }
6113
      key_part_length /= key_part->field->charset()->mbmaxlen;
6114
      key_parts.push_back(new Key_part_spec(cfield->field_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6115 6116 6117
					    key_part_length));
    }
    if (key_parts.elements)
6118 6119
    {
      KEY_CREATE_INFO key_create_info;
6120 6121
      Key *key;
      enum Key::Keytype key_type;
6122 6123 6124 6125 6126 6127
      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)
6128
        key_create_info.parser_name= *plugin_name(key_info->parser);
6129

6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259
      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;
6260
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
6261 6262
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
6263
  char path[FN_REFLEN + 1];
6264
  char reg_path[FN_REFLEN+1];
6265
  ha_rows copied,deleted;
6266
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
6267
  legacy_db_type table_type;
6268
  frm_type_enum frm_type;
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6269
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
6270
#ifdef WITH_PARTITION_STORAGE_ENGINE
6271
  uint fast_alter_partition= 0;
6272 6273
  bool partition_changed= FALSE;
#endif
6274 6275
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
Staale Smedseng's avatar
Staale Smedseng committed
6276 6277 6278 6279 6280
  uint index_drop_count= 0;
  uint *index_drop_buffer= NULL;
  uint index_add_count= 0;
  uint *index_add_buffer= NULL;
  uint candidate_key_count= 0;
6281
  bool committed= 0;
6282
  bool no_pk;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6283 6284
  DBUG_ENTER("mysql_alter_table");

6285 6286 6287 6288 6289 6290
  /*
    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.
  */
6291
  if (table_list && table_list->db && table_list->table_name)
6292
  {
6293
    int table_kind= 0;
6294

6295 6296 6297
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
6298

6299
    if (table_kind)
6300
    {
6301 6302 6303 6304 6305 6306
      /* 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);
      }
6307

6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319
      /* 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)
      {
6320
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
6321 6322 6323
        DBUG_RETURN(TRUE);
      }
#endif
6324 6325 6326
    }
  }

6327 6328 6329 6330 6331
  /*
    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.
  */
6332
  thd_proc_info(thd, "init");
6333
  table_name=table_list->table_name;
6334
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6335
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
6336
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
6337
    new_db= db;
6338 6339
  build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
  build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
6340

6341
  mysql_ha_rm_tables(thd, table_list, FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6342

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6343
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
6344
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
6345
    /* Conditionally writes to binlog. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6346
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
6347
						   alter_info->tablespace_op));
6348 6349 6350
  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);
6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363
  /*
    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.
  */
6364 6365
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
6366
  /* Sic: there is a race here */
6367 6368
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382
    /*
      The following branch handles "ALTER VIEW v1 /no arguments/;"
      This feature is not documented one. 
      However, before "OPTIMIZE TABLE t1;" was implemented, 
      ALTER TABLE with no alter_specifications was used to force-rebuild
      the table. That's why this grammar is allowed. That's why we ignore
      it for views. So just do nothing in such a case.
    */
    if (!new_name)
    {
      my_ok(thd);
      DBUG_RETURN(FALSE);
    }

6383 6384 6385 6386 6387 6388 6389 6390 6391
    /*
      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));
6392
      DBUG_RETURN(TRUE);
6393 6394 6395
    }

    if (wait_if_global_read_lock(thd,0,1))
6396
      DBUG_RETURN(TRUE);
6397 6398
    VOID(pthread_mutex_lock(&LOCK_open));
    if (lock_table_names(thd, table_list))
6399
    {
6400
      error= 1;
6401
      goto view_err;
6402
    }
6403 6404 6405 6406 6407 6408
    
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
6409
        Query_log_event qinfo(thd, thd->query, thd->query_length,
6410
                              0, FALSE, 0);
6411 6412
        mysql_bin_log.write(&qinfo);
      }
6413
      my_ok(thd);
6414 6415 6416 6417 6418 6419 6420 6421 6422
    }

    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);
  }
6423 6424

  if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
6425
    DBUG_RETURN(TRUE);
6426
  table->use_all_columns();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6427

6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441
  /*
    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);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6442 6443 6444
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
6445
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6446
    strmov(new_name_buff,new_name);
6447
    strmov(new_alias= new_alias_buff, new_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6448
    if (lower_case_table_names)
6449 6450 6451
    {
      if (lower_case_table_names != 2)
      {
6452
	my_casedn_str(files_charset_info, new_name_buff);
6453 6454
	new_alias= new_name;			// Create lower case table name
      }
6455
      my_casedn_str(files_charset_info, new_name);
6456
    }
6457
    if (new_db == db &&
monty@mysql.com's avatar
monty@mysql.com committed
6458
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
6459 6460
    {
      /*
6461 6462
	Source and destination table names are equal: make later check
	easier.
6463
      */
6464
      new_alias= new_name= table_name;
6465
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6466 6467
    else
    {
6468
      if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6469 6470 6471
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
6472
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
6473
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6474 6475 6476 6477
	}
      }
      else
      {
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6478 6479 6480 6481 6482 6483 6484 6485
        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);
        }

6486
        build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
6487 6488
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6489 6490
	{
	  /* Table will be closed in do_command() */
6491
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6492
          goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6493 6494 6495 6496 6497
	}
      }
    }
  }
  else
6498 6499 6500 6501
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6502

antony@ppcg5.local's avatar
antony@ppcg5.local committed
6503
  old_db_type= table->s->db_type();
6504
  if (!create_info->db_type)
6505
  {
6506
#ifdef WITH_PARTITION_STORAGE_ENGINE
6507 6508
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
6509 6510 6511 6512
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
6513 6514 6515
        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.
6516
      */
6517
      create_info->db_type= table->part_info->default_engine_type;
6518
    }
6519
    else
6520
#endif
6521
      create_info->db_type= old_db_type;
6522
  }
6523

6524
  if (check_engine(thd, new_name, create_info))
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6525
    goto err;
6526
  new_db_type= create_info->db_type;
6527

6528 6529
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
6530
      !table->file->can_switch_engines())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6531
  {
6532
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6533 6534 6535
    goto err;
  }

6536
  if (create_info->row_type == ROW_TYPE_NOT_USED)
6537
  {
6538
    create_info->row_type= table->s->row_type;
6539 6540
    create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6541

6542 6543 6544
  DBUG_PRINT("info", ("old type: %s  new type: %s",
             ha_resolve_storage_engine_name(old_db_type),
             ha_resolve_storage_engine_name(new_db_type)));
monty@mysql.com's avatar
monty@mysql.com committed
6545
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
6546
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
6547 6548 6549
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6550
    goto err;
6551 6552
  }
  
6553
  thd_proc_info(thd, "setup");
6554
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
6555
      !table->s->tmp_table) // no need to touch frm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6556
  {
6557 6558 6559 6560
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
6561 6562 6563 6564 6565 6566 6567 6568 6569 6570
      /*
        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));
6571
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6572
      VOID(pthread_mutex_unlock(&LOCK_open));
6573
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
6574
      error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6575 6576 6577
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
6578
      VOID(pthread_mutex_lock(&LOCK_open));
6579
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6580
      VOID(pthread_mutex_unlock(&LOCK_open));
6581
      error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6582 6583
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
6584 6585 6586 6587
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
6588 6589 6590
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
6591
      error= 0;
6592 6593 6594 6595 6596
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
    }

6597 6598 6599 6600 6601 6602 6603 6604 6605 6606
    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.
    */

6607
    if (!error && (new_name != table_name || new_db != db))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6608
    {
6609
      thd_proc_info(thd, "rename");
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622
      /*
        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.
      */
6623 6624
      if (!access(new_name_buff,F_OK))
      {
6625
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
6626
	error= -1;
6627 6628 6629
      }
      else
      {
6630 6631 6632 6633 6634 6635 6636 6637 6638 6639
	*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;
        }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6640 6641
      }
    }
6642

6643 6644 6645 6646 6647 6648
    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);
6649
    }
6650

6651 6652 6653
    if (!error)
    {
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
6654
      my_ok(thd);
6655
    }
6656
    else if (error > 0)
6657
    {
6658 6659
      table->file->print_error(error, MYF(0));
      error= -1;
6660
    }
6661 6662 6663 6664 6665 6666
    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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6667 6668
  }

6669
  /* We have to do full alter table. */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6670

6671 6672 6673
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6674
    goto err;
6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687
#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;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6688

6689 6690
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6691 6692
  
  need_copy_table= alter_info->change_level;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6693

6694 6695
  set_table_default_charset(thd, create_info, db);

6696
  if (thd->variables.old_alter_table
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6697
      || (table->s->db_type() != create_info->db_type)
6698
#ifdef WITH_PARTITION_STORAGE_ENGINE
6699
      || partition_changed
6700
#endif
6701
     )
6702
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
6703
  else
6704
  {
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6705
    enum_alter_table_change_level need_copy_table_res;
6706
    /* Check how much the tables differ. */
6707 6708
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6709
                       &need_copy_table_res,
6710 6711
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
6712 6713
                       &index_add_buffer, &index_add_count,
                       &candidate_key_count))
6714
      goto err;
6715 6716 6717
   
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6718 6719 6720 6721 6722 6723 6724 6725 6726 6727
  }

  /*
    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;
6728
    ulong alter_flags= 0;
6729 6730 6731 6732 6733 6734
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

6735
    alter_flags= table->file->alter_table_flags(alter_info->flags);
6736
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6737 6738 6739 6740 6741 6742 6743 6744 6745
    /* 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)
      {
6746 6747 6748 6749 6750
        /* 
           Unique key. Check for "PRIMARY". 
           or if dropping last unique key
        */
        if ((uint) (key - table->key_info) == table->s->primary_key)
6751
        {
6752
          DBUG_PRINT("info", ("Dropping primary key"));
6753 6754 6755 6756
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
6757
          candidate_key_count--;
6758 6759 6760
        }
        else
        {
6761 6762 6763
          KEY_PART_INFO *part_end= key->key_part + key->key_parts;
          bool is_candidate_key= true;

6764 6765 6766
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779

          /*
            Check if all fields in key are declared
            NOT NULL and adjust candidate_key_count
          */
          for (KEY_PART_INFO *key_part= key->key_part;
               key_part < part_end;
               key_part++)
            is_candidate_key=
              (is_candidate_key && 
               (! table->field[key_part->fieldnr-1]->maybe_null()));
          if (is_candidate_key)
            candidate_key_count--;
6780 6781 6782 6783 6784 6785 6786 6787 6788
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_DROP_INDEX;
        needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
      }
    }
6789 6790
    no_pk= ((table->s->primary_key == MAX_KEY) ||
            (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
6791 6792 6793 6794 6795 6796 6797 6798 6799
    /* 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)
      {
6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824
        /* Unique key */

        KEY_PART_INFO *part_end= key->key_part + key->key_parts;    
        bool is_candidate_key= true;

        /*
          Check if all fields in key are declared
          NOT NULL
         */
        for (KEY_PART_INFO *key_part= key->key_part;
             key_part < part_end;
             key_part++)
          is_candidate_key=
            (is_candidate_key && 
             (! table->field[key_part->fieldnr]->maybe_null()));

        /*
           Check for "PRIMARY"
           or if adding first unique key
           defined on non-nullable fields
        */

        if ((!my_strcasecmp(system_charset_info,
                            key->name, primary_key_name)) ||
            (no_pk && candidate_key_count == 0 && is_candidate_key))
6825
        {
6826
          DBUG_PRINT("info", ("Adding primary key"));
6827 6828 6829 6830
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
          pk_changed++;
6831
          no_pk= false;
6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847
        }
        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;
      }
    }

6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861
    if ((candidate_key_count > 0) && 
        (needed_online_flags & HA_ONLINE_DROP_PK_INDEX))
    {
      /*
        Dropped primary key when there is some other unique 
        not null key that should be converted to primary key
      */
      needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
      needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
      pk_changed= 2;
    }

    DBUG_PRINT("info", ("needed_online_flags: 0x%lx, needed_fast_flags: 0x%lx",
                        needed_online_flags, needed_fast_flags));
6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872
    /*
      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. */
6873
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6874 6875 6876 6877 6878
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
6879
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6880 6881 6882 6883 6884
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
6885

6886 6887
  /*
    better have a negative test here, instead of positive, like
monty@mysql.com's avatar
monty@mysql.com committed
6888
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
6889 6890
    so that ALTER TABLE won't break when somebody will add new flag
  */
6891
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
6892
    create_info->frm_only= 1;
6893

6894
#ifdef WITH_PARTITION_STORAGE_ENGINE
6895
  if (fast_alter_partition)
6896
  {
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6897
    DBUG_ASSERT(!name_lock);
6898 6899 6900 6901
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           db, table_name,
                                           fast_alter_partition));
6902
  }
6903
#endif
6904

6905 6906 6907 6908 6909 6910
  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);

6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925
  /*
    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.
6926 6927
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953
      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);
    }
  }
6954 6955
  else
    create_info->data_file_name=create_info->index_file_name=0;
monty@mysql.com's avatar
monty@mysql.com committed
6956

6957 6958 6959 6960 6961 6962
  /*
    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);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6963
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
6964 6965 6966
                                    create_info,
                                    alter_info,
                                    1, 0);
6967 6968
  reenable_binlog(thd);
  if (error)
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6969
    goto err;
6970 6971

  /* Open the table if we need to copy the data. */
6972
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
6973
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
6974
  {
6975
    if (table->s->tmp_table)
6976 6977 6978 6979
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
6980
      tbl.table_name= tbl.alias= tmp_name;
6981
      /* Table is in thd->temporary_tables */
6982 6983
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
6984 6985 6986
    }
    else
    {
6987
      char path[FN_REFLEN + 1];
6988
      /* table is a normal table: Create temporary table in same directory */
6989
      build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
6990
                           FN_IS_TMP);
6991
      /* Open our intermediate table */
6992 6993 6994
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
6995
      goto err1;
6996 6997 6998 6999
    /*
      Note: In case of MERGE table, we do not attach children. We do not
      copy data for MERGE tables. Only the children have data.
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7000 7001
  }

7002
  /* Copy the data if necessary. */
7003
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7004
  thd->cuted_fields=0L;
7005
  copied=deleted=0;
7006 7007 7008 7009
  /*
    We do not copy data for MERGE tables. Only the children have data.
    MERGE tables have HA_NO_COPY_ON_ALTER set.
  */
7010
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
7011
  {
7012
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
monty@mysql.com's avatar
monty@mysql.com committed
7013
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7014
    new_table->next_number_field=new_table->found_next_number_field;
7015
    thd_proc_info(thd, "copy to tmp table");
7016 7017 7018
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
7019
                                    alter_info->keys_onoff,
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
7020
                                    alter_info->error_if_not_empty);
7021
  }
7022
  else
7023 7024 7025
  {
    VOID(pthread_mutex_lock(&LOCK_open));
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
7026
    VOID(pthread_mutex_unlock(&LOCK_open));
7027
    thd_proc_info(thd, "manage keys");
7028 7029
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
7030 7031
    error= ha_autocommit_or_rollback(thd, 0);
    if (end_active_trans(thd))
7032
      error= 1;
7033
  }
7034
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7035

7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048
  /* 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"));

7049
    table->file->ha_prepare_for_alter();
7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076
    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;
7077
        goto err1;
7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099
      }
    }
    /*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));
7100
        goto err1;
7101 7102 7103 7104 7105 7106
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
7107
        goto err1;
7108 7109 7110 7111
      }
    }
    /*end of if (index_drop_count)*/

7112 7113 7114 7115
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
7116

7117 7118
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
7119
    if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
7120
      goto err1;
7121
    committed= 1;
7122 7123 7124
  }
  /*end of if (! new_table) for add/drop index*/

7125
  if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7126 7127 7128
  {
    /* We changed a temporary table */
    if (error)
7129
      goto err1;
7130 7131 7132 7133 7134 7135
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7136
    /* Remove link to old table and rename the new one */
7137
    close_temporary_table(thd, table, 1, 1);
7138 7139
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
7140
      goto err1;
7141
    /* We don't replicate alter table statement on temporary tables */
7142
    if (!thd->is_current_stmt_binlog_format_row())
7143
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7144 7145 7146
    goto end_temporary;
  }

7147 7148
  if (new_table)
  {
7149 7150 7151 7152
    /*
      Close the intermediate table that will be the new table.
      Note that MERGE tables do not have their children attached here.
    */
7153
    intern_close_table(new_table);
7154
    my_free(new_table,MYF(0));
7155
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7156 7157 7158
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
7159
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7160 7161 7162
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
7163

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7164
  /*
7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177
    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.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7178 7179
  */

7180
  thd_proc_info(thd, "rename result table");
7181 7182
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
7183 7184
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7185

7186
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
7187
  close_data_files_and_morph_locks(thd, db, table_name);
7188

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7189
  error=0;
7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204
  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.
  */
7205 7206 7207 7208 7209 7210
  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;
  }
7211 7212
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7213 7214
  {
    error=1;
7215
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7216
  }
7217
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
7218
                              new_alias, FN_FROM_IS_TMP) ||
7219
           ((new_name != table_name || new_db != db) && // we also do rename
7220
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
7221 7222
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
7223
           Table_triggers_list::change_table_name(thd, db, table_name,
7224
                                                  new_db, new_alias)))
7225 7226
  {
    /* Try to get everything back. */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7227
    error=1;
7228 7229 7230 7231
    VOID(quick_rm_table(new_db_type,new_db,new_alias, 0));
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
    VOID(mysql_rename_table(old_db_type, db, old_name, db, alias,
                            FN_FROM_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7232
  }
7233

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7234 7235
  if (error)
  {
7236 7237
    /* This shouldn't happen. But let us play it safe. */
    goto err_with_placeholders;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7238
  }
7239

7240
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7241
  {
7242
    /*
7243 7244
      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.
7245
      NO need to tamper with MERGE tables. The real open is done later.
7246
    */
7247 7248 7249 7250 7251 7252 7253 7254
    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);
7255 7256
      table_list->table= name_lock;
      if (reopen_name_locked_table(thd, table_list, FALSE))
7257 7258
        goto err_with_placeholders;
      t_table= table_list->table;
7259
    }
7260
    else
7261
    {
7262 7263 7264
      if (reopen_table(table))
        goto err_with_placeholders;
      t_table= table;
7265 7266
    }
    /* Tell the handler that a new frm file is in place. */
7267 7268
    if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                               create_info))
7269 7270
      goto err_with_placeholders;
    if (thd->locked_tables && new_name == table_name && new_db == db)
7271
    {
7272 7273 7274 7275 7276 7277 7278 7279
      /*
        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);
7280 7281
    }
  }
7282

7283 7284 7285
  VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));

  if (thd->locked_tables && new_name == table_name && new_db == db)
7286
  {
7287
    thd->in_lock_tables= 1;
7288
    error= reopen_tables(thd, 1, 1);
7289
    thd->in_lock_tables= 0;
7290
    if (error)
7291
      goto err_with_placeholders;
7292
  }
7293
  VOID(pthread_mutex_unlock(&LOCK_open));
7294

7295
  thd_proc_info(thd, "end");
7296

7297 7298
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

7299 7300 7301 7302
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
                      thd->query, thd->query_length,
                      db, table_name);

7303
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
7304
                thd->is_current_stmt_binlog_format_row() &&
7305 7306
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
7307

7308
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
7309
  {
7310 7311 7312
    /*
      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
7313
      shutdown. But we do not need to attach MERGE children.
7314
    */
7315
    char path[FN_REFLEN];
7316
    TABLE *t_table;
7317
    build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0);
7318 7319
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
7320
    {
7321
      intern_close_table(t_table);
7322
      my_free(t_table, MYF(0));
7323
    }
7324
    else
7325
      sql_print_warning("Could not open table %s.%s after rename\n",
serg@serg.mylan's avatar
serg@serg.mylan committed
7326
                        new_db,table_name);
7327
    ha_flush_logs(old_db_type);
7328
  }
7329
  table_list->table=0;				// For query cache
7330
  query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7331

7332
  if (thd->locked_tables && (new_name != table_name || new_db != db))
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
7333
  {
7334 7335 7336 7337 7338 7339
    /*
      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.
    */
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
7340
    pthread_mutex_lock(&LOCK_open);
7341
    unlink_open_table(thd, table, FALSE);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
7342 7343 7344 7345
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7346
end_temporary:
7347 7348 7349
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
7350
  my_ok(thd, copied + deleted, 0L, tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7351
  thd->some_tables_deleted=0;
7352
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7353

7354
err1:
7355 7356 7357 7358 7359 7360
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
7361 7362 7363 7364
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, 
                        create_info->frm_only
                        ? FN_IS_TMP | FRM_ONLY
                        : FN_IS_TMP));
7365

7366
err:
7367 7368 7369 7370 7371 7372
  /*
    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.
  */
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
7373
  if (alter_info->error_if_not_empty && thd->row_count)
7374
  {
7375 7376
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
7377
    switch (alter_info->datetime_field->sql_type)
7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393
    {
      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;
evgen@moonbone.local's avatar
evgen@moonbone.local committed
7394 7395
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                                 f_val, strlength(f_val), t_type,
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
7396
                                 alter_info->datetime_field->field_name);
7397 7398
    thd->abort_on_warning= save_abort_on_warning;
  }
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
7399 7400 7401 7402 7403 7404
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
7405
  DBUG_RETURN(TRUE);
7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416

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));
7417
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7418
}
7419
/* mysql_alter_table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7420 7421

static int
7422
copy_data_between_tables(TABLE *from,TABLE *to,
7423
			 List<Create_field> &create,
7424
                         bool ignore,
7425
			 uint order_num, ORDER *order,
7426
			 ha_rows *copied,
andrey@example.com's avatar
andrey@example.com committed
7427
			 ha_rows *deleted,
7428 7429
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7430 7431 7432 7433 7434
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
7435
  uint length= 0;
7436 7437 7438 7439 7440
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
7441
  ha_rows examined_rows;
7442
  bool auto_increment_field_copied= 0;
7443
  ulong save_sql_mode;
7444
  ulonglong prev_insert_id;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7445 7446
  DBUG_ENTER("copy_data_between_tables");

7447 7448 7449 7450 7451 7452
  /*
    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
  */
7453
  error= ha_enable_transaction(thd, FALSE);
7454 7455
  if (error)
    DBUG_RETURN(-1);
7456
  
7457
  if (!(copy= new Copy_field[to->s->fields]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7458 7459
    DBUG_RETURN(-1);				/* purecov: inspected */

7460
  if (to->file->ha_external_lock(thd, F_WRLCK))
7461
    DBUG_RETURN(-1);
7462

andrey@example.com's avatar
andrey@example.com committed
7463 7464 7465
  /* We need external lock before we can disable/enable keys */
  alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff);

7466 7467 7468 7469 7470
  /* 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));

7471
  from->file->info(HA_STATUS_VARIABLE);
7472
  to->file->ha_start_bulk_insert(from->file->stats.records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7473

7474 7475
  save_sql_mode= thd->variables.sql_mode;

7476 7477
  List_iterator<Create_field> it(create);
  Create_field *def;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7478 7479 7480 7481 7482
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
7483 7484
    {
      if (*ptr == to->next_number_field)
7485
      {
7486
        auto_increment_field_copied= TRUE;
7487 7488 7489 7490 7491 7492 7493 7494 7495
        /*
          If we are going to copy contents of one auto_increment column to
          another auto_increment column it is sensible to preserve zeroes.
          This condition also covers case when we are don't actually alter
          auto_increment column.
        */
        if (def->field == from->found_next_number_field)
          thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7496
      (copy_end++)->set(*ptr,def->field,0);
7497 7498
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7499 7500
  }

7501 7502
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
7503 7504
  if (order)
  {
7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522
    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;
7523

7524 7525 7526 7527 7528 7529 7530 7531 7532 7533
      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;
    }
7534 7535
  };

7536 7537
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
7538
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
7539
  if (ignore)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
7540
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
7541
  thd->row_count= 0;
7542
  restore_record(to, s->default_values);        // Create empty record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7543 7544 7545 7546
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
7547
      thd->send_kill_message();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7548 7549 7550
      error= 1;
      break;
    }
7551
    thd->row_count++;
7552 7553 7554 7555 7556 7557
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
7558 7559
    if (to->next_number_field)
    {
7560
      if (auto_increment_field_copied)
7561
        to->auto_increment_field_not_null= TRUE;
7562 7563 7564
      else
        to->next_number_field->reset();
    }
7565
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7566
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
7567
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7568
      copy_ptr->do_copy(copy_ptr);
7569
    }
7570
    prev_insert_id= to->file->next_insert_id;
7571
    error=to->file->ha_write_row(to->record[0]);
7572 7573
    to->auto_increment_field_not_null= FALSE;
    if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7574
    {
7575
      if (!ignore ||
7576
          to->file->is_fatal_error(error, HA_CHECK_DUP))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7577
      {
7578
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
7579 7580 7581 7582
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
7583
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
7584
             if (key_nr == 0 &&
monty@mysql.com's avatar
monty@mysql.com committed
7585 7586
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
7587
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
monty@mysql.com's avatar
monty@mysql.com committed
7588
             to->file->print_keydup_error(key_nr, err_msg);
7589 7590 7591 7592
             break;
           }
         }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7593 7594 7595
	to->file->print_error(error,MYF(0));
	break;
      }
7596
      to->file->restore_auto_increment(prev_insert_id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7597 7598 7599
      delete_count++;
    }
    else
7600
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7601 7602
  }
  end_read_record(&info);
7603
  free_io_cache(from);
7604
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
7605

7606
  if (to->file->ha_end_bulk_insert() && error <= 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7607
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
7608
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7609 7610
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
7611
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7612

7613 7614 7615 7616 7617 7618
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7619 7620 7621 7622
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
7623
  if (ha_autocommit_or_rollback(thd, 0))
7624
    error=1;
7625
  if (end_active_trans(thd))
7626
    error=1;
7627

7628
 err:
7629
  thd->variables.sql_mode= save_sql_mode;
7630
  thd->abort_on_warning= 0;
7631
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7632 7633
  *copied= found_count;
  *deleted=delete_count;
7634
  to->file->ha_release_auto_increment();
7635
  if (to->file->ha_external_lock(thd,F_UNLCK))
7636
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7637 7638
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7639

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

7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7652
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7653 7654
{
  HA_CREATE_INFO create_info;
7655 7656 7657
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7658 7659 7660 7661 7662 7663
  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;
7664 7665

  bzero((char*) &create_info, sizeof(create_info));
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
7666
  create_info.row_type=ROW_TYPE_NOT_USED;
7667
  create_info.default_table_charset=default_charset_info;
monty@mysql.com's avatar
monty@mysql.com committed
7668
  /* Force alter table to recreate table */
7669
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7670
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7671 7672
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7673 7674 7675
}


7676 7677
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7678 7679 7680 7681 7682
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
7683
  DBUG_ENTER("mysql_checksum_table");
7684 7685 7686

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7687 7688
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7689
  item->maybe_null= 1;
7690 7691
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
7692
    DBUG_RETURN(TRUE);
7693

7694
  /* Open one table after the other to keep lock time as short as possible. */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7695
  for (table= tables; table; table= table->next_local)
7696 7697
  {
    char table_name[NAME_LEN*2+2];
7698
    TABLE *t;
7699

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

7702
    t= table->table= open_n_lock_single_table(thd, table, TL_READ);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7703
    thd->clear_error();			// these errors shouldn't get client
7704 7705 7706 7707

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

7708
    if (!t)
7709
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7710
      /* Table didn't exist */
7711
      protocol->store_null();
7712
      thd->clear_error();
7713 7714 7715
    }
    else
    {
7716
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7717 7718
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7719
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7720
	       (check_opt->flags & T_QUICK))
7721
	protocol->store_null();
7722 7723
      else
      {
7724 7725
	/* calculating table's checksum */
	ha_checksum crc= 0;
7726
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7727

7728
        t->use_all_columns();
7729

7730
	if (t->file->ha_rnd_init(1))
7731 7732 7733
	  protocol->store_null();
	else
	{
7734
	  for (;;)
7735
	  {
7736 7737 7738 7739 7740 7741 7742 7743 7744 7745
            if (thd->killed)
            {
              /* 
                 we've been killed; let handler clean up, and remove the 
                 partial current row from the recordset (embedded lib) 
              */
              t->file->ha_rnd_end();
              thd->protocol->remove_last_row();
              goto err;
            }
7746
	    ha_checksum row_crc= 0;
7747 7748 7749 7750 7751 7752 7753
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
7754 7755 7756 7757
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
serg@mysql.com's avatar
serg@mysql.com committed
7758 7759 7760
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

7761 7762
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7763

7764
	    for (uint i= 0; i < t->s->fields; i++ )
7765 7766
	    {
	      Field *f= t->field[i];
7767
	      if ((f->type() == MYSQL_TYPE_BLOB) ||
7768
                  (f->type() == MYSQL_TYPE_VARCHAR))
7769 7770 7771
	      {
		String tmp;
		f->val_str(&tmp);
7772
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7773 7774
	      }
	      else
7775
		row_crc= my_checksum(row_crc, f->ptr,
7776
				     f->pack_length());
7777
	    }
7778

7779 7780 7781
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
7782
          t->file->ha_rnd_end();
7783
	}
7784
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7785
      thd->clear_error();
7786 7787 7788 7789 7790 7791 7792
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

7793
  my_eof(thd);
7794
  DBUG_RETURN(FALSE);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7795

7796 7797 7798 7799
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
7800
  DBUG_RETURN(TRUE);
7801
}
7802 7803

static bool check_engine(THD *thd, const char *table_name,
7804
                         HA_CREATE_INFO *create_info)
7805
{
7806
  handlerton **new_engine= &create_info->db_type;
7807
  handlerton *req_engine= *new_engine;
7808
  bool no_substitution=
7809
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
7810 7811
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
7812 7813
    return TRUE;

7814
  if (req_engine && req_engine != *new_engine)
7815
  {
serg@janus.mylan's avatar
serg@janus.mylan committed
7816
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
7817 7818
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
7819
                       ha_resolve_storage_engine_name(*new_engine),
7820 7821
                       table_name);
  }
7822 7823 7824 7825 7826
  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)
    {
7827
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7828
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
7829 7830 7831
      *new_engine= 0;
      return TRUE;
    }
7832
    *new_engine= myisam_hton;
7833
  }
7834 7835
  return FALSE;
}