sql_table.cc 253 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"
Konstantin Osipov's avatar
Konstantin Osipov committed
25
#include "transaction.h"
unknown's avatar
unknown committed
26 27 28 29 30

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

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

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

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

unknown's avatar
unknown committed
44
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
unknown's avatar
unknown committed
45
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
unknown's avatar
unknown committed
46
static int
47 48 49 50 51 52 53
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
54 55
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
56
                          Alter_info *alter_info);
unknown's avatar
unknown committed
57

58
#ifndef DBUG_OFF
unknown's avatar
unknown committed
59

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

71

72 73
/**
  @brief Helper function for explain_filename
74 75 76 77 78
  @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
79
*/
80 81
static char* add_identifier(THD* thd, char *to_p, const char * end_p,
                            const char* name, uint name_len)
82 83 84 85 86 87
{
  uint res;
  uint errors;
  const char *conv_name;
  char tmp_name[FN_REFLEN];
  char conv_string[FN_REFLEN];
88
  int quote;
89 90 91 92 93 94 95 96 97 98 99 100 101

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

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

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


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

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

182 183
uint explain_filename(THD* thd,
		      const char *from,
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
                      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;
    }
  }
289 290
  else
    table_name_len= strlen(table_name);
291 292 293 294
  if (db_name)
  {
    if (explain_mode == EXPLAIN_ALL_VERBOSE)
    {
295 296
      to_p= strnmov(to_p, ER(ER_DATABASE_NAME), end_p - to_p);
      *(to_p++)= ' ';
297
      to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
298 299 300 301
      to_p= strnmov(to_p, ", ", end_p - to_p);
    }
    else
    {
302
      to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
303 304 305 306
      to_p= strnmov(to_p, ".", end_p - to_p);
    }
  }
  if (explain_mode == EXPLAIN_ALL_VERBOSE)
307 308 309
  {
    to_p= strnmov(to_p, ER(ER_TABLE_NAME), end_p - to_p);
    *(to_p++)= ' ';
310
    to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
311
  }
312
  else
313
    to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
314 315
  if (part_name)
  {
316
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
317 318 319 320 321 322 323 324 325 326 327 328 329
      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);
    }
330 331
    to_p= strnmov(to_p, ER(ER_PARTITION_NAME), end_p - to_p);
    *(to_p++)= ' ';
332
    to_p= add_identifier(thd, to_p, end_p, part_name, part_name_len);
333 334 335
    if (subpart_name)
    {
      to_p= strnmov(to_p, ", ", end_p - to_p);
336 337
      to_p= strnmov(to_p, ER(ER_SUBPARTITION_NAME), end_p - to_p);
      *(to_p++)= ' ';
338
      to_p= add_identifier(thd, to_p, end_p, subpart_name, subpart_name_len);
339
    }
340
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
341 342 343 344 345 346 347
      to_p= strnmov(to_p, " */", end_p - to_p);
  }
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(to_p - to);
}


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

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

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

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


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


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

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

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


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

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

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

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

481 482
  RETURN
    path length
483 484 485
*/

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

  if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP
    strnmov(tbbuff, table_name, sizeof(tbbuff));
  else
Konstantin Osipov's avatar
Konstantin Osipov committed
497
    (void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff));
498

Konstantin Osipov's avatar
Konstantin Osipov committed
499
  (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff));
500 501 502 503

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

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


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

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

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

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

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

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

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

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

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

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

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


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

608
st_global_ddl_log global_ddl_log;
unknown's avatar
unknown committed
609

610
pthread_mutex_t LOCK_gdl;
unknown's avatar
unknown committed
611

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

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

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

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

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


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

657
static bool write_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
658 659
{
  bool error= FALSE;
660 661 662
  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
663

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


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

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

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


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

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


unknown's avatar
unknown committed
719
/*
720
  Read header of ddl log file
unknown's avatar
unknown committed
721
  SYNOPSIS
722
    read_ddl_log_header()
unknown's avatar
unknown committed
723
  RETURN VALUES
724 725
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
unknown's avatar
unknown committed
726
  DESCRIPTION
727 728 729
    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
730 731
*/

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

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


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

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

793
  if (read_ddl_log_file_entry(read_entry))
794 795 796
  {
    DBUG_RETURN(TRUE);
  }
797
  ddl_log_entry->entry_pos= read_entry;
798 799 800 801
  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;
802 803 804 805 806 807 808
  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
809 810 811 812 813
  DBUG_RETURN(FALSE);
}


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

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

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

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

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

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

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


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

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

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


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

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

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

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


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

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

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

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

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

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


/*
1114
  Write final entry in the ddl log
unknown's avatar
unknown committed
1115
  SYNOPSIS
1116
    write_execute_ddl_log_entry()
1117 1118 1119 1120
    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.
1121 1122 1123
    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
1124 1125 1126
                                   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
1127
  RETURN VALUES
1128 1129 1130 1131
    TRUE                           Error
    FALSE                          Success

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

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

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


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

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

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


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

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

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


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

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

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

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


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

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

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

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

1362

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
/*
  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)
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
1376
    (void) my_close(global_ddl_log.file_id, MYF(MY_WME));
1377 1378 1379 1380 1381 1382
    global_ddl_log.file_id= (File) -1;
  }
  DBUG_VOID_RETURN;
}


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

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

1399 1400 1401 1402 1403 1404 1405
  /*
    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
1406
  global_ddl_log.file_id= (File) -1;
1407

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

1416
  num_entries= read_ddl_log_header();
1417
  for (i= 1; i < num_entries + 1; i++)
unknown's avatar
unknown committed
1418
  {
1419
    if (read_ddl_log_entry(i, &ddl_log_entry))
1420
    {
1421 1422 1423
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1424
    }
1425
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
unknown's avatar
unknown committed
1426
    {
unknown's avatar
Fixes  
unknown committed
1427
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
unknown's avatar
unknown committed
1428
      {
1429 1430
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
unknown's avatar
unknown committed
1431 1432 1433
      }
    }
  }
1434
  close_ddl_log();
1435
  create_ddl_log_file_name(file_name);
Konstantin Osipov's avatar
Konstantin Osipov committed
1436
  (void) my_delete(file_name, MYF(0));
1437 1438 1439 1440
  global_ddl_log.recovery_phase= FALSE;
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
1441
  DBUG_VOID_RETURN;
1442 1443 1444 1445
}


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

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

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

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


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

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

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

unknown's avatar
unknown committed
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 1518
/**
   @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
1519 1520 1521 1522 1523 1524 1525 1526
/*
  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
1527 1528 1529 1530 1531 1532
      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
1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554
      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];
1555 1556
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
unknown's avatar
unknown committed
1557
  char frm_name[FN_REFLEN+1];
1558 1559 1560 1561
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char *part_syntax_buf;
  uint syntax_len;
#endif
unknown's avatar
unknown committed
1562 1563
  DBUG_ENTER("mysql_write_frm");

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

err:
Konstantin Osipov's avatar
Konstantin Osipov committed
1710
    pthread_mutex_unlock(&LOCK_open);
1711
#ifdef WITH_PARTITION_STORAGE_ENGINE
1712
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1713
    part_info->frm_log_entry= NULL;
Konstantin Osipov's avatar
Konstantin Osipov committed
1714
    (void) sync_ddl_log();
1715
#endif
unknown's avatar
unknown committed
1716
  }
1717

unknown's avatar
unknown committed
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
end:
  DBUG_RETURN(error);
}


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

  RETURN VALUES
    NONE

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

void write_bin_log(THD *thd, bool clear_error,
                   char const *query, ulong query_length)
{
  if (mysql_bin_log.is_open())
  {
1744
    int errcode= 0;
unknown's avatar
unknown committed
1745 1746
    if (clear_error)
      thd->clear_error();
1747 1748
    else
      errcode= query_error_code(thd, TRUE);
unknown's avatar
unknown committed
1749
    thd->binlog_query(THD::STMT_QUERY_TYPE,
1750
                      query, query_length, FALSE, FALSE, errcode);
unknown's avatar
unknown committed
1751 1752 1753
  }
}

1754

unknown's avatar
unknown committed
1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769
/*
 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

Konstantin Osipov's avatar
Konstantin Osipov committed
1770 1771
    Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set, but
    not if under LOCK TABLES.
unknown's avatar
unknown committed
1772 1773

  RETURN
unknown's avatar
unknown committed
1774 1775
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
1776 1777

*/
unknown's avatar
unknown committed
1778

unknown's avatar
unknown committed
1779 1780
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
1781
{
Konstantin Osipov's avatar
Konstantin Osipov committed
1782
  bool error= FALSE, need_start_waiting= FALSE;
1783
  Drop_table_error_handler err_handler(thd->get_internal_handler());
unknown's avatar
unknown committed
1784 1785 1786 1787
  DBUG_ENTER("mysql_rm_table");

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

1788
  if (!drop_temporary)
unknown's avatar
unknown committed
1789
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
1790
    if (!thd->locked_tables_mode &&
Konstantin Osipov's avatar
Konstantin Osipov committed
1791
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
1792
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1793
  }
1794

1795
  thd->push_internal_handler(&err_handler);
unknown's avatar
VIEW  
unknown committed
1796
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1797 1798
  thd->pop_internal_handler();

1799

Konstantin Osipov's avatar
Konstantin Osipov committed
1800
  if (need_start_waiting)
1801 1802
    start_waiting_global_read_lock(thd);

1803
  if (error)
unknown's avatar
unknown committed
1804
    DBUG_RETURN(TRUE);
1805
  my_ok(thd);
unknown's avatar
unknown committed
1806
  DBUG_RETURN(FALSE);
1807 1808
}

1809
/*
1810 1811 1812 1813 1814 1815 1816 1817 1818
  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
1819
    drop_view		Allow to delete VIEW .frm
1820 1821
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1822

1823 1824 1825 1826 1827 1828 1829 1830 1831
  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.
1832 1833 1834 1835 1836

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1837
*/
1838 1839

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

unknown's avatar
unknown committed
1854 1855 1856
  LINT_INIT(alias);
  LINT_INIT(path_length);

1857
  if (thd->current_stmt_binlog_row_based && !dont_log_query)
1858 1859 1860 1861 1862 1863 1864
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1865

1866
  mysql_ha_rm_tables(thd, tables);
1867

unknown's avatar
unknown committed
1868 1869 1870 1871 1872
  /*
    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.
  */
1873
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1874 1875 1876
  for (table= tables; table; table= table->next_local)
  {
    TABLE_SHARE *share;
unknown's avatar
unknown committed
1877
    table->db_type= NULL;
unknown's avatar
unknown committed
1878
    if ((share= get_cached_table_share(table->db, table->table_name)))
unknown's avatar
WL#2936  
unknown committed
1879
      table->db_type= share->db_type();
1880 1881

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

1893
  if (!drop_temporary)
1894
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
1895
    if (!thd->locked_tables_mode)
1896 1897 1898 1899 1900
    {
      if (lock_table_names(thd, tables))
        DBUG_RETURN(1);
      pthread_mutex_lock(&LOCK_open);
      for (table= tables; table; table= table->next_local)
Konstantin Osipov's avatar
Konstantin Osipov committed
1901
        tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
1902 1903
      pthread_mutex_unlock(&LOCK_open);
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
1904
    else
1905 1906
    {
      for (table= tables; table; table= table->next_local)
Konstantin Osipov's avatar
Konstantin Osipov committed
1907 1908 1909 1910 1911 1912
        if (find_temporary_table(thd, table->db, table->table_name))
        {
          /*
            Since we don't acquire metadata lock if we have found temporary
            table, we should do something to avoid releasing it at the end.
          */
Konstantin Osipov's avatar
Konstantin Osipov committed
1913
          table->mdl_request= NULL;
Konstantin Osipov's avatar
Konstantin Osipov committed
1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925
        }
        else
        {
          /*
            Since 'tables' list can't contain duplicates (this is ensured
            by parser) it is safe to cache pointer to the TABLE instances
            in its elements.
          */
          table->table= find_write_locked_table(thd->open_tables, table->db,
                                                table->table_name);
          if (!table->table)
            DBUG_RETURN(1);
Konstantin Osipov's avatar
Konstantin Osipov committed
1926
          table->mdl_request->ticket= table->table->mdl_ticket;
Konstantin Osipov's avatar
Konstantin Osipov committed
1927
        }
1928
    }
1929
  }
1930

unknown's avatar
VIEW  
unknown committed
1931
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
1932
  {
1933
    char *db=table->db;
unknown's avatar
unknown committed
1934 1935
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1936

1937 1938 1939
    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));
1940 1941 1942 1943 1944 1945 1946

    error= drop_temporary_table(thd, table);

    switch (error) {
    case  0:
      // removed temporary table
      tmp_table_deleted= 1;
1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965
      if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
          thd->current_stmt_binlog_row_based)
      {
        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("`,");
      }

1966 1967
      continue;
    case -1:
1968
      DBUG_ASSERT(thd->in_sub_stmt);
1969
      error= 1;
Konstantin Osipov's avatar
Konstantin Osipov committed
1970
      goto err;
1971 1972 1973
    default:
      // temporary table not found
      error= 0;
unknown's avatar
unknown committed
1974
    }
unknown's avatar
unknown committed
1975

Konstantin Osipov's avatar
Konstantin Osipov committed
1976
    /* Probably a non-temporary table. */
1977 1978
    if (!drop_temporary)
      non_temp_tables_count++;
Konstantin Osipov's avatar
Konstantin Osipov committed
1979

1980 1981 1982 1983 1984 1985
    /*
      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.
      */
1986
    if (!drop_temporary && thd->current_stmt_binlog_row_based && !dont_log_query)
1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002
    {
      /*
        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
2003
    table_type= table->db_type;
2004
    if (!drop_temporary)
unknown's avatar
unknown committed
2005
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
2006
      if (thd->locked_tables_mode)
2007
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
2008
        if (wait_while_table_is_used(thd, table->table, HA_EXTRA_FORCE_REOPEN))
2009 2010
        {
          error= -1;
Konstantin Osipov's avatar
Konstantin Osipov committed
2011
          goto err;
2012
        }
Konstantin Osipov's avatar
Konstantin Osipov committed
2013
        close_all_tables_for_name(thd, table->table->s, TRUE);
Konstantin Osipov's avatar
Konstantin Osipov committed
2014
        table->table= 0;
2015
      }
unknown's avatar
unknown committed
2016

2017
      if (thd->killed)
2018
      {
2019
        error= -1;
Konstantin Osipov's avatar
Konstantin Osipov committed
2020
        goto err;
2021
      }
2022
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
unknown's avatar
unknown committed
2023
      /* remove .frm file and engine files */
2024 2025
      path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
                                        reg_ext,
2026 2027
                                        table->internal_tmp_table ?
                                        FN_IS_TMP : 0);
unknown's avatar
unknown committed
2028
    }
2029 2030 2031 2032 2033
    /*
      TODO: Investigate what should be done to remove this lock completely.
            Is exclusive meta-data lock enough ?
    */
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2034
    if (drop_temporary ||
Staale Smedseng's avatar
Staale Smedseng committed
2035 2036
        ((table_type == NULL &&        
         access(path, F_OK) &&
unknown's avatar
unknown committed
2037 2038
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
unknown's avatar
unknown committed
2039
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
unknown's avatar
unknown committed
2040
    {
2041
      // Table was not found on disk and table can't be created from engine
2042
      if (if_exists)
2043 2044
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
2045
			    table->table_name);
2046
      else
2047
        error= 1;
unknown's avatar
unknown committed
2048 2049 2050
    }
    else
    {
unknown's avatar
unknown committed
2051
      char *end;
unknown's avatar
unknown committed
2052 2053 2054 2055 2056
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
2057 2058
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
unknown's avatar
unknown committed
2059
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
2060
                             !dont_log_query);
2061
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
unknown's avatar
unknown committed
2062
	  (if_exists || table_type == NULL))
2063
      {
2064
	error= 0;
2065 2066
        thd->clear_error();
      }
unknown's avatar
unknown committed
2067
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
2068 2069
      {
	/* the table is referenced by a foreign key constraint */
2070
	foreign_key_error=1;
unknown's avatar
unknown committed
2071
      }
2072
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
2073
      {
2074
        int new_error;
unknown's avatar
unknown committed
2075 2076
	/* Delete the table definition file */
	strmov(end,reg_ext);
2077
	if (!(new_error=my_delete(path,MYF(MY_WME))))
2078
        {
unknown's avatar
unknown committed
2079
	  some_tables_deleted=1;
2080 2081
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
2082
        }
2083
        error|= new_error;
2084
      }
unknown's avatar
unknown committed
2085
    }
2086
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2087 2088 2089 2090
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
2091
      wrong_tables.append(String(table->table_name,system_charset_info));
unknown's avatar
unknown committed
2092
    }
2093 2094
    DBUG_PRINT("table", ("table: 0x%lx  s: 0x%lx", (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
2095 2096 2097 2098 2099 2100

    DBUG_EXECUTE_IF("bug43138",
                    my_printf_error(ER_BAD_TABLE_ERROR,
                                    ER(ER_BAD_TABLE_ERROR), MYF(0),
                                    table->table_name););

unknown's avatar
unknown committed
2101
  }
unknown's avatar
unknown committed
2102
  thd->thread_specific_used|= tmp_table_deleted;
2103 2104 2105 2106
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
2107
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
2108
                      wrong_tables.c_ptr());
2109
    else
unknown's avatar
unknown committed
2110
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
2111 2112 2113 2114
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
2115
  {
unknown's avatar
unknown committed
2116
    query_cache_invalidate3(thd, tables, 0);
2117
    if (!dont_log_query)
2118
    {
2119
      if (!thd->current_stmt_binlog_row_based ||
Staale Smedseng's avatar
Staale Smedseng committed
2120
          (non_temp_tables_count > 0 && !tmp_table_deleted))
2121 2122 2123 2124 2125 2126 2127 2128
      {
        /*
          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.
         */
2129
        write_bin_log(thd, !error, thd->query(), thd->query_length());
2130
      }
2131
      else if (thd->current_stmt_binlog_row_based &&
2132 2133
               tmp_table_deleted)
      {
2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153
        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());
        }

2154
        /*
2155 2156 2157
          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.
2158
        */
2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171
        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 */");
          write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length());
        }
2172
      }
2173

2174 2175
      /*
        The remaining cases are:
2176 2177
        - no tables were deleted and
        - only temporary tables were deleted and row-based
2178 2179 2180 2181
          replication is used.
        In both these cases, nothing should be written to the binary
        log.
      */
2182
    }
unknown's avatar
unknown committed
2183
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
2184
err:
2185 2186 2187 2188 2189 2190 2191 2192
  if (!drop_temporary)
  {
    /*
      Under LOCK TABLES we should release meta-data locks on the tables
      which were dropped. Otherwise we can rely on close_thread_tables()
      doing this. Unfortunately in this case we are likely to get more
      false positives in lock_table_name_if_not_cached() function. So
      it makes sense to remove exclusive meta-data locks in all cases.
Konstantin Osipov's avatar
Konstantin Osipov committed
2193 2194 2195 2196

      Leave LOCK TABLES mode if we managed to drop all tables which were
      locked. Additional check for 'non_temp_tables_count' is to avoid
      leaving LOCK TABLES mode if we have dropped only temporary tables.
2197
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
2198 2199
    if (thd->locked_tables_mode &&
        thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
Konstantin Osipov's avatar
Konstantin Osipov committed
2200
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
2201
      thd->locked_tables_list.unlock_locked_tables(thd);
Konstantin Osipov's avatar
Konstantin Osipov committed
2202 2203 2204 2205
      goto end;
    }
    for (table= tables; table; table= table->next_local)
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
2206
      if (table->mdl_request)
Konstantin Osipov's avatar
Konstantin Osipov committed
2207 2208 2209 2210 2211 2212
      {
        /*
          Under LOCK TABLES we may have several instances of table open
          and locked and therefore have to remove several metadata lock
          requests associated with them.
        */
Konstantin Osipov's avatar
Konstantin Osipov committed
2213
        thd->mdl_context.release_all_locks_for_name(table->mdl_request->ticket);
Konstantin Osipov's avatar
Konstantin Osipov committed
2214 2215
      }
    }
2216 2217
  }

Konstantin Osipov's avatar
Konstantin Osipov committed
2218
end:
2219
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2220 2221 2222
}


2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237
/*
  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
2238
bool quick_rm_table(handlerton *base,const char *db,
2239
                    const char *table_name, uint flags)
unknown's avatar
unknown committed
2240
{
2241
  char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
2242 2243 2244
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

2245
  uint path_length= build_table_filename(path, sizeof(path) - 1,
2246
                                         db, table_name, reg_ext, flags);
unknown's avatar
unknown committed
2247
  if (my_delete(path,MYF(0)))
unknown's avatar
unknown committed
2248
    error= 1; /* purecov: inspected */
2249
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
2250 2251 2252
  if (!(flags & FRM_ONLY))
    error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2253 2254
}

2255 2256 2257
/*
  Sort keys in the following order:
  - PRIMARY KEY
2258 2259
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
2260 2261 2262 2263 2264 2265 2266 2267 2268 2269
  - 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)
{
2270 2271 2272
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
2273
  {
2274
    if (!(b_flags & HA_NOSAME))
2275
      return -1;
2276
    if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
2277 2278
    {
      /* Sort NOT NULL keys before other keys */
2279
      return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
2280 2281 2282 2283 2284
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
2285 2286 2287
    /* 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;
2288
  }
2289
  else if (b_flags & HA_NOSAME)
2290 2291
    return 1;					// Prefer b

2292
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
2293
  {
2294
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
2295
  }
unknown's avatar
unknown committed
2296
  /*
2297
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
2298 2299 2300 2301 2302
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
2303 2304
}

2305 2306
/*
  Check TYPELIB (set or enum) for duplicates
2307

2308 2309 2310
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
2311 2312
    name	  name of the checked column
    typelib	  list of values for the column
2313
    dup_val_count  returns count of duplicate elements
2314 2315

  DESCRIPTION
2316
    This function prints an warning for each value in list
2317 2318 2319
    which has some duplicates on its right

  RETURN VALUES
2320 2321
    0             ok
    1             Error
2322 2323
*/

2324
bool check_duplicates_in_interval(const char *set_or_name,
2325
                                  const char *name, TYPELIB *typelib,
2326
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
2327
{
2328
  TYPELIB tmp= *typelib;
2329
  const char **cur_value= typelib->type_names;
2330
  unsigned int *cur_length= typelib->type_lengths;
2331
  *dup_val_count= 0;  
2332 2333
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
2334
  {
2335 2336 2337 2338
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
2339
    {
2340 2341
      THD *thd= current_thd;
      ErrConvString err(*cur_value, *cur_length, cs);
2342 2343 2344 2345
      if ((current_thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
2346
                 name, err.ptr(), set_or_name);
2347 2348
        return 1;
      }
2349 2350 2351 2352
      push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_DUPLICATED_VALUE_IN_TYPE,
                          ER(ER_DUPLICATED_VALUE_IN_TYPE),
                          name, err.ptr(), set_or_name);
2353
      (*dup_val_count)++;
2354 2355
    }
  }
2356
  return 0;
2357
}
2358

2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386

/*
  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++)
  {
2387
    size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
2388 2389 2390 2391 2392 2393
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


unknown's avatar
unknown committed
2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404
/*
  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
2405
    This function prepares a Create_field instance.
unknown's avatar
unknown committed
2406 2407 2408 2409 2410 2411 2412
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

unknown's avatar
unknown committed
2413
int prepare_create_field(Create_field *sql_field, 
unknown's avatar
unknown committed
2414 2415
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2416
			 longlong table_flags)
unknown's avatar
unknown committed
2417
{
2418
  unsigned int dup_val_count;
unknown's avatar
unknown committed
2419
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
2420 2421

  /*
2422
    This code came from mysql_prepare_create_table.
unknown's avatar
unknown committed
2423 2424 2425 2426 2427
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
2428 2429 2430 2431
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
2432 2433 2434 2435 2436 2437 2438
    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;
2439
    (*blob_columns)++;
unknown's avatar
unknown committed
2440
    break;
2441
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
2442
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
2443 2444 2445 2446
    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
2447
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2448 2449 2450 2451 2452 2453 2454 2455
    }
    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;
2456
    (*blob_columns)++;
unknown's avatar
unknown committed
2457 2458 2459 2460 2461
    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
2462
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
2463
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
2464
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
2465 2466 2467 2468 2469 2470 2471 2472
    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
2473
      {
unknown's avatar
unknown committed
2474 2475
        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
2476 2477
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
2478 2479 2480
    }
#endif
    /* fall through */
2481
  case MYSQL_TYPE_STRING:
unknown's avatar
unknown committed
2482 2483 2484 2485
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2486
  case MYSQL_TYPE_ENUM:
unknown's avatar
unknown committed
2487 2488 2489 2490 2491
    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;
2492 2493 2494 2495
    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
2496
    break;
2497
  case MYSQL_TYPE_SET:
unknown's avatar
unknown committed
2498 2499 2500 2501 2502
    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;
2503 2504 2505 2506
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2507 2508 2509 2510 2511 2512
    /* 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
2513
    break;
2514 2515 2516 2517 2518
  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
2519 2520
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2521
  case MYSQL_TYPE_BIT:
unknown's avatar
unknown committed
2522
    /* 
2523 2524
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
unknown's avatar
unknown committed
2525
    */
unknown's avatar
unknown committed
2526
    break;
2527
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
2528 2529 2530 2531 2532 2533 2534
    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;
2535
  case MYSQL_TYPE_TIMESTAMP:
unknown's avatar
unknown committed
2536 2537 2538
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2539
      if (!*timestamps)
unknown's avatar
unknown committed
2540
      {
unknown's avatar
unknown committed
2541
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2542
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2543
      }
unknown's avatar
unknown committed
2544 2545 2546 2547
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2548
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2549

2550
    (*timestamps)++;
unknown's avatar
unknown committed
2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565
    /* 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
2566 2567 2568
  DBUG_RETURN(0);
}

2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601

/*
  Get character set from field object generated by parser using
  default values when not set.

  SYNOPSIS
    get_sql_field_charset()
    sql_field                 The sql_field object
    create_info               Info generated by parser

  RETURN VALUES
    cs                        Character set
*/

CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
                                    HA_CREATE_INFO *create_info)
{
  CHARSET_INFO *cs= sql_field->charset;

  if (!cs)
    cs= create_info->default_table_charset;
  /*
    table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname
    if we want change character set 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.
  */
  if (create_info->table_charset && cs != &my_charset_bin)
    cs= create_info->table_charset;
  return cs;
}


unknown's avatar
unknown committed
2602
/*
2603
  Preparation for table creation
unknown's avatar
unknown committed
2604 2605

  SYNOPSIS
2606
    mysql_prepare_create_table()
2607 2608
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2609
      alter_info                List of columns and indexes to create
2610 2611 2612 2613 2614 2615
      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
2616

2617
  DESCRIPTION
2618
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
2619

2620
  NOTES
2621
    sets create_info->varchar if the table has a varchar
2622

unknown's avatar
unknown committed
2623
  RETURN VALUES
2624 2625
    FALSE    OK
    TRUE     error
unknown's avatar
unknown committed
2626
*/
unknown's avatar
unknown committed
2627

unknown's avatar
unknown committed
2628
static int
2629 2630 2631 2632 2633 2634
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
2635
{
2636
  const char	*key_name;
unknown's avatar
unknown committed
2637
  Create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
2638
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
2639
  ulong		record_offset= 0;
2640
  KEY		*key_info;
unknown's avatar
unknown committed
2641
  KEY_PART_INFO *key_part_info;
2642 2643 2644
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
unknown's avatar
unknown committed
2645 2646
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
unknown's avatar
unknown committed
2647
  uint total_uneven_bit_length= 0;
2648
  DBUG_ENTER("mysql_prepare_create_table");
unknown's avatar
unknown committed
2649

2650
  select_field_pos= alter_info->create_list.elements - select_field_count;
unknown's avatar
unknown committed
2651
  null_fields=blob_columns=0;
2652
  create_info->varchar= 0;
unknown's avatar
unknown committed
2653
  max_key_length= file->max_key_length();
2654

2655
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
2656
  {
2657 2658
    CHARSET_INFO *save_cs;

2659 2660 2661 2662 2663 2664
    /*
      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;
2665 2666
    save_cs= sql_field->charset= get_sql_field_charset(sql_field,
                                                       create_info);
2667 2668 2669 2670 2671
    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];
2672 2673
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2674
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2675
      DBUG_RETURN(TRUE);
2676
    }
2677

2678
    /*
2679
      Convert the default value from client character
2680 2681 2682
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2683
        save_cs != sql_field->def->collation.collation &&
2684 2685 2686 2687
        (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))
2688
    {
2689
      /*
unknown's avatar
unknown committed
2690
        Starting from 5.1 we work here with a copy of Create_field
2691 2692 2693 2694 2695 2696 2697 2698
        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.
      */
2699
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2700 2701 2702 2703 2704

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

2709 2710
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2711 2712 2713
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2714
      TYPELIB *interval= sql_field->interval;
2715 2716 2717 2718 2719 2720

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2721
      if (!interval)
2722
      {
2723
        /*
2724 2725 2726
          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.
2727
        */
2728
        interval= sql_field->interval= typelib(thd->mem_root,
2729
                                               sql_field->interval_list);
2730
        List_iterator<String> int_it(sql_field->interval_list);
2731
        String conv, *tmp;
2732 2733 2734 2735 2736
        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);
2737
        for (uint i= 0; (tmp= int_it++); i++)
2738
        {
2739
          size_t lengthsp;
2740 2741 2742 2743 2744
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2745
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
unknown's avatar
unknown committed
2746
                                                  conv.length());
2747 2748
            interval->type_lengths[i]= conv.length();
          }
2749

2750
          // Strip trailing spaces.
unknown's avatar
unknown committed
2751 2752
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2753 2754
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2755
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2756 2757 2758 2759 2760
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2761 2762
              ErrConvString err(tmp->ptr(), tmp->length(), cs);
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
2763
              DBUG_RETURN(TRUE);
2764 2765
            }
          }
2766
        }
2767
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2768 2769
      }

2770
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2771
      {
2772
        uint32 field_length;
2773
        if (sql_field->def != NULL)
2774 2775 2776 2777 2778
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2779 2780 2781 2782 2783
          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);
2784
              DBUG_RETURN(TRUE);
2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796
            }

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

2797 2798 2799
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2800
            DBUG_RETURN(TRUE);
2801 2802
          }
        }
2803 2804
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2805
      }
2806
      else  /* MYSQL_TYPE_ENUM */
2807
      {
2808
        uint32 field_length;
2809
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2810
        if (sql_field->def != NULL)
2811 2812
        {
          String str, *def= sql_field->def->val_str(&str);
2813
          if (def == NULL) /* SQL "NULL" maps to NULL */
2814
          {
2815 2816 2817
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2818
              DBUG_RETURN(TRUE);
2819 2820 2821 2822 2823 2824 2825 2826 2827 2828
            }

            /* 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);
2829
              DBUG_RETURN(TRUE);
2830
            }
2831 2832
          }
        }
2833 2834
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2835 2836 2837 2838
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2839
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2840
    { 
unknown's avatar
unknown committed
2841
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2842
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2843 2844 2845 2846 2847
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2848
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
2849
    if (prepare_blob_field(thd, sql_field))
2850
      DBUG_RETURN(TRUE);
2851

unknown's avatar
unknown committed
2852 2853
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
2854

unknown's avatar
unknown committed
2855 2856
    if (check_column_name(sql_field->field_name))
    {
unknown's avatar
unknown committed
2857
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2858
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2859
    }
unknown's avatar
unknown committed
2860

2861 2862
    /* 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
2863
    {
unknown's avatar
unknown committed
2864
      if (my_strcasecmp(system_charset_info,
2865 2866
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
2867
      {
2868 2869 2870 2871
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2872 2873
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2874
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2875
	  DBUG_RETURN(TRUE);
2876 2877 2878
	}
	else
	{
2879
	  /* Field redefined */
2880
	  sql_field->def=		dup_field->def;
2881
	  sql_field->sql_type=		dup_field->sql_type;
2882 2883 2884
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2885
	  sql_field->length=		dup_field->char_length;
2886
          sql_field->pack_length=	dup_field->pack_length;
2887
          sql_field->key_length=	dup_field->key_length;
2888
	  sql_field->decimals=		dup_field->decimals;
2889
	  sql_field->create_length_to_internal_length();
2890
	  sql_field->unireg_check=	dup_field->unireg_check;
2891 2892 2893 2894 2895 2896 2897 2898
          /* 
            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
2899
          sql_field->interval=          dup_field->interval;
2900 2901 2902
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2903
	}
unknown's avatar
unknown committed
2904 2905
      }
    }
2906 2907
    /* 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
2908 2909
	(sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
2910
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
2911 2912
    it2.rewind();
  }
2913 2914 2915

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
2916
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
2917 2918 2919 2920

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

unknown's avatar
unknown committed
2923 2924
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2925
			     file->ha_table_flags()))
2926
      DBUG_RETURN(TRUE);
2927
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2928
      create_info->varchar= TRUE;
2929
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
2930 2931
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2932
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
2933
  }
2934 2935
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
2936 2937
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2938
    DBUG_RETURN(TRUE);
2939
  }
unknown's avatar
unknown committed
2940 2941
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
2942
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2943
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2944 2945
  }
  if (auto_increment &&
2946
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
2947
  {
unknown's avatar
unknown committed
2948 2949
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2950
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2951 2952
  }

2953
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
2954
  {
unknown's avatar
unknown committed
2955 2956
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2957
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2958 2959 2960
  }

  /* Create keys */
2961

2962 2963
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2964
  uint key_parts=0, fk_key_count=0;
2965
  bool primary_key=0,unique_key=0;
2966
  Key *key, *key2;
unknown's avatar
unknown committed
2967
  uint tmp, key_number;
2968 2969
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2970

2971
  /* Calculate number of key segements */
2972
  *key_count= 0;
2973

unknown's avatar
unknown committed
2974 2975
  while ((key=key_iterator++))
  {
2976
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name.str ? key->name.str :
2977
                        "(none)" , key->type));
2978 2979 2980
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
unknown's avatar
unknown committed
2981
      Foreign_key *fk_key= (Foreign_key*) key;
2982 2983 2984
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2985
        my_error(ER_WRONG_FK_DEF, MYF(0),
2986 2987
                 (fk_key->name.str ? fk_key->name.str :
                                     "foreign key without name"),
2988
                 ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
2989
	DBUG_RETURN(TRUE);
2990 2991 2992
      }
      continue;
    }
2993
    (*key_count)++;
unknown's avatar
unknown committed
2994
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
2995 2996 2997
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2998
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2999
    }
3000
    if (check_string_char_length(&key->name, "", NAME_CHAR_LEN,
3001
                                 system_charset_info, 1))
unknown's avatar
unknown committed
3002
    {
3003
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str);
3004
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3005
    }
3006
    key_iterator2.rewind ();
3007
    if (key->type != Key::FOREIGN_KEY)
3008
    {
3009
      while ((key2 = key_iterator2++) != key)
3010
      {
unknown's avatar
unknown committed
3011
	/*
3012 3013 3014
          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
3015
        */
3016
        if ((key2->type != Key::FOREIGN_KEY &&
3017
             key2->name.str != ignore_key &&
3018
             !foreign_key_prefix(key, key2)))
3019
        {
3020
          /* TODO: issue warning message */
3021 3022 3023 3024
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
3025
            key->name.str= ignore_key;
3026 3027
          else
          {
3028
            key2->name.str= ignore_key;
3029 3030
            key_parts-= key2->columns.elements;
            (*key_count)--;
3031 3032 3033
          }
          break;
        }
3034 3035
      }
    }
3036
    if (key->name.str != ignore_key)
3037 3038 3039
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
3040 3041
    if (key->name.str && !tmp_table && (key->type != Key::PRIMARY) &&
	!my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
3042
    {
3043
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
3044
      DBUG_RETURN(TRUE);
3045
    }
3046
  }
unknown's avatar
unknown committed
3047
  tmp=file->max_keys();
3048
  if (*key_count > tmp)
3049 3050
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
3051
    DBUG_RETURN(TRUE);
3052
  }
3053

3054
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
3055
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
3056
  if (!*key_info_buffer || ! key_part_info)
3057
    DBUG_RETURN(TRUE);				// Out of memory
3058

3059
  key_iterator.rewind();
unknown's avatar
unknown committed
3060
  key_number=0;
unknown's avatar
unknown committed
3061
  for (; (key=key_iterator++) ; key_number++)
3062 3063
  {
    uint key_length=0;
unknown's avatar
unknown committed
3064
    Key_part_spec *column;
3065

3066
    if (key->name.str == ignore_key)
3067 3068 3069 3070
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
3071
      while (key && key->name.str == ignore_key);
3072 3073 3074 3075
      if (!key)
	break;
    }

3076
    switch (key->type) {
unknown's avatar
unknown committed
3077
    case Key::MULTIPLE:
3078
	key_info->flags= 0;
3079
	break;
unknown's avatar
unknown committed
3080
    case Key::FULLTEXT:
3081
	key_info->flags= HA_FULLTEXT;
unknown's avatar
unknown committed
3082
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
3083
          key_info->flags|= HA_USES_PARSER;
3084 3085
        else
          key_info->parser_name= 0;
3086
	break;
unknown's avatar
unknown committed
3087
    case Key::SPATIAL:
unknown's avatar
SCRUM:  
unknown committed
3088
#ifdef HAVE_SPATIAL
3089
	key_info->flags= HA_SPATIAL;
3090
	break;
unknown's avatar
SCRUM:  
unknown committed
3091
#else
3092 3093
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
3094
	DBUG_RETURN(TRUE);
unknown's avatar
SCRUM:  
unknown committed
3095
#endif
3096 3097 3098 3099
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
3100 3101
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
3102
    }
3103 3104
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
3105

unknown's avatar
unknown committed
3106 3107
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
3108
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
3109
    key_info->algorithm= key->key_create_info.algorithm;
unknown's avatar
unknown committed
3110

3111 3112
    if (key->type == Key::FULLTEXT)
    {
3113
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
3114
      {
unknown's avatar
unknown committed
3115 3116
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
3117
	DBUG_RETURN(TRUE);
3118 3119
      }
    }
unknown's avatar
unknown committed
3120 3121 3122
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
3123
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
3124 3125 3126
       in near future when new frm file is ready
       checking for proper key parts number:
    */
3127

3128
    /* TODO: Add proper checks if handler supports key_type and algorithm */
3129
    if (key_info->flags & HA_SPATIAL)
unknown's avatar
unknown committed
3130
    {
3131
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
unknown's avatar
unknown committed
3132 3133 3134
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
3135
        DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3136
      }
unknown's avatar
unknown committed
3137 3138
      if (key_info->key_parts != 1)
      {
3139
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
3140
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3141
      }
unknown's avatar
unknown committed
3142
    }
unknown's avatar
unknown committed
3143
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
3144
    {
unknown's avatar
SCRUM:  
unknown committed
3145
#ifdef HAVE_RTREE_KEYS
unknown's avatar
unknown committed
3146 3147
      if ((key_info->key_parts & 1) == 1)
      {
3148
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
3149
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3150
      }
3151
      /* TODO: To be deleted */
3152
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
3153
      DBUG_RETURN(TRUE);
unknown's avatar
SCRUM:  
unknown committed
3154
#else
3155 3156
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
3157
      DBUG_RETURN(TRUE);
unknown's avatar
SCRUM:  
unknown committed
3158
#endif
unknown's avatar
unknown committed
3159
    }
3160

3161 3162 3163 3164 3165
    /* 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
3166 3167
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
3168 3169 3170 3171 3172
                           create_info->key_block_size);

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

unknown's avatar
unknown committed
3173
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
3174
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
3175 3176
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
3177
      uint length;
unknown's avatar
unknown committed
3178
      Key_part_spec *dup_column;
unknown's avatar
unknown committed
3179

unknown's avatar
unknown committed
3180 3181 3182
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
3183
	     my_strcasecmp(system_charset_info,
3184
			   column->field_name.str,
3185
			   sql_field->field_name))
unknown's avatar
unknown committed
3186 3187 3188
	field++;
      if (!sql_field)
      {
3189
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
3190
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3191
      }
unknown's avatar
unknown committed
3192
      while ((dup_column= cols2++) != column)
3193 3194
      {
        if (!my_strcasecmp(system_charset_info,
3195
	     	           column->field_name.str, dup_column->field_name.str))
3196 3197 3198
	{
	  my_printf_error(ER_DUP_FIELDNAME,
			  ER(ER_DUP_FIELDNAME),MYF(0),
3199
			  column->field_name.str);
3200
	  DBUG_RETURN(TRUE);
3201 3202 3203
	}
      }
      cols2.rewind();
3204
      if (key->type == Key::FULLTEXT)
3205
      {
3206 3207
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
3208 3209
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
3210
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
3211 3212
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
3213
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224
	    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));
3225
      }
3226
      else
3227
      {
3228 3229
	column->length*= sql_field->charset->mbmaxlen;

unknown's avatar
unknown committed
3230 3231 3232
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
3233
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3234 3235
	}

3236 3237
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
3238
	{
3239
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
3240
	  {
3241
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str);
3242
	    DBUG_RETURN(TRUE);
3243
	  }
3244 3245
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
3246
            column->length= 25;
3247 3248
	  if (!column->length)
	  {
3249
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
3250
	    DBUG_RETURN(TRUE);
3251 3252
	  }
	}
unknown's avatar
SCRUM:  
unknown committed
3253
#ifdef HAVE_SPATIAL
3254
	if (key->type == Key::SPATIAL)
3255
	{
3256
	  if (!column->length)
3257 3258
	  {
	    /*
3259 3260
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
3261
	    */
3262
	    column->length= 4*sizeof(double);
3263 3264
	  }
	}
unknown's avatar
SCRUM:  
unknown committed
3265
#endif
3266 3267 3268 3269 3270 3271 3272
	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
3273
            null_fields--;
3274 3275
	  }
	  else
3276 3277 3278 3279
          {
            key_info->flags|= HA_NULL_PART_KEY;
            if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
            {
3280
              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
3281
              DBUG_RETURN(TRUE);
3282 3283 3284 3285 3286
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
3287
              DBUG_RETURN(TRUE);
3288 3289
            }
          }
3290 3291 3292
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
3293
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
3294 3295
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
3296
      }
3297

unknown's avatar
unknown committed
3298 3299 3300
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
3301 3302
      length= sql_field->key_length;

unknown's avatar
unknown committed
3303 3304 3305 3306
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
3307
	  if ((length=column->length) > max_key_length ||
3308
	      length > file->max_key_part_length())
3309
	  {
unknown's avatar
unknown committed
3310
	    length=min(max_key_length, file->max_key_part_length());
3311 3312 3313 3314 3315 3316 3317 3318
	    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);
3319 3320
              /* Align key length to multibyte char boundary */
              length-= length % sql_field->charset->mbmaxlen;
3321 3322 3323 3324
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
3325
	      DBUG_RETURN(TRUE);
3326 3327
	    }
	  }
unknown's avatar
unknown committed
3328
	}
3329
	else if (!f_is_geom(sql_field->pack_flag) &&
3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344
                 ((column->length > length &&
                   !Field::type_can_have_key_part (sql_field->sql_type)) ||
                  ((f_is_packed(sql_field->pack_flag) ||
                    ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
                     (key_info->flags & HA_NOSAME))) &&
                   column->length != length)))
        {
          /* Catch invalid uses of partial keys.
             A key is identified as 'partial' if column->length != length.
             A partial key is invalid if they data type does
             not allow it, or the field is packed (as in MyISAM),
             or the storage engine doesn't allow prefixed search and
             the key is primary key.
          */

unknown's avatar
unknown committed
3345
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
3346
	  DBUG_RETURN(TRUE);
3347
	}
3348
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
3349
	  length=column->length;
unknown's avatar
unknown committed
3350 3351 3352
      }
      else if (length == 0)
      {
3353
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str);
3354
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3355
      }
3356
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
3357
      {
3358
        length= file->max_key_part_length();
3359 3360 3361 3362 3363 3364 3365 3366
	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);
3367 3368
          /* Align key length to multibyte char boundary */
          length-= length % sql_field->charset->mbmaxlen;
3369 3370 3371 3372
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
3373
	  DBUG_RETURN(TRUE);
3374
	}
3375 3376
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
3377
      /* Use packed keys for long strings on the first column */
3378
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
3379
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
3380 3381
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
3382 3383
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
Staale Smedseng's avatar
Staale Smedseng committed
3384
	if ((column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB)) ||
3385 3386
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
unknown's avatar
unknown committed
3387 3388 3389
	else
	  key_info->flags|= HA_PACK_KEY;
      }
3390 3391 3392 3393
      /* 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
3394 3395 3396 3397 3398 3399 3400
      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)
3401 3402 3403
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
3404 3405
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
3406
	    DBUG_RETURN(TRUE);
3407 3408 3409 3410
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
3411
	else if (!(key_name= key->name.str))
unknown's avatar
unknown committed
3412
	  key_name=make_unique_key_name(sql_field->field_name,
3413 3414
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
3415
	{
3416
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
3417
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3418 3419 3420 3421
	}
	key_info->name=(char*) key_name;
      }
    }
3422 3423
    if (!key_info->name || check_column_name(key_info->name))
    {
3424
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
3425
      DBUG_RETURN(TRUE);
3426
    }
3427 3428
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
3429
    key_info->key_length=(uint16) key_length;
3430
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
3431
    {
3432
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
3433
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3434
    }
unknown's avatar
unknown committed
3435
    key_info++;
unknown's avatar
unknown committed
3436
  }
3437
  if (!unique_key && !primary_key &&
3438
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
3439
  {
unknown's avatar
unknown committed
3440
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
3441
    DBUG_RETURN(TRUE);
3442
  }
unknown's avatar
unknown committed
3443 3444
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
3445
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3446
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3447
  }
3448
  /* Sort keys in optimized order */
3449 3450
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
unknown's avatar
unknown committed
3451
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
3452

3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483
  /* 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);
    }
  }

3484
  DBUG_RETURN(FALSE);
3485 3486
}

3487

unknown's avatar
unknown committed
3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503
/*
  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)
{
3504 3505 3506 3507 3508
  /*
    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
3509 3510 3511
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3512 3513 3514

    load_db_opt_by_name(thd, db, &db_info);

unknown's avatar
unknown committed
3515 3516 3517 3518 3519
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


unknown's avatar
unknown committed
3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532
/*
  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
3533
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
unknown's avatar
unknown committed
3534 3535 3536 3537 3538 3539 3540 3541 3542
{
  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];

3543 3544
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
3545 3546 3547 3548 3549
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3550
    sql_field->sql_type= MYSQL_TYPE_BLOB;
unknown's avatar
unknown committed
3551
    sql_field->flags|= BLOB_FLAG;
Sergei Golubchik's avatar
Sergei Golubchik committed
3552
    my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name,
3553
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
3554 3555 3556 3557
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
3558

unknown's avatar
unknown committed
3559 3560
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
3561 3562 3563
    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
3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574
    {
      /* 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);
}


3575
/*
unknown's avatar
unknown committed
3576
  Preparation of Create_field for SP function return values.
3577 3578
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589

  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
3590
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3591
{
3592 3593
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3594 3595
  {
    uint32 field_length, dummy;
3596
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3597 3598 3599 3600 3601 3602 3603
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3604
    else /* MYSQL_TYPE_ENUM */
3605 3606 3607 3608 3609 3610 3611 3612 3613
    {
      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);
  }

3614
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3615 3616 3617 3618 3619
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
3620 3621 3622 3623
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3624 3625


3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656
/*
  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 &&
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3657
    write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3658 3659 3660
}


3661 3662 3663 3664
/*
  Create a table

  SYNOPSIS
unknown's avatar
unknown committed
3665
    mysql_create_table_no_lock()
3666 3667 3668
    thd			Thread object
    db			Database
    table_name		Table name
3669
    create_info	        Create information (like MAX_ROWS)
3670 3671
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
3672
    internal_tmp_table  Set to 1 if this is an internal temporary table
3673
			(From ALTER TABLE)
3674
    select_field_count
3675 3676

  DESCRIPTION
3677
    If one creates a temporary table, this is automatically opened
3678

unknown's avatar
unknown committed
3679
    Note that this function assumes that caller already have taken
Konstantin Osipov's avatar
Konstantin Osipov committed
3680 3681 3682
    exclusive metadata 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.
unknown's avatar
unknown committed
3683

3684 3685 3686 3687 3688 3689
    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
3690 3691
    FALSE OK
    TRUE  error
3692 3693
*/

unknown's avatar
unknown committed
3694
bool mysql_create_table_no_lock(THD *thd,
unknown's avatar
unknown committed
3695
                                const char *db, const char *table_name,
3696 3697 3698 3699
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3700
{
3701
  char		path[FN_REFLEN + 1];
3702
  uint          path_length;
3703 3704 3705 3706
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
3707
  bool		error= TRUE;
unknown's avatar
unknown committed
3708
  DBUG_ENTER("mysql_create_table_no_lock");
3709 3710
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3711

3712

3713
  /* Check for duplicate fields and check type of table to create */
3714
  if (!alter_info->create_list.elements)
3715
  {
unknown's avatar
unknown committed
3716 3717
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
3718
    DBUG_RETURN(TRUE);
3719
  }
3720
  if (check_engine(thd, table_name, create_info))
3721
    DBUG_RETURN(TRUE);
3722 3723 3724

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

3725
  db_options= create_info->table_options;
3726 3727 3728
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3729 3730
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3731
  {
unknown's avatar
unknown committed
3732
    mem_alloc_error(sizeof(handler));
3733 3734
    DBUG_RETURN(TRUE);
  }
3735
#ifdef WITH_PARTITION_STORAGE_ENGINE
3736 3737
  partition_info *part_info= thd->work_part_info;

unknown's avatar
unknown committed
3738 3739 3740 3741 3742 3743 3744 3745
  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
3746
    thd->work_part_info= part_info= new partition_info();
unknown's avatar
unknown committed
3747 3748 3749 3750 3751 3752
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
unknown's avatar
unknown committed
3753
    part_info->default_engine_type= create_info->db_type;
3754
    part_info->is_auto_partitioned= TRUE;
unknown's avatar
unknown committed
3755
  }
3756 3757 3758
  if (part_info)
  {
    /*
unknown's avatar
unknown committed
3759 3760 3761 3762 3763 3764 3765 3766 3767
      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.
3768
    */
3769
    List_iterator<Key> key_iterator(alter_info->key_list);
unknown's avatar
unknown committed
3770
    Key *key;
unknown's avatar
unknown committed
3771
    handlerton *part_engine_type= create_info->db_type;
3772 3773
    char *part_syntax_buf;
    uint syntax_len;
unknown's avatar
unknown committed
3774
    handlerton *engine_type;
3775 3776 3777 3778 3779
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
unknown's avatar
unknown committed
3780 3781
    while ((key= key_iterator++))
    {
3782 3783
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
unknown's avatar
unknown committed
3784
      {
3785
        my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
unknown's avatar
unknown committed
3786 3787 3788
        goto err;
      }
    }
3789
    if ((part_engine_type == partition_hton) &&
3790
        part_info->default_engine_type)
3791 3792 3793 3794 3795 3796
    {
      /*
        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
3797
      ;
3798
    }
3799 3800
    else
    {
unknown's avatar
unknown committed
3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812
      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);
        }
      }
3813
    }
unknown's avatar
unknown committed
3814 3815 3816
    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)));
3817
    if (part_info->check_partition_info(thd, &engine_type, file,
3818
                                        create_info, FALSE))
unknown's avatar
unknown committed
3819
      goto err;
unknown's avatar
unknown committed
3820
    part_info->default_engine_type= engine_type;
unknown's avatar
unknown committed
3821

3822 3823 3824 3825 3826 3827
    /*
      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,
3828 3829 3830
                                                     TRUE, TRUE,
                                                     create_info,
                                                     alter_info)))
unknown's avatar
unknown committed
3831
      goto err;
3832 3833
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
3834 3835
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3836
        create_info->db_type == partition_hton)
3837 3838 3839 3840 3841
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
unknown's avatar
unknown committed
3842
      DBUG_PRINT("info", ("db_type: %s",
unknown's avatar
unknown committed
3843
                        ha_resolve_storage_engine_name(create_info->db_type)));
3844
      delete file;
3845
      create_info->db_type= partition_hton;
3846 3847 3848 3849
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3850 3851 3852 3853 3854 3855
      /*
        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.
      */
3856 3857 3858
      if (part_info->use_default_num_partitions &&
          part_info->num_parts &&
          (int)part_info->num_parts !=
3859
          file->get_default_no_partitions(create_info))
3860
      {
3861
        uint i;
3862
        List_iterator<partition_element> part_it(part_info->partitions);
3863 3864 3865 3866
        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;
3867 3868
      }
      else if (part_info->is_sub_partitioned() &&
3869 3870 3871
               part_info->use_default_num_subpartitions &&
               part_info->num_subparts &&
               (int)part_info->num_subparts !=
3872
                 file->get_default_no_partitions(create_info))
3873
      {
3874
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3875
        part_info->num_subparts= file->get_default_no_partitions(create_info);
3876 3877 3878 3879
      }
    }
    else if (create_info->db_type != engine_type)
    {
unknown's avatar
unknown committed
3880 3881 3882 3883 3884 3885
      /*
        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.
      */
3886
      delete file;
3887 3888
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3889 3890 3891 3892
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3893 3894 3895
    }
  }
#endif
3896

3897 3898 3899 3900 3901
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3902
    goto err;
unknown's avatar
unknown committed
3903 3904 3905 3906

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3907
    path_length= build_tmptable_filename(thd, path, sizeof(path));
unknown's avatar
unknown committed
3908 3909
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3910 3911
  else  
  {
3912
    path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,
3913
                                      internal_tmp_table ? FN_IS_TMP : 0);
3914
  }
3915

unknown's avatar
unknown committed
3916
  /* Check if table already exists */
unknown's avatar
unknown committed
3917 3918
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
unknown's avatar
unknown committed
3919
  {
3920
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3921 3922
    {
      create_info->table_existed= 1;		// Mark that table existed
3923 3924 3925
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
unknown's avatar
unknown committed
3926
      error= 0;
3927
      write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
3928
      goto err;
3929
    }
unknown's avatar
unknown committed
3930
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3931
    goto err;
unknown's avatar
unknown committed
3932
  }
3933

Konstantin Osipov's avatar
Konstantin Osipov committed
3934
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3935
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
3936 3937 3938 3939
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3940 3941
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
3942
      goto unlock_and_end;
unknown's avatar
unknown committed
3943
    }
3944 3945 3946 3947 3948 3949 3950 3951
    /*
      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.
    */
3952
    if (get_cached_table_share(db, table_name))
3953 3954 3955 3956
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
unknown's avatar
unknown committed
3957 3958
  }

unknown's avatar
unknown committed
3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971
  /*
    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;
3972 3973 3974
    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
3975
    {
3976 3977 3978 3979 3980
      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
3981

3982 3983 3984 3985 3986 3987 3988 3989 3990
        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
3991 3992 3993
    }
  }

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

3997
#ifdef HAVE_READLINK
3998
  {
3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034
    size_t dirlen;
    char   dirpath[FN_REFLEN];

    /*
      data_file_name and index_file_name include the table name without
      extension. Mostly this does not refer to an existing file. When
      comparing data_file_name or index_file_name against the data
      directory, we try to resolve all symbolic links. On some systems,
      we use realpath(3) for the resolution. This returns ENOENT if the
      resolved path does not refer to an existing file. my_realpath()
      does then copy the requested path verbatim, without symlink
      resolution. Thereafter the comparison can fail even if the
      requested path is within the data directory. E.g. if symlinks to
      another file system are used. To make realpath(3) return the
      resolved path, we strip the table name and compare the directory
      path only. If the directory doesn't exist either, table creation
      will fail anyway.
    */
    if (create_info->data_file_name)
    {
      dirname_part(dirpath, create_info->data_file_name, &dirlen);
      if (test_if_data_home_dir(dirpath))
      {
        my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
        goto unlock_and_end;
      }
    }
    if (create_info->index_file_name)
    {
      dirname_part(dirpath, create_info->index_file_name, &dirlen);
      if (test_if_data_home_dir(dirpath))
      {
        my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
        goto unlock_and_end;
      }
    }
4035 4036 4037 4038 4039 4040 4041 4042 4043
  }

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

4044
  if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
4045
#endif /* HAVE_READLINK */
4046 4047
  {
    if (create_info->data_file_name)
4048 4049 4050
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "DATA DIRECTORY");
4051
    if (create_info->index_file_name)
4052 4053 4054
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "INDEX DIRECTORY");
4055
    create_info->data_file_name= create_info->index_file_name= 0;
4056
  }
unknown's avatar
unknown committed
4057
  create_info->table_options=db_options;
4058

4059
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
4060 4061
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
unknown's avatar
unknown committed
4062
                       key_count, key_info_buffer, file))
unknown's avatar
unknown committed
4063
    goto unlock_and_end;
unknown's avatar
unknown committed
4064

unknown's avatar
unknown committed
4065 4066 4067 4068 4069 4070
  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
4071
      goto unlock_and_end;
unknown's avatar
unknown committed
4072
    }
unknown's avatar
unknown committed
4073
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
4074
  }
unknown's avatar
unknown committed
4075

4076
  write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4077
  error= FALSE;
unknown's avatar
unknown committed
4078
unlock_and_end:
Konstantin Osipov's avatar
Konstantin Osipov committed
4079
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4080 4081

err:
4082
  thd_proc_info(thd, "After create");
unknown's avatar
unknown committed
4083
  delete file;
unknown's avatar
unknown committed
4084
  DBUG_RETURN(error);
4085 4086

warn:
unknown's avatar
unknown committed
4087
  error= FALSE;
4088 4089 4090 4091
  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
4092
  write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4093
  goto unlock_and_end;
unknown's avatar
unknown committed
4094 4095
}

unknown's avatar
unknown committed
4096

4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110
/**
   Auxiliary function which obtains exclusive meta-data lock on the
   table if there are no shared or exclusive on it already.

   See mdl_try_acquire_exclusive_lock() function for more info.

   TODO: This function is here mostly to simplify current patch
         and probably should be removed.
   TODO: Investigate if it is kosher to leave lock request in the
         context in the case when we fail to obtain the lock.
*/

static bool lock_table_name_if_not_cached(THD *thd, const char *db,
                                          const char *table_name,
Konstantin Osipov's avatar
Konstantin Osipov committed
4111
                                          MDL_request **mdl_request)
4112
{
Konstantin Osipov's avatar
Konstantin Osipov committed
4113 4114
  bool conflict;

Konstantin Osipov's avatar
Konstantin Osipov committed
4115
  if (!(*mdl_request= MDL_request::create(0, db, table_name, thd->mem_root)))
4116
    return TRUE;
Konstantin Osipov's avatar
Konstantin Osipov committed
4117 4118 4119
  (*mdl_request)->set_type(MDL_EXCLUSIVE);
  thd->mdl_context.add_request(*mdl_request);
  if (thd->mdl_context.try_acquire_exclusive_lock(*mdl_request, &conflict))
4120
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4121 4122 4123 4124
    /*
      To simplify our life under LOCK TABLES we remove unsatisfied
      lock request from the context.
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
4125
    thd->mdl_context.remove_request(*mdl_request);
Konstantin Osipov's avatar
Konstantin Osipov committed
4126 4127 4128 4129 4130 4131
    if (!conflict)
    {
      /* Probably OOM. */
      return TRUE;
    }
    else
Konstantin Osipov's avatar
Konstantin Osipov committed
4132
      *mdl_request= NULL;
4133 4134 4135 4136 4137
  }
  return FALSE;
}


unknown's avatar
unknown committed
4138
/*
unknown's avatar
unknown committed
4139
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
unknown's avatar
unknown committed
4140 4141 4142 4143
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
4144 4145 4146
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
unknown's avatar
unknown committed
4147
{
Konstantin Osipov's avatar
Konstantin Osipov committed
4148
  MDL_request *target_mdl_request= NULL;
unknown's avatar
unknown committed
4149 4150 4151 4152 4153 4154
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
Konstantin Osipov's avatar
Konstantin Osipov committed
4155
         my_hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
unknown's avatar
unknown committed
4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168
  {
    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
4169 4170
  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4171
    if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request))
unknown's avatar
unknown committed
4172 4173 4174 4175
    {
      result= TRUE;
      goto unlock;
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
4176
    if (!target_mdl_request)
unknown's avatar
unknown committed
4177 4178 4179 4180 4181 4182 4183 4184
    {
      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;
4185
        write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4186 4187 4188 4189 4190 4191 4192 4193 4194
      }
      else
      {
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        result= TRUE;
      }
      goto unlock;
    }
  }
unknown's avatar
unknown committed
4195

unknown's avatar
unknown committed
4196
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
4197 4198 4199
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
unknown's avatar
unknown committed
4200

unknown's avatar
unknown committed
4201
unlock:
Konstantin Osipov's avatar
Konstantin Osipov committed
4202
  if (target_mdl_request)
Konstantin Osipov's avatar
Konstantin Osipov committed
4203
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4204 4205
    thd->mdl_context.release_lock(target_mdl_request->ticket);
    thd->mdl_context.remove_request(target_mdl_request);
Konstantin Osipov's avatar
Konstantin Osipov committed
4206
  }
unknown's avatar
unknown committed
4207 4208 4209 4210 4211 4212 4213 4214
  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
4215 4216 4217 4218 4219 4220 4221 4222
/*
** 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++)
4223
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
4224 4225 4226 4227 4228 4229 4230 4231 4232 4233
      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;

4234 4235
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
4236
    return (char*) field_name;			// Use fieldname
4237 4238 4239 4240 4241 4242
  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
  */
4243
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
4244
  {
4245 4246
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
4247 4248 4249
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
4250
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
4251 4252
}

4253

unknown's avatar
unknown committed
4254 4255 4256 4257
/****************************************************************************
** Alter a table definition
****************************************************************************/

4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271

/*
  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.
4272 4273
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
4274 4275

  RETURN
4276 4277
    FALSE   OK
    TRUE    Error
4278 4279
*/

4280
bool
4281 4282 4283
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
4284
{
4285
  THD *thd= current_thd;
4286 4287
  char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
    lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
4288 4289
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
unknown's avatar
unknown committed
4290
  handler *file;
unknown's avatar
unknown committed
4291
  int error=0;
unknown's avatar
unknown committed
4292
  DBUG_ENTER("mysql_rename_table");
4293 4294
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
unknown's avatar
unknown committed
4295

unknown's avatar
unknown committed
4296
  file= (base == NULL ? 0 :
unknown's avatar
unknown committed
4297 4298
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

4299
  build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
4300
                       flags & FN_FROM_IS_TMP);
4301
  build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
4302
                       flags & FN_TO_IS_TMP);
4303 4304 4305 4306 4307 4308

  /*
    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.
   */
4309
  if (lower_case_table_names == 2 && file &&
4310
      !(file->ha_table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
4311
  {
4312 4313
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
4314
    build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
4315
                         flags & FN_FROM_IS_TMP);
4316
    from_base= lc_from;
unknown's avatar
unknown committed
4317

4318 4319
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
4320
    build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
4321
                         flags & FN_TO_IS_TMP);
4322
    to_base= lc_to;
unknown's avatar
unknown committed
4323 4324
  }

4325
  if (!file || !(error=file->ha_rename_table(from_base, to_base)))
4326
  {
4327
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
4328
    {
unknown's avatar
unknown committed
4329
      error=my_errno;
4330
      /* Restore old file name */
4331
      if (file)
4332
        file->ha_rename_table(to_base, from_base);
4333 4334
    }
  }
unknown's avatar
unknown committed
4335
  delete file;
4336 4337 4338
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
4339 4340
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
4341 4342
}

unknown's avatar
unknown committed
4343

unknown's avatar
unknown committed
4344
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
4345
			     const char* operator_name, const char* errmsg)
4346

unknown's avatar
unknown committed
4347
{
unknown's avatar
unknown committed
4348 4349
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
4350 4351
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
4352
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4353
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
4354
  thd->clear_error();
unknown's avatar
unknown committed
4355
  if (protocol->write())
unknown's avatar
unknown committed
4356 4357 4358 4359
    return -1;
  return 1;
}

4360

unknown's avatar
unknown committed
4361
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
4362
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
4363
{
unknown's avatar
unknown committed
4364 4365
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
4366 4367 4368 4369
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
Konstantin Osipov's avatar
Konstantin Osipov committed
4370
  MDL_request *mdl_request= NULL;
Konstantin Osipov's avatar
Konstantin Osipov committed
4371
  enum enum_open_table_action ot_action_unused;
unknown's avatar
unknown committed
4372
  DBUG_ENTER("prepare_for_repair");
Konstantin Osipov's avatar
Konstantin Osipov committed
4373 4374
  uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH |
                                 MYSQL_OPEN_HAS_MDL_LOCK);
unknown's avatar
unknown committed
4375 4376 4377

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

Konstantin Osipov's avatar
Konstantin Osipov committed
4379
  if (!(table= table_list->table))
unknown's avatar
unknown committed
4380
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4381 4382 4383 4384
    /*
      Attempt to do full-blown table open in mysql_admin_table() has failed.
      Let us try to open at least a .FRM for this table.
    */
unknown's avatar
unknown committed
4385 4386 4387 4388
    char key[MAX_DBKEY_LENGTH];
    uint key_length;

    key_length= create_table_def_key(thd, key, table_list, 0);
Konstantin Osipov's avatar
Konstantin Osipov committed
4389 4390 4391 4392 4393
    mdl_request= MDL_request::create(0, table_list->db,
                                     table_list->table_name, thd->mem_root);
    mdl_request->set_type(MDL_EXCLUSIVE);
    thd->mdl_context.add_request(mdl_request);
    if (thd->mdl_context.acquire_exclusive_locks())
Konstantin Osipov's avatar
Konstantin Osipov committed
4394
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4395
      thd->mdl_context.remove_request(mdl_request);
4396
      DBUG_RETURN(0);
Konstantin Osipov's avatar
Konstantin Osipov committed
4397
    }
4398

unknown's avatar
unknown committed
4399 4400 4401 4402 4403
    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
4404
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
4405 4406
    }

unknown's avatar
unknown committed
4407
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
unknown's avatar
unknown committed
4408
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4409
      release_table_share(share);
unknown's avatar
unknown committed
4410 4411 4412 4413
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
    pthread_mutex_unlock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
4414
    table= &tmp_table;
Konstantin Osipov's avatar
Konstantin Osipov committed
4415
    table_list->mdl_request= mdl_request;
Konstantin Osipov's avatar
Konstantin Osipov committed
4416
  }
4417 4418

  /* A MERGE table must not come here. */
Konstantin Osipov's avatar
Konstantin Osipov committed
4419
  DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
4420

4421 4422 4423 4424 4425 4426 4427 4428 4429
  /*
    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
4430

unknown's avatar
unknown committed
4431 4432 4433 4434 4435 4436 4437 4438 4439
  /*
    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
4440

4441 4442 4443
  if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  {
    error= send_check_errmsg(thd, table_list, "repair",
Timothy Smith's avatar
Timothy Smith committed
4444
                             "Failed repairing incompatible .frm file");
4445 4446
    goto end;
  }
4447

unknown's avatar
unknown committed
4448 4449
  /*
    Check if this is a table type that stores index and data separately,
4450 4451 4452
    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
4453
  */
unknown's avatar
unknown committed
4454
  ext= table->file->bas_ext();
unknown's avatar
unknown committed
4455 4456
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
4457

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

4463 4464
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
4465

4466 4467
  if (table_list->table)
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4468 4469 4470 4471
    /*
      Table was successfully open in mysql_admin_table(). Now we need
      to close it, but leave it protected by exclusive metadata lock.
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
4472
    if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
4473
      goto end;
Konstantin Osipov's avatar
Konstantin Osipov committed
4474
    close_all_tables_for_name(thd, table_list->table->s, FALSE);
4475
    table_list->table= 0;
unknown's avatar
unknown committed
4476
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4477 4478 4479 4480 4481
  /*
    After this point we have an exclusive metadata lock on our table
    in both cases when table was successfully open in mysql_admin_table()
    and when it was open in prepare_for_repair().
  */
4482

unknown's avatar
unknown committed
4483
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
4484
  {
unknown's avatar
unknown committed
4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
unknown's avatar
unknown committed
4500 4501
  }

Konstantin Osipov's avatar
Konstantin Osipov committed
4502 4503 4504
  if (thd->locked_tables_list.reopen_tables(thd))
    goto end;

4505 4506 4507 4508
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
4509 4510
  if (open_table(thd, table_list, thd->mem_root,
                 &ot_action_unused, reopen_for_repair_flags))
unknown's avatar
unknown committed
4511
  {
4512 4513 4514
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
4515
  }
unknown's avatar
unknown committed
4516 4517

end:
Konstantin Osipov's avatar
Konstantin Osipov committed
4518
  thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
unknown's avatar
unknown committed
4519
  if (table == &tmp_table)
unknown's avatar
unknown committed
4520 4521 4522 4523 4524
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4525
  /* In case of a temporary table there will be no metadata lock. */
Konstantin Osipov's avatar
Konstantin Osipov committed
4526
  if (error && mdl_request)
Konstantin Osipov's avatar
Konstantin Osipov committed
4527
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4528 4529
    thd->mdl_context.release_lock(mdl_request->ticket);
    thd->mdl_context.remove_request(mdl_request);
Konstantin Osipov's avatar
Konstantin Osipov committed
4530
  }
unknown's avatar
unknown committed
4531
  DBUG_RETURN(error);
unknown's avatar
unknown committed
4532
}
4533

4534

4535

unknown's avatar
unknown committed
4536 4537
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
4538 4539 4540
    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
4541
*/
unknown's avatar
unknown committed
4542 4543 4544 4545 4546
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,
4547
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
4548 4549 4550
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
4551 4552 4553
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
4554
{
4555
  TABLE_LIST *table;
4556
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
4557
  List<Item> field_list;
unknown's avatar
unknown committed
4558 4559
  Item *item;
  Protocol *protocol= thd->protocol;
4560
  LEX *lex= thd->lex;
4561
  int result_code;
4562
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
4563

4564
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
unknown's avatar
unknown committed
4565 4566 4567 4568 4569 4570 4571
  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;
4572
  if (protocol->send_result_set_metadata(&field_list,
4573
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4574
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4575

4576
  mysql_ha_rm_tables(thd, tables);
4577

unknown's avatar
VIEW  
unknown committed
4578
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
4579 4580
  {
    char table_name[NAME_LEN*2+2];
4581
    char* db = table->db;
4582
    bool fatal_error=0;
unknown's avatar
unknown committed
4583

4584 4585
    DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
    DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
unknown's avatar
merged  
unknown committed
4586
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
4587
    thd->open_options|= extra_open_options;
4588 4589
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4590
    {
4591 4592 4593 4594 4595
      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;
4596
      select->table_list.first= (uchar*)table;
4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608
      /*
        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;
4609

Konstantin Osipov's avatar
Konstantin Osipov committed
4610 4611
      open_and_lock_tables_derived(thd, table, TRUE,
                                   MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
4612 4613 4614 4615
      thd->no_warnings_for_error= 0;
      table->next_global= save_next_global;
      table->next_local= save_next_local;
      thd->open_options&= ~extra_open_options;
4616
#ifdef WITH_PARTITION_STORAGE_ENGINE
4617
      if (table->table)
4618 4619 4620 4621
      {
        /*
          Set up which partitions that should be processed
          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
4622
          CACHE INDEX/LOAD INDEX for specified partitions
4623 4624 4625
        */
        Alter_info *alter_info= &lex->alter_info;

4626
        if (alter_info->flags & ALTER_ADMIN_PARTITION)
4627
        {
4628 4629 4630 4631 4632
          if (!table->table->part_info)
          {
            my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
            DBUG_RETURN(TRUE);
          }
4633 4634 4635
          uint num_parts_found;
          uint num_parts_opt= alter_info->partition_names.elements;
          num_parts_found= set_part_state(alter_info, table->table->part_info,
4636
                                          PART_ADMIN);
4637
          if (num_parts_found != num_parts_opt &&
4638 4639 4640
              (!(alter_info->flags & ALTER_ALL_PARTITION)))
          {
            char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4641
            size_t length;
4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658
            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
4659
    }
4660 4661
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

unknown's avatar
unknown committed
4662
    if (prepare_func)
4663
    {
4664
      DBUG_PRINT("admin", ("calling prepare_func"));
unknown's avatar
unknown committed
4665
      switch ((*prepare_func)(thd, table, check_opt)) {
4666
      case  1:           // error, message written to net
Konstantin Osipov's avatar
Konstantin Osipov committed
4667 4668
        trans_rollback_stmt(thd);
        trans_rollback(thd);
4669
        close_thread_tables(thd);
4670
        DBUG_PRINT("admin", ("simple error, admin next table"));
4671 4672
        continue;
      case -1:           // error, message could be written to net
4673 4674
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4675
        goto err;
4676
        /* purecov: end */
4677
      default:           // should be 0 otherwise
4678
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4679
        ;
unknown's avatar
unknown committed
4680
      }
4681
    }
4682

4683
    /*
unknown's avatar
unknown committed
4684 4685 4686 4687 4688 4689
      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)
4690
    */
unknown's avatar
unknown committed
4691 4692
    if (!table->table)
    {
4693
      DBUG_PRINT("admin", ("open table failed"));
Marc Alff's avatar
Marc Alff committed
4694 4695
      if (thd->warning_info->is_empty())
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4696
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4697 4698 4699
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
Marc Alff's avatar
Marc Alff committed
4700
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4701
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
Marc Alff's avatar
Marc Alff committed
4702 4703 4704
      if (thd->stmt_da->is_error() &&
          (thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE ||
           thd->stmt_da->sql_errno() == ER_FILE_NOT_FOUND))
Staale Smedseng's avatar
Staale Smedseng committed
4705 4706 4707 4708 4709
        /* 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;
4710
      goto send_result;
unknown's avatar
unknown committed
4711
    }
4712 4713 4714

    if (table->view)
    {
4715
      DBUG_PRINT("admin", ("calling view_operator_func"));
4716 4717 4718 4719
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4720
    if (table->schema_table)
unknown's avatar
unknown committed
4721
    {
4722 4723
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
unknown's avatar
unknown committed
4724 4725
    }

4726
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
4727
    {
4728
      /* purecov: begin inspected */
unknown's avatar
unknown committed
4729
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4730
      size_t length;
4731
      DBUG_PRINT("admin", ("sending error message"));
unknown's avatar
unknown committed
4732
      protocol->prepare_for_resend();
4733 4734
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4735
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4736 4737 4738
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
Konstantin Osipov's avatar
Konstantin Osipov committed
4739 4740
      trans_commit_stmt(thd);
      trans_commit(thd);
4741
      close_thread_tables(thd);
4742 4743
      if (!thd->locked_tables_mode)
        thd->mdl_context.release_all_locks();
4744
      lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
4745
      table->table=0;				// For query cache
unknown's avatar
unknown committed
4746
      if (protocol->write())
unknown's avatar
unknown committed
4747
	goto err;
Marc Alff's avatar
Marc Alff committed
4748
      thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
4749
      continue;
4750
      /* purecov: end */
unknown's avatar
unknown committed
4751 4752
    }

4753
    /* Close all instances of the table to allow repair to rename files */
4754
    if (lock_type == TL_WRITE && table->table->s->version)
4755
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4756 4757 4758 4759 4760 4761 4762
      if (wait_while_table_is_used(thd, table->table,
                                   HA_EXTRA_PREPARE_FOR_RENAME))
        goto err;
      DBUG_EXECUTE_IF("wait_in_mysql_admin_table",
                      wait_for_kill_signal(thd);
                      if (thd->killed)
                        goto err;);
4763 4764 4765
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4766 4767
    }

unknown's avatar
unknown committed
4768
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4769
    {
4770 4771
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4772 4773 4774
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4775 4776 4777
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4778 4779
      if (protocol->write())
        goto err;
4780
      /* purecov: end */
4781 4782
    }

4783 4784
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
unknown's avatar
unknown committed
4785 4786 4787 4788 4789
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4790
        DBUG_PRINT("admin", ("recreating table"));
Konstantin Osipov's avatar
Konstantin Osipov committed
4791
        trans_rollback_stmt(thd);
4792
        trans_rollback(thd);
unknown's avatar
unknown committed
4793
        close_thread_tables(thd);
4794 4795
        if (!thd->locked_tables_mode)
          thd->mdl_context.release_all_locks();
unknown's avatar
unknown committed
4796
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4797
        result_code= mysql_recreate_table(thd, table);
unknown's avatar
unknown committed
4798
        reenable_binlog(thd);
4799 4800 4801 4802 4803 4804
        /*
          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.
        */
Marc Alff's avatar
Marc Alff committed
4805 4806
        if (thd->stmt_da->is_ok())
          thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
4807 4808 4809 4810
        goto send_result;
      }
    }

4811
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4812
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4813
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4814 4815 4816

send_result:

4817
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
4818
    thd->clear_error();  // these errors shouldn't get client
4819
    {
Marc Alff's avatar
Marc Alff committed
4820
      List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
4821 4822 4823 4824 4825 4826
      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);
Marc Alff's avatar
Marc Alff committed
4827 4828
        protocol->store(warning_level_names[err->get_level()].str,
                        warning_level_names[err->get_level()].length,
4829
                        system_charset_info);
Marc Alff's avatar
Marc Alff committed
4830
        protocol->store(err->get_message_text(), system_charset_info);
4831 4832 4833
        if (protocol->write())
          goto err;
      }
Marc Alff's avatar
Marc Alff committed
4834
      thd->warning_info->clear_warning_info(thd->query_id);
4835
    }
unknown's avatar
unknown committed
4836
    protocol->prepare_for_resend();
4837 4838
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
4839

4840 4841 4842
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4843 4844
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4845
      {
4846
       char buf[MYSQL_ERRMSG_SIZE];
4847
       size_t length=my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
4848
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4849
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4850
	protocol->store(buf, length, system_charset_info);
4851
      }
unknown's avatar
unknown committed
4852 4853
      break;

unknown's avatar
unknown committed
4854 4855
    case HA_ADMIN_NOT_BASE_TABLE:
      {
4856
        char buf[MYSQL_ERRMSG_SIZE];
4857
        size_t length= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
4858
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4859
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4860
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
4861 4862 4863
      }
      break;

4864
    case HA_ADMIN_OK:
4865 4866
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
4867 4868
      break;

4869
    case HA_ADMIN_FAILED:
4870 4871 4872
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
4873 4874
      break;

unknown's avatar
unknown committed
4875
    case HA_ADMIN_REJECT:
4876 4877 4878
      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
4879
      open_for_modify= FALSE;
unknown's avatar
unknown committed
4880 4881
      break;

4882
    case HA_ADMIN_ALREADY_DONE:
4883 4884 4885
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4886 4887
      break;

4888
    case HA_ADMIN_CORRUPT:
4889 4890
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4891
      fatal_error=1;
unknown's avatar
unknown committed
4892 4893
      break;

unknown's avatar
unknown committed
4894
    case HA_ADMIN_INVALID:
4895 4896 4897
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
4898 4899
      break;

4900 4901 4902 4903 4904 4905
    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.
4906
        We have to end the row, so analyze could return more rows.
4907
      */
4908 4909 4910 4911 4912
      trans_commit_stmt(thd);
      trans_commit(thd);
      close_thread_tables(thd);
      if (!thd->locked_tables_mode)
        thd->mdl_context.release_all_locks();
4913 4914 4915 4916 4917 4918 4919
      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;
      DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
unknown's avatar
VIEW  
unknown committed
4920 4921 4922
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4923
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4924
      result_code= mysql_recreate_table(thd, table);
4925
      reenable_binlog(thd);
4926 4927 4928 4929 4930 4931
      /*
        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.
      */
Marc Alff's avatar
Marc Alff committed
4932 4933
      if (thd->stmt_da->is_ok())
        thd->stmt_da->reset_diagnostics_area();
Konstantin Osipov's avatar
Konstantin Osipov committed
4934
      trans_commit_stmt(thd);
4935
      trans_commit(thd);
unknown's avatar
unknown committed
4936
      close_thread_tables(thd);
4937 4938
      if (!thd->locked_tables_mode)
        thd->mdl_context.release_all_locks();
4939 4940
      if (!result_code) // recreation went ok
      {
4941
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
4942
            ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
4943 4944
          result_code= 0; // analyze went ok
      }
4945 4946 4947 4948
      /* 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);
4949 4950
      if (result_code) // either mysql_recreate_table or analyze failed
      {
4951 4952
        DBUG_ASSERT(thd->is_error());
        if (thd->is_error())
4953
        {
Marc Alff's avatar
Marc Alff committed
4954
          const char *err_msg= thd->stmt_da->message();
4955 4956
          if (!thd->vio_ok())
          {
Staale Smedseng's avatar
Staale Smedseng committed
4957
            sql_print_error("%s", err_msg);
4958 4959 4960 4961
          }
          else
          {
            /* Hijack the row already in-progress. */
4962
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4963
            protocol->store(err_msg, system_charset_info);
4964 4965
            if (protocol->write())
              goto err;
4966 4967 4968 4969 4970
            /* 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);
          }
4971
          thd->clear_error();
4972 4973
        }
      }
4974
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
4975 4976
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4977 4978
      goto send_result_message;
    }
4979 4980
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4981
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4982 4983
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4984 4985
      break;
    }
4986

unknown's avatar
unknown committed
4987 4988 4989
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
4990
      char buf[MYSQL_ERRMSG_SIZE];
4991
      size_t length;
unknown's avatar
unknown committed
4992 4993

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4994 4995
      length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
                         table->table_name);
unknown's avatar
unknown committed
4996 4997 4998 4999 5000
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

5001
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
5002
      {
5003
        char buf[MYSQL_ERRMSG_SIZE];
5004
        size_t length=my_snprintf(buf, sizeof(buf),
5005 5006
                                "Unknown - internal error %d during operation",
                                result_code);
5007
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
5008 5009 5010 5011
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
5012
    }
unknown's avatar
unknown committed
5013
    if (table->table)
5014
    {
unknown's avatar
unknown committed
5015 5016
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
5017
      else if (open_for_modify)
unknown's avatar
unknown committed
5018
      {
unknown's avatar
unknown committed
5019
        if (table->table->s->tmp_table)
unknown's avatar
merging  
unknown committed
5020 5021 5022
          table->table->file->info(HA_STATUS_CONST);
        else
        {
Konstantin Osipov's avatar
Konstantin Osipov committed
5023 5024 5025 5026
          TABLE_LIST *save_next_global= table->next_global;
          table->next_global= 0;
          close_cached_tables(thd, table, FALSE, FALSE);
          table->next_global= save_next_global;
unknown's avatar
merging  
unknown committed
5027 5028
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
5029 5030
        query_cache_invalidate3(thd, table->table, 0);
      }
5031
    }
5032
    /* Error path, a admin command failed. */
Konstantin Osipov's avatar
Konstantin Osipov committed
5033 5034
    trans_commit_stmt(thd);
    trans_commit_implicit(thd);
unknown's avatar
unknown committed
5035
    close_thread_tables(thd);
unknown's avatar
unknown committed
5036
    table->table=0;				// For query cache
unknown's avatar
unknown committed
5037
    if (protocol->write())
unknown's avatar
unknown committed
5038 5039 5040
      goto err;
  }

5041
  my_eof(thd);
unknown's avatar
unknown committed
5042
  DBUG_RETURN(FALSE);
5043

5044
err:
Konstantin Osipov's avatar
Konstantin Osipov committed
5045 5046
  trans_rollback_stmt(thd);
  trans_rollback(thd);
5047
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
5048 5049
  if (table)
    table->table=0;
unknown's avatar
unknown committed
5050
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5051 5052
}

unknown's avatar
unknown committed
5053

unknown's avatar
unknown committed
5054
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5055 5056 5057
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5058 5059 5060
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
5061
				&prepare_for_repair,
unknown's avatar
unknown committed
5062
				&handler::ha_repair, 0));
5063 5064
}

5065

unknown's avatar
unknown committed
5066
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5067 5068 5069
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5070
				"optimize", TL_WRITE, 1,0,0,0,
5071
				&handler::ha_optimize, 0));
5072 5073 5074
}


unknown's avatar
unknown committed
5075 5076 5077 5078 5079
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
5080 5081
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5082 5083

  RETURN VALUES
unknown's avatar
unknown committed
5084 5085
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
5086 5087
*/

unknown's avatar
unknown committed
5088
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
5089
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
5090
{
5091
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
5092
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
5093
  DBUG_ENTER("mysql_assign_to_keycache");
5094 5095 5096 5097 5098 5099 5100

  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
5101
    DBUG_RETURN(TRUE);
5102 5103 5104 5105
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
5106
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
5107
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
5108 5109
}

unknown's avatar
unknown committed
5110 5111 5112 5113 5114 5115

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

  SYNOPSIS
    reassign_keycache_tables()
5116 5117 5118
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
5119

5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132
  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
5133 5134 5135
    0	  ok
*/

5136 5137
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
5138 5139 5140
{
  DBUG_ENTER("reassign_keycache_tables");

5141 5142
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
unknown's avatar
unknown committed
5143
  src_cache->param_buff_size= 0;		// Free key cache
5144 5145
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
5146
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5147 5148 5149
}


unknown's avatar
unknown committed
5150 5151 5152 5153 5154
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
5155 5156
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5157 5158

  RETURN VALUES
unknown's avatar
unknown committed
5159 5160
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
5161 5162
*/

unknown's avatar
unknown committed
5163
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
5164 5165
{
  DBUG_ENTER("mysql_preload_keys");
5166 5167 5168 5169 5170
  /*
    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
5171
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
5172
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
5173
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
5174 5175 5176
}


5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225

/**
  @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
5226 5227 5228 5229 5230
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
5231
    thd		Thread object
unknown's avatar
unknown committed
5232 5233
    table       Table list element for target table
    src_table   Table list element for source table
unknown's avatar
unknown committed
5234 5235 5236
    create_info Create info

  RETURN VALUES
unknown's avatar
unknown committed
5237 5238
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
5239 5240
*/

unknown's avatar
unknown committed
5241
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
5242
                             HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
5243
{
Konstantin Osipov's avatar
Konstantin Osipov committed
5244
  MDL_request *target_mdl_request= NULL;
5245
  char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
5246
  uint dst_path_length;
unknown's avatar
unknown committed
5247
  char *db= table->db;
5248
  char *table_name= table->table_name;
unknown's avatar
unknown committed
5249
  int  err;
unknown's avatar
unknown committed
5250
  bool res= TRUE;
unknown's avatar
unknown committed
5251
  uint not_used;
5252 5253 5254
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
5255
  char ts_name[FN_LEN + 1];
5256
  myf flags= MY_DONT_OVERWRITE_FILE;
unknown's avatar
unknown committed
5257
  DBUG_ENTER("mysql_create_like_table");
5258

5259

unknown's avatar
unknown committed
5260
  /*
Konstantin Osipov's avatar
Konstantin Osipov committed
5261 5262 5263 5264 5265 5266 5267
    By opening source table and thus acquiring shared metadata lock on it
    we guarantee that it exists and no concurrent DDL operation will mess
    with it. Later we also take an exclusive metadata 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.
5268
  */
unknown's avatar
unknown committed
5269
  if (open_tables(thd, &src_table, &not_used, 0))
5270 5271
    DBUG_RETURN(TRUE);

5272 5273 5274 5275 5276 5277
  /*
    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
5278
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
5279 5280 5281 5282 5283
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

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

unknown's avatar
unknown committed
5286
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
unknown's avatar
unknown committed
5287

unknown's avatar
unknown committed
5288 5289 5290
  /*
    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
5291 5292 5293 5294 5295
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
5296
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
unknown's avatar
unknown committed
5297 5298 5299 5300
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5301
    if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request))
unknown's avatar
unknown committed
5302
      goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
5303
    if (!target_mdl_request)
unknown's avatar
unknown committed
5304
      goto table_exists;
5305
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
5306
                                          db, table_name, reg_ext, 0);
unknown's avatar
unknown committed
5307 5308
    if (!access(dst_path, F_OK))
      goto table_exists;
Konstantin Osipov's avatar
Konstantin Osipov committed
5309 5310 5311 5312
    /*
      Make the metadata lock available to open_table() called to
      reopen the table down the road.
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
5313
    table->mdl_request= target_mdl_request;
unknown's avatar
unknown committed
5314 5315
  }

unknown's avatar
unknown committed
5316 5317
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

5318 5319
  if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    flags|= MY_SYNC;
5320

5321
  /*
unknown's avatar
unknown committed
5322
    Create a new table by copying from source table
5323
    and sync the new table if the flag MY_SYNC is set
unknown's avatar
unknown committed
5324

Konstantin Osipov's avatar
Konstantin Osipov committed
5325 5326 5327 5328 5329 5330 5331
    TODO: Obtaining LOCK_open mutex here is actually a legacy from the
          times when some operations (e.g. I_S implementation) ignored
          exclusive metadata lock on target table. 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). So we should double check and probably fix this code
          to not acquire this mutex.
5332
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
5333
  pthread_mutex_lock(&LOCK_open);
5334 5335 5336 5337
  if (src_table->schema_table)
  {
    if (mysql_create_like_schema_frm(thd, src_table, dst_path, create_info))
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
5338
      pthread_mutex_unlock(&LOCK_open);
5339 5340 5341
      goto err;
    }
  }
5342
  else if (my_copy(src_path, dst_path, flags))
5343 5344 5345 5346 5347
  {
    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);
Konstantin Osipov's avatar
Konstantin Osipov committed
5348
    pthread_mutex_unlock(&LOCK_open);
5349
    goto err;
5350
  }
unknown's avatar
unknown committed
5351 5352

  /*
5353 5354
    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
5355 5356
    and temporary tables).
  */
unknown's avatar
unknown committed
5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369
#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
5370 5371 5372

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

5373
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
5374 5375
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
unknown's avatar
unknown committed
5376
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
Konstantin Osipov's avatar
Konstantin Osipov committed
5377
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5378

unknown's avatar
unknown committed
5379 5380 5381 5382
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
5383 5384
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
5385
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
5386
    }
5387
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
5388 5389 5390
  }
  else if (err)
  {
5391
    (void) quick_rm_table(create_info->db_type, db,
5392
			  table_name, 0); /* purecov: inspected */
5393
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
5394
  }
5395

5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413
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
5414 5415
  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););

5416 5417 5418
  /*
    We have to write the query before we unlock the tables.
  */
5419
  if (thd->current_stmt_binlog_row_based)
5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437
  {
    /*
       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
5438
      if (src_table->table->s->tmp_table)               // Case 2
5439 5440 5441 5442
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
Konstantin Osipov's avatar
Konstantin Osipov committed
5443
        enum enum_open_table_action ot_action_unused;
5444
        /*
unknown's avatar
unknown committed
5445
          Here we open the destination table, on which we already have
Konstantin Osipov's avatar
Konstantin Osipov committed
5446 5447 5448
          exclusive metadata lock. This is needed for store_create_info()
          to work. The table will be closed by close_thread_table() at
          the end of this branch.
5449
        */
Konstantin Osipov's avatar
Konstantin Osipov committed
5450 5451
        if (open_table(thd, table, thd->mem_root, &ot_action_unused,
                       MYSQL_OPEN_REOPEN))
5452 5453
          goto err;

5454
        int result __attribute__((unused))=
5455 5456
          store_create_info(thd, table, &query,
                            create_info, FALSE /* show_database */);
5457 5458 5459

        DBUG_ASSERT(result == 0); // store_create_info() always return 0
        write_bin_log(thd, TRUE, query.ptr(), query.length());
5460

Konstantin Osipov's avatar
Konstantin Osipov committed
5461
        DBUG_ASSERT(thd->open_tables == table->table);
5462
        pthread_mutex_lock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
5463 5464 5465 5466 5467 5468
        /*
          When opening the table, we ignored the locked tables
          (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without
          risking to close some locked table.
        */
        close_thread_table(thd, &thd->open_tables);
5469
        pthread_mutex_unlock(&LOCK_open);
5470 5471
      }
      else                                      // Case 1
5472
        write_bin_log(thd, TRUE, thd->query(), thd->query_length());
5473 5474 5475 5476 5477
    }
    /*
      Case 3 and 4 does nothing under RBR
    */
  }
5478
  else
5479
    write_bin_log(thd, TRUE, thd->query(), thd->query_length());
5480

unknown's avatar
unknown committed
5481
  res= FALSE;
5482 5483

err:
Konstantin Osipov's avatar
Konstantin Osipov committed
5484
  if (target_mdl_request)
Konstantin Osipov's avatar
Konstantin Osipov committed
5485
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5486 5487
    thd->mdl_context.release_lock(target_mdl_request->ticket);
    thd->mdl_context.remove_request(target_mdl_request);
Konstantin Osipov's avatar
Konstantin Osipov committed
5488
  }
5489
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5490 5491 5492
}


unknown's avatar
unknown committed
5493
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5494
{
unknown's avatar
unknown committed
5495 5496
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5497 5498
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5499
				"analyze", lock_type, 1, 0, 0, 0,
5500
				&handler::ha_analyze, 0));
5501 5502 5503
}


unknown's avatar
unknown committed
5504
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
5505
{
unknown's avatar
unknown committed
5506 5507
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5508 5509
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
5510
				"check", lock_type,
5511
				0, 0, HA_OPEN_FOR_REPAIR, 0,
unknown's avatar
unknown committed
5512
				&handler::ha_check, &view_checksum));
5513 5514
}

unknown's avatar
unknown committed
5515

unknown's avatar
unknown committed
5516
/* table_list should contain just one table */
unknown's avatar
unknown committed
5517 5518 5519 5520
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
5521 5522 5523 5524 5525 5526
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
5527 5528 5529 5530
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
5531

5532
  thd_proc_info(thd, "discard_or_import_tablespace");
unknown's avatar
unknown committed
5533

unknown's avatar
unknown committed
5534
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
5535

unknown's avatar
unknown committed
5536 5537 5538 5539 5540
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
5541
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
unknown's avatar
unknown committed
5542 5543 5544 5545
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
5546

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

5549
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
5550 5551 5552 5553

  if (error)
    goto err;

unknown's avatar
unknown committed
5554 5555 5556 5557
  /*
    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
5558 5559 5560
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
Konstantin Osipov's avatar
Konstantin Osipov committed
5561 5562
  error= trans_commit_stmt(thd);
  if (trans_commit_implicit(thd))
unknown's avatar
unknown committed
5563 5564 5565
    error=1;
  if (error)
    goto err;
5566
  write_bin_log(thd, FALSE, thd->query(), thd->query_length());
5567

unknown's avatar
unknown committed
5568
err:
Konstantin Osipov's avatar
Konstantin Osipov committed
5569
  trans_rollback_stmt(thd);
unknown's avatar
unknown committed
5570
  thd->tablespace_op=FALSE;
5571

unknown's avatar
unknown committed
5572 5573
  if (error == 0)
  {
5574
    my_ok(thd);
unknown's avatar
unknown committed
5575
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5576
  }
unknown's avatar
unknown committed
5577

5578
  table->file->print_error(error, MYF(0));
5579

unknown's avatar
unknown committed
5580
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
5581
}
unknown's avatar
unknown committed
5582

5583

unknown's avatar
unknown committed
5584 5585
/*
  SYNOPSIS
5586 5587
    compare_tables()
      table                     The original table.
5588 5589
      alter_info                Alter options, fields and keys for the new
                                table.
5590 5591
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
5592 5593 5594 5595 5596 5597 5598 5599
      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
5600 5601 5602 5603
      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.
5604
      candidate_key_count OUT   The number of candidate keys in original table.
unknown's avatar
unknown committed
5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615

  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.

5616 5617 5618 5619 5620
    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
5621
  RETURN VALUES
5622 5623
    TRUE   error
    FALSE  success
unknown's avatar
unknown committed
5624 5625
*/

5626 5627 5628 5629 5630 5631
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
unknown's avatar
unknown committed
5632
               enum_alter_table_change_level *need_copy_table,
5633 5634
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
5635 5636
               uint **index_add_buffer, uint *index_add_count,
               uint *candidate_key_count)
unknown's avatar
unknown committed
5637 5638 5639
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
5640
  uint key_count;
5641 5642
  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
  Create_field *new_field, *tmp_new_field;
5643 5644
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5645
  THD *thd= table->in_use;
5646 5647 5648 5649 5650
  /*
    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;
5651 5652 5653
  bool not_nullable= true;
  DBUG_ENTER("compare_tables");

5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672
  /*
    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,
5673 5674 5675 5676 5677
                                 &tmp_alter_info,
                                 (table->s->tmp_table != NO_TMP_TABLE),
                                 &db_options,
                                 table->file, key_info_buffer,
                                 &key_count, 0))
5678 5679 5680
    DBUG_RETURN(1);
  /* Allocate result buffers. */
  if (! (*index_drop_buffer=
5681
         (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
5682
      ! (*index_add_buffer=
5683
         (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
5684
    DBUG_RETURN(1);
5685
  
unknown's avatar
unknown committed
5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702
  /*
    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
5703 5704 5705 5706 5707 5708 5709 5710

    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
5711
  */
5712
  if (table->s->fields != alter_info->create_list.elements ||
unknown's avatar
WL#2936  
unknown committed
5713
      table->s->db_type() != create_info->db_type ||
unknown's avatar
unknown committed
5714 5715 5716 5717
      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 ||
5718
      (table->s->row_type != create_info->row_type) ||
5719 5720
      create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
      create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
5721
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5722
      order_num ||
unknown's avatar
unknown committed
5723
      !table->s->mysql_version ||
5724
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5725 5726 5727 5728
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5729

5730 5731 5732 5733 5734 5735 5736
  /*
    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
5737 5738 5739 5740
  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
5741 5742 5743 5744 5745
  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
5746 5747 5748 5749
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5750

unknown's avatar
unknown committed
5751
    /* Check that NULL behavior is same for old and new fields */
5752
    if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
unknown's avatar
unknown committed
5753
	(uint) (field->flags & NOT_NULL_FLAG))
5754 5755 5756 5757
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
5758 5759 5760

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

5766
    /* Check if field was renamed */
5767
    field->flags&= ~FIELD_IS_RENAMED;
5768 5769
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
5770
		      tmp_new_field->field_name))
5771
      field->flags|= FIELD_IS_RENAMED;      
5772

unknown's avatar
unknown committed
5773
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
5774
    if (!(tmp= field->is_equal(tmp_new_field)))
5775 5776 5777 5778
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5779
    // Clear indexed marker
5780
    field->flags&= ~FIELD_IN_ADD_INDEX;
unknown's avatar
unknown committed
5781 5782 5783 5784 5785 5786 5787
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5788 5789 5790
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5791
  KEY *new_key_end= *key_info_buffer + key_count;
unknown's avatar
unknown committed
5792

5793 5794 5795 5796 5797 5798 5799
  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;
5800
  *candidate_key_count= 0;
5801
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
unknown's avatar
unknown committed
5802
  {
5803 5804 5805 5806
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822
   /*
      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)++;

5823
    /* Search a new key with the same name. */
5824
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5825 5826 5827 5828 5829 5830 5831
    {
      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. */
5832
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5833 5834 5835 5836 5837 5838 5839 5840 5841 5842
      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
5843 5844 5845 5846 5847

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5848 5849 5850
    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
5851 5852 5853
    {
      /*
	Key definition has changed if we are using a different field or
5854 5855
	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
5856
      */
5857 5858 5859
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
unknown's avatar
unknown committed
5860
    }
5861 5862 5863 5864
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5865 5866
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5867 5868 5869 5870 5871 5872
    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];
5873
      field->flags|= FIELD_IN_ADD_INDEX;
5874
    }
5875
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
unknown's avatar
unknown committed
5876
  }
5877
  /*end of for (; table_key < table_key_end;) */
unknown's avatar
unknown committed
5878

5879 5880 5881
  /*
    Step through all keys of the new table and find matching old keys.
  */
5882
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5883 5884 5885 5886 5887 5888 5889 5890 5891 5892
  {
    /* 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. */
5893
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5894 5895 5896 5897 5898 5899
      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];
5900
        field->flags|= FIELD_IN_ADD_INDEX;
5901
      }
unknown's avatar
unknown committed
5902
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5903 5904
    }
  }
5905 5906 5907

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5908 5909 5910 5911
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5912

5913
  if (*index_drop_count || *index_add_count)
5914 5915 5916 5917
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5918

5919 5920
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5921 5922 5923
}


unknown's avatar
unknown committed
5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949
/*
  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:
5950
    error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5951 5952 5953 5954 5955 5956
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
5957
    error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5958 5959 5960 5961 5962
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5963 5964
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
unknown's avatar
unknown committed
5965 5966 5967 5968 5969 5970 5971 5972
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011
/**
  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
6012
*/
6013

6014 6015 6016 6017
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
unknown's avatar
unknown committed
6018
{
6019
  /* New column definitions are added here */
unknown's avatar
unknown committed
6020
  List<Create_field> new_create_list;
6021 6022 6023
  /* 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
6024
  List_iterator<Create_field> def_it(alter_info->create_list);
6025 6026
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
unknown's avatar
unknown committed
6027 6028 6029
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
6030 6031 6032 6033 6034
  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;
6035

6036
  DBUG_ENTER("mysql_prepare_alter_table");
6037

6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048
  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)
6049
  {
6050 6051 6052
    /* 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;
6053
  }
6054 6055
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
unknown's avatar
unknown committed
6056

6057
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
6058
  {
6059
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
6060
    /*
6061 6062
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
6063
    */
6064 6065 6066 6067 6068
    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
6069
  Create_field *def;
6070

6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082
  /*
    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++))
6083
    {
6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095
      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;
      }
6096
    }
6097
    if (drop)
6098
    {
6099 6100
      drop_it.remove();
      continue;
6101
    }
6102 6103 6104
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
6105
    {
6106 6107 6108
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
6109
    }
6110 6111 6112 6113
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
unknown's avatar
unknown committed
6114
      {
6115 6116
	new_create_list.push_back(def);
	def_it.remove();
unknown's avatar
unknown committed
6117 6118
      }
    }
6119
    else
6120 6121
    {
      /*
6122 6123
        This field was not dropped and not changed, add it to the list
        for the new table.
6124
      */
unknown's avatar
unknown committed
6125
      def= new Create_field(field, field);
6126 6127 6128 6129
      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
6130
      {
6131 6132
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
6133
      }
6134
      if (alter)
unknown's avatar
unknown committed
6135
      {
6136
	if (def->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
6137
	{
6138
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
6139
          goto err;
unknown's avatar
unknown committed
6140
	}
6141 6142 6143 6144 6145
	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
6146 6147 6148
      }
    }
  }
6149 6150
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
6151
  {
6152
    if (def->change && ! def->field)
6153
    {
6154
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
6155
      goto err;
6156
    }
unknown's avatar
unknown committed
6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173
    /*
      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;
    }
6174 6175 6176 6177
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
6178
    else
6179
    {
unknown's avatar
unknown committed
6180
      Create_field *find;
6181 6182 6183 6184 6185 6186 6187 6188
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
6189
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
6190 6191 6192
        goto err;
      }
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
6193
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
6194
    }
6195
  }
6196
  if (alter_info->alter_list.elements)
6197
  {
6198
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
6199
             alter_info->alter_list.head()->name, table->s->table_name.str);
unknown's avatar
unknown committed
6200
    goto err;
6201
  }
6202
  if (!new_create_list.elements)
unknown's avatar
unknown committed
6203
  {
unknown's avatar
unknown committed
6204 6205
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
6206
    goto err;
unknown's avatar
unknown committed
6207 6208 6209
  }

  /*
6210 6211
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
6212 6213
  */

6214
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
6215
  {
6216
    char *key_name= key_info->name;
unknown's avatar
unknown committed
6217 6218 6219 6220 6221
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
6222
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237
	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
6238
      Create_field *cfield;
unknown's avatar
unknown committed
6239 6240 6241 6242 6243
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
6244 6245
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
6246 6247
	    break;
	}
6248
	else if (!my_strcasecmp(system_charset_info,
6249
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
6250
	  break;
unknown's avatar
unknown committed
6251 6252 6253 6254 6255
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
6256 6257 6258 6259 6260 6261 6262
      {
        /*
          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.

6263 6264 6265
          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.

6266 6267 6268 6269
          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
6270
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
6271 6272
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
unknown's avatar
unknown committed
6273 6274
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
6275 6276 6277
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
6278
      }
6279
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
6280
      key_parts.push_back(new Key_part_spec(cfield->field_name,
6281
                                            strlen(cfield->field_name),
unknown's avatar
unknown committed
6282 6283 6284
					    key_part_length));
    }
    if (key_parts.elements)
6285 6286
    {
      KEY_CREATE_INFO key_create_info;
6287 6288
      Key *key;
      enum Key::Keytype key_type;
6289 6290 6291 6292 6293 6294
      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)
6295
        key_create_info.parser_name= *plugin_name(key_info->parser);
6296

6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310
      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;

6311
      key= new Key(key_type, key_name, strlen(key_name),
6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323
                   &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);
6324 6325
      if (key->name.str &&
	  !my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
6326
      {
6327
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424
        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)
{
6425
  TABLE *table, *new_table= 0;
Konstantin Osipov's avatar
Konstantin Osipov committed
6426 6427
  MDL_ticket *mdl_ticket;
  MDL_request *target_mdl_request= NULL;
6428
  int error= 0;
6429
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
6430 6431
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
6432
  char path[FN_REFLEN + 1];
6433
  char reg_path[FN_REFLEN+1];
unknown's avatar
unknown committed
6434
  ha_rows copied,deleted;
6435
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
6436
  legacy_db_type table_type;
6437
  frm_type_enum frm_type;
unknown's avatar
unknown committed
6438
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
6439
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6440
  uint fast_alter_partition= 0;
6441 6442
  bool partition_changed= FALSE;
#endif
6443 6444
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
Staale Smedseng's avatar
Staale Smedseng committed
6445 6446 6447 6448 6449
  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
6450
  bool committed= 0;
6451
  bool no_pk;
unknown's avatar
unknown committed
6452 6453
  DBUG_ENTER("mysql_alter_table");

6454 6455 6456 6457 6458 6459
  /*
    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.
  */
6460
  if (table_list && table_list->db && table_list->table_name)
6461
  {
6462
    int table_kind= 0;
6463

6464 6465 6466
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
6467

6468
    if (table_kind)
6469
    {
6470 6471 6472 6473 6474 6475
      /* 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);
      }
6476

6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488
      /* 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)
      {
6489
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
6490 6491 6492
        DBUG_RETURN(TRUE);
      }
#endif
6493 6494 6495
    }
  }

6496 6497 6498 6499 6500
  /*
    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.
  */
6501
  thd_proc_info(thd, "init");
6502
  table_name=table_list->table_name;
unknown's avatar
unknown committed
6503
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
unknown's avatar
unknown committed
6504
  db=table_list->db;
unknown's avatar
unknown committed
6505
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
6506
    new_db= db;
6507 6508
  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);
6509

6510
  mysql_ha_rm_tables(thd, table_list);
unknown's avatar
unknown committed
6511

unknown's avatar
unknown committed
6512
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
6513
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
6514
    /* Conditionally writes to binlog. */
unknown's avatar
unknown committed
6515
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
6516
						   alter_info->tablespace_op));
6517 6518 6519
  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);
6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532
  /*
    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.
  */
6533 6534
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
6535
  /* Sic: there is a race here */
6536 6537
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551
    /*
      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);
    }

6552 6553 6554 6555 6556
    /*
      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
    */

Konstantin Osipov's avatar
Konstantin Osipov committed
6557
    if (thd->locked_tables_mode || thd->active_transaction())
6558 6559 6560
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
6561
      DBUG_RETURN(TRUE);
6562 6563 6564
    }

    if (wait_if_global_read_lock(thd,0,1))
6565
      DBUG_RETURN(TRUE);
6566
    if (lock_table_names(thd, table_list))
6567
    {
unknown's avatar
unknown committed
6568
      error= 1;
6569
      goto view_err;
6570
    }
6571 6572 6573

    pthread_mutex_lock(&LOCK_open);

6574 6575 6576 6577 6578
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
6579
        Query_log_event qinfo(thd, thd->query(), thd->query_length(),
6580
                              0, FALSE, 0);
6581 6582
        mysql_bin_log.write(&qinfo);
      }
6583
      my_ok(thd);
6584
    }
6585
    pthread_mutex_unlock(&LOCK_open);
6586

6587
    unlock_table_names(thd);
6588 6589 6590 6591 6592

view_err:
    start_waiting_global_read_lock(thd);
    DBUG_RETURN(error);
  }
6593

Konstantin Osipov's avatar
Konstantin Osipov committed
6594 6595
  if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ,
                                        MYSQL_OPEN_TAKE_UPGRADABLE_MDL)))
unknown's avatar
unknown committed
6596
    DBUG_RETURN(TRUE);
6597
  table->use_all_columns();
Konstantin Osipov's avatar
Konstantin Osipov committed
6598
  mdl_ticket= table->mdl_ticket;
unknown's avatar
unknown committed
6599

6600 6601 6602 6603 6604 6605
  /*
    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.
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
6606
  if (thd->locked_tables_mode &&
6607 6608 6609 6610 6611 6612 6613
      (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
6614 6615 6616
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
6617
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
unknown's avatar
unknown committed
6618
    strmov(new_name_buff,new_name);
unknown's avatar
unknown committed
6619
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
6620
    if (lower_case_table_names)
unknown's avatar
unknown committed
6621 6622 6623
    {
      if (lower_case_table_names != 2)
      {
6624
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
6625 6626
	new_alias= new_name;			// Create lower case table name
      }
6627
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
6628
    }
6629
    if (new_db == db &&
unknown's avatar
unknown committed
6630
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
6631 6632
    {
      /*
6633 6634
	Source and destination table names are equal: make later check
	easier.
6635
      */
unknown's avatar
unknown committed
6636
      new_alias= new_name= table_name;
6637
    }
unknown's avatar
unknown committed
6638 6639
    else
    {
unknown's avatar
unknown committed
6640
      if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6641 6642 6643
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
6644
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
6645
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6646 6647 6648 6649
	}
      }
      else
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6650
        if (lock_table_name_if_not_cached(thd, new_db, new_name,
Konstantin Osipov's avatar
Konstantin Osipov committed
6651
                                          &target_mdl_request))
unknown's avatar
unknown committed
6652
          DBUG_RETURN(TRUE);
Konstantin Osipov's avatar
Konstantin Osipov committed
6653
        if (!target_mdl_request)
unknown's avatar
unknown committed
6654 6655 6656 6657 6658
        {
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
	  DBUG_RETURN(TRUE);
        }

6659
        build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
6660 6661
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
unknown's avatar
unknown committed
6662 6663
	{
	  /* Table will be closed in do_command() */
6664
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
6665
          goto err;
unknown's avatar
unknown committed
6666 6667 6668 6669 6670
	}
      }
    }
  }
  else
6671 6672 6673 6674
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
6675

unknown's avatar
WL#2936  
unknown committed
6676
  old_db_type= table->s->db_type();
unknown's avatar
unknown committed
6677
  if (!create_info->db_type)
6678
  {
6679
#ifdef WITH_PARTITION_STORAGE_ENGINE
6680 6681
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
6682 6683 6684 6685
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
6686 6687 6688
        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.
6689
      */
6690
      create_info->db_type= table->part_info->default_engine_type;
6691
    }
6692
    else
6693
#endif
6694
      create_info->db_type= old_db_type;
6695
  }
unknown's avatar
unknown committed
6696

6697
  if (check_engine(thd, new_name, create_info))
unknown's avatar
unknown committed
6698
    goto err;
6699
  new_db_type= create_info->db_type;
6700

6701 6702
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
6703
      !table->file->can_switch_engines())
unknown's avatar
unknown committed
6704
  {
6705
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
unknown's avatar
unknown committed
6706 6707 6708
    goto err;
  }

6709 6710 6711 6712 6713
  /*
   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.
  */
6714
  if (create_info->row_type == ROW_TYPE_NOT_USED)
6715
  {
6716
    /* ALTER TABLE without explicit row type */
6717
    create_info->row_type= table->s->row_type;
6718 6719 6720 6721
  }
  else
  {
    /* ALTER TABLE with specific row type */
6722 6723
    create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
  }
unknown's avatar
unknown committed
6724

6725 6726 6727
  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
6728
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
6729
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
6730 6731 6732
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
6733
    goto err;
6734 6735
  }
  
6736
  thd_proc_info(thd, "setup");
6737
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
6738
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
6739
  {
6740 6741 6742 6743
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
6744 6745
      if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
        goto err;
6746
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
6747
      error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6748 6749 6750
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
6751 6752
      if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
        goto err;
6753
      error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6754 6755
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
6756 6757 6758 6759
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
6760 6761 6762
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
unknown's avatar
unknown committed
6763
      error= 0;
6764
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
Konstantin Osipov's avatar
Konstantin Osipov committed
6765 6766
                          ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                          table->alias);
6767 6768 6769
    }

    if (!error && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6770
    {
6771
      thd_proc_info(thd, "rename");
unknown's avatar
unknown committed
6772 6773 6774
      /*
        Then do a 'simple' rename of the table. First we need to close all
        instances of 'source' table.
Konstantin Osipov's avatar
Konstantin Osipov committed
6775
        Note that if wait_while_table_is_used() returns error here (i.e. if
6776
        this thread was killed) then it must be that previous step of
Konstantin Osipov's avatar
Konstantin Osipov committed
6777
        simple rename did nothing and therefore we can safely return
6778
        without additional clean-up.
unknown's avatar
unknown committed
6779
      */
Konstantin Osipov's avatar
Konstantin Osipov committed
6780
      if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
6781
        goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
6782
      close_all_tables_for_name(thd, table->s, TRUE);
unknown's avatar
unknown committed
6783 6784 6785
      /*
        Then, we want check once again that target table does not exist.
        Actually the order of these two steps does not matter since
Konstantin Osipov's avatar
Konstantin Osipov committed
6786 6787 6788
        earlier we took exclusive metadata 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 lock and where this order really matters.
unknown's avatar
unknown committed
6789 6790
        TODO: Investigate if we need this access() check at all.
      */
6791 6792
      if (!access(new_name_buff,F_OK))
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6793 6794
        my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
        error= -1;
6795 6796 6797
      }
      else
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6798
        *fn_ext(new_name)=0;
6799
        pthread_mutex_lock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
6800 6801
        if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
          error= -1;
6802 6803 6804
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
Konstantin Osipov's avatar
Konstantin Osipov committed
6805
          (void) mysql_rename_table(old_db_type, new_db, new_alias, db,
Konstantin Osipov's avatar
Konstantin Osipov committed
6806
                                    table_name, 0);
6807 6808
          error= -1;
        }
6809
        pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6810 6811
      }
    }
6812

6813 6814 6815 6816
    if (error == HA_ERR_WRONG_COMMAND)
    {
      error= 0;
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
Konstantin Osipov's avatar
Konstantin Osipov committed
6817 6818
                          ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                          table->alias);
6819
    }
6820

6821 6822
    if (!error)
    {
6823
      write_bin_log(thd, TRUE, thd->query(), thd->query_length());
6824
      my_ok(thd);
6825
    }
6826
    else if (error > 0)
6827
    {
6828 6829
      table->file->print_error(error, MYF(0));
      error= -1;
6830
    }
6831 6832
    table_list->table= NULL;                    // For query cache
    query_cache_invalidate3(thd, table_list, 0);
6833

Konstantin Osipov's avatar
Konstantin Osipov committed
6834
    if (thd->locked_tables_mode)
6835 6836 6837 6838 6839 6840 6841
    {
      /*
        Under LOCK TABLES we should adjust meta-data locks before finishing
        statement. Otherwise we can rely on close_thread_tables() releasing
        them.

        TODO: Investigate what should be done with upgraded table-level
Konstantin Osipov's avatar
Konstantin Osipov committed
6842
        lock here...
6843 6844
      */
      if (new_name != table_name || new_db != db)
Konstantin Osipov's avatar
Konstantin Osipov committed
6845
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6846 6847 6848
        thd->mdl_context.release_lock(target_mdl_request->ticket);
        thd->mdl_context.remove_request(target_mdl_request);
        thd->mdl_context.release_all_locks_for_name(mdl_ticket);
Konstantin Osipov's avatar
Konstantin Osipov committed
6849
      }
6850
      else
Konstantin Osipov's avatar
Konstantin Osipov committed
6851
        mdl_ticket->downgrade_exclusive_lock();
6852
    }
6853
    DBUG_RETURN(error);
unknown's avatar
unknown committed
6854 6855
  }

6856
  /* We have to do full alter table. */
unknown's avatar
unknown committed
6857

6858 6859 6860
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6861
    goto err;
6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874
#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
6875

6876 6877
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
6878

unknown's avatar
unknown committed
6879
  need_copy_table= alter_info->change_level;
unknown's avatar
unknown committed
6880

unknown's avatar
unknown committed
6881 6882
  set_table_default_charset(thd, create_info, db);

6883
  if (thd->variables.old_alter_table
unknown's avatar
WL#2936  
unknown committed
6884
      || (table->s->db_type() != create_info->db_type)
6885
#ifdef WITH_PARTITION_STORAGE_ENGINE
6886
      || partition_changed
6887
#endif
6888
     )
6889
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
unknown's avatar
unknown committed
6890
  else
6891
  {
unknown's avatar
unknown committed
6892
    enum_alter_table_change_level need_copy_table_res;
6893
    /* Check how much the tables differ. */
6894 6895
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6896
                       &need_copy_table_res,
6897 6898
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
6899 6900
                       &index_add_buffer, &index_add_count,
                       &candidate_key_count))
6901
      goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
6902

6903 6904
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6905 6906 6907 6908 6909 6910 6911 6912 6913 6914
  }

  /*
    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
6915
    ulong alter_flags= 0;
6916 6917 6918 6919 6920 6921
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

6922
    alter_flags= table->file->alter_table_flags(alter_info->flags);
unknown's avatar
unknown committed
6923
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6924 6925 6926 6927 6928 6929 6930 6931 6932
    /* 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)
      {
6933 6934 6935 6936 6937
        /* 
           Unique key. Check for "PRIMARY". 
           or if dropping last unique key
        */
        if ((uint) (key - table->key_info) == table->s->primary_key)
6938
        {
6939
          DBUG_PRINT("info", ("Dropping primary key"));
6940 6941 6942 6943
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
6944
          candidate_key_count--;
6945 6946 6947
        }
        else
        {
6948 6949 6950
          KEY_PART_INFO *part_end= key->key_part + key->key_parts;
          bool is_candidate_key= true;

6951 6952 6953
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966

          /*
            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--;
6967 6968 6969 6970 6971 6972 6973 6974 6975
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_DROP_INDEX;
        needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
      }
    }
6976 6977
    no_pk= ((table->s->primary_key == MAX_KEY) ||
            (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
6978 6979 6980 6981 6982 6983 6984 6985 6986
    /* 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)
      {
6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011
        /* 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))
7012
        {
7013
          DBUG_PRINT("info", ("Adding primary key"));
7014 7015 7016 7017
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
          pk_changed++;
7018
          no_pk= false;
7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034
        }
        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;
      }
    }

7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048
    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));
7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059
    /*
      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. */
7060
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
7061 7062 7063 7064 7065
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
7066
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
7067 7068 7069 7070 7071
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
unknown's avatar
unknown committed
7072

7073 7074
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
7075
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
7076 7077
    so that ALTER TABLE won't break when somebody will add new flag
  */
7078
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7079
    create_info->frm_only= 1;
7080

7081
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
7082
  if (fast_alter_partition)
unknown's avatar
unknown committed
7083
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
7084
    DBUG_ASSERT(!target_mdl_request);
unknown's avatar
unknown committed
7085 7086 7087 7088
    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
7089
  }
7090
#endif
unknown's avatar
unknown committed
7091

7092 7093 7094 7095 7096 7097
  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);

unknown's avatar
unknown committed
7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112
  /*
    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.
7113 7114
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
unknown's avatar
unknown committed
7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140
      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);
    }
  }
7141 7142
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
7143

7144 7145 7146 7147 7148 7149
  /*
    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
7150
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
7151 7152 7153
                                    create_info,
                                    alter_info,
                                    1, 0);
7154 7155
  reenable_binlog(thd);
  if (error)
unknown's avatar
unknown committed
7156
    goto err;
7157 7158

  /* Open the table if we need to copy the data. */
7159
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
7160
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7161
  {
7162
    if (table->s->tmp_table)
7163
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7164
      enum_open_table_action not_used;
7165 7166 7167
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
7168
      tbl.table_name= tbl.alias= tmp_name;
unknown's avatar
unknown committed
7169
      /* Table is in thd->temporary_tables */
Konstantin Osipov's avatar
Konstantin Osipov committed
7170 7171 7172
      (void) open_table(thd, &tbl, thd->mem_root, &not_used,
                        MYSQL_LOCK_IGNORE_FLUSH);
      new_table= tbl.table;
7173 7174 7175
    }
    else
    {
7176
      char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
7177
      /* table is a normal table: Create temporary table in same directory */
7178
      build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
7179
                           FN_IS_TMP);
7180
      /* Open our intermediate table */
7181
      new_table= open_temporary_table(thd, path, new_db, tmp_name, 1);
7182 7183
    }
    if (!new_table)
7184
      goto err_new_table_cleanup;
7185 7186 7187 7188
    /*
      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
7189 7190
  }

7191
  /* Copy the data if necessary. */
7192
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
7193
  thd->cuted_fields=0L;
unknown's avatar
unknown committed
7194
  copied=deleted=0;
7195 7196 7197 7198
  /*
    We do not copy data for MERGE tables. Only the children have data.
    MERGE tables have HA_NO_COPY_ON_ALTER set.
  */
7199
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
7200
  {
7201
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
unknown's avatar
unknown committed
7202
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7203
    new_table->next_number_field=new_table->found_next_number_field;
7204
    thd_proc_info(thd, "copy to tmp table");
7205 7206 7207
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
7208
                                    alter_info->keys_onoff,
unknown's avatar
unknown committed
7209
                                    alter_info->error_if_not_empty);
7210
  }
7211
  else
7212
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
7213 7214
    if (!table->s->tmp_table &&
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
7215
      goto err_new_table_cleanup;
7216
    thd_proc_info(thd, "manage keys");
7217 7218
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
Konstantin Osipov's avatar
Konstantin Osipov committed
7219 7220
    error= trans_commit_stmt(thd);
    if (trans_commit_implicit(thd))
7221
      error= 1;
7222
  }
7223
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
7224

7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237
  /* 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"));

7238
    table->file->ha_prepare_for_alter();
7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265
    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;
7266
        goto err_new_table_cleanup;
7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288
      }
    }
    /*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));
7289
        goto err_new_table_cleanup;
7290 7291 7292 7293 7294 7295
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
7296
        goto err_new_table_cleanup;
7297 7298 7299 7300
      }
    }
    /*end of if (index_drop_count)*/

7301 7302 7303 7304
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
7305

7306 7307
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
Konstantin Osipov's avatar
Konstantin Osipov committed
7308
    if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
7309
      goto err_new_table_cleanup;
7310
    committed= 1;
7311 7312 7313
  }
  /*end of if (! new_table) for add/drop index*/

7314 7315 7316
  if (error)
    goto err_new_table_cleanup;

unknown's avatar
unknown committed
7317
  if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
7318
  {
7319
    /* Close lock if this is a transactional table */
Konstantin Osipov's avatar
Konstantin Osipov committed
7320
    if (thd->lock && ! thd->locked_tables_mode)
7321 7322 7323 7324
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
unknown's avatar
unknown committed
7325
    /* Remove link to old table and rename the new one */
unknown's avatar
unknown committed
7326
    close_temporary_table(thd, table, 1, 1);
7327 7328
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
7329
      goto err_new_table_cleanup;
7330
    /* We don't replicate alter table statement on temporary tables */
7331
    if (!thd->current_stmt_binlog_row_based)
7332
      write_bin_log(thd, TRUE, thd->query(), thd->query_length());
unknown's avatar
unknown committed
7333 7334 7335
    goto end_temporary;
  }

7336 7337 7338 7339 7340
  /*
    Close the intermediate table that will be the new table, but do
    not delete it! Even altough MERGE tables do not have their children
    attached here it is safe to call close_temporary_table().
  */
7341 7342
  if (new_table)
  {
7343 7344
    close_temporary_table(thd, new_table, 1, 0);
    new_table= 0;
unknown's avatar
unknown committed
7345
  }
7346

unknown's avatar
unknown committed
7347
  /*
7348
    Data is copied. Now we:
Konstantin Osipov's avatar
Konstantin Osipov committed
7349 7350
    1) Wait until all other threads will stop using old version of table
       by upgrading shared metadata lock to exclusive one.
7351
    2) Close instances of table open by this thread and replace them
Konstantin Osipov's avatar
Konstantin Osipov committed
7352
       with placeholders to simplify reopen process.
7353 7354 7355 7356 7357 7358
    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
Konstantin Osipov's avatar
Konstantin Osipov committed
7359
       remove placeholders and release metadata locks.
7360
    7) If we are not not under LOCK TABLES we rely on close_thread_tables()
Konstantin Osipov's avatar
Konstantin Osipov committed
7361
       call to remove placeholders and releasing metadata locks.
unknown's avatar
unknown committed
7362 7363
  */

7364
  thd_proc_info(thd, "rename result table");
7365 7366
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
7367 7368
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
unknown's avatar
unknown committed
7369

7370 7371 7372 7373
  if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
    goto err_new_table_cleanup;


Konstantin Osipov's avatar
Konstantin Osipov committed
7374 7375
  close_all_tables_for_name(thd, table->s,
                            new_name != table_name || new_db != db);
unknown's avatar
unknown committed
7376

unknown's avatar
unknown committed
7377
  error=0;
Konstantin Osipov's avatar
Konstantin Osipov committed
7378
  table_list->table= table= 0;                  /* Safety */
7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393
  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.
  */
7394 7395 7396 7397 7398 7399
  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;
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
7400
  pthread_mutex_lock(&LOCK_open);
7401 7402
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
unknown's avatar
unknown committed
7403 7404
  {
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7405
    (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
unknown's avatar
unknown committed
7406
  }
7407
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
7408
                              new_alias, FN_FROM_IS_TMP) ||
7409
           ((new_name != table_name || new_db != db) && // we also do rename
7410
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
7411 7412
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
7413
           Table_triggers_list::change_table_name(thd, db, table_name,
7414
                                                  new_db, new_alias)))
7415 7416
  {
    /* Try to get everything back. */
unknown's avatar
unknown committed
7417
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7418 7419 7420 7421
    (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
7422
  }
7423

Konstantin Osipov's avatar
Konstantin Osipov committed
7424 7425 7426 7427 7428
  if (! error)
    (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);

  pthread_mutex_unlock(&LOCK_open);

unknown's avatar
unknown committed
7429 7430
  if (error)
  {
7431
    /* This shouldn't happen. But let us play it safe. */
Konstantin Osipov's avatar
Konstantin Osipov committed
7432
    goto err_with_mdl;
unknown's avatar
unknown committed
7433
  }
7434

7435
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7436
  {
unknown's avatar
unknown committed
7437
    /*
7438 7439
      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.
7440
      NO need to tamper with MERGE tables. The real open is done later.
unknown's avatar
unknown committed
7441
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
7442
    enum enum_open_table_action ot_action_unused;
7443 7444 7445 7446 7447 7448 7449 7450
    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);
Konstantin Osipov's avatar
Konstantin Osipov committed
7451
      table_list->mdl_request= target_mdl_request;
unknown's avatar
unknown committed
7452
    }
7453
    else
7454
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7455
      /*
Konstantin Osipov's avatar
Konstantin Osipov committed
7456
        Under LOCK TABLES, we have a different mdl_lock_ticket
Konstantin Osipov's avatar
Konstantin Osipov committed
7457 7458 7459
        points to a different instance than the one set initially
        to request the lock.
      */
Konstantin Osipov's avatar
Konstantin Osipov committed
7460
      table_list->mdl_request->ticket= mdl_ticket;
7461
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
7462 7463
    if (open_table(thd, table_list, thd->mem_root,
                   &ot_action_unused, MYSQL_OPEN_REOPEN))
7464
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7465
      goto err_with_mdl;
7466
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
7467
    t_table= table_list->table;
7468

Konstantin Osipov's avatar
Konstantin Osipov committed
7469 7470 7471 7472 7473 7474 7475 7476 7477
    /* Tell the handler that a new frm file is in place. */
    error= t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                               create_info);

    DBUG_ASSERT(thd->open_tables == t_table);
    pthread_mutex_lock(&LOCK_open);
    close_thread_table(thd, &thd->open_tables);
    pthread_mutex_unlock(&LOCK_open);
    table_list->table= 0;
7478

unknown's avatar
unknown committed
7479
    if (error)
Konstantin Osipov's avatar
Konstantin Osipov committed
7480
      goto err_with_mdl;
unknown's avatar
unknown committed
7481
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
7482 7483
  if (thd->locked_tables_list.reopen_tables(thd))
    goto err_with_mdl;
7484

7485
  thd_proc_info(thd, "end");
7486

7487 7488
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

7489
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
7490
                      thd->query(), thd->query_length(),
7491 7492
                      db, table_name);

unknown's avatar
unknown committed
7493 7494
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
                thd->current_stmt_binlog_row_based &&
7495
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
7496
  write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7497

7498
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
7499
  {
unknown's avatar
unknown committed
7500 7501 7502
    /*
      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
7503
      shutdown. But we do not need to attach MERGE children.
unknown's avatar
unknown committed
7504
    */
7505
    char path[FN_REFLEN];
7506
    TABLE *t_table;
7507
    build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0);
7508 7509
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
unknown's avatar
unknown committed
7510
    {
7511
      intern_close_table(t_table);
7512
      my_free(t_table, MYF(0));
unknown's avatar
unknown committed
7513
    }
7514
    else
7515
      sql_print_warning("Could not open table %s.%s after rename\n",
unknown's avatar
unknown committed
7516
                        new_db,table_name);
7517
    ha_flush_logs(old_db_type);
7518
  }
unknown's avatar
unknown committed
7519
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
7520
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
7521

Konstantin Osipov's avatar
Konstantin Osipov committed
7522
  if (thd->locked_tables_mode)
unknown's avatar
unknown committed
7523
  {
7524 7525
    if ((new_name != table_name || new_db != db))
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7526 7527 7528
      thd->mdl_context.release_lock(target_mdl_request->ticket);
      thd->mdl_context.remove_request(target_mdl_request);
      thd->mdl_context.release_all_locks_for_name(mdl_ticket);
7529 7530
    }
    else
Konstantin Osipov's avatar
Konstantin Osipov committed
7531
      mdl_ticket->downgrade_exclusive_lock();
unknown's avatar
unknown committed
7532 7533
  }

unknown's avatar
unknown committed
7534
end_temporary:
7535 7536
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
7537
	      (ulong) thd->warning_info->statement_warn_count());
7538
  my_ok(thd, copied + deleted, 0L, tmp_name);
unknown's avatar
unknown committed
7539
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
7540
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7541

7542
err_new_table_cleanup:
7543 7544 7545 7546 7547 7548
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
Konstantin Osipov's avatar
Konstantin Osipov committed
7549 7550
    (void) quick_rm_table(new_db_type, new_db, tmp_name,
                          create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
7551

7552
err:
7553 7554 7555 7556 7557 7558
  /*
    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.
  */
Marc Alff's avatar
Marc Alff committed
7559 7560
  if (alter_info->error_if_not_empty &&
      thd->warning_info->current_row_for_warning())
7561
  {
7562 7563
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
unknown's avatar
unknown committed
7564
    switch (alter_info->datetime_field->sql_type)
7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580
    {
      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;
Marc Alff's avatar
Marc Alff committed
7581
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
unknown's avatar
unknown committed
7582
                                 f_val, strlength(f_val), t_type,
unknown's avatar
unknown committed
7583
                                 alter_info->datetime_field->field_name);
7584 7585
    thd->abort_on_warning= save_abort_on_warning;
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
7586
  if (target_mdl_request)
Konstantin Osipov's avatar
Konstantin Osipov committed
7587
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
7588 7589
    thd->mdl_context.release_lock(target_mdl_request->ticket);
    thd->mdl_context.remove_request(target_mdl_request);
Konstantin Osipov's avatar
Konstantin Osipov committed
7590
  }
unknown's avatar
unknown committed
7591
  DBUG_RETURN(TRUE);
7592

Konstantin Osipov's avatar
Konstantin Osipov committed
7593
err_with_mdl:
7594
  /*
Konstantin Osipov's avatar
Konstantin Osipov committed
7595
    An error happened while we were holding exclusive name metadata lock
Konstantin Osipov's avatar
Konstantin Osipov committed
7596 7597 7598
    on table being altered. To be safe under LOCK TABLES we should
    remove all references to the altered table from the list of locked
    tables and release the exclusive metadata lock.
7599
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
7600
  thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
Konstantin Osipov's avatar
Konstantin Osipov committed
7601
  if (target_mdl_request)
Konstantin Osipov's avatar
Konstantin Osipov committed
7602
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
7603 7604
    thd->mdl_context.release_lock(target_mdl_request->ticket);
    thd->mdl_context.remove_request(target_mdl_request);
Konstantin Osipov's avatar
Konstantin Osipov committed
7605
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
7606
  thd->mdl_context.release_all_locks_for_name(mdl_ticket);
unknown's avatar
unknown committed
7607
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7608
}
unknown's avatar
unknown committed
7609
/* mysql_alter_table */
unknown's avatar
unknown committed
7610 7611

static int
unknown's avatar
unknown committed
7612
copy_data_between_tables(TABLE *from,TABLE *to,
unknown's avatar
unknown committed
7613
			 List<Create_field> &create,
7614
                         bool ignore,
7615
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
7616
			 ha_rows *copied,
unknown's avatar
unknown committed
7617
			 ha_rows *deleted,
7618 7619
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
unknown's avatar
unknown committed
7620 7621 7622 7623 7624
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
7625
  uint length= 0;
unknown's avatar
unknown committed
7626 7627 7628 7629 7630
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
7631
  ha_rows examined_rows;
7632
  bool auto_increment_field_copied= 0;
7633
  ulong save_sql_mode;
7634
  ulonglong prev_insert_id;
unknown's avatar
unknown committed
7635 7636
  DBUG_ENTER("copy_data_between_tables");

7637 7638 7639 7640 7641 7642
  /*
    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
  */
7643
  error= ha_enable_transaction(thd, FALSE);
7644 7645
  if (error)
    DBUG_RETURN(-1);
7646
  
7647
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
7648 7649
    DBUG_RETURN(-1);				/* purecov: inspected */

7650
  if (to->file->ha_external_lock(thd, F_WRLCK))
7651
    DBUG_RETURN(-1);
7652

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

7656 7657 7658 7659 7660
  /* 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));

7661
  from->file->info(HA_STATUS_VARIABLE);
7662
  to->file->ha_start_bulk_insert(from->file->stats.records);
unknown's avatar
unknown committed
7663

7664 7665
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
7666 7667
  List_iterator<Create_field> it(create);
  Create_field *def;
unknown's avatar
unknown committed
7668 7669 7670 7671 7672
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
7673 7674
    {
      if (*ptr == to->next_number_field)
7675
      {
7676
        auto_increment_field_copied= TRUE;
7677 7678 7679 7680 7681 7682 7683 7684 7685
        /*
          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
7686
      (copy_end++)->set(*ptr,def->field,0);
7687 7688
    }

unknown's avatar
unknown committed
7689 7690
  }

7691 7692
  found_count=delete_count=0;

unknown's avatar
unknown committed
7693 7694
  if (order)
  {
7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712
    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
7713

7714 7715 7716 7717 7718 7719 7720 7721 7722 7723
      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
7724 7725
  };

7726 7727
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
7728
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
7729
  if (ignore)
unknown's avatar
unknown committed
7730
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
Marc Alff's avatar
Marc Alff committed
7731
  thd->warning_info->reset_current_row_for_warning();
7732
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
7733 7734 7735 7736
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
7737
      thd->send_kill_message();
unknown's avatar
unknown committed
7738 7739 7740
      error= 1;
      break;
    }
7741 7742 7743 7744 7745 7746
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
7747 7748
    if (to->next_number_field)
    {
7749
      if (auto_increment_field_copied)
7750
        to->auto_increment_field_not_null= TRUE;
7751 7752 7753
      else
        to->next_number_field->reset();
    }
7754
    
unknown's avatar
unknown committed
7755
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
7756
    {
unknown's avatar
unknown committed
7757
      copy_ptr->do_copy(copy_ptr);
7758
    }
7759
    prev_insert_id= to->file->next_insert_id;
7760
    error=to->file->ha_write_row(to->record[0]);
7761 7762
    to->auto_increment_field_not_null= FALSE;
    if (error)
unknown's avatar
unknown committed
7763
    {
7764
      if (!ignore ||
7765
          to->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
7766
      {
7767
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
7768 7769 7770 7771
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
unknown's avatar
unknown committed
7772
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
7773
             if (key_nr == 0 &&
unknown's avatar
unknown committed
7774 7775
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
7776
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
unknown's avatar
unknown committed
7777
             to->file->print_keydup_error(key_nr, err_msg);
7778 7779 7780 7781
             break;
           }
         }

unknown's avatar
unknown committed
7782 7783 7784
	to->file->print_error(error,MYF(0));
	break;
      }
7785
      to->file->restore_auto_increment(prev_insert_id);
unknown's avatar
unknown committed
7786 7787 7788
      delete_count++;
    }
    else
7789
      found_count++;
Marc Alff's avatar
Marc Alff committed
7790
    thd->warning_info->inc_current_row_for_warning();
unknown's avatar
unknown committed
7791 7792
  }
  end_read_record(&info);
unknown's avatar
unknown committed
7793
  free_io_cache(from);
7794
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
7795

7796
  if (to->file->ha_end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
7797
  {
unknown's avatar
unknown committed
7798
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
7799 7800
    error=1;
  }
unknown's avatar
unknown committed
7801
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7802

7803 7804 7805 7806 7807 7808
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7809 7810 7811 7812
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
7813
  if (trans_commit_stmt(thd))
7814
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7815
  if (trans_commit_implicit(thd))
7816
    error=1;
7817

unknown's avatar
unknown committed
7818
 err:
7819
  thd->variables.sql_mode= save_sql_mode;
7820
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
7821
  free_io_cache(from);
unknown's avatar
unknown committed
7822 7823
  *copied= found_count;
  *deleted=delete_count;
7824
  to->file->ha_release_auto_increment();
7825
  if (to->file->ha_external_lock(thd,F_UNLCK))
7826
    error=1;
unknown's avatar
unknown committed
7827 7828
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7829

unknown's avatar
unknown committed
7830

7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7842
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7843 7844
{
  HA_CREATE_INFO create_info;
7845 7846 7847
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7848 7849 7850 7851 7852 7853
  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;
7854 7855

  bzero((char*) &create_info, sizeof(create_info));
unknown's avatar
unknown committed
7856
  create_info.row_type=ROW_TYPE_NOT_USED;
7857
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
7858
  /* Force alter table to recreate table */
7859
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7860
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7861 7862
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7863 7864 7865
}


7866 7867
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7868 7869 7870 7871 7872
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
7873
  DBUG_ENTER("mysql_checksum_table");
7874 7875 7876

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7877 7878
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7879
  item->maybe_null= 1;
7880
  if (protocol->send_result_set_metadata(&field_list,
7881
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
7882
    DBUG_RETURN(TRUE);
7883

7884
  /* Open one table after the other to keep lock time as short as possible. */
unknown's avatar
VIEW  
unknown committed
7885
  for (table= tables; table; table= table->next_local)
7886 7887
  {
    char table_name[NAME_LEN*2+2];
7888
    TABLE *t;
7889

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

Konstantin Osipov's avatar
Konstantin Osipov committed
7892
    t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
unknown's avatar
unknown committed
7893
    thd->clear_error();			// these errors shouldn't get client
7894 7895 7896 7897

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

7898
    if (!t)
7899
    {
unknown's avatar
unknown committed
7900
      /* Table didn't exist */
7901
      protocol->store_null();
unknown's avatar
unknown committed
7902
      thd->clear_error();
7903 7904 7905
    }
    else
    {
7906
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7907 7908
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7909
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
7910
	       (check_opt->flags & T_QUICK))
7911
	protocol->store_null();
7912 7913
      else
      {
7914 7915
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
7916
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7917

7918
        t->use_all_columns();
7919

unknown's avatar
unknown committed
7920
	if (t->file->ha_rnd_init(1))
7921 7922 7923
	  protocol->store_null();
	else
	{
7924
	  for (;;)
7925
	  {
7926 7927 7928 7929 7930 7931 7932 7933 7934 7935
            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;
            }
7936
	    ha_checksum row_crc= 0;
7937 7938 7939 7940 7941 7942 7943
            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
7944 7945 7946 7947
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
7948 7949 7950
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
7951 7952
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7953

7954
	    for (uint i= 0; i < t->s->fields; i++ )
7955 7956
	    {
	      Field *f= t->field[i];
7957 7958 7959 7960 7961 7962 7963 7964
        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))
7965 7966 7967
	      {
		String tmp;
		f->val_str(&tmp);
7968
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7969 7970
	      }
	      else
7971
		row_crc= my_checksum(row_crc, f->ptr,
unknown's avatar
unknown committed
7972
				     f->pack_length());
7973
	    }
7974

7975 7976 7977
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
7978
          t->file->ha_rnd_end();
7979
	}
7980
      }
unknown's avatar
unknown committed
7981
      thd->clear_error();
7982 7983 7984 7985 7986 7987 7988
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

7989
  my_eof(thd);
unknown's avatar
unknown committed
7990
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7991

7992 7993 7994 7995
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
7996
  DBUG_RETURN(TRUE);
7997
}
7998 7999

static bool check_engine(THD *thd, const char *table_name,
8000
                         HA_CREATE_INFO *create_info)
8001
{
8002
  handlerton **new_engine= &create_info->db_type;
unknown's avatar
unknown committed
8003
  handlerton *req_engine= *new_engine;
unknown's avatar
unknown committed
8004
  bool no_substitution=
8005
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
8006 8007
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
8008 8009
    return TRUE;

unknown's avatar
unknown committed
8010
  if (req_engine && req_engine != *new_engine)
8011
  {
unknown's avatar
unknown committed
8012
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
8013 8014
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
unknown's avatar
unknown committed
8015
                       ha_resolve_storage_engine_name(*new_engine),
8016 8017
                       table_name);
  }
8018 8019 8020 8021 8022
  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
8023
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
unknown's avatar
WL#2936  
unknown committed
8024
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
8025 8026 8027
      *new_engine= 0;
      return TRUE;
    }
8028
    *new_engine= myisam_hton;
8029
  }
8030 8031
  return FALSE;
}