sql_table.cc 248 KB
Newer Older
1
/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
unknown's avatar
unknown committed
2 3 4

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

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

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

/* drop and alter of tables */

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

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

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

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

static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
static int copy_data_between_tables(TABLE *from,TABLE *to,
unknown's avatar
unknown committed
37
                                    List<Create_field> &create, bool ignore,
38
				    uint order_num, ORDER *order,
39
				    ha_rows *copied,ha_rows *deleted,
40 41
                                    enum enum_enable_or_disable keys_onoff,
                                    bool error_if_not_empty);
42

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

57
#ifndef DBUG_OFF
unknown's avatar
unknown committed
58

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

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

70

71 72
/**
  @brief Helper function for explain_filename
73 74 75 76 77
  @param thd          Thread handle
  @param to_p         Explained name in system_charset_info
  @param end_p        End of the to_p buffer
  @param name         Name to be converted
  @param name_len     Length of the name, in bytes
78
*/
79 80
static char* add_identifier(THD* thd, char *to_p, const char * end_p,
                            const char* name, uint name_len)
81 82 83 84 85 86
{
  uint res;
  uint errors;
  const char *conv_name;
  char tmp_name[FN_REFLEN];
  char conv_string[FN_REFLEN];
87
  int quote;
88 89 90 91 92 93 94 95 96 97 98 99 100

  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)
101 102
  {
    DBUG_PRINT("error", ("strconvert of '%s' failed with %u (errors: %u)", conv_name, res, errors));
103
    conv_name= name;
104
  }
105 106 107 108 109 110
  else
  {
    DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string));
    conv_name= conv_string;
  }

111 112 113
  quote = thd ? get_quote_char_for_identifier(thd, conv_name, res - 1) : '"';

  if (quote != EOF && (end_p - to_p > 2))
114
  {
115
    *(to_p++)= (char) quote;
116 117 118 119 120
    while (*conv_name && (end_p - to_p - 1) > 0)
    {
      uint length= my_mbcharlen(system_charset_info, *conv_name);
      if (!length)
        length= 1;
121
      if (length == 1 && *conv_name == (char) quote)
122 123 124
      { 
        if ((end_p - to_p) < 3)
          break;
125
        *(to_p++)= (char) quote;
126 127
        *(to_p++)= *(conv_name++);
      }
128
      else if (((long) length) < (end_p - to_p))
129 130 131 132 133 134 135
      {
        to_p= strnmov(to_p, conv_name, length);
        conv_name+= length;
      }
      else
        break;                               /* string already filled */
    }
136 137 138 139 140
    if (end_p > to_p) {
      *(to_p++)= (char) quote;
      if (end_p > to_p)
	*to_p= 0; /* terminate by NUL, but do not include it in the count */
    }
141
  }
142
  else
143 144
    to_p= strnmov(to_p, conv_name, end_p - to_p);
  DBUG_RETURN(to_p);
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
}


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

160
   @param      thd          Thread handle
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
   @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
*/

181 182
uint explain_filename(THD* thd,
		      const char *from,
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
                      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;
    }
  }
288 289
  else
    table_name_len= strlen(table_name);
290 291 292 293
  if (db_name)
  {
    if (explain_mode == EXPLAIN_ALL_VERBOSE)
    {
294 295
      to_p= strnmov(to_p, ER(ER_DATABASE_NAME), end_p - to_p);
      *(to_p++)= ' ';
296
      to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
297 298 299 300
      to_p= strnmov(to_p, ", ", end_p - to_p);
    }
    else
    {
301
      to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
302 303 304 305
      to_p= strnmov(to_p, ".", end_p - to_p);
    }
  }
  if (explain_mode == EXPLAIN_ALL_VERBOSE)
306 307 308
  {
    to_p= strnmov(to_p, ER(ER_TABLE_NAME), end_p - to_p);
    *(to_p++)= ' ';
309
    to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
310
  }
311
  else
312
    to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
313 314
  if (part_name)
  {
315
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
316 317 318 319 320 321 322 323 324 325 326 327 328
      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);
    }
329 330
    to_p= strnmov(to_p, ER(ER_PARTITION_NAME), end_p - to_p);
    *(to_p++)= ' ';
331
    to_p= add_identifier(thd, to_p, end_p, part_name, part_name_len);
332 333 334
    if (subpart_name)
    {
      to_p= strnmov(to_p, ", ", end_p - to_p);
335 336
      to_p= strnmov(to_p, ER(ER_SUBPARTITION_NAME), end_p - to_p);
      *(to_p++)= ' ';
337
      to_p= add_identifier(thd, to_p, end_p, subpart_name, subpart_name_len);
338
    }
339
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
340 341 342 343 344 345 346
      to_p= strnmov(to_p, " */", end_p - to_p);
  }
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(to_p - to);
}


347 348 349 350 351 352 353 354 355 356 357 358 359
/*
  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.
*/

360 361
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
362
  uint errors;
363
  size_t res;
364 365 366 367
  DBUG_ENTER("filename_to_tablename");
  DBUG_PRINT("enter", ("from '%s'", from));

  if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length))
368
  {
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    /* 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.
      */
    }
386
  }
387 388 389

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
390 391 392
}


Ramil Kalimullin's avatar
Ramil Kalimullin committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
/**
  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;
}


417 418 419 420 421 422 423 424 425 426 427 428 429
/*
  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.
*/

430 431
uint tablename_to_filename(const char *from, char *to, uint to_length)
{
432
  uint errors, length;
433 434 435
  DBUG_ENTER("tablename_to_filename");
  DBUG_PRINT("enter", ("from '%s'", from));

Ramil Kalimullin's avatar
Ramil Kalimullin committed
436 437
  if ((length= check_n_cut_mysql50_prefix(from, to, to_length)))
    DBUG_RETURN(length);
438 439 440 441 442 443 444 445
  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;
  }
446 447
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
448 449 450
}


451
/*
452
  Creates path to a file: mysql_data_dir/db/table.ext
453 454

  SYNOPSIS
455
   build_table_filename()
456
     buff                       Where to write result in my_charset_filename.
457
                                This may be the same as table_name.
458 459 460 461 462 463
     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.
464 465 466 467 468 469

  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".
470 471
    Unless flags indicate a temporary table name.
    'db' is always converted.
472
    'ext' is not converted.
473

474 475 476 477 478
    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.
479

480 481
  RETURN
    path length
482 483 484
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
485
                          const char *table_name, const char *ext, uint flags)
486 487 488
{
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
489
  DBUG_ENTER("build_table_filename");
490 491
  DBUG_PRINT("enter", ("db: '%s'  table_name: '%s'  ext: '%s'  flags: %x",
                       db, table_name, ext, flags));
492 493 494 495 496 497

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

498
  VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
499 500 501 502

  char *end = buff + bufflen;
  /* Don't add FN_ROOTDIR if mysql_data_home already includes it */
  char *pos = strnmov(buff, mysql_data_home, bufflen);
503
  size_t rootdir_len= strlen(FN_ROOTDIR);
504 505 506
  if (pos - rootdir_len >= buff &&
      memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0)
    pos= strnmov(pos, FN_ROOTDIR, end - pos);
507 508 509 510 511 512
  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);
513

514
  DBUG_PRINT("exit", ("buff: '%s'", buff));
515
  DBUG_RETURN(pos - buff);
516 517 518
}


519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
/*
  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
*/

537
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
538
{
539 540
  DBUG_ENTER("build_tmptable_filename");

541 542 543 544
  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);
545

546 547 548 549 550
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }
551

552
  size_t length= unpack_filename(buff, buff);
553 554
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
555 556
}

unknown's avatar
unknown committed
557 558 559
/*
--------------------------------------------------------------------------

560
   MODULE: DDL log
unknown's avatar
unknown committed
561 562 563 564 565 566 567 568
   -----------------

   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
569
   ddl log while we are executing. These entries are dropped when the
unknown's avatar
unknown committed
570 571 572 573
   operation is completed.

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

574
   There is only one ddl log in the system and it is protected by a mutex
unknown's avatar
unknown committed
575 576 577
   and there is a global struct that contains information about its current
   state.

578 579
   History:
   First version written in 2006 by Mikael Ronstrom
unknown's avatar
unknown committed
580 581 582 583
--------------------------------------------------------------------------
*/


584
struct st_global_ddl_log
unknown's avatar
unknown committed
585
{
586 587 588 589 590 591
  /*
    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.
  */
592
  char file_entry_buf[4*IO_SIZE];
unknown's avatar
unknown committed
593 594
  char file_name_str[FN_REFLEN];
  char *file_name;
595 596 597
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
unknown's avatar
unknown committed
598 599
  File file_id;
  uint name_len;
600
  uint io_size;
601
  bool inited;
602
  bool do_release;
603
  bool recovery_phase;
604 605
  st_global_ddl_log() : inited(false), do_release(false) {}
};
unknown's avatar
unknown committed
606

607
st_global_ddl_log global_ddl_log;
unknown's avatar
unknown committed
608

609
pthread_mutex_t LOCK_gdl;
unknown's avatar
unknown committed
610

611 612 613 614 615
#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
616

617 618
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
619
#define DDL_LOG_IO_SIZE_POS 8
unknown's avatar
unknown committed
620

unknown's avatar
unknown committed
621
/*
622
  Read one entry from ddl log file
unknown's avatar
unknown committed
623
  SYNOPSIS
624
    read_ddl_log_file_entry()
625
    entry_no                     Entry number to read
unknown's avatar
unknown committed
626
  RETURN VALUES
627 628
    TRUE                         Error
    FALSE                        Success
unknown's avatar
unknown committed
629 630
*/

631
static bool read_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
632 633
{
  bool error= FALSE;
634
  File file_id= global_ddl_log.file_id;
635
  uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
636 637
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
unknown's avatar
unknown committed
638

639
  if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
640
               MYF(MY_WME)) != io_size)
unknown's avatar
unknown committed
641 642 643 644 645 646
    error= TRUE;
  DBUG_RETURN(error);
}


/*
647
  Write one entry from ddl log file
unknown's avatar
unknown committed
648
  SYNOPSIS
649
    write_ddl_log_file_entry()
unknown's avatar
unknown committed
650 651 652 653 654 655
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

656
static bool write_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
657 658
{
  bool error= FALSE;
659 660 661
  File file_id= global_ddl_log.file_id;
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_ddl_log_file_entry");
unknown's avatar
unknown committed
662

663
  if (my_pwrite(file_id, (uchar*)file_entry_buf,
664
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
unknown's avatar
unknown committed
665 666 667 668 669 670
    error= TRUE;
  DBUG_RETURN(error);
}


/*
671
  Write ddl log header
unknown's avatar
unknown committed
672
  SYNOPSIS
673
    write_ddl_log_header()
unknown's avatar
unknown committed
674 675 676 677 678
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

679
static bool write_ddl_log_header()
unknown's avatar
unknown committed
680 681
{
  uint16 const_var;
682
  bool error= FALSE;
683
  DBUG_ENTER("write_ddl_log_header");
unknown's avatar
unknown committed
684

685 686
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
687
  const_var= FN_LEN;
688
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
unknown's avatar
unknown committed
689
            (ulong) const_var);
690
  const_var= IO_SIZE;
691
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
unknown's avatar
unknown committed
692
            (ulong) const_var);
693
  if (write_ddl_log_file_entry(0UL))
694 695 696 697 698
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
699
  DBUG_RETURN(error);
unknown's avatar
unknown committed
700 701 702
}


703
/*
704
  Create ddl log file name
705
  SYNOPSIS
706
    create_ddl_log_file_name()
707 708 709 710 711
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

712
static inline void create_ddl_log_file_name(char *file_name)
713
{
714
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
715 716 717
}


unknown's avatar
unknown committed
718
/*
719
  Read header of ddl log file
unknown's avatar
unknown committed
720
  SYNOPSIS
721
    read_ddl_log_header()
unknown's avatar
unknown committed
722
  RETURN VALUES
723 724
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
unknown's avatar
unknown committed
725
  DESCRIPTION
726 727 728
    When we read the ddl log header we get information about maximum sizes
    of names in the ddl log and we also get information about the number
    of entries in the ddl log.
unknown's avatar
unknown committed
729 730
*/

731
static uint read_ddl_log_header()
unknown's avatar
unknown committed
732
{
733
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
734
  char file_name[FN_REFLEN];
735
  uint entry_no;
736
  bool successful_open= FALSE;
737
  DBUG_ENTER("read_ddl_log_header");
unknown's avatar
unknown committed
738

739
  create_ddl_log_file_name(file_name);
740
  if ((global_ddl_log.file_id= my_open(file_name,
741
                                        O_RDWR | O_BINARY, MYF(0))) >= 0)
unknown's avatar
unknown committed
742
  {
743
    if (read_ddl_log_file_entry(0UL))
744
    {
745 746
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
747
    }
748 749
    else
      successful_open= TRUE;
unknown's avatar
unknown committed
750
  }
751 752
  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]);
753 754
  if (successful_open)
  {
755
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
756 757 758
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
759
  else
760 761 762
  {
    entry_no= 0;
  }
763 764 765 766
  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));
767
  global_ddl_log.do_release= true;
unknown's avatar
unknown committed
768
  DBUG_RETURN(entry_no);
unknown's avatar
unknown committed
769 770 771 772
}


/*
773
  Read a ddl log entry
unknown's avatar
unknown committed
774
  SYNOPSIS
775
    read_ddl_log_entry()
unknown's avatar
unknown committed
776 777 778 779 780 781
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
782
    Read a specified entry in the ddl log
unknown's avatar
unknown committed
783 784
*/

785
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
786
{
787
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
788
  uint inx;
789
  uchar single_char;
790
  DBUG_ENTER("read_ddl_log_entry");
791

792
  if (read_ddl_log_file_entry(read_entry))
793 794 795
  {
    DBUG_RETURN(TRUE);
  }
796
  ddl_log_entry->entry_pos= read_entry;
797 798 799 800
  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;
801 802 803 804 805 806 807
  ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
  ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
  ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
  inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
  ddl_log_entry->from_name= &file_entry_buf[inx];
  inx+= global_ddl_log.name_len;
  ddl_log_entry->handler_name= &file_entry_buf[inx];
unknown's avatar
unknown committed
808 809 810 811 812
  DBUG_RETURN(FALSE);
}


/*
813
  Initialise ddl log
unknown's avatar
unknown committed
814
  SYNOPSIS
815
    init_ddl_log()
unknown's avatar
unknown committed
816

unknown's avatar
unknown committed
817
  DESCRIPTION
818
    Write the header of the ddl log file and length of names. Also set
unknown's avatar
unknown committed
819
    number of entries to zero.
unknown's avatar
unknown committed
820 821 822 823

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
unknown's avatar
unknown committed
824 825
*/

826
static bool init_ddl_log()
unknown's avatar
unknown committed
827
{
828
  char file_name[FN_REFLEN];
829
  DBUG_ENTER("init_ddl_log");
unknown's avatar
unknown committed
830

831
  if (global_ddl_log.inited)
unknown's avatar
unknown committed
832 833
    goto end;

834
  global_ddl_log.io_size= IO_SIZE;
835
  create_ddl_log_file_name(file_name);
836 837 838 839
  if ((global_ddl_log.file_id= my_create(file_name,
                                         CREATE_MODE,
                                         O_RDWR | O_TRUNC | O_BINARY,
                                         MYF(MY_WME))) < 0)
840
  {
841
    /* Couldn't create ddl log file, this is serious error */
842 843
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
844
  }
unknown's avatar
Fixes  
unknown committed
845
  global_ddl_log.inited= TRUE;
846
  if (write_ddl_log_header())
847
  {
unknown's avatar
unknown committed
848
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
unknown's avatar
Fixes  
unknown committed
849
    global_ddl_log.inited= FALSE;
850
    DBUG_RETURN(TRUE);
851
  }
unknown's avatar
unknown committed
852 853

end:
854
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
855 856 857 858
}


/*
859
  Execute one action in a ddl log entry
unknown's avatar
unknown committed
860
  SYNOPSIS
861 862
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
unknown's avatar
unknown committed
863
  RETURN VALUES
864 865
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
866 867
*/

868
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
869
{
870 871
  bool frm_action= FALSE;
  LEX_STRING handler_name;
872
  handler *file= NULL;
873
  MEM_ROOT mem_root;
874
  int error= TRUE;
875
  char to_path[FN_REFLEN];
876
  char from_path[FN_REFLEN];
877
#ifdef WITH_PARTITION_STORAGE_ENGINE
878
  char *par_ext= (char*)".par";
879
#endif
880
  handlerton *hton;
881
  DBUG_ENTER("execute_ddl_log_action");
882

883
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
884 885 886
  {
    DBUG_RETURN(FALSE);
  }
887 888
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
889
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
890
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
891 892 893
    frm_action= TRUE;
  else
  {
unknown's avatar
unknown committed
894 895
    plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
    if (!plugin)
896 897 898 899
    {
      my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
      goto error;
    }
unknown's avatar
unknown committed
900 901
    hton= plugin_data(plugin, handlerton*);
    file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
902
    if (!file)
903 904
    {
      mem_alloc_error(sizeof(handler));
905
      goto error;
906
    }
907
  }
908
  switch (ddl_log_entry->action_type)
909
  {
910
    case DDL_LOG_REPLACE_ACTION:
911
    case DDL_LOG_DELETE_ACTION:
912
    {
913
      if (ddl_log_entry->phase == 0)
914 915 916
      {
        if (frm_action)
        {
917 918 919
          strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
          if ((error= my_delete(to_path, MYF(MY_WME))))
          {
920
            if (my_errno != ENOENT)
921 922
              break;
          }
923
#ifdef WITH_PARTITION_STORAGE_ENGINE
924 925
          strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
          VOID(my_delete(to_path, MYF(MY_WME)));
926
#endif
927 928 929
        }
        else
        {
930
          if ((error= file->ha_delete_table(ddl_log_entry->name)))
931 932 933 934
          {
            if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
              break;
          }
935
        }
936
        if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
937 938 939
          break;
        VOID(sync_ddl_log());
        error= FALSE;
940 941
        if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
          break;
942
      }
943 944 945 946 947 948
      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.
      */
949
    }
950
    case DDL_LOG_RENAME_ACTION:
951
    {
952 953 954
      error= TRUE;
      if (frm_action)
      {
955
        strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
956
        strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
957
        if (my_rename(from_path, to_path, MYF(MY_WME)))
958
          break;
959
#ifdef WITH_PARTITION_STORAGE_ENGINE
960
        strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
961
        strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
962
        VOID(my_rename(from_path, to_path, MYF(MY_WME)));
963
#endif
964 965 966
      }
      else
      {
967 968
        if (file->ha_rename_table(ddl_log_entry->from_name,
                                  ddl_log_entry->name))
969
          break;
970 971
      }
      if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
972 973 974
        break;
      VOID(sync_ddl_log());
      error= FALSE;
975
      break;
976
    }
977 978 979 980 981 982 983 984
    default:
      DBUG_ASSERT(0);
      break;
  }
  delete file;
error:
  free_root(&mem_root, MYF(0)); 
  DBUG_RETURN(error);
unknown's avatar
unknown committed
985 986 987
}


unknown's avatar
unknown committed
988
/*
989
  Get a free entry in the ddl log
unknown's avatar
unknown committed
990
  SYNOPSIS
991 992
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
unknown's avatar
unknown committed
993
  RETURN VALUES
994 995
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
996 997
*/

998 999
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
unknown's avatar
unknown committed
1000
{
1001 1002 1003
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
1004

1005
  if (global_ddl_log.first_free == NULL)
1006
  {
1007 1008
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
1009
    {
1010
      sql_print_error("Failed to allocate memory for ddl log free list");
1011 1012
      DBUG_RETURN(TRUE);
    }
1013
    global_ddl_log.num_entries++;
1014
    used_entry->entry_pos= global_ddl_log.num_entries;
1015
    *write_header= TRUE;
1016 1017 1018
  }
  else
  {
1019 1020
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
1021
    *write_header= FALSE;
1022 1023 1024 1025 1026 1027
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
1028
  global_ddl_log.first_used= used_entry;
1029 1030 1031 1032
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
1033
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
1034 1035 1036 1037
}


/*
1038
  External interface methods for the DDL log Module
1039 1040 1041 1042
  ---------------------------------------------------
*/

/*
unknown's avatar
unknown committed
1043
  SYNOPSIS
1044 1045 1046
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
1047

unknown's avatar
unknown committed
1048
  RETURN VALUES
1049 1050 1051 1052
    TRUE                      Error
    FALSE                     Success

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

1057 1058
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
1059
{
1060
  bool error, write_header;
1061 1062 1063 1064 1065 1066
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
1067 1068
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
1069
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
1070
                                    (char)ddl_log_entry->action_type;
1071 1072 1073 1074
  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);
1075 1076
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
1077 1078
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
unknown's avatar
unknown committed
1079
  {
1080
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
1081 1082
    strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
          ddl_log_entry->from_name, FN_LEN - 1);
unknown's avatar
unknown committed
1083
  }
1084
  else
1085 1086
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
1087 1088
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
1089
  if (get_free_ddl_log_entry(active_entry, &write_header))
1090 1091 1092 1093
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
1094
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
1095
  {
1096
    error= TRUE;
1097 1098 1099
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
1100 1101
  if (write_header && !error)
  {
1102 1103
    VOID(sync_ddl_log());
    if (write_ddl_log_header())
1104 1105
      error= TRUE;
  }
1106
  if (error)
1107
    release_ddl_log_memory_entry(*active_entry);
1108
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1109 1110 1111 1112
}


/*
1113
  Write final entry in the ddl log
unknown's avatar
unknown committed
1114
  SYNOPSIS
1115
    write_execute_ddl_log_entry()
1116 1117 1118 1119
    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.
1120 1121 1122
    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
1123 1124 1125
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
unknown's avatar
unknown committed
1126
  RETURN VALUES
1127 1128 1129 1130
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
1131
    This is the last write in the ddl log. The previous log entries have
1132
    already been written but not yet synched to disk.
1133 1134 1135 1136
    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 
1137
*/ 
unknown's avatar
unknown committed
1138

1139 1140 1141
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
1142
{
1143
  bool write_header= FALSE;
1144 1145
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
1146

1147 1148 1149 1150
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
1151 1152
  if (!complete)
  {
1153 1154 1155 1156 1157 1158
    /*
      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.
    */
1159
    VOID(sync_ddl_log());
1160
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
1161 1162
  }
  else
1163
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
1164 1165 1166 1167 1168 1169
  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;
1170
  if (!(*active_entry))
1171
  {
1172
    if (get_free_ddl_log_entry(active_entry, &write_header))
1173 1174 1175
    {
      DBUG_RETURN(TRUE);
    }
1176
  }
1177
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
1178
  {
1179
    sql_print_error("Error writing execute entry in ddl log");
1180
    release_ddl_log_memory_entry(*active_entry);
1181 1182
    DBUG_RETURN(TRUE);
  }
1183
  VOID(sync_ddl_log());
1184 1185
  if (write_header)
  {
1186
    if (write_ddl_log_header())
1187
    {
1188
      release_ddl_log_memory_entry(*active_entry);
1189 1190 1191
      DBUG_RETURN(TRUE);
    }
  }
unknown's avatar
unknown committed
1192 1193 1194 1195
  DBUG_RETURN(FALSE);
}


1196
/*
1197
  For complex rename operations we need to deactivate individual entries.
1198
  SYNOPSIS
1199
    deactivate_ddl_log_entry()
1200 1201 1202 1203 1204 1205 1206 1207
    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
1208
    do in a safe manner unless the ddl log is informed of the phases in
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
    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.
*/

1220
bool deactivate_ddl_log_entry(uint entry_no)
1221
{
1222 1223
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
1224

1225
  if (!read_ddl_log_file_entry(entry_no))
1226
  {
1227
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
1228
    {
1229 1230 1231 1232 1233 1234
      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)
1235
      {
1236 1237
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
1238 1239 1240 1241 1242
      }
      else
      {
        DBUG_ASSERT(0);
      }
1243 1244 1245 1246 1247 1248
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
1249 1250
    }
  }
1251 1252 1253 1254 1255 1256
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
1257 1258 1259 1260
}


/*
1261
  Sync ddl log file
1262
  SYNOPSIS
1263
    sync_ddl_log()
1264 1265 1266 1267 1268
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

1269
bool sync_ddl_log()
1270 1271
{
  bool error= FALSE;
1272
  DBUG_ENTER("sync_ddl_log");
1273

1274 1275
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
1276 1277 1278 1279
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
1280 1281
  {
    /* Write to error log */
1282
    sql_print_error("Failed to sync ddl log");
1283 1284 1285 1286 1287 1288
    error= TRUE;
  }
  DBUG_RETURN(error);
}


1289 1290 1291
/*
  Release a log memory entry
  SYNOPSIS
1292
    release_ddl_log_memory_entry()
1293 1294 1295 1296 1297
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

1298
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
1299
{
1300 1301 1302 1303
  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");
1304

1305
  global_ddl_log.first_free= log_entry;
1306 1307 1308 1309 1310
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1311
    global_ddl_log.first_used= next_log_entry;
1312 1313
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1314
  DBUG_VOID_RETURN;
1315 1316 1317
}


unknown's avatar
unknown committed
1318
/*
1319
  Execute one entry in the ddl log. Executing an entry means executing
unknown's avatar
unknown committed
1320 1321
  a linked list of actions.
  SYNOPSIS
1322
    execute_ddl_log_entry()
unknown's avatar
unknown committed
1323 1324 1325 1326 1327 1328
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1329
bool execute_ddl_log_entry(THD *thd, uint first_entry)
unknown's avatar
unknown committed
1330
{
1331
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
unknown committed
1332
  uint read_entry= first_entry;
1333
  DBUG_ENTER("execute_ddl_log_entry");
unknown's avatar
unknown committed
1334

1335
  pthread_mutex_lock(&LOCK_gdl);
unknown's avatar
unknown committed
1336 1337
  do
  {
1338
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1339 1340
    {
      /* Write to error log and continue with next log entry */
1341 1342
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1343 1344
      break;
    }
1345 1346
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1347

1348
    if (execute_ddl_log_action(thd, &ddl_log_entry))
unknown's avatar
unknown committed
1349
    {
1350
      /* Write to error log and continue with next log entry */
1351 1352
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1353
      break;
unknown's avatar
unknown committed
1354
    }
1355
    read_entry= ddl_log_entry.next_entry;
unknown's avatar
unknown committed
1356
  } while (read_entry);
1357
  pthread_mutex_unlock(&LOCK_gdl);
unknown's avatar
unknown committed
1358 1359 1360
  DBUG_RETURN(FALSE);
}

1361

1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
/*
  Close the ddl log
  SYNOPSIS
    close_ddl_log()
  RETURN VALUES
    NONE
*/

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


unknown's avatar
unknown committed
1382
/*
1383
  Execute the ddl log at recovery of MySQL Server
unknown's avatar
unknown committed
1384
  SYNOPSIS
1385
    execute_ddl_log_recovery()
unknown's avatar
unknown committed
1386 1387 1388 1389
  RETURN VALUES
    NONE
*/

1390
void execute_ddl_log_recovery()
unknown's avatar
unknown committed
1391
{
1392
  uint num_entries, i;
unknown's avatar
Fixes  
unknown committed
1393
  THD *thd;
1394
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
Fixes  
unknown committed
1395
  char file_name[FN_REFLEN];
1396
  DBUG_ENTER("execute_ddl_log_recovery");
unknown's avatar
unknown committed
1397

1398 1399 1400 1401 1402 1403 1404
  /*
    Initialise global_ddl_log struct
  */
  bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
  global_ddl_log.inited= FALSE;
  global_ddl_log.recovery_phase= TRUE;
  global_ddl_log.io_size= IO_SIZE;
unknown's avatar
unknown committed
1405
  global_ddl_log.file_id= (File) -1;
1406

1407 1408 1409 1410 1411 1412 1413 1414
  /*
    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();

1415
  num_entries= read_ddl_log_header();
1416
  for (i= 1; i < num_entries + 1; i++)
unknown's avatar
unknown committed
1417
  {
1418
    if (read_ddl_log_entry(i, &ddl_log_entry))
1419
    {
1420 1421 1422
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1423
    }
1424
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
unknown's avatar
unknown committed
1425
    {
unknown's avatar
Fixes  
unknown committed
1426
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
unknown's avatar
unknown committed
1427
      {
1428 1429
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
unknown's avatar
unknown committed
1430 1431 1432
      }
    }
  }
1433
  close_ddl_log();
1434 1435 1436 1437 1438 1439
  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);
1440
  DBUG_VOID_RETURN;
1441 1442 1443 1444
}


/*
1445
  Release all memory allocated to the ddl log
1446
  SYNOPSIS
1447
    release_ddl_log()
1448 1449 1450 1451
  RETURN VALUES
    NONE
*/

1452
void release_ddl_log()
1453
{
1454 1455 1456
  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");
1457

1458 1459 1460
  if (!global_ddl_log.do_release)
    DBUG_VOID_RETURN;

1461
  pthread_mutex_lock(&LOCK_gdl);
1462 1463
  while (used_list)
  {
1464
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1465
    my_free(used_list, MYF(0));
unknown's avatar
unknown committed
1466
    used_list= tmp;
1467 1468 1469
  }
  while (free_list)
  {
1470
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1471
    my_free(free_list, MYF(0));
unknown's avatar
unknown committed
1472
    free_list= tmp;
1473
  }
1474
  close_ddl_log();
unknown's avatar
unknown committed
1475
  global_ddl_log.inited= 0;
1476
  pthread_mutex_unlock(&LOCK_gdl);
1477
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1478
  global_ddl_log.do_release= false;
1479
  DBUG_VOID_RETURN;
1480 1481 1482
}


unknown's avatar
unknown committed
1483 1484 1485
/*
---------------------------------------------------------------------------

1486
  END MODULE DDL log
unknown's avatar
unknown committed
1487 1488 1489 1490 1491
  --------------------

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

unknown's avatar
unknown committed
1492

1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517
/**
   @brief construct a temporary shadow file name.

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

   @retval     path length
*/

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


unknown's avatar
unknown committed
1518 1519 1520 1521 1522 1523 1524 1525
/*
  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
1526 1527 1528 1529 1530 1531
      WFRM_INSTALL_SHADOW       If set we should install the new frm
      WFRM_KEEP_SHARE           If set we know that the share is to be
                                retained and thus we should ensure share
                                object is correct, if not set we don't
                                set the new partition syntax string since
                                we know the share object is destroyed.
unknown's avatar
unknown committed
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553
      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];
1554 1555
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
unknown's avatar
unknown committed
1556
  char frm_name[FN_REFLEN+1];
1557 1558 1559 1560
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char *part_syntax_buf;
  uint syntax_len;
#endif
unknown's avatar
unknown committed
1561 1562
  DBUG_ENTER("mysql_write_frm");

1563 1564 1565
  /*
    Build shadow frm file name
  */
1566
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
1567
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1568
  if (flags & WFRM_WRITE_SHADOW)
unknown's avatar
unknown committed
1569
  {
1570 1571 1572 1573 1574 1575 1576 1577
    if (mysql_prepare_create_table(lpt->thd, lpt->create_info,
                                   lpt->alter_info,
                                   /*tmp_table*/ 1,
                                   &lpt->db_options,
                                   lpt->table->file,
                                   &lpt->key_info_buffer,
                                   &lpt->key_count,
                                   /*select_field_count*/ 0))
unknown's avatar
unknown committed
1578 1579 1580 1581 1582
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1583 1584
      partition_info *part_info= lpt->table->part_info;
      if (part_info)
unknown's avatar
unknown committed
1585
      {
1586 1587
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1588
                                                         TRUE, TRUE)))
1589 1590 1591 1592
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
1593
        part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
1594 1595 1596
      }
    }
#endif
1597 1598 1599 1600
    /* 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,
1601
                          lpt->alter_info->create_list, lpt->key_count,
1602
                          lpt->key_info_buffer, lpt->table->file)) ||
1603 1604 1605
        lpt->table->file->ha_create_handler_files(shadow_path, NULL,
                                                  CHF_CREATE_FLAG,
                                                  lpt->create_info))
1606 1607 1608 1609 1610
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
unknown's avatar
unknown committed
1611 1612 1613 1614 1615 1616 1617 1618 1619
  }
  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.
    */
1620 1621
    uchar *data;
    size_t length;
1622
    if (readfrm(shadow_path, &data, &length) ||
unknown's avatar
unknown committed
1623 1624
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
1625 1626
      my_free(data, MYF(MY_ALLOW_ZERO_PTR));
      my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1627 1628 1629 1630
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1631
    error= my_delete(shadow_frm_name, MYF(MY_WME));
unknown's avatar
unknown committed
1632
  }
1633 1634
  if (flags & WFRM_INSTALL_SHADOW)
  {
1635 1636 1637
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1638 1639 1640
    /*
      Build frm file name
    */
1641
    build_table_filename(path, sizeof(path) - 1, lpt->db,
1642
                         lpt->table_name, "", 0);
1643 1644 1645 1646
    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.
1647 1648 1649 1650 1651 1652
      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.
1653 1654 1655
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1656
#ifdef WITH_PARTITION_STORAGE_ENGINE
1657 1658
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_DELETE_FLAG, NULL) ||
1659 1660
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1661
#endif
1662
#ifdef WITH_PARTITION_STORAGE_ENGINE
1663
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1664 1665
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_RENAME_FLAG, NULL))
1666 1667 1668
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1669 1670
    {
      error= 1;
1671
      goto err;
1672
    }
1673
#ifdef WITH_PARTITION_STORAGE_ENGINE
1674
    if (part_info && (flags & WFRM_KEEP_SHARE))
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700
    {
      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;
1701
    }
1702 1703 1704
#endif

err:
1705
    VOID(pthread_mutex_unlock(&LOCK_open));
1706
#ifdef WITH_PARTITION_STORAGE_ENGINE
1707
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1708
    part_info->frm_log_entry= NULL;
1709
    VOID(sync_ddl_log());
1710
#endif
unknown's avatar
unknown committed
1711
  }
1712

unknown's avatar
unknown committed
1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734
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,
1735
                   char const *query, ulong query_length, bool is_trans)
unknown's avatar
unknown committed
1736 1737 1738
{
  if (mysql_bin_log.is_open())
  {
1739
    int errcode= 0;
unknown's avatar
unknown committed
1740 1741
    if (clear_error)
      thd->clear_error();
1742 1743
    else
      errcode= query_error_code(thd, TRUE);
1744

unknown's avatar
unknown committed
1745
    thd->binlog_query(THD::STMT_QUERY_TYPE,
1746
                      query, query_length, is_trans, FALSE, FALSE, errcode);
unknown's avatar
unknown committed
1747 1748 1749
  }
}

1750

unknown's avatar
unknown committed
1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768
/*
 delete (drop) tables.

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

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

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

  RETURN
unknown's avatar
unknown committed
1769 1770
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
1771 1772

*/
unknown's avatar
unknown committed
1773

unknown's avatar
unknown committed
1774 1775
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
1776
{
1777
  bool error= FALSE, need_start_waiters= FALSE;
1778
  Drop_table_error_handler err_handler(thd->get_internal_handler());
unknown's avatar
unknown committed
1779 1780 1781 1782
  DBUG_ENTER("mysql_rm_table");

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

1783
  if (!drop_temporary)
unknown's avatar
unknown committed
1784
  {
1785
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1786
    {
1787
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1788
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1789
    }
1790 1791
    else
      need_start_waiters= TRUE;
unknown's avatar
unknown committed
1792
  }
1793 1794 1795 1796 1797 1798

  /*
    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.
  */
1799
  thd->push_internal_handler(&err_handler);
unknown's avatar
VIEW  
unknown committed
1800
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1801 1802
  thd->pop_internal_handler();

1803

1804 1805 1806
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1807
  if (error)
unknown's avatar
unknown committed
1808
    DBUG_RETURN(TRUE);
1809
  my_ok(thd);
unknown's avatar
unknown committed
1810
  DBUG_RETURN(FALSE);
1811 1812
}

1813
/*
1814 1815 1816 1817 1818 1819 1820 1821 1822
  Execute the drop of a normal or temporary table

  SYNOPSIS
    mysql_rm_table_part2()
    thd			Thread handler
    tables		Tables to drop
    if_exists		If set, don't give an error if table doesn't exists.
			In this case we give an warning of level 'NOTE'
    drop_temporary	Only drop temporary tables
unknown's avatar
VIEW  
unknown committed
1823
    drop_view		Allow to delete VIEW .frm
1824 1825
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1826

1827 1828 1829 1830 1831 1832 1833 1834 1835
  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.
1836 1837 1838 1839 1840

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1841
*/
1842 1843

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
unknown's avatar
VIEW  
unknown committed
1844 1845
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1846 1847
{
  TABLE_LIST *table;
1848
  char path[FN_REFLEN + 1], *alias;
1849
  uint path_length;
1850
  String wrong_tables;
1851
  int error= 0;
1852
  int non_temp_tables_count= 0;
unknown's avatar
unknown committed
1853
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1854
  String built_query;
1855
  String built_tmp_query;
1856 1857
  DBUG_ENTER("mysql_rm_table_part2");

1858 1859 1860
  LINT_INIT(alias);
  LINT_INIT(path_length);

1861
  if (thd->is_current_stmt_binlog_format_row() && !dont_log_query)
1862 1863 1864 1865 1866 1867 1868
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1869

1870
  mysql_ha_rm_tables(thd, tables, FALSE);
1871

1872 1873
  pthread_mutex_lock(&LOCK_open);

unknown's avatar
unknown committed
1874 1875 1876 1877 1878 1879 1880 1881 1882
  /*
    If we have the table in the definition cache, we don't have to check the
    .frm file to find if the table is a normal table (not view) and what
    engine to use.
  */

  for (table= tables; table; table= table->next_local)
  {
    TABLE_SHARE *share;
unknown's avatar
unknown committed
1883
    table->db_type= NULL;
unknown's avatar
unknown committed
1884
    if ((share= get_cached_table_share(table->db, table->table_name)))
unknown's avatar
unknown committed
1885
      table->db_type= share->db_type();
1886 1887

    /* Disable drop of enabled log tables */
1888
    if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) &&
1889 1890
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1891
    {
1892
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1893
      pthread_mutex_unlock(&LOCK_open);
1894 1895
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
1896 1897
  }

1898 1899
  if (!drop_temporary && lock_table_names_exclusively(thd, tables))
  {
1900
    pthread_mutex_unlock(&LOCK_open);
1901
    DBUG_RETURN(1);
1902
  }
1903

unknown's avatar
VIEW  
unknown committed
1904
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
1905
  {
1906
    char *db=table->db;
unknown's avatar
unknown committed
1907 1908
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1909

1910 1911 1912
    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));
1913 1914 1915 1916 1917 1918 1919

    error= drop_temporary_table(thd, table);

    switch (error) {
    case  0:
      // removed temporary table
      tmp_table_deleted= 1;
1920
      if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
1921
          thd->is_current_stmt_binlog_format_row())
1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938
      {
        if (built_tmp_query.is_empty()) 
        {
          built_tmp_query.set_charset(system_charset_info);
          built_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS ");
        }

        built_tmp_query.append("`");
        if (thd->db == NULL || strcmp(db,thd->db) != 0)
        {
          built_tmp_query.append(db);
          built_tmp_query.append("`.`");
        }
        built_tmp_query.append(table->table_name);
        built_tmp_query.append("`,");
      }

1939 1940
      continue;
    case -1:
1941
      DBUG_ASSERT(thd->in_sub_stmt);
1942 1943 1944 1945 1946
      error= 1;
      goto err_with_placeholders;
    default:
      // temporary table not found
      error= 0;
unknown's avatar
unknown committed
1947
    }
unknown's avatar
unknown committed
1948

1949 1950 1951 1952 1953 1954
    /*
      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.
      */
1955
    if (!drop_temporary && thd->is_current_stmt_binlog_format_row() && !dont_log_query)
1956
    {
1957
      non_temp_tables_count++;
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972
      /*
        Don't write the database name if it is the current one (or if
        thd->db is NULL).
      */
      built_query.append("`");
      if (thd->db == NULL || strcmp(db,thd->db) != 0)
      {
        built_query.append(db);
        built_query.append("`.`");
      }

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

unknown's avatar
unknown committed
1973
    table_type= table->db_type;
1974
    if (!drop_temporary)
unknown's avatar
unknown committed
1975
    {
1976
      TABLE *locked_table;
unknown's avatar
unknown committed
1977 1978 1979 1980
      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);
1981 1982 1983 1984 1985 1986 1987
      /*
        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;

1988
      if (thd->killed)
1989
      {
1990 1991
        error= -1;
        goto err_with_placeholders;
1992
      }
1993
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
unknown's avatar
unknown committed
1994
      /* remove .frm file and engine files */
1995 1996
      path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
                                        reg_ext,
1997 1998
                                        table->internal_tmp_table ?
                                        FN_IS_TMP : 0);
unknown's avatar
unknown committed
1999
    }
2000
    if (drop_temporary ||
Staale Smedseng's avatar
Staale Smedseng committed
2001 2002
        ((table_type == NULL &&        
         access(path, F_OK) &&
unknown's avatar
unknown committed
2003 2004
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
unknown's avatar
unknown committed
2005
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
unknown's avatar
unknown committed
2006
    {
2007
      // Table was not found on disk and table can't be created from engine
2008
      if (if_exists)
2009 2010
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
2011
			    table->table_name);
2012
      else
2013
        error= 1;
unknown's avatar
unknown committed
2014 2015 2016
    }
    else
    {
unknown's avatar
unknown committed
2017
      char *end;
unknown's avatar
unknown committed
2018 2019 2020 2021 2022
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
2023 2024
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
unknown's avatar
unknown committed
2025
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
2026
                             !dont_log_query);
2027
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
unknown's avatar
unknown committed
2028
	  (if_exists || table_type == NULL))
2029
      {
2030
	error= 0;
2031 2032
        thd->clear_error();
      }
unknown's avatar
unknown committed
2033
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
2034 2035
      {
	/* the table is referenced by a foreign key constraint */
2036
	foreign_key_error=1;
unknown's avatar
unknown committed
2037
      }
2038
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
2039
      {
2040
        int new_error;
unknown's avatar
unknown committed
2041 2042
	/* Delete the table definition file */
	strmov(end,reg_ext);
2043
	if (!(new_error=my_delete(path,MYF(MY_WME))))
2044
        {
unknown's avatar
unknown committed
2045
	  some_tables_deleted=1;
2046 2047
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
2048
        }
2049
        error|= new_error;
2050
      }
unknown's avatar
unknown committed
2051 2052 2053 2054 2055
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
2056
      wrong_tables.append(String(table->table_name,system_charset_info));
unknown's avatar
unknown committed
2057
    }
2058 2059
    DBUG_PRINT("table", ("table: 0x%lx  s: 0x%lx", (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
unknown's avatar
unknown committed
2060
  }
2061 2062 2063 2064 2065
  /*
    It's safe to unlock LOCK_open: we have an exclusive lock
    on the table name.
  */
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2066
  thd->thread_specific_used|= tmp_table_deleted;
2067 2068 2069 2070
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
2071
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
2072
                      wrong_tables.c_ptr());
2073
    else
unknown's avatar
unknown committed
2074
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
2075 2076 2077 2078
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
2079
  {
unknown's avatar
unknown committed
2080
    query_cache_invalidate3(thd, tables, 0);
2081
    if (!dont_log_query)
2082
    {
2083
      if (!thd->is_current_stmt_binlog_format_row() ||
Staale Smedseng's avatar
Staale Smedseng committed
2084
          (non_temp_tables_count > 0 && !tmp_table_deleted))
2085 2086 2087 2088 2089 2090 2091 2092
      {
        /*
          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.
         */
2093
        write_bin_log(thd, !error, thd->query(), thd->query_length());
2094
      }
2095
      else if (thd->is_current_stmt_binlog_format_row() &&
2096 2097
               tmp_table_deleted)
      {
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117
        if (non_temp_tables_count > 0)
        {
          /*
            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 may have not been created on the
              slave - check "if" branch below, 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());
        }

2118
        /*
2119 2120 2121
          One needs to always log any temporary table drop, if:
            1. thread logging format is mixed mode; AND
            2. current statement logging format is set to row.
2122
        */
2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133
        if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED)
        {
          /*
            In this case we have deleted some temporary tables but we are using
            row based logging for the statement. However, thread uses mixed mode
            format, thence we need to log the dropping as we cannot tell for
            sure whether the create was logged as statement previously or not, ie,
            before switching to row mode.
          */
          built_tmp_query.chop();                  // Chop of the last comma
          built_tmp_query.append(" /* generated by server */");
2134 2135
          write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length(),
                        thd->in_multi_stmt_transaction());
2136
        }
2137
      }
2138

2139 2140
      /*
        The remaining cases are:
2141 2142
        - no tables were deleted and
        - only temporary tables were deleted and row-based
2143 2144 2145 2146
          replication is used.
        In both these cases, nothing should be written to the binary
        log.
      */
2147
    }
unknown's avatar
unknown committed
2148
  }
2149 2150
  pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
2151
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
2152
  pthread_mutex_unlock(&LOCK_open);
2153
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2154 2155 2156
}


2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171
/*
  Quickly remove a table.

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

  RETURN
    0           OK
    != 0        Error
*/

unknown's avatar
unknown committed
2172
bool quick_rm_table(handlerton *base,const char *db,
2173
                    const char *table_name, uint flags)
unknown's avatar
unknown committed
2174
{
2175
  char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
2176 2177 2178
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

2179
  uint path_length= build_table_filename(path, sizeof(path) - 1,
2180
                                         db, table_name, reg_ext, flags);
unknown's avatar
unknown committed
2181
  if (my_delete(path,MYF(0)))
unknown's avatar
unknown committed
2182
    error= 1; /* purecov: inspected */
2183
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
2184 2185 2186
  if (!(flags & FRM_ONLY))
    error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2187 2188
}

2189 2190 2191
/*
  Sort keys in the following order:
  - PRIMARY KEY
2192 2193
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203
  - 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)
{
2204 2205 2206
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
2207
  {
2208
    if (!(b_flags & HA_NOSAME))
2209
      return -1;
2210
    if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
2211 2212
    {
      /* Sort NOT NULL keys before other keys */
2213
      return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
2214 2215 2216 2217 2218
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
2219 2220 2221
    /* 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;
2222
  }
2223
  else if (b_flags & HA_NOSAME)
2224 2225
    return 1;					// Prefer b

2226
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
2227
  {
2228
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
2229
  }
unknown's avatar
unknown committed
2230
  /*
2231
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
2232 2233 2234 2235 2236
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
2237 2238
}

2239 2240
/*
  Check TYPELIB (set or enum) for duplicates
2241

2242 2243 2244
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
2245 2246
    name	  name of the checked column
    typelib	  list of values for the column
2247
    dup_val_count  returns count of duplicate elements
2248 2249

  DESCRIPTION
2250
    This function prints an warning for each value in list
2251 2252 2253
    which has some duplicates on its right

  RETURN VALUES
2254 2255
    0             ok
    1             Error
2256 2257
*/

2258
bool check_duplicates_in_interval(const char *set_or_name,
2259
                                  const char *name, TYPELIB *typelib,
2260
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
2261
{
2262
  TYPELIB tmp= *typelib;
2263
  const char **cur_value= typelib->type_names;
2264
  unsigned int *cur_length= typelib->type_lengths;
2265
  *dup_val_count= 0;  
2266 2267
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
2268
  {
2269 2270 2271 2272
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
2273
    {
2274 2275 2276 2277 2278 2279 2280
      if ((current_thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
                 name,*cur_value,set_or_name);
        return 1;
      }
unknown's avatar
unknown committed
2281
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
2282 2283 2284
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
2285
      (*dup_val_count)++;
2286 2287
    }
  }
2288
  return 0;
2289
}
2290

2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318

/*
  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++)
  {
2319
    size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
2320 2321 2322 2323 2324 2325
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


unknown's avatar
unknown committed
2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336
/*
  Prepare a create_table instance for packing

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

  DESCRIPTION
unknown's avatar
unknown committed
2337
    This function prepares a Create_field instance.
unknown's avatar
unknown committed
2338 2339 2340 2341 2342 2343 2344
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

unknown's avatar
unknown committed
2345
int prepare_create_field(Create_field *sql_field, 
unknown's avatar
unknown committed
2346 2347
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2348
			 longlong table_flags)
unknown's avatar
unknown committed
2349
{
2350
  unsigned int dup_val_count;
unknown's avatar
unknown committed
2351
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
2352 2353

  /*
2354
    This code came from mysql_prepare_create_table.
unknown's avatar
unknown committed
2355 2356 2357 2358 2359
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
2360 2361 2362 2363
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
2364 2365 2366 2367 2368 2369 2370
    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;
2371
    (*blob_columns)++;
unknown's avatar
unknown committed
2372
    break;
2373
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
2374
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
2375 2376 2377 2378
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
unknown's avatar
unknown committed
2379
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2380 2381 2382 2383 2384 2385 2386 2387
    }
    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;
2388
    (*blob_columns)++;
unknown's avatar
unknown committed
2389 2390 2391 2392 2393
    break;
#else
    my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
                    sym_group_geom.name, sym_group_geom.needed_define);
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2394
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
2395
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
2396
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
2397 2398 2399 2400 2401 2402 2403 2404
    if (table_flags & HA_NO_VARCHAR)
    {
      /* convert VARCHAR to CHAR because handler is not yet up to date */
      sql_field->sql_type=    MYSQL_TYPE_VAR_STRING;
      sql_field->pack_length= calc_pack_length(sql_field->sql_type,
                                               (uint) sql_field->length);
      if ((sql_field->length / sql_field->charset->mbmaxlen) >
          MAX_FIELD_CHARLENGTH)
unknown's avatar
unknown committed
2405
      {
unknown's avatar
unknown committed
2406 2407
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
unknown's avatar
unknown committed
2408 2409
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
2410 2411 2412
    }
#endif
    /* fall through */
2413
  case MYSQL_TYPE_STRING:
unknown's avatar
unknown committed
2414 2415 2416 2417
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2418
  case MYSQL_TYPE_ENUM:
unknown's avatar
unknown committed
2419 2420 2421 2422 2423
    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;
2424 2425 2426 2427
    if (check_duplicates_in_interval("ENUM",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2428
    break;
2429
  case MYSQL_TYPE_SET:
unknown's avatar
unknown committed
2430 2431 2432 2433 2434
    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;
2435 2436 2437 2438
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2439 2440 2441 2442 2443 2444
    /* Check that count of unique members is not more then 64 */
    if (sql_field->interval->count -  dup_val_count > sizeof(longlong)*8)
    {
       my_error(ER_TOO_BIG_SET, MYF(0), sql_field->field_name);
       DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
2445
    break;
2446 2447 2448 2449 2450
  case MYSQL_TYPE_DATE:			// Rest of string types
  case MYSQL_TYPE_NEWDATE:
  case MYSQL_TYPE_TIME:
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_NULL:
unknown's avatar
unknown committed
2451 2452
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2453
  case MYSQL_TYPE_BIT:
unknown's avatar
unknown committed
2454
    /* 
2455 2456
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
unknown's avatar
unknown committed
2457
    */
unknown's avatar
unknown committed
2458
    break;
2459
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
2460 2461 2462 2463 2464 2465 2466
    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;
2467
  case MYSQL_TYPE_TIMESTAMP:
unknown's avatar
unknown committed
2468 2469 2470
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2471
      if (!*timestamps)
unknown's avatar
unknown committed
2472
      {
unknown's avatar
unknown committed
2473
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2474
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2475
      }
unknown's avatar
unknown committed
2476 2477 2478 2479
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2480
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2481

2482
    (*timestamps)++;
unknown's avatar
unknown committed
2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497
    /* fall-through */
  default:
    sql_field->pack_flag=(FIELDFLAG_NUMBER |
                          (sql_field->flags & UNSIGNED_FLAG ? 0 :
                           FIELDFLAG_DECIMAL) |
                          (sql_field->flags & ZEROFILL_FLAG ?
                           FIELDFLAG_ZEROFILL : 0) |
                          f_settype((uint) sql_field->sql_type) |
                          (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
    break;
  }
  if (!(sql_field->flags & NOT_NULL_FLAG))
    sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
  if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
    sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
unknown's avatar
unknown committed
2498 2499 2500
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2501
/*
2502
  Preparation for table creation
unknown's avatar
unknown committed
2503 2504

  SYNOPSIS
2505
    mysql_prepare_create_table()
2506 2507
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2508
      alter_info                List of columns and indexes to create
2509 2510 2511 2512 2513 2514
      tmp_table                 If a temporary table is to be created.
      db_options          INOUT Table options (like HA_OPTION_PACK_RECORD).
      file                      The handler for the new table.
      key_info_buffer     OUT   An array of KEY structs for the indexes.
      key_count           OUT   The number of elements in the array.
      select_field_count        The number of fields coming from a select table.
unknown's avatar
unknown committed
2515

2516
  DESCRIPTION
2517
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
2518

2519
  NOTES
2520
    sets create_info->varchar if the table has a varchar
2521

unknown's avatar
unknown committed
2522
  RETURN VALUES
2523 2524
    FALSE    OK
    TRUE     error
unknown's avatar
unknown committed
2525
*/
unknown's avatar
unknown committed
2526

unknown's avatar
unknown committed
2527
static int
2528 2529 2530 2531 2532 2533
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
                           Alter_info *alter_info,
                           bool tmp_table,
                           uint *db_options,
                           handler *file, KEY **key_info_buffer,
                           uint *key_count, int select_field_count)
unknown's avatar
unknown committed
2534
{
2535
  const char	*key_name;
unknown's avatar
unknown committed
2536
  Create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
2537
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
2538
  ulong		record_offset= 0;
2539
  KEY		*key_info;
unknown's avatar
unknown committed
2540
  KEY_PART_INFO *key_part_info;
2541 2542 2543
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
unknown's avatar
unknown committed
2544 2545
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
unknown's avatar
unknown committed
2546
  uint total_uneven_bit_length= 0;
2547
  DBUG_ENTER("mysql_prepare_create_table");
unknown's avatar
unknown committed
2548

2549
  select_field_pos= alter_info->create_list.elements - select_field_count;
unknown's avatar
unknown committed
2550
  null_fields=blob_columns=0;
2551
  create_info->varchar= 0;
unknown's avatar
unknown committed
2552
  max_key_length= file->max_key_length();
2553

2554
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
2555
  {
2556 2557
    CHARSET_INFO *save_cs;

2558 2559 2560 2561 2562 2563
    /*
      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;
2564
    if (!sql_field->charset)
2565 2566 2567
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2568 2569 2570
      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.
2571
    */
2572
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2573
      sql_field->charset= create_info->table_charset;
2574

2575
    save_cs= sql_field->charset;
2576 2577 2578 2579 2580
    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];
2581 2582
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2583
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2584
      DBUG_RETURN(TRUE);
2585
    }
2586

2587
    /*
2588
      Convert the default value from client character
2589 2590 2591
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2592
        save_cs != sql_field->def->collation.collation &&
2593 2594 2595 2596
        (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))
2597
    {
2598
      /*
unknown's avatar
unknown committed
2599
        Starting from 5.1 we work here with a copy of Create_field
2600 2601 2602 2603 2604 2605 2606 2607
        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.
      */
2608
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2609 2610 2611 2612 2613

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

2618 2619
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2620 2621 2622
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2623
      TYPELIB *interval= sql_field->interval;
2624 2625 2626 2627 2628 2629

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2630
      if (!interval)
2631
      {
2632
        /*
2633 2634 2635
          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.
2636
        */
2637
        interval= sql_field->interval= typelib(thd->mem_root,
2638
                                               sql_field->interval_list);
2639
        List_iterator<String> int_it(sql_field->interval_list);
2640
        String conv, *tmp;
2641 2642 2643 2644 2645
        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);
2646
        for (uint i= 0; (tmp= int_it++); i++)
2647
        {
2648
          size_t lengthsp;
2649 2650 2651 2652 2653
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2654
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
unknown's avatar
unknown committed
2655
                                                  conv.length());
2656 2657
            interval->type_lengths[i]= conv.length();
          }
2658

2659
          // Strip trailing spaces.
unknown's avatar
unknown committed
2660 2661
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2662 2663
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2664
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2665 2666 2667 2668 2669
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
unknown's avatar
unknown committed
2670
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2671
              DBUG_RETURN(TRUE);
2672 2673
            }
          }
2674
        }
2675
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2676 2677
      }

2678
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2679
      {
2680
        uint32 field_length;
2681
        if (sql_field->def != NULL)
2682 2683 2684 2685 2686
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2687 2688 2689 2690 2691
          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);
2692
              DBUG_RETURN(TRUE);
2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704
            }

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

2705 2706 2707
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2708
            DBUG_RETURN(TRUE);
2709 2710
          }
        }
2711 2712
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2713
      }
2714
      else  /* MYSQL_TYPE_ENUM */
2715
      {
2716
        uint32 field_length;
2717
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2718
        if (sql_field->def != NULL)
2719 2720
        {
          String str, *def= sql_field->def->val_str(&str);
2721
          if (def == NULL) /* SQL "NULL" maps to NULL */
2722
          {
2723 2724 2725
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2726
              DBUG_RETURN(TRUE);
2727 2728 2729 2730 2731 2732 2733 2734 2735 2736
            }

            /* 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);
2737
              DBUG_RETURN(TRUE);
2738
            }
2739 2740
          }
        }
2741 2742
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2743 2744 2745 2746
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2747
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2748
    { 
unknown's avatar
unknown committed
2749
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2750
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2751 2752 2753 2754 2755
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2756
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
2757
    if (prepare_blob_field(thd, sql_field))
2758
      DBUG_RETURN(TRUE);
2759

unknown's avatar
unknown committed
2760 2761
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
2762

unknown's avatar
unknown committed
2763 2764
    if (check_column_name(sql_field->field_name))
    {
2765
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2766
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2767
    }
unknown's avatar
unknown committed
2768

2769 2770
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
unknown's avatar
unknown committed
2771
    {
2772
      if (my_strcasecmp(system_charset_info,
2773 2774
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
2775
      {
2776 2777 2778 2779
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2780 2781
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2782
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2783
	  DBUG_RETURN(TRUE);
2784 2785 2786
	}
	else
	{
2787
	  /* Field redefined */
2788
	  sql_field->def=		dup_field->def;
2789
	  sql_field->sql_type=		dup_field->sql_type;
2790 2791 2792
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2793
	  sql_field->length=		dup_field->char_length;
2794
          sql_field->pack_length=	dup_field->pack_length;
2795
          sql_field->key_length=	dup_field->key_length;
2796
	  sql_field->decimals=		dup_field->decimals;
2797
	  sql_field->create_length_to_internal_length();
2798
	  sql_field->unireg_check=	dup_field->unireg_check;
2799 2800 2801 2802 2803 2804 2805 2806
          /* 
            We're making one field from two, the result field will have
            dup_field->flags as flags. If we've incremented null_fields
            because of sql_field->flags, decrement it back.
          */
          if (!(sql_field->flags & NOT_NULL_FLAG))
            null_fields--;
	  sql_field->flags=		dup_field->flags;
unknown's avatar
unknown committed
2807
          sql_field->interval=          dup_field->interval;
2808 2809 2810
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2811
	}
unknown's avatar
unknown committed
2812 2813
      }
    }
2814 2815
    /* 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
2816 2817
	(sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
2818
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
2819 2820
    it2.rewind();
  }
2821 2822 2823

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
2824
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
2825 2826 2827 2828

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

unknown's avatar
unknown committed
2831 2832
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2833
			     file->ha_table_flags()))
2834
      DBUG_RETURN(TRUE);
2835
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2836
      create_info->varchar= TRUE;
2837
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
2838 2839
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2840
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
2841
  }
2842 2843
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
2844 2845
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2846
    DBUG_RETURN(TRUE);
2847
  }
unknown's avatar
unknown committed
2848 2849
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
2850
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2851
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2852 2853
  }
  if (auto_increment &&
2854
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
2855
  {
unknown's avatar
unknown committed
2856 2857
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2858
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2859 2860
  }

2861
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
2862
  {
unknown's avatar
unknown committed
2863 2864
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2865
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2866 2867 2868
  }

  /* Create keys */
2869

2870 2871
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2872
  uint key_parts=0, fk_key_count=0;
2873
  bool primary_key=0,unique_key=0;
2874
  Key *key, *key2;
unknown's avatar
unknown committed
2875
  uint tmp, key_number;
2876 2877
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2878

2879
  /* Calculate number of key segements */
2880
  *key_count= 0;
2881

unknown's avatar
unknown committed
2882 2883
  while ((key=key_iterator++))
  {
2884 2885
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2886
    LEX_STRING key_name_str;
2887 2888 2889
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
unknown's avatar
unknown committed
2890
      Foreign_key *fk_key= (Foreign_key*) key;
2891 2892 2893
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2894 2895 2896
        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));
2897
	DBUG_RETURN(TRUE);
2898 2899 2900
      }
      continue;
    }
2901
    (*key_count)++;
unknown's avatar
unknown committed
2902
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
2903 2904 2905
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2906
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2907
    }
2908 2909 2910 2911
    key_name_str.str= (char*) key->name;
    key_name_str.length= key->name ? strlen(key->name) : 0;
    if (check_string_char_length(&key_name_str, "", NAME_CHAR_LEN,
                                 system_charset_info, 1))
unknown's avatar
unknown committed
2912
    {
2913
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
2914
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2915
    }
2916
    key_iterator2.rewind ();
2917
    if (key->type != Key::FOREIGN_KEY)
2918
    {
2919
      while ((key2 = key_iterator2++) != key)
2920
      {
unknown's avatar
unknown committed
2921
	/*
2922 2923 2924
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
unknown's avatar
unknown committed
2925
        */
2926 2927 2928
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2929
        {
2930
          /* TODO: issue warning message */
2931 2932 2933 2934 2935 2936 2937
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2938 2939 2940
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2941 2942 2943
          }
          break;
        }
2944 2945 2946 2947 2948 2949
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2950
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2951 2952 2953
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
2954
      DBUG_RETURN(TRUE);
2955
    }
2956
  }
unknown's avatar
unknown committed
2957
  tmp=file->max_keys();
2958
  if (*key_count > tmp)
2959 2960
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
2961
    DBUG_RETURN(TRUE);
2962
  }
2963

2964
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
2965
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
2966
  if (!*key_info_buffer || ! key_part_info)
2967
    DBUG_RETURN(TRUE);				// Out of memory
2968

2969
  key_iterator.rewind();
unknown's avatar
unknown committed
2970
  key_number=0;
unknown's avatar
unknown committed
2971
  for (; (key=key_iterator++) ; key_number++)
2972 2973
  {
    uint key_length=0;
unknown's avatar
unknown committed
2974
    Key_part_spec *column;
2975

2976 2977 2978 2979 2980 2981 2982 2983 2984 2985
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

2986
    switch (key->type) {
unknown's avatar
unknown committed
2987
    case Key::MULTIPLE:
2988
	key_info->flags= 0;
2989
	break;
unknown's avatar
unknown committed
2990
    case Key::FULLTEXT:
2991
	key_info->flags= HA_FULLTEXT;
unknown's avatar
unknown committed
2992
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
2993
          key_info->flags|= HA_USES_PARSER;
2994 2995
        else
          key_info->parser_name= 0;
2996
	break;
unknown's avatar
unknown committed
2997
    case Key::SPATIAL:
unknown's avatar
unknown committed
2998
#ifdef HAVE_SPATIAL
2999
	key_info->flags= HA_SPATIAL;
3000
	break;
unknown's avatar
unknown committed
3001
#else
3002 3003
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
3004
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3005
#endif
3006 3007 3008 3009
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
3010 3011
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
3012
    }
3013 3014
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
3015

unknown's avatar
unknown committed
3016 3017
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
3018
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
3019
    key_info->algorithm= key->key_create_info.algorithm;
unknown's avatar
unknown committed
3020

3021 3022
    if (key->type == Key::FULLTEXT)
    {
3023
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
3024
      {
unknown's avatar
unknown committed
3025 3026
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
3027
	DBUG_RETURN(TRUE);
3028 3029
      }
    }
unknown's avatar
unknown committed
3030 3031 3032
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
3033
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
3034 3035 3036
       in near future when new frm file is ready
       checking for proper key parts number:
    */
3037

3038
    /* TODO: Add proper checks if handler supports key_type and algorithm */
3039
    if (key_info->flags & HA_SPATIAL)
3040
    {
3041
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
3042 3043 3044
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
3045
        DBUG_RETURN(TRUE);
3046
      }
3047 3048
      if (key_info->key_parts != 1)
      {
3049
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
3050
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3051
      }
3052
    }
unknown's avatar
unknown committed
3053
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
3054
    {
unknown's avatar
unknown committed
3055
#ifdef HAVE_RTREE_KEYS
3056 3057
      if ((key_info->key_parts & 1) == 1)
      {
3058
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
3059
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3060
      }
3061
      /* TODO: To be deleted */
3062
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
3063
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3064
#else
3065 3066
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
3067
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3068
#endif
unknown's avatar
unknown committed
3069
    }
3070

3071 3072 3073 3074 3075
    /* Take block size from key part or table part */
    /*
      TODO: Add warning if block size changes. We can't do it here, as
      this may depend on the size of the key
    */
unknown's avatar
unknown committed
3076 3077
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
3078 3079 3080 3081 3082
                           create_info->key_block_size);

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

unknown's avatar
unknown committed
3083
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
3084
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
3085 3086
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
3087
      uint length;
unknown's avatar
unknown committed
3088
      Key_part_spec *dup_column;
unknown's avatar
unknown committed
3089

unknown's avatar
unknown committed
3090 3091 3092
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
3093
	     my_strcasecmp(system_charset_info,
3094 3095
			   column->field_name,
			   sql_field->field_name))
unknown's avatar
unknown committed
3096 3097 3098
	field++;
      if (!sql_field)
      {
3099
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
3100
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3101
      }
unknown's avatar
unknown committed
3102
      while ((dup_column= cols2++) != column)
3103 3104 3105 3106 3107 3108 3109
      {
        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);
3110
	  DBUG_RETURN(TRUE);
3111 3112 3113
	}
      }
      cols2.rewind();
3114
      if (key->type == Key::FULLTEXT)
3115
      {
3116 3117
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
3118 3119
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
3120
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
3121 3122
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
3123
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134
	    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));
3135
      }
3136
      else
3137
      {
3138 3139
	column->length*= sql_field->charset->mbmaxlen;

unknown's avatar
unknown committed
3140 3141 3142
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
3143
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3144 3145
	}

3146 3147
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
3148
	{
3149
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
3150
	  {
3151
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
3152
	    DBUG_RETURN(TRUE);
3153
	  }
3154 3155
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
3156
            column->length= 25;
3157 3158
	  if (!column->length)
	  {
3159
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
3160
	    DBUG_RETURN(TRUE);
3161 3162
	  }
	}
unknown's avatar
unknown committed
3163
#ifdef HAVE_SPATIAL
3164
	if (key->type == Key::SPATIAL)
3165
	{
3166
	  if (!column->length)
3167 3168
	  {
	    /*
3169 3170
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
3171
	    */
3172
	    column->length= 4*sizeof(double);
3173 3174
	  }
	}
unknown's avatar
unknown committed
3175
#endif
3176 3177 3178 3179 3180 3181 3182
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
unknown's avatar
unknown committed
3183
            null_fields--;
3184 3185
	  }
	  else
3186 3187 3188 3189 3190
          {
            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);
3191
              DBUG_RETURN(TRUE);
3192 3193 3194 3195 3196
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
3197
              DBUG_RETURN(TRUE);
3198 3199
            }
          }
3200 3201 3202
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
3203
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
3204 3205
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
3206
      }
3207

unknown's avatar
unknown committed
3208 3209 3210
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
3211 3212
      length= sql_field->key_length;

unknown's avatar
unknown committed
3213 3214 3215 3216
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
3217
	  if ((length=column->length) > max_key_length ||
3218
	      length > file->max_key_part_length())
3219
	  {
unknown's avatar
unknown committed
3220
	    length=min(max_key_length, file->max_key_part_length());
3221 3222 3223 3224 3225 3226 3227 3228
	    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);
3229 3230
              /* Align key length to multibyte char boundary */
              length-= length % sql_field->charset->mbmaxlen;
3231 3232 3233 3234
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
3235
	      DBUG_RETURN(TRUE);
3236 3237
	    }
	  }
unknown's avatar
unknown committed
3238
	}
3239
	else if (!f_is_geom(sql_field->pack_flag) &&
3240
		  (column->length > length ||
unknown's avatar
unknown committed
3241
                   !Field::type_can_have_key_part (sql_field->sql_type) ||
3242
		   ((f_is_packed(sql_field->pack_flag) ||
3243
		     ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
3244 3245 3246
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
unknown's avatar
unknown committed
3247
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
3248
	  DBUG_RETURN(TRUE);
3249
	}
3250
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
3251
	  length=column->length;
unknown's avatar
unknown committed
3252 3253 3254
      }
      else if (length == 0)
      {
3255
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
3256
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3257
      }
3258
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
3259
      {
3260
        length= file->max_key_part_length();
3261 3262 3263 3264 3265 3266 3267 3268
	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);
3269 3270
          /* Align key length to multibyte char boundary */
          length-= length % sql_field->charset->mbmaxlen;
3271 3272 3273 3274
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
3275
	  DBUG_RETURN(TRUE);
3276
	}
3277 3278
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
3279
      /* Use packed keys for long strings on the first column */
3280
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
3281
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
3282 3283
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
3284 3285
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
Staale Smedseng's avatar
Staale Smedseng committed
3286
	if ((column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB)) ||
3287 3288
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
unknown's avatar
unknown committed
3289 3290 3291
	else
	  key_info->flags|= HA_PACK_KEY;
      }
3292 3293 3294 3295
      /* Check if the key segment is partial, set the key flag accordingly */
      if (length != sql_field->key_length)
        key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;

unknown's avatar
unknown committed
3296 3297 3298 3299 3300 3301 3302
      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)
3303 3304 3305
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
3306 3307
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
3308
	    DBUG_RETURN(TRUE);
3309 3310 3311 3312
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
3313
	else if (!(key_name = key->name))
unknown's avatar
unknown committed
3314
	  key_name=make_unique_key_name(sql_field->field_name,
3315 3316
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
3317
	{
3318
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
3319
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3320 3321 3322 3323
	}
	key_info->name=(char*) key_name;
      }
    }
3324 3325
    if (!key_info->name || check_column_name(key_info->name))
    {
3326
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
3327
      DBUG_RETURN(TRUE);
3328
    }
3329 3330
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
3331
    key_info->key_length=(uint16) key_length;
3332
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
3333
    {
3334
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
3335
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3336
    }
unknown's avatar
unknown committed
3337
    key_info++;
unknown's avatar
unknown committed
3338
  }
3339
  if (!unique_key && !primary_key &&
3340
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
3341
  {
unknown's avatar
unknown committed
3342
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
3343
    DBUG_RETURN(TRUE);
3344
  }
unknown's avatar
unknown committed
3345 3346
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
3347
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3348
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3349
  }
3350
  /* Sort keys in optimized order */
3351 3352
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
unknown's avatar
unknown committed
3353
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
3354

3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385
  /* 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);
    }
  }

3386
  DBUG_RETURN(FALSE);
3387 3388
}

3389

unknown's avatar
unknown committed
3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405
/*
  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)
{
3406 3407 3408 3409 3410
  /*
    If the table character set was not given explicitly,
    let's fetch the database default character set and
    apply it to the table.
  */
unknown's avatar
unknown committed
3411 3412 3413
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3414 3415 3416

    load_db_opt_by_name(thd, db, &db_info);

unknown's avatar
unknown committed
3417 3418 3419 3420 3421
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


unknown's avatar
unknown committed
3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434
/*
  Extend long VARCHAR fields to blob & prepare field if it's a blob

  SYNOPSIS
    prepare_blob_field()
    sql_field		Field to check

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

unknown's avatar
unknown committed
3435
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
unknown's avatar
unknown committed
3436 3437 3438 3439 3440 3441 3442 3443 3444
{
  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];

3445 3446
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
3447 3448 3449 3450 3451
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3452
    sql_field->sql_type= MYSQL_TYPE_BLOB;
unknown's avatar
unknown committed
3453
    sql_field->flags|= BLOB_FLAG;
Sergei Golubchik's avatar
Sergei Golubchik committed
3454
    my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name,
3455
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
3456 3457 3458 3459
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
3460

unknown's avatar
unknown committed
3461 3462
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
3463 3464 3465
    if (sql_field->sql_type == FIELD_TYPE_BLOB ||
        sql_field->sql_type == FIELD_TYPE_TINY_BLOB ||
        sql_field->sql_type == FIELD_TYPE_MEDIUM_BLOB)
unknown's avatar
unknown committed
3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476
    {
      /* 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);
}


3477
/*
unknown's avatar
unknown committed
3478
  Preparation of Create_field for SP function return values.
3479 3480
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

unknown's avatar
unknown committed
3492
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3493
{
3494 3495
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3496 3497
  {
    uint32 field_length, dummy;
3498
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3499 3500 3501 3502 3503 3504 3505
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3506
    else /* MYSQL_TYPE_ENUM */
3507 3508 3509 3510 3511 3512 3513 3514 3515
    {
      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);
  }

3516
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3517 3518 3519 3520 3521
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
3522 3523 3524 3525
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3526 3527


3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555
/*
  Write CREATE TABLE binlog

  SYNOPSIS
    write_create_table_bin_log()
    thd               Thread object
    create_info       Create information
    internal_tmp_table  Set to 1 if this is an internal temporary table

  DESCRIPTION
    This function only is called in mysql_create_table_no_lock and
    mysql_create_table

  RETURN VALUES
    NONE
 */
static inline void write_create_table_bin_log(THD *thd,
                                              const HA_CREATE_INFO *create_info,
                                              bool internal_tmp_table)
{
  /*
    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.
    Otherwise, the statement shall be binlogged.
   */
  if (!internal_tmp_table &&
3556 3557
      (!thd->is_current_stmt_binlog_format_row() ||
       (thd->is_current_stmt_binlog_format_row() &&
3558
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3559
    write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3560 3561 3562
}


3563 3564 3565 3566
/*
  Create a table

  SYNOPSIS
unknown's avatar
unknown committed
3567
    mysql_create_table_no_lock()
3568 3569 3570
    thd			Thread object
    db			Database
    table_name		Table name
3571
    create_info	        Create information (like MAX_ROWS)
3572 3573
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
3574
    internal_tmp_table  Set to 1 if this is an internal temporary table
3575
			(From ALTER TABLE)
3576
    select_field_count
3577 3578

  DESCRIPTION
3579
    If one creates a temporary table, this is automatically opened
3580

unknown's avatar
unknown committed
3581 3582 3583 3584 3585
    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.

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

  RETURN VALUES
unknown's avatar
unknown committed
3592 3593
    FALSE OK
    TRUE  error
3594 3595
*/

unknown's avatar
unknown committed
3596
bool mysql_create_table_no_lock(THD *thd,
unknown's avatar
unknown committed
3597
                                const char *db, const char *table_name,
3598 3599 3600 3601
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3602
{
3603
  char		path[FN_REFLEN + 1];
3604
  uint          path_length;
3605 3606 3607 3608
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
3609
  bool		error= TRUE;
unknown's avatar
unknown committed
3610
  DBUG_ENTER("mysql_create_table_no_lock");
3611 3612
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3613

3614

3615
  /* Check for duplicate fields and check type of table to create */
3616
  if (!alter_info->create_list.elements)
3617
  {
unknown's avatar
unknown committed
3618 3619
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
3620
    DBUG_RETURN(TRUE);
3621
  }
3622
  if (check_engine(thd, table_name, create_info))
3623
    DBUG_RETURN(TRUE);
3624
  db_options= create_info->table_options;
3625 3626 3627
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3628 3629
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3630
  {
unknown's avatar
unknown committed
3631
    mem_alloc_error(sizeof(handler));
3632 3633
    DBUG_RETURN(TRUE);
  }
3634
#ifdef WITH_PARTITION_STORAGE_ENGINE
3635 3636
  partition_info *part_info= thd->work_part_info;

unknown's avatar
unknown committed
3637 3638 3639 3640 3641 3642 3643 3644
  if (!part_info && create_info->db_type->partition_flags &&
      (create_info->db_type->partition_flags() & HA_USE_AUTO_PARTITION))
  {
    /*
      Table is not defined as a partitioned table but the engine handles
      all tables as partitioned. The handler will set up the partition info
      object with the default settings.
    */
unknown's avatar
unknown committed
3645
    thd->work_part_info= part_info= new partition_info();
unknown's avatar
unknown committed
3646 3647 3648 3649 3650 3651
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
unknown's avatar
unknown committed
3652
    part_info->default_engine_type= create_info->db_type;
3653
    part_info->is_auto_partitioned= TRUE;
unknown's avatar
unknown committed
3654
  }
3655 3656 3657
  if (part_info)
  {
    /*
unknown's avatar
unknown committed
3658 3659 3660 3661 3662 3663 3664 3665 3666
      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.
3667
    */
3668
    List_iterator<Key> key_iterator(alter_info->key_list);
unknown's avatar
unknown committed
3669
    Key *key;
unknown's avatar
unknown committed
3670
    handlerton *part_engine_type= create_info->db_type;
3671 3672
    char *part_syntax_buf;
    uint syntax_len;
unknown's avatar
unknown committed
3673
    handlerton *engine_type;
3674 3675 3676 3677 3678
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
unknown's avatar
unknown committed
3679 3680
    while ((key= key_iterator++))
    {
3681 3682
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
unknown's avatar
unknown committed
3683
      {
3684
        my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
unknown's avatar
unknown committed
3685 3686 3687
        goto err;
      }
    }
3688
    if ((part_engine_type == partition_hton) &&
3689
        part_info->default_engine_type)
3690 3691 3692 3693 3694 3695
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
unknown's avatar
unknown committed
3696
      ;
3697
    }
3698 3699
    else
    {
unknown's avatar
unknown committed
3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711
      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);
        }
      }
3712
    }
unknown's avatar
unknown committed
3713 3714 3715
    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)));
3716
    if (part_info->check_partition_info(thd, &engine_type, file,
3717
                                        create_info, TRUE))
unknown's avatar
unknown committed
3718
      goto err;
unknown's avatar
unknown committed
3719
    part_info->default_engine_type= engine_type;
unknown's avatar
unknown committed
3720

3721 3722 3723 3724 3725 3726
    /*
      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,
3727
                                                     TRUE, TRUE)))
unknown's avatar
unknown committed
3728
      goto err;
3729 3730
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
3731 3732
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3733
        create_info->db_type == partition_hton)
3734 3735 3736 3737 3738
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
unknown's avatar
unknown committed
3739
      DBUG_PRINT("info", ("db_type: %s",
unknown's avatar
unknown committed
3740
                        ha_resolve_storage_engine_name(create_info->db_type)));
3741
      delete file;
3742
      create_info->db_type= partition_hton;
3743 3744 3745 3746
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3747 3748 3749 3750 3751 3752 3753 3754
      /*
        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 &&
3755 3756
          (int)part_info->no_parts !=
          file->get_default_no_partitions(create_info))
3757
      {
3758
        uint i;
3759
        List_iterator<partition_element> part_it(part_info->partitions);
3760 3761 3762 3763
        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;
3764 3765 3766 3767
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3768
               (int)part_info->no_subparts !=
3769
                 file->get_default_no_partitions(create_info))
3770
      {
3771
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3772
        part_info->no_subparts= file->get_default_no_partitions(create_info);
3773 3774 3775 3776
      }
    }
    else if (create_info->db_type != engine_type)
    {
3777 3778 3779 3780 3781 3782
      /*
        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.
      */
3783
      delete file;
3784 3785
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3786 3787 3788 3789
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3790 3791 3792
    }
  }
#endif
3793

unknown's avatar
unknown committed
3794
  set_table_default_charset(thd, create_info, (char*) db);
3795

3796 3797 3798 3799 3800
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3801
    goto err;
unknown's avatar
unknown committed
3802 3803 3804 3805

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3806
    path_length= build_tmptable_filename(thd, path, sizeof(path));
unknown's avatar
unknown committed
3807 3808
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3809 3810
  else  
  {
3811
    path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,
3812
                                      internal_tmp_table ? FN_IS_TMP : 0);
3813
  }
3814

unknown's avatar
unknown committed
3815
  /* Check if table already exists */
unknown's avatar
unknown committed
3816 3817
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
unknown's avatar
unknown committed
3818
  {
3819
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3820 3821
    {
      create_info->table_existed= 1;		// Mark that table existed
3822 3823 3824
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
unknown's avatar
unknown committed
3825
      error= 0;
3826
      write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
3827
      goto err;
3828
    }
unknown's avatar
unknown committed
3829
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3830
    goto err;
unknown's avatar
unknown committed
3831
  }
3832

unknown's avatar
unknown committed
3833
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
3834
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
3835 3836 3837 3838
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3839 3840
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
3841
      goto unlock_and_end;
unknown's avatar
unknown committed
3842
    }
3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855
    /*
      We don't assert here, but check the result, because the table could be
      in the table definition cache and in the same time the .frm could be
      missing from the disk, in case of manual intervention which deletes
      the .frm file. The user has to use FLUSH TABLES; to clear the cache.
      Then she could create the table. This case is pretty obscure and
      therefore we don't introduce a new error message only for it.
    */
    if (get_cached_table_share(db, alias))
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
unknown's avatar
unknown committed
3856 3857
  }

unknown's avatar
unknown committed
3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870
  /*
    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;
3871 3872 3873
    int retcode = ha_table_exists_in_engine(thd, db, table_name);
    DBUG_PRINT("info", ("exists_in_engine: %u",retcode));
    switch (retcode)
unknown's avatar
unknown committed
3874
    {
3875 3876 3877 3878 3879
      case HA_ERR_NO_SUCH_TABLE:
        /* Normal case, no table exists. we can go and create it */
        break;
      case HA_ERR_TABLE_EXIST:
        DBUG_PRINT("info", ("Table existed in handler"));
unknown's avatar
unknown committed
3880

3881 3882 3883 3884 3885 3886 3887 3888 3889
        if (create_if_not_exists)
          goto warn;
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        goto unlock_and_end;
        break;
      default:
        DBUG_PRINT("info", ("error: %u from storage engine", retcode));
        my_error(retcode, MYF(0),table_name);
        goto unlock_and_end;
unknown's avatar
unknown committed
3890 3891 3892
    }
  }

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

3896
#ifdef HAVE_READLINK
3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914
  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 */

3915
  if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
3916
#endif /* HAVE_READLINK */
3917 3918
  {
    if (create_info->data_file_name)
3919 3920 3921
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "DATA DIRECTORY");
3922
    if (create_info->index_file_name)
3923 3924 3925
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "INDEX DIRECTORY");
3926
    create_info->data_file_name= create_info->index_file_name= 0;
3927
  }
unknown's avatar
unknown committed
3928
  create_info->table_options=db_options;
3929

3930
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3931 3932
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
unknown's avatar
unknown committed
3933
                       key_count, key_info_buffer, file))
unknown's avatar
unknown committed
3934
    goto unlock_and_end;
unknown's avatar
unknown committed
3935

unknown's avatar
unknown committed
3936 3937 3938 3939 3940 3941
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    /* Open table and put in temporary table list */
    if (!(open_temporary_table(thd, path, db, table_name, 1)))
    {
      (void) rm_temporary_table(create_info->db_type, path);
unknown's avatar
unknown committed
3942
      goto unlock_and_end;
unknown's avatar
unknown committed
3943
    }
unknown's avatar
unknown committed
3944
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
3945
  }
unknown's avatar
unknown committed
3946

3947
  write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
3948
  error= FALSE;
unknown's avatar
unknown committed
3949
unlock_and_end:
unknown's avatar
unknown committed
3950
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
3951 3952

err:
3953
  thd_proc_info(thd, "After create");
unknown's avatar
unknown committed
3954
  delete file;
unknown's avatar
unknown committed
3955
  DBUG_RETURN(error);
3956 3957

warn:
3958
  error= FALSE;
3959 3960 3961 3962
  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
3963
  write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
3964
  goto unlock_and_end;
unknown's avatar
unknown committed
3965 3966
}

unknown's avatar
unknown committed
3967 3968

/*
unknown's avatar
unknown committed
3969
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
unknown's avatar
unknown committed
3970 3971 3972 3973
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
3974 3975 3976
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
unknown's avatar
unknown committed
3977
{
unknown's avatar
unknown committed
3978
  TABLE *name_lock= 0;
unknown's avatar
unknown committed
3979 3980 3981 3982 3983 3984
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
3985
         hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
unknown's avatar
unknown committed
3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998
  {
    wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
    pthread_mutex_lock(&LOCK_lock_db);
  }

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

unknown's avatar
unknown committed
3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014
  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;
4015
        write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4016 4017 4018 4019 4020 4021 4022 4023 4024
      }
      else
      {
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        result= TRUE;
      }
      goto unlock;
    }
  }
unknown's avatar
unknown committed
4025

unknown's avatar
unknown committed
4026
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
4027 4028 4029
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
unknown's avatar
unknown committed
4030

unknown's avatar
unknown committed
4031 4032 4033 4034 4035 4036 4037
unlock:
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
4038 4039 4040 4041 4042 4043 4044 4045
  pthread_mutex_lock(&LOCK_lock_db);
  if (!--creating_table && creating_database)
    pthread_cond_signal(&COND_refresh);
  pthread_mutex_unlock(&LOCK_lock_db);
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
4046 4047 4048 4049 4050 4051 4052 4053
/*
** 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++)
4054
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
4055 4056 4057 4058 4059 4060 4061 4062 4063 4064
      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;

4065 4066
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
4067
    return (char*) field_name;			// Use fieldname
4068 4069 4070 4071 4072 4073
  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
  */
4074
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
4075
  {
4076 4077
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
4078 4079 4080
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
4081
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
4082 4083
}

4084

unknown's avatar
unknown committed
4085 4086 4087 4088
/****************************************************************************
** Alter a table definition
****************************************************************************/

4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102

/*
  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.
4103 4104
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
4105 4106

  RETURN
4107 4108
    FALSE   OK
    TRUE    Error
4109 4110
*/

4111
bool
4112 4113 4114
mysql_rename_table(handlerton *base, const char *old_db,
                   const char *old_name, const char *new_db,
                   const char *new_name, uint flags)
unknown's avatar
unknown committed
4115
{
4116
  THD *thd= current_thd;
4117 4118
  char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
    lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
4119 4120
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
unknown's avatar
unknown committed
4121
  handler *file;
unknown's avatar
unknown committed
4122
  int error=0;
unknown's avatar
unknown committed
4123
  DBUG_ENTER("mysql_rename_table");
4124 4125
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
unknown's avatar
unknown committed
4126

unknown's avatar
unknown committed
4127
  file= (base == NULL ? 0 :
unknown's avatar
unknown committed
4128 4129
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

4130
  build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
4131
                       flags & FN_FROM_IS_TMP);
4132
  build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
4133
                       flags & FN_TO_IS_TMP);
4134 4135 4136 4137 4138 4139

  /*
    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.
   */
4140
  if (lower_case_table_names == 2 && file &&
4141
      !(file->ha_table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
4142
  {
4143 4144
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
4145
    build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
4146
                         flags & FN_FROM_IS_TMP);
4147
    from_base= lc_from;
unknown's avatar
unknown committed
4148

4149 4150
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
4151
    build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
4152
                         flags & FN_TO_IS_TMP);
4153
    to_base= lc_to;
unknown's avatar
unknown committed
4154 4155
  }

4156
  if (!file || !(error=file->ha_rename_table(from_base, to_base)))
4157
  {
4158
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
4159
    {
unknown's avatar
unknown committed
4160
      error=my_errno;
4161
      /* Restore old file name */
4162
      if (file)
4163
        file->ha_rename_table(to_base, from_base);
4164 4165
    }
  }
unknown's avatar
unknown committed
4166
  delete file;
4167 4168 4169
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
4170 4171
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
4172 4173
}

unknown's avatar
unknown committed
4174

unknown's avatar
unknown committed
4175
/*
4176 4177 4178 4179 4180 4181
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
4182 4183 4184
    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
4185 4186 4187 4188 4189 4190 4191
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
unknown's avatar
unknown committed
4192 4193
*/

4194 4195
void wait_while_table_is_used(THD *thd, TABLE *table,
                              enum ha_extra_function function)
unknown's avatar
unknown committed
4196
{
4197
  DBUG_ENTER("wait_while_table_is_used");
unknown's avatar
unknown committed
4198
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %lu",
unknown's avatar
unknown committed
4199 4200
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
unknown's avatar
unknown committed
4201

4202 4203
  safe_mutex_assert_owner(&LOCK_open);

4204
  VOID(table->file->extra(function));
4205
  /* Mark all tables that are in use as 'old' */
unknown's avatar
unknown committed
4206
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
4207 4208

  /* Wait until all there are no other threads that has this table open */
unknown's avatar
unknown committed
4209 4210 4211
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
4212 4213
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
4214

4215 4216
/*
  Close a cached table
unknown's avatar
unknown committed
4217

4218
  SYNOPSIS
unknown's avatar
unknown committed
4219
    close_cached_table()
4220 4221 4222 4223 4224 4225
    thd			Thread handler
    table		Table to remove from cache

  NOTES
    Function ends by signaling threads waiting for the table to try to
    reopen the table.
unknown's avatar
unknown committed
4226

4227 4228 4229 4230
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
4231

4232
void close_cached_table(THD *thd, TABLE *table)
4233 4234
{
  DBUG_ENTER("close_cached_table");
4235

4236
  wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
4237 4238
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
4239
  {
4240 4241
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
4242
  }
4243
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
unknown's avatar
unknown committed
4244
  unlink_open_table(thd, table, TRUE);
4245 4246

  /* When lock on LOCK_open is freed other threads can continue */
4247
  broadcast_refresh();
unknown's avatar
unknown committed
4248
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
4249 4250
}

4251
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
4252
			     const char* operator_name, const char* errmsg)
4253

unknown's avatar
unknown committed
4254
{
4255 4256
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
4257 4258
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
4259
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4260
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
4261
  thd->clear_error();
4262
  if (protocol->write())
unknown's avatar
unknown committed
4263 4264 4265 4266
    return -1;
  return 1;
}

unknown's avatar
unknown committed
4267
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
4268
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
4269
{
unknown's avatar
unknown committed
4270 4271
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
4272 4273 4274 4275
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
unknown's avatar
unknown committed
4276 4277 4278 4279
  DBUG_ENTER("prepare_for_repair");

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

  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
4282
  {
unknown's avatar
unknown committed
4283 4284 4285 4286 4287 4288 4289 4290 4291
    char key[MAX_DBKEY_LENGTH];
    uint key_length;

    key_length= create_table_def_key(thd, key, table_list, 0);
    pthread_mutex_lock(&LOCK_open);
    if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
                                  &error))))
    {
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4292
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
4293 4294
    }

unknown's avatar
unknown committed
4295
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
unknown's avatar
unknown committed
4296 4297 4298 4299 4300
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
unknown's avatar
unknown committed
4301
    table= &tmp_table;
unknown's avatar
unknown committed
4302
    pthread_mutex_unlock(&LOCK_open);
4303
  }
4304 4305 4306 4307

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

4308 4309 4310 4311 4312 4313 4314 4315 4316
  /*
    REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
  */
  if (table->s->tmp_table)
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Cannot repair temporary table from .frm file");
    goto end;
  }
unknown's avatar
unknown committed
4317

unknown's avatar
unknown committed
4318 4319 4320 4321 4322 4323 4324 4325 4326
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
unknown's avatar
unknown committed
4327

4328 4329 4330
  if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  {
    error= send_check_errmsg(thd, table_list, "repair",
Timothy Smith's avatar
Timothy Smith committed
4331
                             "Failed repairing incompatible .frm file");
4332 4333
    goto end;
  }
4334

unknown's avatar
unknown committed
4335 4336
  /*
    Check if this is a table type that stores index and data separately,
4337 4338 4339
    like ISAM or MyISAM. We assume fixed order of engine file name
    extentions array. First element of engine file name extentions array
    is meta/index file extention. Second element - data file extention. 
unknown's avatar
unknown committed
4340
  */
unknown's avatar
unknown committed
4341
  ext= table->file->bas_ext();
unknown's avatar
unknown committed
4342 4343
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
4344

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

4350 4351
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
4352

4353 4354 4355 4356 4357 4358 4359
  /* If we could open the table, close it */
  if (table_list->table)
  {
    pthread_mutex_lock(&LOCK_open);
    close_cached_table(thd, table);
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
4360
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
4361
  {
unknown's avatar
unknown committed
4362 4363
    error= -1;
    goto end;
unknown's avatar
unknown committed
4364
  }
unknown's avatar
unknown committed
4365
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
4366
  {
4367
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4368
    unlock_table_name(thd, table_list);
4369
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
unknown's avatar
unknown committed
4391 4392
  }

4393 4394 4395 4396
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
4397
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4398
  if (reopen_name_locked_table(thd, table_list, TRUE))
unknown's avatar
unknown committed
4399
  {
unknown's avatar
unknown committed
4400
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
4401
    pthread_mutex_unlock(&LOCK_open);
4402 4403 4404
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
4405
  }
4406
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4407 4408 4409

end:
  if (table == &tmp_table)
unknown's avatar
unknown committed
4410 4411 4412 4413 4414
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
4415
  DBUG_RETURN(error);
unknown's avatar
unknown committed
4416
}
4417

4418

4419

unknown's avatar
unknown committed
4420 4421
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
4422 4423 4424
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
unknown's avatar
unknown committed
4425
*/
unknown's avatar
unknown committed
4426 4427 4428 4429 4430
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,
4431
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
4432 4433 4434
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
4435 4436 4437
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
4438
{
4439
  TABLE_LIST *table;
4440
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
4441
  List<Item> field_list;
4442 4443
  Item *item;
  Protocol *protocol= thd->protocol;
4444
  LEX *lex= thd->lex;
4445
  int result_code;
4446
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
4447

4448 4449
  if (end_active_trans(thd))
    DBUG_RETURN(1);
4450
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
unknown's avatar
unknown committed
4451 4452 4453 4454 4455 4456 4457
  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;
4458 4459
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4460
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4461

4462
  mysql_ha_rm_tables(thd, tables, FALSE);
4463

unknown's avatar
VIEW  
unknown committed
4464
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
4465 4466
  {
    char table_name[NAME_LEN*2+2];
4467
    char* db = table->db;
4468
    bool fatal_error=0;
unknown's avatar
unknown committed
4469

4470 4471
    DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
    DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
unknown's avatar
unknown committed
4472
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
4473
    thd->open_options|= extra_open_options;
4474 4475
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4476
    {
4477 4478 4479 4480 4481
      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;
4482
      select->table_list.first= (uchar*)table;
4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494
      /*
        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;
4495

4496 4497 4498 4499 4500
      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;
4501
#ifdef WITH_PARTITION_STORAGE_ENGINE
4502
      if (table->table)
4503 4504 4505 4506 4507 4508 4509
      {
        /*
          Set up which partitions that should be processed
          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
        */
        Alter_info *alter_info= &lex->alter_info;

4510
        if (alter_info->flags & ALTER_ADMIN_PARTITION)
4511
        {
4512 4513 4514 4515 4516
          if (!table->table->part_info)
          {
            my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
            DBUG_RETURN(TRUE);
          }
4517 4518 4519 4520 4521 4522 4523 4524
          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];
4525
            size_t length;
4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542
            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
4543
    }
4544 4545
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

unknown's avatar
unknown committed
4546
    if (prepare_func)
4547
    {
4548
      DBUG_PRINT("admin", ("calling prepare_func"));
unknown's avatar
unknown committed
4549
      switch ((*prepare_func)(thd, table, check_opt)) {
4550
      case  1:           // error, message written to net
4551
        ha_autocommit_or_rollback(thd, 1);
4552
        end_trans(thd, ROLLBACK);
4553
        close_thread_tables(thd);
4554
        DBUG_PRINT("admin", ("simple error, admin next table"));
4555 4556
        continue;
      case -1:           // error, message could be written to net
4557 4558
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4559
        goto err;
4560
        /* purecov: end */
4561
      default:           // should be 0 otherwise
4562
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4563
        ;
unknown's avatar
unknown committed
4564
      }
4565
    }
4566

4567
    /*
unknown's avatar
unknown committed
4568 4569 4570 4571 4572 4573
      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)
4574
    */
unknown's avatar
unknown committed
4575 4576
    if (!table->table)
    {
4577
      DBUG_PRINT("admin", ("open table failed"));
4578 4579 4580
      if (!thd->warn_list.elements)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4581 4582 4583
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
4584 4585
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
Staale Smedseng's avatar
Staale Smedseng committed
4586
      if (thd->main_da.is_error() && 
4587 4588
          (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
4589 4590 4591 4592 4593
        /* 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;
4594
      goto send_result;
unknown's avatar
unknown committed
4595
    }
4596 4597 4598

    if (table->view)
    {
4599
      DBUG_PRINT("admin", ("calling view_operator_func"));
4600 4601 4602 4603
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4604
    if (table->schema_table)
unknown's avatar
unknown committed
4605
    {
4606 4607
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
unknown's avatar
unknown committed
4608 4609
    }

4610
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
4611
    {
4612
      /* purecov: begin inspected */
unknown's avatar
unknown committed
4613
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4614
      size_t length;
4615
      DBUG_PRINT("admin", ("sending error message"));
4616
      protocol->prepare_for_resend();
4617 4618
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4619
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4620 4621 4622
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4623
      ha_autocommit_or_rollback(thd, 0);
4624
      end_trans(thd, COMMIT);
4625
      close_thread_tables(thd);
4626
      lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
4627
      table->table=0;				// For query cache
4628
      if (protocol->write())
unknown's avatar
unknown committed
4629
	goto err;
4630
      thd->main_da.reset_diagnostics_area();
unknown's avatar
unknown committed
4631
      continue;
4632
      /* purecov: end */
unknown's avatar
unknown committed
4633 4634
    }

4635
    /* Close all instances of the table to allow repair to rename files */
4636
    if (lock_type == TL_WRITE && table->table->s->version)
4637
    {
4638
      DBUG_PRINT("admin", ("removing table from cache"));
4639
      pthread_mutex_lock(&LOCK_open);
4640 4641
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
unknown's avatar
unknown committed
4642
      mysql_lock_abort(thd,table->table, TRUE);
unknown's avatar
unknown committed
4643 4644
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
unknown's avatar
unknown committed
4645 4646
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4647
      thd->exit_cond(old_message);
4648
      DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
4649 4650
      if (thd->killed)
	goto err;
4651 4652 4653
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4654 4655
    }

unknown's avatar
unknown committed
4656
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4657
    {
4658 4659
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4660 4661 4662
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4663 4664 4665
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4666 4667
      if (protocol->write())
        goto err;
4668
      /* purecov: end */
4669 4670
    }

4671 4672
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
unknown's avatar
unknown committed
4673 4674 4675 4676 4677
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4678
        DBUG_PRINT("admin", ("recreating table"));
4679
        ha_autocommit_or_rollback(thd, 1);
unknown's avatar
unknown committed
4680 4681
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4682
        result_code= mysql_recreate_table(thd, table);
unknown's avatar
unknown committed
4683
        reenable_binlog(thd);
4684 4685 4686 4687 4688 4689 4690 4691
        /*
          mysql_recreate_table() can push OK or ERROR.
          Clear 'OK' status. If there is an error, keep it:
          we will store the error message in a result set row 
          and then clear.
        */
        if (thd->main_da.is_ok())
          thd->main_da.reset_diagnostics_area();
unknown's avatar
unknown committed
4692 4693 4694 4695
        goto send_result;
      }
    }

4696
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4697
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4698
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4699 4700 4701

send_result:

4702
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
4703
    thd->clear_error();  // these errors shouldn't get client
4704
    {
4705 4706 4707 4708 4709 4710 4711
      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);
4712 4713 4714
        protocol->store(warning_level_names[err->level].str,
                        warning_level_names[err->level].length,
                        system_charset_info);
4715 4716 4717 4718 4719
        protocol->store(err->msg, system_charset_info);
        if (protocol->write())
          goto err;
      }
      mysql_reset_errors(thd, true);
4720
    }
4721
    protocol->prepare_for_resend();
4722 4723
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
4724

4725 4726 4727
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4728 4729
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4730
      {
4731
       char buf[MYSQL_ERRMSG_SIZE];
4732
       size_t length=my_snprintf(buf, sizeof(buf),
4733
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4734
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4735
	protocol->store(buf, length, system_charset_info);
4736
      }
unknown's avatar
unknown committed
4737 4738
      break;

unknown's avatar
unknown committed
4739 4740
    case HA_ADMIN_NOT_BASE_TABLE:
      {
4741
        char buf[MYSQL_ERRMSG_SIZE];
4742
        size_t length= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
4743
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4744
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4745
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
4746 4747 4748
      }
      break;

4749
    case HA_ADMIN_OK:
4750 4751
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
4752 4753
      break;

4754
    case HA_ADMIN_FAILED:
4755 4756 4757
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
4758 4759
      break;

unknown's avatar
unknown committed
4760
    case HA_ADMIN_REJECT:
4761 4762 4763
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation need committed state"),
                      system_charset_info);
unknown's avatar
unknown committed
4764
      open_for_modify= FALSE;
unknown's avatar
unknown committed
4765 4766
      break;

4767
    case HA_ADMIN_ALREADY_DONE:
4768 4769 4770
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4771 4772
      break;

4773
    case HA_ADMIN_CORRUPT:
4774 4775
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4776
      fatal_error=1;
unknown's avatar
unknown committed
4777 4778
      break;

unknown's avatar
unknown committed
4779
    case HA_ADMIN_INVALID:
4780 4781 4782
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
4783 4784
      break;

4785 4786 4787 4788 4789 4790
    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.
4791
        We have to end the row, so analyze could return more rows.
4792
      */
4793 4794 4795 4796 4797 4798
      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;
4799
      ha_autocommit_or_rollback(thd, 0);
4800
      close_thread_tables(thd);
4801
      DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
unknown's avatar
VIEW  
unknown committed
4802 4803 4804
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4805
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4806
      result_code= mysql_recreate_table(thd, table);
4807
      reenable_binlog(thd);
4808 4809 4810 4811 4812 4813 4814 4815
      /*
        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();
4816
      ha_autocommit_or_rollback(thd, 0);
unknown's avatar
unknown committed
4817
      close_thread_tables(thd);
4818 4819
      if (!result_code) // recreation went ok
      {
4820
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
4821
            ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
4822 4823
          result_code= 0; // analyze went ok
      }
4824 4825 4826 4827
      /* 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);
4828 4829
      if (result_code) // either mysql_recreate_table or analyze failed
      {
4830 4831
        DBUG_ASSERT(thd->is_error());
        if (thd->is_error())
4832
        {
4833
          const char *err_msg= thd->main_da.message();
4834 4835
          if (!thd->vio_ok())
          {
Staale Smedseng's avatar
Staale Smedseng committed
4836
            sql_print_error("%s", err_msg);
4837 4838 4839 4840
          }
          else
          {
            /* Hijack the row already in-progress. */
4841
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4842
            protocol->store(err_msg, system_charset_info);
4843 4844
            if (protocol->write())
              goto err;
4845 4846 4847 4848 4849
            /* 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);
          }
4850
          thd->clear_error();
4851 4852
        }
      }
4853
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
4854 4855
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4856 4857
      goto send_result_message;
    }
4858 4859
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4860
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4861 4862
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4863 4864
      break;
    }
4865

unknown's avatar
unknown committed
4866 4867 4868
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
4869
      char buf[MYSQL_ERRMSG_SIZE];
4870
      size_t length;
unknown's avatar
unknown committed
4871 4872

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4873 4874
      length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
                         table->table_name);
unknown's avatar
unknown committed
4875 4876 4877 4878 4879
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

4880
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4881
      {
4882
        char buf[MYSQL_ERRMSG_SIZE];
4883
        size_t length=my_snprintf(buf, sizeof(buf),
4884 4885
                                "Unknown - internal error %d during operation",
                                result_code);
4886
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4887 4888 4889 4890
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
4891
    }
unknown's avatar
unknown committed
4892
    if (table->table)
4893
    {
unknown's avatar
unknown committed
4894 4895
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4896
      else if (open_for_modify)
unknown's avatar
unknown committed
4897
      {
unknown's avatar
unknown committed
4898
        if (table->table->s->tmp_table)
unknown's avatar
unknown committed
4899 4900 4901 4902
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4903 4904
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
unknown's avatar
unknown committed
4905 4906 4907
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
4908 4909
        query_cache_invalidate3(thd, table->table, 0);
      }
4910
    }
4911
    ha_autocommit_or_rollback(thd, 0);
4912
    end_trans(thd, COMMIT);
unknown's avatar
unknown committed
4913
    close_thread_tables(thd);
unknown's avatar
unknown committed
4914
    table->table=0;				// For query cache
4915
    if (protocol->write())
unknown's avatar
unknown committed
4916 4917 4918
      goto err;
  }

4919
  my_eof(thd);
unknown's avatar
unknown committed
4920
  DBUG_RETURN(FALSE);
4921

4922
err:
4923
  ha_autocommit_or_rollback(thd, 1);
4924
  end_trans(thd, ROLLBACK);
4925
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
4926 4927
  if (table)
    table->table=0;
unknown's avatar
unknown committed
4928
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4929 4930
}

unknown's avatar
unknown committed
4931

unknown's avatar
unknown committed
4932
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4933 4934 4935
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4936 4937 4938
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4939
				&prepare_for_repair,
unknown's avatar
unknown committed
4940
				&handler::ha_repair, 0));
4941 4942
}

4943

unknown's avatar
unknown committed
4944
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4945 4946 4947
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4948
				"optimize", TL_WRITE, 1,0,0,0,
4949
				&handler::ha_optimize, 0));
4950 4951 4952
}


unknown's avatar
unknown committed
4953 4954 4955 4956 4957
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4958 4959
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
4960 4961

  RETURN VALUES
unknown's avatar
unknown committed
4962 4963
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
4964 4965
*/

unknown's avatar
unknown committed
4966
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4967
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
4968
{
4969
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
4970
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
4971
  DBUG_ENTER("mysql_assign_to_keycache");
4972 4973 4974 4975 4976 4977 4978

  check_opt.init();
  pthread_mutex_lock(&LOCK_global_system_variables);
  if (!(key_cache= get_key_cache(key_cache_name)))
  {
    pthread_mutex_unlock(&LOCK_global_system_variables);
    my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
unknown's avatar
unknown committed
4979
    DBUG_RETURN(TRUE);
4980 4981 4982 4983
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4984
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4985
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
4986 4987
}

unknown's avatar
unknown committed
4988 4989 4990 4991 4992 4993

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

  SYNOPSIS
    reassign_keycache_tables()
4994 4995 4996
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
4997

4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010
  NOTES
    This is called when one sets a key cache size to zero, in which
    case we have to move the tables associated to this key cache to
    the "default" one.

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

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

 RETURN VALUES
unknown's avatar
unknown committed
5011 5012 5013
    0	  ok
*/

5014 5015
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
5016 5017 5018
{
  DBUG_ENTER("reassign_keycache_tables");

5019 5020
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
unknown's avatar
unknown committed
5021
  src_cache->param_buff_size= 0;		// Free key cache
5022 5023
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
5024
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5025 5026 5027
}


unknown's avatar
unknown committed
5028 5029 5030 5031 5032
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
5033 5034
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5035 5036

  RETURN VALUES
unknown's avatar
unknown committed
5037 5038
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
5039 5040
*/

unknown's avatar
unknown committed
5041
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
5042 5043
{
  DBUG_ENTER("mysql_preload_keys");
5044 5045 5046 5047 5048
  /*
    We cannot allow concurrent inserts. The storage engine reads
    directly from the index file, bypassing the cache. It could read
    outdated information if parallel inserts into cache blocks happen.
  */
unknown's avatar
unknown committed
5049
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
5050
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
5051
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
5052 5053 5054
}


5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103

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

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

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


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

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


unknown's avatar
unknown committed
5104 5105 5106 5107 5108
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
5109
    thd		Thread object
unknown's avatar
unknown committed
5110 5111
    table       Table list element for target table
    src_table   Table list element for source table
unknown's avatar
unknown committed
5112 5113 5114
    create_info Create info

  RETURN VALUES
unknown's avatar
unknown committed
5115 5116
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
5117 5118
*/

unknown's avatar
unknown committed
5119
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
5120
                             HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
5121
{
unknown's avatar
unknown committed
5122
  TABLE *name_lock= 0;
5123
  char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
5124
  uint dst_path_length;
unknown's avatar
unknown committed
5125
  char *db= table->db;
5126
  char *table_name= table->table_name;
unknown's avatar
unknown committed
5127
  int  err;
unknown's avatar
unknown committed
5128
  bool res= TRUE;
unknown's avatar
unknown committed
5129
  uint not_used;
5130 5131 5132
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
5133
  char ts_name[FN_LEN + 1];
5134
  myf flags= MY_DONT_OVERWRITE_FILE;
unknown's avatar
unknown committed
5135
  DBUG_ENTER("mysql_create_like_table");
5136

5137

unknown's avatar
unknown committed
5138 5139 5140 5141 5142 5143 5144 5145
  /*
    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.
5146
  */
unknown's avatar
unknown committed
5147
  if (open_tables(thd, &src_table, &not_used, 0))
5148 5149
    DBUG_RETURN(TRUE);

5150 5151 5152 5153 5154 5155
  /*
    For bug#25875, Newly created table through CREATE TABLE .. LIKE
                   has no ndb_dd attributes;
    Add something to get possible tablespace info from src table,
    it can get valid tablespace name only for disk-base ndb table
  */
unknown's avatar
unknown committed
5156
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
5157 5158 5159 5160 5161
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

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

unknown's avatar
unknown committed
5164
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
unknown's avatar
unknown committed
5165

unknown's avatar
unknown committed
5166 5167 5168
  /*
    Check that destination tables does not exist. Note that its name
    was already checked when it was added to the table list.
unknown's avatar
unknown committed
5169 5170 5171 5172 5173
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
5174
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
unknown's avatar
unknown committed
5175 5176 5177 5178
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
unknown's avatar
unknown committed
5179 5180 5181 5182
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
      goto err;
    if (!name_lock)
      goto table_exists;
5183
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
5184
                                          db, table_name, reg_ext, 0);
unknown's avatar
unknown committed
5185 5186 5187 5188
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

unknown's avatar
unknown committed
5189 5190
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

5191 5192
  if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    flags|= MY_SYNC;
5193

5194
  /*
unknown's avatar
unknown committed
5195
    Create a new table by copying from source table
5196
    and sync the new table if the flag MY_SYNC is set
unknown's avatar
unknown committed
5197 5198 5199 5200 5201 5202 5203 5204 5205 5206

    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.
5207
  */
unknown's avatar
unknown committed
5208
  VOID(pthread_mutex_lock(&LOCK_open));
5209 5210 5211 5212 5213 5214 5215 5216
  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;
    }
  }
5217
  else if (my_copy(src_path, dst_path, flags))
5218 5219 5220 5221 5222
  {
    if (my_errno == ENOENT)
      my_error(ER_BAD_DB_ERROR,MYF(0),db);
    else
      my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
unknown's avatar
unknown committed
5223
    VOID(pthread_mutex_unlock(&LOCK_open));
5224
    goto err;
5225
  }
unknown's avatar
unknown committed
5226 5227

  /*
5228 5229
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
unknown's avatar
unknown committed
5230 5231
    and temporary tables).
  */
unknown's avatar
unknown committed
5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244
#ifdef WITH_PARTITION_STORAGE_ENGINE
  /*
    For partitioned tables we need to copy the .par file as well since
    it is used in open_table_def to even be able to create a new handler.
    There is no way to find out here if the original table is a
    partitioned table so we copy the file and ignore any errors.
  */
  fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
  strmov(dst_path, tmp_path);
  fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
  strmov(src_path, tmp_path);
  my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
#endif
unknown's avatar
unknown committed
5245 5246 5247

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

5248
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
5249 5250
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
unknown's avatar
unknown committed
5251
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
unknown's avatar
unknown committed
5252
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
5253

unknown's avatar
unknown committed
5254 5255 5256 5257
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
5258 5259
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
5260
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
5261
    }
5262
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
5263 5264 5265
  }
  else if (err)
  {
5266
    (void) quick_rm_table(create_info->db_type, db,
5267
			  table_name, 0); /* purecov: inspected */
5268
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
5269
  }
5270

5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288
goto binlog;

table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
		 ER_TABLE_EXISTS_ERROR,warn_buff);
  }
  else
  {
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
    goto err;
  }

binlog:
unknown's avatar
unknown committed
5289 5290
  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););

5291 5292 5293
  /*
    We have to write the query before we unlock the tables.
  */
5294
  if (thd->is_current_stmt_binlog_format_row())
5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312
  {
    /*
       Since temporary tables are not replicated under row-based
       replication, CREATE TABLE ... LIKE ... needs special
       treatement.  We have four cases to consider, according to the
       following decision table:

           ==== ========= ========= ==============================
           Case    Target    Source Write to binary log
           ==== ========= ========= ==============================
           1       normal    normal Original statement
           2       normal temporary Generated statement
           3    temporary    normal Nothing
           4    temporary temporary Nothing
           ==== ========= ========= ==============================
    */
    if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    {
unknown's avatar
unknown committed
5313
      if (src_table->table->s->tmp_table)               // Case 2
5314 5315 5316 5317
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
5318 5319

        /*
unknown's avatar
unknown committed
5320 5321 5322 5323
          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.
5324
        */
unknown's avatar
unknown committed
5325 5326 5327 5328 5329
        table->table= name_lock;
        VOID(pthread_mutex_lock(&LOCK_open));
        if (reopen_name_locked_table(thd, table, FALSE))
        {
          VOID(pthread_mutex_unlock(&LOCK_open));
5330
          goto err;
unknown's avatar
unknown committed
5331 5332
        }
        VOID(pthread_mutex_unlock(&LOCK_open));
5333

5334 5335 5336 5337 5338 5339 5340 5341 5342 5343
       /*
         The condition avoids a crash as described in BUG#48506. Other
         binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE
         when the existing object is a view will be solved by BUG 47442.
       */
        if (!table->view)
        {
          IF_DBUG(int result=)
            store_create_info(thd, table, &query,
                              create_info, FALSE /* show_database */);
5344

5345 5346 5347
          DBUG_ASSERT(result == 0); // store_create_info() always return 0
          write_bin_log(thd, TRUE, query.ptr(), query.length());
        }
5348 5349
      }
      else                                      // Case 1
5350
        write_bin_log(thd, TRUE, thd->query(), thd->query_length());
5351 5352 5353 5354 5355
    }
    /*
      Case 3 and 4 does nothing under RBR
    */
  }
5356
  else
5357
    write_bin_log(thd, TRUE, thd->query(), thd->query_length());
5358

unknown's avatar
unknown committed
5359
  res= FALSE;
5360 5361

err:
unknown's avatar
unknown committed
5362
  if (name_lock)
5363 5364
  {
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5365
    unlink_open_table(thd, name_lock, FALSE);
5366 5367
    pthread_mutex_unlock(&LOCK_open);
  }
5368
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5369 5370 5371
}


unknown's avatar
unknown committed
5372
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5373
{
unknown's avatar
unknown committed
5374 5375
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5376 5377
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5378
				"analyze", lock_type, 1, 0, 0, 0,
5379
				&handler::ha_analyze, 0));
5380 5381 5382
}


unknown's avatar
unknown committed
5383
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
5384
{
unknown's avatar
unknown committed
5385 5386
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5387 5388
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
5389
				"check", lock_type,
5390
				0, 0, HA_OPEN_FOR_REPAIR, 0,
unknown's avatar
unknown committed
5391
				&handler::ha_check, &view_checksum));
5392 5393
}

unknown's avatar
unknown committed
5394

unknown's avatar
unknown committed
5395
/* table_list should contain just one table */
unknown's avatar
unknown committed
5396 5397 5398 5399
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
5400 5401 5402 5403 5404 5405
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
5406 5407 5408 5409
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
5410

5411
  thd_proc_info(thd, "discard_or_import_tablespace");
unknown's avatar
unknown committed
5412

unknown's avatar
unknown committed
5413
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
5414

unknown's avatar
unknown committed
5415 5416 5417 5418 5419
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
5420
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
unknown's avatar
unknown committed
5421 5422 5423 5424
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
5425

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

5428
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
5429 5430 5431 5432

  if (error)
    goto err;

unknown's avatar
unknown committed
5433 5434 5435 5436
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
unknown's avatar
unknown committed
5437 5438 5439
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
5440 5441
  error = ha_autocommit_or_rollback(thd, 0);
  if (end_active_trans(thd))
unknown's avatar
unknown committed
5442 5443 5444
    error=1;
  if (error)
    goto err;
5445
  write_bin_log(thd, FALSE, thd->query(), thd->query_length());
5446

unknown's avatar
unknown committed
5447
err:
5448
  ha_autocommit_or_rollback(thd, error);
unknown's avatar
unknown committed
5449
  thd->tablespace_op=FALSE;
5450
  
unknown's avatar
unknown committed
5451 5452
  if (error == 0)
  {
5453
    my_ok(thd);
unknown's avatar
unknown committed
5454
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5455
  }
unknown's avatar
unknown committed
5456

5457 5458
  table->file->print_error(error, MYF(0));
    
unknown's avatar
unknown committed
5459
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
5460
}
unknown's avatar
unknown committed
5461

5462

unknown's avatar
unknown committed
5463 5464
/*
  SYNOPSIS
5465 5466
    compare_tables()
      table                     The original table.
5467 5468
      alter_info                Alter options, fields and keys for the new
                                table.
5469 5470
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
5471 5472 5473 5474 5475 5476 5477 5478
      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
5479 5480 5481 5482
      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.
5483
      candidate_key_count OUT   The number of candidate keys in original table.
unknown's avatar
unknown committed
5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494

  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.

5495 5496 5497 5498 5499
    If there are no data changes, but index changes, 'index_drop_buffer'
    and/or 'index_add_buffer' are populated with offsets into
    table->key_info or key_info_buffer respectively for the indexes
    that need to be dropped and/or (re-)created.

unknown's avatar
unknown committed
5500
  RETURN VALUES
5501 5502
    TRUE   error
    FALSE  success
unknown's avatar
unknown committed
5503 5504
*/

5505 5506 5507 5508 5509 5510
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
unknown's avatar
unknown committed
5511
               enum_alter_table_change_level *need_copy_table,
5512 5513
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
5514 5515
               uint **index_add_buffer, uint *index_add_count,
               uint *candidate_key_count)
unknown's avatar
unknown committed
5516 5517 5518
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
5519
  uint key_count;
5520 5521
  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
  Create_field *new_field, *tmp_new_field;
5522 5523
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5524
  THD *thd= table->in_use;
5525 5526 5527 5528 5529
  /*
    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;
5530 5531 5532
  bool not_nullable= true;
  DBUG_ENTER("compare_tables");

5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551
  /*
    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,
5552 5553 5554 5555 5556
                                 &tmp_alter_info,
                                 (table->s->tmp_table != NO_TMP_TABLE),
                                 &db_options,
                                 table->file, key_info_buffer,
                                 &key_count, 0))
5557 5558 5559
    DBUG_RETURN(1);
  /* Allocate result buffers. */
  if (! (*index_drop_buffer=
5560
         (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
5561
      ! (*index_add_buffer=
5562
         (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
5563
    DBUG_RETURN(1);
5564
  
unknown's avatar
unknown committed
5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581
  /*
    Some very basic checks. If number of fields changes, or the
    handler, we need to run full ALTER TABLE. In the future
    new fields can be added and old dropped without copy, but
    not yet.

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

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

    At the moment we can't handle altering temporary tables without a copy.
    We also test if OPTIMIZE TABLE was given and was mapped to alter table.
    In that case we always do full copy.
unknown's avatar
unknown committed
5582 5583 5584 5585 5586 5587 5588 5589

    There was a bug prior to mysql-4.0.25. Number of null fields was
    calculated incorrectly. As a result frm and data files gets out of
    sync after fast alter table. There is no way to determine by which
    mysql version (in 4.0 and 4.1 branches) table was created, thus we
    disable fast alter table for all tables created by mysql versions
    prior to 5.0 branch.
    See BUG#6236.
unknown's avatar
unknown committed
5590
  */
5591
  if (table->s->fields != alter_info->create_list.elements ||
unknown's avatar
unknown committed
5592
      table->s->db_type() != create_info->db_type ||
unknown's avatar
unknown committed
5593 5594 5595 5596
      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 ||
5597
      (table->s->row_type != create_info->row_type) ||
5598 5599
      create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
      create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
5600
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5601
      order_num ||
unknown's avatar
unknown committed
5602
      !table->s->mysql_version ||
5603
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5604 5605 5606 5607
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5608

5609 5610 5611 5612 5613 5614 5615
  /*
    Use transformed info to evaluate possibility of fast ALTER TABLE
    but use the preserved field to persist modifications.
  */
  new_field_it.init(alter_info->create_list);
  tmp_new_field_it.init(tmp_alter_info.create_list);

unknown's avatar
unknown committed
5616 5617 5618 5619
  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
5620 5621 5622 5623 5624
  for (f_ptr= table->field, new_field= new_field_it++,
       tmp_new_field= tmp_new_field_it++;
       (field= *f_ptr);
       f_ptr++, new_field= new_field_it++,
       tmp_new_field= tmp_new_field_it++)
unknown's avatar
unknown committed
5625 5626 5627 5628
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5629

unknown's avatar
unknown committed
5630
    /* Check that NULL behavior is same for old and new fields */
5631
    if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
unknown's avatar
unknown committed
5632
	(uint) (field->flags & NOT_NULL_FLAG))
5633 5634 5635 5636
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
5637 5638 5639

    /* Don't pack rows in old tables if the user has requested this. */
    if (create_info->row_type == ROW_TYPE_DYNAMIC ||
5640
	(tmp_new_field->flags & BLOB_FLAG) ||
Staale Smedseng's avatar
Staale Smedseng committed
5641 5642
	(tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
unknown's avatar
unknown committed
5643 5644
      create_info->table_options|= HA_OPTION_PACK_RECORD;

5645
    /* Check if field was renamed */
5646
    field->flags&= ~FIELD_IS_RENAMED;
5647 5648
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
5649
		      tmp_new_field->field_name))
5650
      field->flags|= FIELD_IS_RENAMED;      
5651

unknown's avatar
unknown committed
5652
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
5653
    if (!(tmp= field->is_equal(tmp_new_field)))
5654 5655 5656 5657
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5658
    // Clear indexed marker
5659
    field->flags&= ~FIELD_IN_ADD_INDEX;
unknown's avatar
unknown committed
5660 5661 5662 5663 5664 5665 5666
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5667 5668 5669
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5670
  KEY *new_key_end= *key_info_buffer + key_count;
unknown's avatar
unknown committed
5671

5672 5673 5674 5675 5676 5677 5678
  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;
5679
  *candidate_key_count= 0;
5680
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
unknown's avatar
unknown committed
5681
  {
5682 5683 5684 5685
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701
   /*
      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)++;

5702
    /* Search a new key with the same name. */
5703
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5704 5705 5706 5707 5708 5709 5710
    {
      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. */
5711
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5712 5713 5714 5715 5716 5717 5718 5719 5720 5721
      DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
      continue;
    }

    /* Check that the key types are compatible between old and new tables. */
    if ((table_key->algorithm != new_key->algorithm) ||
	((table_key->flags & HA_KEYFLAG_MASK) !=
         (new_key->flags & HA_KEYFLAG_MASK)) ||
        (table_key->key_parts != new_key->key_parts))
      goto index_changed;
unknown's avatar
unknown committed
5722 5723 5724 5725 5726

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5727 5728 5729
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
unknown's avatar
unknown committed
5730 5731 5732
    {
      /*
	Key definition has changed if we are using a different field or
5733 5734
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
unknown's avatar
unknown committed
5735
      */
5736 5737 5738
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
unknown's avatar
unknown committed
5739
    }
5740 5741 5742 5743
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5744 5745
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5746 5747 5748 5749 5750 5751
    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];
5752
      field->flags|= FIELD_IN_ADD_INDEX;
5753
    }
5754
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
unknown's avatar
unknown committed
5755
  }
5756
  /*end of for (; table_key < table_key_end;) */
unknown's avatar
unknown committed
5757

5758 5759 5760
  /*
    Step through all keys of the new table and find matching old keys.
  */
5761
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5762 5763 5764 5765 5766 5767 5768 5769 5770 5771
  {
    /* 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. */
5772
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5773 5774 5775 5776 5777 5778
      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];
5779
        field->flags|= FIELD_IN_ADD_INDEX;
5780
      }
unknown's avatar
unknown committed
5781
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5782 5783
    }
  }
5784 5785 5786

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5787 5788 5789 5790
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5791

5792
  if (*index_drop_count || *index_add_count)
5793 5794 5795 5796
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5797

5798 5799
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5800 5801 5802
}


unknown's avatar
unknown committed
5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828
/*
  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:
5829
    error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5830 5831 5832 5833 5834 5835
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
5836
    error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5837 5838 5839 5840 5841
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5842 5843
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
unknown's avatar
unknown committed
5844 5845 5846 5847 5848 5849 5850 5851
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890
/**
  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
5891
*/
5892

5893 5894 5895 5896
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
unknown's avatar
unknown committed
5897
{
5898
  /* New column definitions are added here */
unknown's avatar
unknown committed
5899
  List<Create_field> new_create_list;
5900 5901 5902
  /* New key definitions are added here */
  List<Key> new_key_list;
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
5903
  List_iterator<Create_field> def_it(alter_info->create_list);
5904 5905
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
unknown's avatar
unknown committed
5906 5907 5908
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5909 5910 5911 5912 5913
  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;
5914

5915
  DBUG_ENTER("mysql_prepare_alter_table");
5916

5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927
  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)
5928
  {
5929 5930 5931
    /* 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;
5932
  }
5933 5934
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
unknown's avatar
unknown committed
5935

5936
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
5937
  {
5938
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
5939
    /*
5940 5941
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
5942
    */
5943 5944 5945 5946 5947
    if (tablespace &&
        (table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
      create_info->tablespace= tablespace;
  }
  restore_record(table, s->default_values);     // Empty record for DEFAULT
unknown's avatar
unknown committed
5948
  Create_field *def;
5949

5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961
  /*
    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++))
5962
    {
5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974
      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;
      }
5975
    }
5976
    if (drop)
5977
    {
5978 5979
      drop_it.remove();
      continue;
5980
    }
5981 5982 5983
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
5984
    {
5985 5986 5987
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
5988
    }
5989 5990 5991 5992
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
unknown's avatar
unknown committed
5993
      {
5994 5995
	new_create_list.push_back(def);
	def_it.remove();
unknown's avatar
unknown committed
5996 5997
      }
    }
5998
    else
5999 6000
    {
      /*
6001 6002
        This field was not dropped and not changed, add it to the list
        for the new table.
6003
      */
unknown's avatar
unknown committed
6004
      def= new Create_field(field, field);
6005 6006 6007 6008
      new_create_list.push_back(def);
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
unknown's avatar
unknown committed
6009
      {
6010 6011
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
6012
      }
6013
      if (alter)
unknown's avatar
unknown committed
6014
      {
6015
	if (def->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
6016
	{
6017
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
6018
          goto err;
unknown's avatar
unknown committed
6019
	}
6020 6021 6022 6023 6024
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
	alter_it.remove();
unknown's avatar
unknown committed
6025 6026 6027
      }
    }
  }
6028 6029
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
6030
  {
6031
    if (def->change && ! def->field)
6032
    {
6033
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
6034
      goto err;
6035
    }
unknown's avatar
unknown committed
6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052
    /*
      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;
    }
6053 6054 6055 6056
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
6057
    else
6058
    {
unknown's avatar
unknown committed
6059
      Create_field *find;
6060 6061 6062 6063 6064 6065 6066 6067
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
6068
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
6069 6070 6071
        goto err;
      }
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
6072
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
6073
    }
6074
  }
6075
  if (alter_info->alter_list.elements)
6076
  {
6077
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
6078
             alter_info->alter_list.head()->name, table->s->table_name.str);
unknown's avatar
unknown committed
6079
    goto err;
6080
  }
6081
  if (!new_create_list.elements)
unknown's avatar
unknown committed
6082
  {
unknown's avatar
unknown committed
6083 6084
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
6085
    goto err;
unknown's avatar
unknown committed
6086 6087 6088
  }

  /*
6089 6090
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
6091 6092
  */

6093
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
6094
  {
6095
    char *key_name= key_info->name;
unknown's avatar
unknown committed
6096 6097 6098 6099 6100
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
6101
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116
	break;
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }

    KEY_PART_INFO *key_part= key_info->key_part;
    key_parts.empty();
    for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
    {
      if (!key_part->field)
	continue;				// Wrong field (from UNIREG)
      const char *key_part_name=key_part->field->field_name;
unknown's avatar
unknown committed
6117
      Create_field *cfield;
unknown's avatar
unknown committed
6118 6119 6120 6121 6122
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
6123 6124
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
6125 6126
	    break;
	}
6127
	else if (!my_strcasecmp(system_charset_info,
6128
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
6129
	  break;
unknown's avatar
unknown committed
6130 6131 6132 6133 6134
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
6135 6136 6137 6138 6139 6140 6141
      {
        /*
          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.

6142 6143 6144
          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.

6145 6146 6147 6148
          BLOBs may have cfield->length == 0, which is why we test it before
          checking whether cfield->length < key_part_length (in chars).
         */
        if (!Field::type_can_have_key_part(cfield->field->type()) ||
unknown's avatar
unknown committed
6149
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
6150 6151
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
unknown's avatar
unknown committed
6152 6153
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
6154 6155 6156
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
6157
      }
6158
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
6159
      key_parts.push_back(new Key_part_spec(cfield->field_name,
unknown's avatar
unknown committed
6160 6161 6162
					    key_part_length));
    }
    if (key_parts.elements)
6163 6164
    {
      KEY_CREATE_INFO key_create_info;
6165 6166
      Key *key;
      enum Key::Keytype key_type;
6167 6168 6169 6170 6171 6172
      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)
6173
        key_create_info.parser_name= *plugin_name(key_info->parser);
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 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304
      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;
6305
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
6306 6307
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
6308
  char path[FN_REFLEN + 1];
6309
  char reg_path[FN_REFLEN+1];
unknown's avatar
unknown committed
6310
  ha_rows copied,deleted;
6311
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
6312
  legacy_db_type table_type;
6313
  frm_type_enum frm_type;
unknown's avatar
unknown committed
6314
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
6315
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6316
  uint fast_alter_partition= 0;
6317 6318
  bool partition_changed= FALSE;
#endif
6319 6320
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
Staale Smedseng's avatar
Staale Smedseng committed
6321 6322 6323 6324 6325
  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;
unknown's avatar
unknown committed
6326
  bool committed= 0;
6327
  bool no_pk;
unknown's avatar
unknown committed
6328 6329
  DBUG_ENTER("mysql_alter_table");

6330 6331 6332 6333 6334 6335
  /*
    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.
  */
6336
  if (table_list && table_list->db && table_list->table_name)
6337
  {
6338
    int table_kind= 0;
6339

6340 6341 6342
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
6343

6344
    if (table_kind)
6345
    {
6346 6347 6348 6349 6350 6351
      /* 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);
      }
6352

6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364
      /* 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)
      {
6365
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
6366 6367 6368
        DBUG_RETURN(TRUE);
      }
#endif
6369 6370 6371
    }
  }

6372 6373 6374 6375 6376
  /*
    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.
  */
6377
  thd_proc_info(thd, "init");
6378
  table_name=table_list->table_name;
unknown's avatar
unknown committed
6379
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
unknown's avatar
unknown committed
6380
  db=table_list->db;
unknown's avatar
unknown committed
6381
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
6382
    new_db= db;
6383 6384
  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);
6385

6386
  mysql_ha_rm_tables(thd, table_list, FALSE);
unknown's avatar
unknown committed
6387

unknown's avatar
unknown committed
6388
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
6389
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
6390
    /* Conditionally writes to binlog. */
unknown's avatar
unknown committed
6391
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
6392
						   alter_info->tablespace_op));
6393 6394 6395
  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);
6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408
  /*
    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.
  */
6409 6410
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
6411
  /* Sic: there is a race here */
6412 6413
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427
    /*
      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);
    }

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

    if (wait_if_global_read_lock(thd,0,1))
6441
      DBUG_RETURN(TRUE);
6442 6443
    VOID(pthread_mutex_lock(&LOCK_open));
    if (lock_table_names(thd, table_list))
6444
    {
unknown's avatar
unknown committed
6445
      error= 1;
6446
      goto view_err;
6447
    }
6448 6449 6450 6451 6452 6453
    
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
6454
        Query_log_event qinfo(thd, thd->query(), thd->query_length(),
6455
                              FALSE, TRUE, FALSE, 0);
6456 6457
        mysql_bin_log.write(&qinfo);
      }
6458
      my_ok(thd);
6459 6460 6461 6462 6463 6464 6465 6466 6467
    }

    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);
  }
6468 6469

  if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
unknown's avatar
unknown committed
6470
    DBUG_RETURN(TRUE);
6471
  table->use_all_columns();
unknown's avatar
unknown committed
6472

6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486
  /*
    Prohibit changing of the UNION list of a non-temporary MERGE table
    under LOCK tables. It would be quite difficult to reuse a shrinked
    set of tables from the old table or to open a new TABLE object for
    an extended list and verify that they belong to locked tables.
  */
  if (thd->locked_tables &&
      (create_info->used_fields & HA_CREATE_USED_UNION) &&
      (table->s->tmp_table == NO_TMP_TABLE))
  {
    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
    DBUG_RETURN(TRUE);
  }

unknown's avatar
unknown committed
6487 6488 6489
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
6490
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
unknown's avatar
unknown committed
6491
    strmov(new_name_buff,new_name);
6492
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
6493
    if (lower_case_table_names)
unknown's avatar
unknown committed
6494 6495 6496
    {
      if (lower_case_table_names != 2)
      {
6497
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
6498 6499
	new_alias= new_name;			// Create lower case table name
      }
6500
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
6501
    }
6502
    if (new_db == db &&
unknown's avatar
unknown committed
6503
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
6504 6505
    {
      /*
6506 6507
	Source and destination table names are equal: make later check
	easier.
6508
      */
unknown's avatar
unknown committed
6509
      new_alias= new_name= table_name;
6510
    }
unknown's avatar
unknown committed
6511 6512
    else
    {
unknown's avatar
unknown committed
6513
      if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6514 6515 6516
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
6517
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
6518
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6519 6520 6521 6522
	}
      }
      else
      {
unknown's avatar
unknown committed
6523 6524 6525 6526 6527 6528 6529 6530
        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);
        }

6531
        build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
6532 6533
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
unknown's avatar
unknown committed
6534 6535
	{
	  /* Table will be closed in do_command() */
6536
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
6537
          goto err;
unknown's avatar
unknown committed
6538 6539 6540 6541 6542
	}
      }
    }
  }
  else
6543 6544 6545 6546
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
6547

unknown's avatar
unknown committed
6548
  old_db_type= table->s->db_type();
unknown's avatar
unknown committed
6549
  if (!create_info->db_type)
6550
  {
6551
#ifdef WITH_PARTITION_STORAGE_ENGINE
6552 6553
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
6554 6555 6556 6557
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
6558 6559 6560
        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.
6561
      */
6562
      create_info->db_type= table->part_info->default_engine_type;
6563
    }
6564
    else
6565
#endif
6566
      create_info->db_type= old_db_type;
6567
  }
unknown's avatar
unknown committed
6568

6569
  if (check_engine(thd, new_name, create_info))
unknown's avatar
unknown committed
6570
    goto err;
6571
  new_db_type= create_info->db_type;
6572

6573 6574
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
6575
      !table->file->can_switch_engines())
unknown's avatar
unknown committed
6576
  {
6577
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
unknown's avatar
unknown committed
6578 6579 6580
    goto err;
  }

6581 6582 6583 6584 6585
  /*
   If this is an ALTER TABLE and no explicit row type specified reuse
   the table's row type.
   Note : this is the same as if the row type was specified explicitly.
  */
6586
  if (create_info->row_type == ROW_TYPE_NOT_USED)
6587
  {
6588
    /* ALTER TABLE without explicit row type */
6589
    create_info->row_type= table->s->row_type;
6590 6591 6592 6593
  }
  else
  {
    /* ALTER TABLE with specific row type */
6594 6595
    create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
  }
unknown's avatar
unknown committed
6596

6597 6598 6599
  DBUG_PRINT("info", ("old type: %s  new type: %s",
             ha_resolve_storage_engine_name(old_db_type),
             ha_resolve_storage_engine_name(new_db_type)));
unknown's avatar
unknown committed
6600
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
6601
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
6602 6603 6604
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
6605
    goto err;
6606 6607
  }
  
6608
  thd_proc_info(thd, "setup");
6609
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
6610
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
6611
  {
6612 6613 6614 6615
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
6616 6617 6618 6619 6620 6621 6622 6623 6624 6625
      /*
        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));
6626
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6627
      VOID(pthread_mutex_unlock(&LOCK_open));
6628
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
6629
      error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6630 6631 6632
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
6633
      VOID(pthread_mutex_lock(&LOCK_open));
6634
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6635
      VOID(pthread_mutex_unlock(&LOCK_open));
6636
      error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6637 6638
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
6639 6640 6641 6642
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
6643 6644 6645
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
unknown's avatar
unknown committed
6646
      error= 0;
6647 6648 6649 6650 6651
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
    }

6652 6653 6654 6655 6656 6657 6658 6659 6660 6661
    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.
    */

6662
    if (!error && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6663
    {
6664
      thd_proc_info(thd, "rename");
unknown's avatar
unknown committed
6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677
      /*
        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.
      */
6678 6679
      if (!access(new_name_buff,F_OK))
      {
6680
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
6681
	error= -1;
6682 6683 6684
      }
      else
      {
6685 6686 6687 6688 6689 6690 6691 6692 6693 6694
	*fn_ext(new_name)=0;
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
	  error= -1;
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
          VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
                                  table_name, 0));
          error= -1;
        }
unknown's avatar
unknown committed
6695 6696
      }
    }
6697

6698 6699 6700 6701 6702 6703
    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);
6704
    }
6705

6706 6707
    if (!error)
    {
6708
      write_bin_log(thd, TRUE, thd->query(), thd->query_length());
6709
      my_ok(thd);
6710
    }
6711
    else if (error > 0)
6712
    {
6713 6714
      table->file->print_error(error, MYF(0));
      error= -1;
6715
    }
6716 6717 6718 6719 6720 6721
    if (name_lock)
      unlink_open_table(thd, name_lock, FALSE);
    VOID(pthread_mutex_unlock(&LOCK_open));
    table_list->table= NULL;                    // For query cache
    query_cache_invalidate3(thd, table_list, 0);
    DBUG_RETURN(error);
unknown's avatar
unknown committed
6722 6723
  }

6724
  /* We have to do full alter table. */
unknown's avatar
unknown committed
6725

6726 6727 6728
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6729
    goto err;
6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742
#endif
  /*
    If the old table had partitions and we are doing ALTER TABLE ...
    engine= <new_engine>, the new table must preserve the original
    partitioning. That means that the new engine is still the
    partitioning engine, not the engine specified in the parser.
    This is discovered  in prep_alter_part_table, which in such case
    updates create_info->db_type.
    Now we need to update the stack copy of create_info->db_type,
    as otherwise we won't be able to correctly move the files of the
    temporary table to the result table files.
  */
  new_db_type= create_info->db_type;
unknown's avatar
unknown committed
6743

6744 6745
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
unknown's avatar
unknown committed
6746 6747
  
  need_copy_table= alter_info->change_level;
unknown's avatar
unknown committed
6748

unknown's avatar
unknown committed
6749 6750
  set_table_default_charset(thd, create_info, db);

6751
  if (thd->variables.old_alter_table
unknown's avatar
unknown committed
6752
      || (table->s->db_type() != create_info->db_type)
6753
#ifdef WITH_PARTITION_STORAGE_ENGINE
6754
      || partition_changed
6755
#endif
6756
     )
6757
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
unknown's avatar
unknown committed
6758
  else
6759
  {
unknown's avatar
unknown committed
6760
    enum_alter_table_change_level need_copy_table_res;
6761
    /* Check how much the tables differ. */
6762 6763
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6764
                       &need_copy_table_res,
6765 6766
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
6767 6768
                       &index_add_buffer, &index_add_count,
                       &candidate_key_count))
6769
      goto err;
6770 6771 6772
   
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6773 6774 6775 6776 6777 6778 6779 6780 6781 6782
  }

  /*
    If there are index changes only, try to do them online. "Index
    changes only" means also that the handler for the table does not
    change. The table is open and locked. The handler can be accessed.
  */
  if (need_copy_table == ALTER_TABLE_INDEX_CHANGED)
  {
    int   pk_changed= 0;
unknown's avatar
unknown committed
6783
    ulong alter_flags= 0;
6784 6785 6786 6787 6788 6789
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

6790
    alter_flags= table->file->alter_table_flags(alter_info->flags);
unknown's avatar
unknown committed
6791
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6792 6793 6794 6795 6796 6797 6798 6799 6800
    /* 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)
      {
6801 6802 6803 6804 6805
        /* 
           Unique key. Check for "PRIMARY". 
           or if dropping last unique key
        */
        if ((uint) (key - table->key_info) == table->s->primary_key)
6806
        {
6807
          DBUG_PRINT("info", ("Dropping primary key"));
6808 6809 6810 6811
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
6812
          candidate_key_count--;
6813 6814 6815
        }
        else
        {
6816 6817 6818
          KEY_PART_INFO *part_end= key->key_part + key->key_parts;
          bool is_candidate_key= true;

6819 6820 6821
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834

          /*
            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--;
6835 6836 6837 6838 6839 6840 6841 6842 6843
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_DROP_INDEX;
        needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
      }
    }
6844 6845
    no_pk= ((table->s->primary_key == MAX_KEY) ||
            (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
6846 6847 6848 6849 6850 6851 6852 6853 6854
    /* 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)
      {
6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879
        /* 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))
6880
        {
6881
          DBUG_PRINT("info", ("Adding primary key"));
6882 6883 6884 6885
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
          pk_changed++;
6886
          no_pk= false;
6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902
        }
        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;
      }
    }

6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916
    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));
6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927
    /*
      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. */
6928
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6929 6930 6931 6932 6933
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
6934
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6935 6936 6937 6938 6939
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
unknown's avatar
unknown committed
6940

6941 6942
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
6943
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
6944 6945
    so that ALTER TABLE won't break when somebody will add new flag
  */
6946
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
6947
    create_info->frm_only= 1;
6948

6949
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6950
  if (fast_alter_partition)
unknown's avatar
unknown committed
6951
  {
unknown's avatar
unknown committed
6952
    DBUG_ASSERT(!name_lock);
unknown's avatar
unknown committed
6953 6954 6955 6956
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           db, table_name,
                                           fast_alter_partition));
unknown's avatar
unknown committed
6957
  }
6958
#endif
unknown's avatar
unknown committed
6959

6960 6961 6962 6963 6964 6965
  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);

6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980
  /*
    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.
6981 6982
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008
      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);
    }
  }
7009 7010
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
7011

7012 7013 7014 7015 7016 7017
  /*
    Create a table with a temporary name.
    With create_info->frm_only == 1 this creates a .frm file only.
    We don't log the statement, it will be logged later.
  */
  tmp_disable_binlog(thd);
unknown's avatar
unknown committed
7018
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
7019 7020 7021
                                    create_info,
                                    alter_info,
                                    1, 0);
7022 7023
  reenable_binlog(thd);
  if (error)
unknown's avatar
unknown committed
7024
    goto err;
7025 7026

  /* Open the table if we need to copy the data. */
7027
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
7028
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7029
  {
7030
    if (table->s->tmp_table)
7031 7032 7033 7034
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
7035
      tbl.table_name= tbl.alias= tmp_name;
unknown's avatar
unknown committed
7036
      /* Table is in thd->temporary_tables */
7037 7038
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
7039 7040 7041
    }
    else
    {
7042
      char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
7043
      /* table is a normal table: Create temporary table in same directory */
7044
      build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
7045
                           FN_IS_TMP);
7046
      /* Open our intermediate table */
7047 7048 7049
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
7050
      goto err1;
7051 7052 7053 7054
    /*
      Note: In case of MERGE table, we do not attach children. We do not
      copy data for MERGE tables. Only the children have data.
    */
unknown's avatar
unknown committed
7055 7056
  }

7057
  /* Copy the data if necessary. */
7058
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
7059
  thd->cuted_fields=0L;
unknown's avatar
unknown committed
7060
  copied=deleted=0;
7061 7062 7063 7064
  /*
    We do not copy data for MERGE tables. Only the children have data.
    MERGE tables have HA_NO_COPY_ON_ALTER set.
  */
7065
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
7066
  {
7067
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
unknown's avatar
unknown committed
7068
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7069
    new_table->next_number_field=new_table->found_next_number_field;
7070
    thd_proc_info(thd, "copy to tmp table");
7071 7072 7073
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
7074
                                    alter_info->keys_onoff,
unknown's avatar
unknown committed
7075
                                    alter_info->error_if_not_empty);
7076
  }
7077
  else
7078 7079 7080
  {
    VOID(pthread_mutex_lock(&LOCK_open));
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
7081
    VOID(pthread_mutex_unlock(&LOCK_open));
7082
    thd_proc_info(thd, "manage keys");
7083 7084
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
7085 7086
    error= ha_autocommit_or_rollback(thd, 0);
    if (end_active_trans(thd))
7087
      error= 1;
7088
  }
7089
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
7090

7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103
  /* 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"));

7104
    table->file->ha_prepare_for_alter();
7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131
    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;
7132
        goto err1;
7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154
      }
    }
    /*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));
7155
        goto err1;
7156 7157 7158 7159 7160 7161
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
7162
        goto err1;
7163 7164 7165 7166
      }
    }
    /*end of if (index_drop_count)*/

7167 7168 7169 7170
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
7171

7172 7173
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
7174
    if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
7175
      goto err1;
7176
    committed= 1;
7177 7178 7179
  }
  /*end of if (! new_table) for add/drop index*/

unknown's avatar
unknown committed
7180
  if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
7181 7182 7183
  {
    /* We changed a temporary table */
    if (error)
7184
      goto err1;
7185 7186 7187 7188 7189 7190
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
unknown's avatar
unknown committed
7191
    /* Remove link to old table and rename the new one */
unknown's avatar
unknown committed
7192
    close_temporary_table(thd, table, 1, 1);
7193 7194
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
7195
      goto err1;
7196
    /* We don't replicate alter table statement on temporary tables */
7197
    if (!thd->is_current_stmt_binlog_format_row())
7198
      write_bin_log(thd, TRUE, thd->query(), thd->query_length());
unknown's avatar
unknown committed
7199 7200 7201
    goto end_temporary;
  }

7202 7203
  if (new_table)
  {
7204 7205 7206 7207
    /*
      Close the intermediate table that will be the new table.
      Note that MERGE tables do not have their children attached here.
    */
unknown's avatar
unknown committed
7208
    intern_close_table(new_table);
7209
    my_free(new_table,MYF(0));
7210
  }
unknown's avatar
unknown committed
7211 7212 7213
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
7214
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
unknown's avatar
unknown committed
7215 7216 7217
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
7218

unknown's avatar
unknown committed
7219
  /*
7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232
    Data is copied. Now we:
    1) Wait until all other threads close old version of table.
    2) Close instances of table open by this thread and replace them
       with exclusive name-locks.
    3) Rename the old table to a temp name, rename the new one to the
       old name.
    4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
       we reopen new version of table.
    5) Write statement to the binary log.
    6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
       remove name-locks from list of open tables and table cache.
    7) If we are not not under LOCK TABLES we rely on close_thread_tables()
       call to remove name-locks from table cache and list of open table.
unknown's avatar
unknown committed
7233 7234
  */

7235
  thd_proc_info(thd, "rename result table");
7236 7237
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
7238 7239
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
unknown's avatar
unknown committed
7240

7241
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
7242
  close_data_files_and_morph_locks(thd, db, table_name);
unknown's avatar
unknown committed
7243

unknown's avatar
unknown committed
7244
  error=0;
7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259
  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.
  */
7260 7261 7262 7263 7264 7265
  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;
  }
7266 7267
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
unknown's avatar
unknown committed
7268 7269
  {
    error=1;
7270
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
unknown's avatar
unknown committed
7271
  }
7272
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
7273
                              new_alias, FN_FROM_IS_TMP) ||
7274
           ((new_name != table_name || new_db != db) && // we also do rename
7275
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
7276 7277
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
7278
           Table_triggers_list::change_table_name(thd, db, table_name,
7279
                                                  new_db, new_alias)))
7280 7281
  {
    /* Try to get everything back. */
unknown's avatar
unknown committed
7282
    error=1;
7283 7284 7285 7286
    VOID(quick_rm_table(new_db_type,new_db,new_alias, 0));
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
    VOID(mysql_rename_table(old_db_type, db, old_name, db, alias,
                            FN_FROM_IS_TMP));
unknown's avatar
unknown committed
7287
  }
7288

unknown's avatar
unknown committed
7289 7290
  if (error)
  {
7291 7292
    /* This shouldn't happen. But let us play it safe. */
    goto err_with_placeholders;
unknown's avatar
unknown committed
7293
  }
7294

7295
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7296
  {
unknown's avatar
unknown committed
7297
    /*
7298 7299
      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.
7300
      NO need to tamper with MERGE tables. The real open is done later.
unknown's avatar
unknown committed
7301
    */
7302 7303 7304 7305 7306 7307 7308 7309
    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);
7310 7311
      table_list->table= name_lock;
      if (reopen_name_locked_table(thd, table_list, FALSE))
7312 7313
        goto err_with_placeholders;
      t_table= table_list->table;
unknown's avatar
unknown committed
7314
    }
7315
    else
7316
    {
7317 7318 7319
      if (reopen_table(table))
        goto err_with_placeholders;
      t_table= table;
7320 7321
    }
    /* Tell the handler that a new frm file is in place. */
7322 7323
    if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                               create_info))
7324 7325
      goto err_with_placeholders;
    if (thd->locked_tables && new_name == table_name && new_db == db)
7326
    {
7327 7328 7329 7330 7331 7332 7333 7334
      /*
        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);
7335 7336
    }
  }
7337

7338 7339 7340
  VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));

  if (thd->locked_tables && new_name == table_name && new_db == db)
unknown's avatar
unknown committed
7341
  {
7342
    thd->in_lock_tables= 1;
7343
    error= reopen_tables(thd, 1, 1);
7344
    thd->in_lock_tables= 0;
unknown's avatar
unknown committed
7345
    if (error)
7346
      goto err_with_placeholders;
unknown's avatar
unknown committed
7347
  }
7348
  VOID(pthread_mutex_unlock(&LOCK_open));
7349

7350
  thd_proc_info(thd, "end");
7351

7352 7353
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

7354
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
7355
                      thd->query(), thd->query_length(),
7356 7357
                      db, table_name);

unknown's avatar
unknown committed
7358
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
7359
                thd->is_current_stmt_binlog_format_row() &&
7360
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
7361
  write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7362

7363
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
7364
  {
unknown's avatar
unknown committed
7365 7366 7367
    /*
      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
7368
      shutdown. But we do not need to attach MERGE children.
unknown's avatar
unknown committed
7369
    */
7370
    char path[FN_REFLEN];
7371
    TABLE *t_table;
7372
    build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0);
7373 7374
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
unknown's avatar
unknown committed
7375
    {
7376
      intern_close_table(t_table);
7377
      my_free(t_table, MYF(0));
unknown's avatar
unknown committed
7378
    }
7379
    else
7380
      sql_print_warning("Could not open table %s.%s after rename\n",
unknown's avatar
unknown committed
7381
                        new_db,table_name);
7382
    ha_flush_logs(old_db_type);
7383
  }
unknown's avatar
unknown committed
7384
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
7385
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
7386

7387
  if (thd->locked_tables && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
7388
  {
7389 7390 7391 7392 7393 7394
    /*
      If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
      to remove placeholders for the old table and for the target table
      from the list of open tables and table cache. If we are not under
      LOCK TABLES we can rely on close_thread_tables() doing this job.
    */
unknown's avatar
unknown committed
7395
    pthread_mutex_lock(&LOCK_open);
7396
    unlink_open_table(thd, table, FALSE);
unknown's avatar
unknown committed
7397 7398 7399 7400
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }

unknown's avatar
unknown committed
7401
end_temporary:
7402 7403 7404
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
7405
  my_ok(thd, copied + deleted, 0L, tmp_name);
unknown's avatar
unknown committed
7406
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
7407
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7408

7409
err1:
7410 7411 7412 7413 7414 7415
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
7416 7417 7418 7419
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, 
                        create_info->frm_only
                        ? FN_IS_TMP | FRM_ONLY
                        : FN_IS_TMP));
7420

7421
err:
7422 7423 7424 7425 7426 7427
  /*
    No default value was provided for a DATE/DATETIME field, the
    current sql_mode doesn't allow the '0000-00-00' value and
    the table to be altered isn't empty.
    Report error here.
  */
unknown's avatar
unknown committed
7428
  if (alter_info->error_if_not_empty && thd->row_count)
7429
  {
7430 7431
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
unknown's avatar
unknown committed
7432
    switch (alter_info->datetime_field->sql_type)
7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448
    {
      case MYSQL_TYPE_DATE:
      case MYSQL_TYPE_NEWDATE:
        f_val= "0000-00-00";
        t_type= MYSQL_TIMESTAMP_DATE;
        break;
      case MYSQL_TYPE_DATETIME:
        f_val= "0000-00-00 00:00:00";
        t_type= MYSQL_TIMESTAMP_DATETIME;
        break;
      default:
        /* Shouldn't get here. */
        DBUG_ASSERT(0);
    }
    bool save_abort_on_warning= thd->abort_on_warning;
    thd->abort_on_warning= TRUE;
unknown's avatar
unknown committed
7449 7450
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                                 f_val, strlength(f_val), t_type,
unknown's avatar
unknown committed
7451
                                 alter_info->datetime_field->field_name);
7452 7453
    thd->abort_on_warning= save_abort_on_warning;
  }
unknown's avatar
unknown committed
7454 7455 7456 7457 7458 7459
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
7460
  DBUG_RETURN(TRUE);
7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471

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

static int
unknown's avatar
unknown committed
7477
copy_data_between_tables(TABLE *from,TABLE *to,
unknown's avatar
unknown committed
7478
			 List<Create_field> &create,
7479
                         bool ignore,
7480
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
7481
			 ha_rows *copied,
unknown's avatar
unknown committed
7482
			 ha_rows *deleted,
7483 7484
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
unknown's avatar
unknown committed
7485 7486 7487 7488 7489
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
7490
  uint length= 0;
unknown's avatar
unknown committed
7491 7492 7493 7494 7495
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
7496
  ha_rows examined_rows;
7497
  bool auto_increment_field_copied= 0;
7498
  ulong save_sql_mode;
7499
  ulonglong prev_insert_id;
unknown's avatar
unknown committed
7500 7501
  DBUG_ENTER("copy_data_between_tables");

7502 7503 7504 7505 7506 7507
  /*
    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
  */
7508
  error= ha_enable_transaction(thd, FALSE);
7509 7510
  if (error)
    DBUG_RETURN(-1);
7511
  
7512
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
7513 7514
    DBUG_RETURN(-1);				/* purecov: inspected */

7515
  if (to->file->ha_external_lock(thd, F_WRLCK))
7516
    DBUG_RETURN(-1);
7517

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

7521 7522 7523 7524 7525
  /* 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));

7526
  from->file->info(HA_STATUS_VARIABLE);
7527
  to->file->ha_start_bulk_insert(from->file->stats.records);
unknown's avatar
unknown committed
7528

7529 7530
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
7531 7532
  List_iterator<Create_field> it(create);
  Create_field *def;
unknown's avatar
unknown committed
7533 7534 7535 7536 7537
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
7538 7539
    {
      if (*ptr == to->next_number_field)
7540
      {
7541
        auto_increment_field_copied= TRUE;
7542 7543 7544 7545 7546 7547 7548 7549 7550
        /*
          If we are going to copy contents of one auto_increment column to
          another auto_increment column it is sensible to preserve zeroes.
          This condition also covers case when we are don't actually alter
          auto_increment column.
        */
        if (def->field == from->found_next_number_field)
          thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
      }
unknown's avatar
unknown committed
7551
      (copy_end++)->set(*ptr,def->field,0);
7552 7553
    }

unknown's avatar
unknown committed
7554 7555
  }

7556 7557
  found_count=delete_count=0;

unknown's avatar
unknown committed
7558 7559
  if (order)
  {
7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577
    if (to->s->primary_key != MAX_KEY && to->file->primary_key_is_clustered())
    {
      char warn_buff[MYSQL_ERRMSG_SIZE];
      my_snprintf(warn_buff, sizeof(warn_buff), 
                  "ORDER BY ignored as there is a user-defined clustered index"
                  " in the table '%-.192s'", from->s->table_name.str);
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
                   warn_buff);
    }
    else
    {
      from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
                                                MYF(MY_FAE | MY_ZEROFILL));
      bzero((char *) &tables, sizeof(tables));
      tables.table= from;
      tables.alias= tables.table_name= from->s->table_name.str;
      tables.db= from->s->db.str;
      error= 1;
unknown's avatar
unknown committed
7578

7579 7580 7581 7582 7583 7584 7585 7586 7587 7588
      if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
          setup_order(thd, thd->lex->select_lex.ref_pointer_array,
                      &tables, fields, all_fields, order) ||
          !(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
          (from->sort.found_records= filesort(thd, from, sortorder, length,
                                              (SQL_SELECT *) 0, HA_POS_ERROR,
                                              1, &examined_rows)) ==
          HA_POS_ERROR)
        goto err;
    }
unknown's avatar
unknown committed
7589 7590
  };

7591 7592
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
7593
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
7594
  if (ignore)
unknown's avatar
unknown committed
7595
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
7596
  thd->row_count= 0;
7597
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
7598 7599 7600 7601
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
7602
      thd->send_kill_message();
unknown's avatar
unknown committed
7603 7604 7605
      error= 1;
      break;
    }
7606
    thd->row_count++;
7607 7608 7609 7610 7611 7612
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
7613 7614
    if (to->next_number_field)
    {
7615
      if (auto_increment_field_copied)
7616
        to->auto_increment_field_not_null= TRUE;
7617 7618 7619
      else
        to->next_number_field->reset();
    }
7620
    
unknown's avatar
unknown committed
7621
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
7622
    {
unknown's avatar
unknown committed
7623
      copy_ptr->do_copy(copy_ptr);
7624
    }
7625
    prev_insert_id= to->file->next_insert_id;
7626
    error=to->file->ha_write_row(to->record[0]);
7627 7628
    to->auto_increment_field_not_null= FALSE;
    if (error)
unknown's avatar
unknown committed
7629
    {
7630
      if (!ignore ||
7631
          to->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
7632
      {
7633
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
7634 7635 7636 7637
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
unknown's avatar
unknown committed
7638
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
7639
             if (key_nr == 0 &&
unknown's avatar
unknown committed
7640 7641
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
7642
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
unknown's avatar
unknown committed
7643
             to->file->print_keydup_error(key_nr, err_msg);
7644 7645 7646 7647
             break;
           }
         }

unknown's avatar
unknown committed
7648 7649 7650
	to->file->print_error(error,MYF(0));
	break;
      }
7651
      to->file->restore_auto_increment(prev_insert_id);
unknown's avatar
unknown committed
7652 7653 7654
      delete_count++;
    }
    else
7655
      found_count++;
unknown's avatar
unknown committed
7656 7657
  }
  end_read_record(&info);
7658
  free_io_cache(from);
7659
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
7660

7661
  if (to->file->ha_end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
7662
  {
unknown's avatar
unknown committed
7663
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
7664 7665
    error=1;
  }
unknown's avatar
unknown committed
7666
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7667

7668 7669 7670 7671 7672 7673
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7674 7675 7676 7677
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
7678
  if (ha_autocommit_or_rollback(thd, 0))
7679
    error=1;
7680
  if (end_active_trans(thd))
7681
    error=1;
7682

unknown's avatar
unknown committed
7683
 err:
7684
  thd->variables.sql_mode= save_sql_mode;
7685
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
7686
  free_io_cache(from);
unknown's avatar
unknown committed
7687 7688
  *copied= found_count;
  *deleted=delete_count;
7689
  to->file->ha_release_auto_increment();
7690
  if (to->file->ha_external_lock(thd,F_UNLCK))
7691
    error=1;
unknown's avatar
unknown committed
7692 7693
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7694

unknown's avatar
unknown committed
7695

7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7707
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7708 7709
{
  HA_CREATE_INFO create_info;
7710 7711 7712
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7713 7714 7715 7716 7717 7718
  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;
7719 7720

  bzero((char*) &create_info, sizeof(create_info));
unknown's avatar
unknown committed
7721
  create_info.row_type=ROW_TYPE_NOT_USED;
7722
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
7723
  /* Force alter table to recreate table */
7724
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7725
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7726 7727
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7728 7729 7730
}


7731 7732
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7733 7734 7735 7736 7737
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
7738
  DBUG_ENTER("mysql_checksum_table");
7739 7740 7741

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7742 7743
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7744
  item->maybe_null= 1;
7745 7746
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
7747
    DBUG_RETURN(TRUE);
7748

7749
  /* Open one table after the other to keep lock time as short as possible. */
unknown's avatar
VIEW  
unknown committed
7750
  for (table= tables; table; table= table->next_local)
7751 7752
  {
    char table_name[NAME_LEN*2+2];
7753
    TABLE *t;
7754

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

7757
    t= table->table= open_n_lock_single_table(thd, table, TL_READ);
unknown's avatar
unknown committed
7758
    thd->clear_error();			// these errors shouldn't get client
7759 7760 7761 7762

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

7763
    if (!t)
7764
    {
unknown's avatar
unknown committed
7765
      /* Table didn't exist */
7766
      protocol->store_null();
unknown's avatar
unknown committed
7767
      thd->clear_error();
7768 7769 7770
    }
    else
    {
7771
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7772 7773
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7774
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
7775
	       (check_opt->flags & T_QUICK))
7776
	protocol->store_null();
7777 7778
      else
      {
7779 7780
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
7781
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7782

7783
        t->use_all_columns();
7784

unknown's avatar
unknown committed
7785
	if (t->file->ha_rnd_init(1))
7786 7787 7788
	  protocol->store_null();
	else
	{
7789
	  for (;;)
7790
	  {
7791 7792 7793 7794 7795 7796 7797 7798 7799 7800
            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;
            }
7801
	    ha_checksum row_crc= 0;
7802 7803 7804 7805 7806 7807 7808
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
unknown's avatar
unknown committed
7809 7810 7811 7812
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
7813 7814 7815
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
7816 7817
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7818

7819
	    for (uint i= 0; i < t->s->fields; i++ )
7820 7821
	    {
	      Field *f= t->field[i];
7822 7823 7824 7825 7826 7827 7828 7829
        enum_field_types field_type= f->type();
        /*
          BLOB and VARCHAR have pointers in their field, we must convert
          to string; GEOMETRY is implemented on top of BLOB.
        */
        if ((field_type == MYSQL_TYPE_BLOB) ||
            (field_type == MYSQL_TYPE_VARCHAR) ||
            (field_type == MYSQL_TYPE_GEOMETRY))
7830 7831 7832
	      {
		String tmp;
		f->val_str(&tmp);
7833
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7834 7835
	      }
	      else
7836
		row_crc= my_checksum(row_crc, f->ptr,
unknown's avatar
unknown committed
7837
				     f->pack_length());
7838
	    }
7839

7840 7841 7842
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
7843
          t->file->ha_rnd_end();
7844
	}
7845
      }
unknown's avatar
unknown committed
7846
      thd->clear_error();
7847 7848 7849 7850 7851 7852 7853
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

7854
  my_eof(thd);
unknown's avatar
unknown committed
7855
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7856

7857 7858 7859 7860
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
7861
  DBUG_RETURN(TRUE);
7862
}
7863 7864

static bool check_engine(THD *thd, const char *table_name,
7865
                         HA_CREATE_INFO *create_info)
7866
{
7867
  handlerton **new_engine= &create_info->db_type;
unknown's avatar
unknown committed
7868
  handlerton *req_engine= *new_engine;
unknown's avatar
unknown committed
7869
  bool no_substitution=
7870
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
7871 7872
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
7873 7874
    return TRUE;

unknown's avatar
unknown committed
7875
  if (req_engine && req_engine != *new_engine)
7876
  {
unknown's avatar
unknown committed
7877
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
7878 7879
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
unknown's avatar
unknown committed
7880
                       ha_resolve_storage_engine_name(*new_engine),
7881 7882
                       table_name);
  }
7883 7884 7885 7886 7887
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE &&
      ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED))
  {
    if (create_info->used_fields & HA_CREATE_USED_ENGINE)
    {
unknown's avatar
unknown committed
7888
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
unknown's avatar
unknown committed
7889
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
7890 7891 7892
      *new_engine= 0;
      return TRUE;
    }
7893
    *new_engine= myisam_hton;
7894
  }
7895 7896
  return FALSE;
}