sql_table.cc 255 KB
Newer Older
Marc Alff's avatar
Marc Alff committed
1
/* Copyright 2000-2008 MySQL AB, 2008-2009 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"
Konstantin Osipov's avatar
Konstantin Osipov committed
19
#include "debug_sync.h"
20
#include <hash.h>
unknown's avatar
unknown committed
21
#include <myisam.h>
22
#include <my_dir.h>
23
#include "sp_head.h"
Konstantin Osipov's avatar
Konstantin Osipov committed
24
#include "sp.h"
25
#include "sql_trigger.h"
26
#include "sql_show.h"
Konstantin Osipov's avatar
Konstantin Osipov committed
27
#include "transaction.h"
28
#include "keycaches.h"
unknown's avatar
unknown committed
29 30 31 32 33

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

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

unknown's avatar
unknown committed
36
const char *primary_key_name="PRIMARY";
unknown's avatar
unknown committed
37 38 39 40

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
41
                                    List<Create_field> &create, bool ignore,
42
				    uint order_num, ORDER *order,
43
				    ha_rows *copied,ha_rows *deleted,
44 45
                                    enum enum_enable_or_disable keys_onoff,
                                    bool error_if_not_empty);
46

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

61
#ifndef DBUG_OFF
unknown's avatar
unknown committed
62

63 64 65 66 67 68 69 70 71 72
/* 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
73

74

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

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

115 116 117
  quote = thd ? get_quote_char_for_identifier(thd, conv_name, res - 1) : '"';

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


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

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

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


356 357 358 359 360 361 362 363 364 365 366 367 368
/*
  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.
*/

369 370
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
371
  uint errors;
372
  size_t res;
373 374 375 376
  DBUG_ENTER("filename_to_tablename");
  DBUG_PRINT("enter", ("from '%s'", from));

  if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length))
377
  {
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
    /* 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.
      */
    }
395
  }
396 397 398

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
399 400 401
}


Ramil Kalimullin's avatar
Fix for  
Ramil Kalimullin committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
/**
  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;
}


426 427 428 429 430 431 432 433 434 435 436 437 438
/*
  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.
*/

439 440
uint tablename_to_filename(const char *from, char *to, uint to_length)
{
441
  uint errors, length;
442 443 444
  DBUG_ENTER("tablename_to_filename");
  DBUG_PRINT("enter", ("from '%s'", from));

Ramil Kalimullin's avatar
Fix for  
Ramil Kalimullin committed
445 446
  if ((length= check_n_cut_mysql50_prefix(from, to, to_length)))
    DBUG_RETURN(length);
447 448 449 450 451 452 453 454
  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;
  }
455 456
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
457 458 459
}


460
/*
461
  Creates path to a file: mysql_data_dir/db/table.ext
462 463

  SYNOPSIS
464
   build_table_filename()
465
     buff                       Where to write result in my_charset_filename.
466
                                This may be the same as table_name.
467 468 469 470 471 472
     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.
473 474 475 476 477 478

  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".
479 480
    Unless flags indicate a temporary table name.
    'db' is always converted.
481
    'ext' is not converted.
482

483 484 485 486 487
    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.
488

489 490
  RETURN
    path length
491 492 493
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
494
                          const char *table_name, const char *ext, uint flags)
495 496 497
{
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
498
  DBUG_ENTER("build_table_filename");
499 500
  DBUG_PRINT("enter", ("db: '%s'  table_name: '%s'  ext: '%s'  flags: %x",
                       db, table_name, ext, flags));
501 502 503 504

  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
505
    (void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff));
506

Konstantin Osipov's avatar
Konstantin Osipov committed
507
  (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff));
508 509 510 511

  char *end = buff + bufflen;
  /* Don't add FN_ROOTDIR if mysql_data_home already includes it */
  char *pos = strnmov(buff, mysql_data_home, bufflen);
512
  size_t rootdir_len= strlen(FN_ROOTDIR);
513 514 515
  if (pos - rootdir_len >= buff &&
      memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0)
    pos= strnmov(pos, FN_ROOTDIR, end - pos);
516 517 518 519 520 521
  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);
522

523
  DBUG_PRINT("exit", ("buff: '%s'", buff));
524
  DBUG_RETURN(pos - buff);
525 526 527
}


528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
/*
  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
*/

546
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
547
{
548 549
  DBUG_ENTER("build_tmptable_filename");

550 551 552 553
  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);
554

555 556 557 558 559
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }
560

561
  size_t length= unpack_filename(buff, buff);
562 563
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
564 565
}

unknown's avatar
unknown committed
566 567 568
/*
--------------------------------------------------------------------------

569
   MODULE: DDL log
unknown's avatar
unknown committed
570 571 572 573 574 575 576 577
   -----------------

   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
578
   ddl log while we are executing. These entries are dropped when the
unknown's avatar
unknown committed
579 580 581 582
   operation is completed.

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

583
   There is only one ddl log in the system and it is protected by a mutex
unknown's avatar
unknown committed
584 585 586
   and there is a global struct that contains information about its current
   state.

587 588
   History:
   First version written in 2006 by Mikael Ronstrom
unknown's avatar
unknown committed
589 590 591 592
--------------------------------------------------------------------------
*/


593
struct st_global_ddl_log
unknown's avatar
unknown committed
594
{
595 596 597 598 599 600
  /*
    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.
  */
601
  char file_entry_buf[4*IO_SIZE];
unknown's avatar
unknown committed
602 603
  char file_name_str[FN_REFLEN];
  char *file_name;
604 605 606
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
unknown's avatar
unknown committed
607 608
  File file_id;
  uint name_len;
609
  uint io_size;
610
  bool inited;
611
  bool do_release;
612
  bool recovery_phase;
613 614
  st_global_ddl_log() : inited(false), do_release(false) {}
};
unknown's avatar
unknown committed
615

616
st_global_ddl_log global_ddl_log;
unknown's avatar
unknown committed
617

Marc Alff's avatar
Marc Alff committed
618
mysql_mutex_t LOCK_gdl;
unknown's avatar
unknown committed
619

620 621 622 623 624
#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
625

626 627
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
628
#define DDL_LOG_IO_SIZE_POS 8
unknown's avatar
unknown committed
629

unknown's avatar
unknown committed
630
/*
631
  Read one entry from ddl log file
unknown's avatar
unknown committed
632
  SYNOPSIS
633
    read_ddl_log_file_entry()
634
    entry_no                     Entry number to read
unknown's avatar
unknown committed
635
  RETURN VALUES
636 637
    TRUE                         Error
    FALSE                        Success
unknown's avatar
unknown committed
638 639
*/

640
static bool read_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
641 642
{
  bool error= FALSE;
643
  File file_id= global_ddl_log.file_id;
644
  uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
645 646
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
unknown's avatar
unknown committed
647

Marc Alff's avatar
Marc Alff committed
648 649
  if (mysql_file_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
                       MYF(MY_WME)) != io_size)
unknown's avatar
unknown committed
650 651 652 653 654 655
    error= TRUE;
  DBUG_RETURN(error);
}


/*
656
  Write one entry from ddl log file
unknown's avatar
unknown committed
657
  SYNOPSIS
658
    write_ddl_log_file_entry()
659
    entry_no                     Entry number to write
unknown's avatar
unknown committed
660 661 662 663 664
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

665
static bool write_ddl_log_file_entry(uint entry_no)
unknown's avatar
unknown committed
666 667
{
  bool error= FALSE;
668 669 670
  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
671

Marc Alff's avatar
Marc Alff committed
672 673
  if (mysql_file_pwrite(file_id, (uchar*)file_entry_buf,
                        IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
unknown's avatar
unknown committed
674 675 676 677 678 679
    error= TRUE;
  DBUG_RETURN(error);
}


/*
680
  Write ddl log header
unknown's avatar
unknown committed
681
  SYNOPSIS
682
    write_ddl_log_header()
unknown's avatar
unknown committed
683 684 685 686 687
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

688
static bool write_ddl_log_header()
unknown's avatar
unknown committed
689 690
{
  uint16 const_var;
691
  bool error= FALSE;
692
  DBUG_ENTER("write_ddl_log_header");
unknown's avatar
unknown committed
693

694 695
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
696
  const_var= FN_LEN;
697
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
unknown's avatar
unknown committed
698
            (ulong) const_var);
699
  const_var= IO_SIZE;
700
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
unknown's avatar
unknown committed
701
            (ulong) const_var);
702
  if (write_ddl_log_file_entry(0UL))
703 704 705 706
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
707
  (void) sync_ddl_log();
708
  DBUG_RETURN(error);
unknown's avatar
unknown committed
709 710 711
}


712
/*
713
  Create ddl log file name
714
  SYNOPSIS
715
    create_ddl_log_file_name()
716 717 718 719 720
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

721
static inline void create_ddl_log_file_name(char *file_name)
722
{
723
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
724 725 726
}


unknown's avatar
unknown committed
727
/*
728
  Read header of ddl log file
unknown's avatar
unknown committed
729
  SYNOPSIS
730
    read_ddl_log_header()
unknown's avatar
unknown committed
731
  RETURN VALUES
732 733
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
unknown's avatar
unknown committed
734
  DESCRIPTION
735 736 737
    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
738 739
*/

740
static uint read_ddl_log_header()
unknown's avatar
unknown committed
741
{
742
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
743
  char file_name[FN_REFLEN];
744
  uint entry_no;
745
  bool successful_open= FALSE;
746
  DBUG_ENTER("read_ddl_log_header");
unknown's avatar
unknown committed
747

748
  create_ddl_log_file_name(file_name);
Marc Alff's avatar
Marc Alff committed
749 750 751
  if ((global_ddl_log.file_id= mysql_file_open(key_file_global_ddl_log,
                                               file_name,
                                               O_RDWR | O_BINARY, MYF(0))) >= 0)
unknown's avatar
unknown committed
752
  {
753
    if (read_ddl_log_file_entry(0UL))
754
    {
755 756
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
757
    }
758 759
    else
      successful_open= TRUE;
unknown's avatar
unknown committed
760
  }
761 762
  if (successful_open)
  {
763 764
    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]);
765
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
766 767 768
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
769
  else
770 771 772
  {
    entry_no= 0;
  }
773 774 775
  global_ddl_log.first_free= NULL;
  global_ddl_log.first_used= NULL;
  global_ddl_log.num_entries= 0;
Marc Alff's avatar
Marc Alff committed
776
  mysql_mutex_init(key_LOCK_gdl, &LOCK_gdl, MY_MUTEX_INIT_FAST);
777
  global_ddl_log.do_release= true;
unknown's avatar
unknown committed
778
  DBUG_RETURN(entry_no);
unknown's avatar
unknown committed
779 780 781 782
}


/*
783
  Read a ddl log entry
unknown's avatar
unknown committed
784
  SYNOPSIS
785
    read_ddl_log_entry()
unknown's avatar
unknown committed
786 787 788 789 790 791
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
792
    Read a specified entry in the ddl log
unknown's avatar
unknown committed
793 794
*/

795
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
796
{
797
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
798
  uint inx;
799
  uchar single_char;
800
  DBUG_ENTER("read_ddl_log_entry");
801

802
  if (read_ddl_log_file_entry(read_entry))
803 804 805
  {
    DBUG_RETURN(TRUE);
  }
806
  ddl_log_entry->entry_pos= read_entry;
807 808 809 810
  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;
811 812 813 814 815 816 817
  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
818 819 820 821 822
  DBUG_RETURN(FALSE);
}


/*
823
  Initialise ddl log
unknown's avatar
unknown committed
824
  SYNOPSIS
825
    init_ddl_log()
unknown's avatar
unknown committed
826

unknown's avatar
unknown committed
827
  DESCRIPTION
828
    Write the header of the ddl log file and length of names. Also set
unknown's avatar
unknown committed
829
    number of entries to zero.
unknown's avatar
unknown committed
830 831 832 833

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
unknown's avatar
unknown committed
834 835
*/

836
static bool init_ddl_log()
unknown's avatar
unknown committed
837
{
838
  char file_name[FN_REFLEN];
839
  DBUG_ENTER("init_ddl_log");
unknown's avatar
unknown committed
840

841
  if (global_ddl_log.inited)
unknown's avatar
unknown committed
842 843
    goto end;

844
  global_ddl_log.io_size= IO_SIZE;
845
  global_ddl_log.name_len= FN_LEN;
846
  create_ddl_log_file_name(file_name);
Marc Alff's avatar
Marc Alff committed
847 848 849 850
  if ((global_ddl_log.file_id= mysql_file_create(key_file_global_ddl_log,
                                                 file_name, CREATE_MODE,
                                                 O_RDWR | O_TRUNC | O_BINARY,
                                                 MYF(MY_WME))) < 0)
851
  {
852
    /* Couldn't create ddl log file, this is serious error */
853 854
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
855
  }
unknown's avatar
Fixes  
unknown committed
856
  global_ddl_log.inited= TRUE;
857
  if (write_ddl_log_header())
858
  {
Marc Alff's avatar
Marc Alff committed
859
    (void) mysql_file_close(global_ddl_log.file_id, MYF(MY_WME));
unknown's avatar
Fixes  
unknown committed
860
    global_ddl_log.inited= FALSE;
861
    DBUG_RETURN(TRUE);
862
  }
unknown's avatar
unknown committed
863 864

end:
865
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
866 867 868 869
}


/*
870
  Execute one action in a ddl log entry
unknown's avatar
unknown committed
871
  SYNOPSIS
872 873
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
unknown's avatar
unknown committed
874
  RETURN VALUES
875 876
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
877 878
*/

879
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
unknown's avatar
unknown committed
880
{
881 882
  bool frm_action= FALSE;
  LEX_STRING handler_name;
883
  handler *file= NULL;
884
  MEM_ROOT mem_root;
885
  int error= TRUE;
886
  char to_path[FN_REFLEN];
887
  char from_path[FN_REFLEN];
888
#ifdef WITH_PARTITION_STORAGE_ENGINE
889
  char *par_ext= (char*)".par";
890
#endif
891
  handlerton *hton;
892
  DBUG_ENTER("execute_ddl_log_action");
893

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


unknown's avatar
unknown committed
1006
/*
1007
  Get a free entry in the ddl log
unknown's avatar
unknown committed
1008
  SYNOPSIS
1009 1010
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
unknown's avatar
unknown committed
1011
  RETURN VALUES
1012 1013
    TRUE                       Error
    FALSE                      Success
unknown's avatar
unknown committed
1014 1015
*/

1016 1017
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
unknown's avatar
unknown committed
1018
{
1019 1020 1021
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
1022

1023
  if (global_ddl_log.first_free == NULL)
1024
  {
1025 1026
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
1027
    {
1028
      sql_print_error("Failed to allocate memory for ddl log free list");
1029 1030
      DBUG_RETURN(TRUE);
    }
1031
    global_ddl_log.num_entries++;
1032
    used_entry->entry_pos= global_ddl_log.num_entries;
1033
    *write_header= TRUE;
1034 1035 1036
  }
  else
  {
1037 1038
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
1039
    *write_header= FALSE;
1040 1041 1042 1043 1044 1045
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
1046
  global_ddl_log.first_used= used_entry;
1047 1048 1049 1050
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
1051
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
1052 1053 1054 1055
}


/*
1056
  External interface methods for the DDL log Module
1057 1058 1059 1060
  ---------------------------------------------------
*/

/*
unknown's avatar
unknown committed
1061
  SYNOPSIS
1062 1063 1064
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
1065

unknown's avatar
unknown committed
1066
  RETURN VALUES
1067 1068 1069 1070
    TRUE                      Error
    FALSE                     Success

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

1075 1076
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
1077
{
1078
  bool error, write_header;
1079 1080 1081 1082 1083 1084
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
1085 1086
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
1087
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
1088
                                    (char)ddl_log_entry->action_type;
1089 1090 1091 1092
  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);
1093 1094
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
1095 1096
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
unknown's avatar
unknown committed
1097
  {
1098
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
1099 1100
    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
1101
  }
1102
  else
1103 1104
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
1105 1106
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
1107
  if (get_free_ddl_log_entry(active_entry, &write_header))
1108 1109 1110 1111
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
1112 1113 1114 1115 1116 1117 1118 1119 1120
  DBUG_PRINT("ddl_log",
             ("write type %c next %u name '%s' from_name '%s' handler '%s'",
             (char) global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS],
             ddl_log_entry->next_entry,
             (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
             (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS
                                                    + FN_LEN],
             (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS
                                                    + (2*FN_LEN)]));
1121
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
1122
  {
1123
    error= TRUE;
1124 1125 1126
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
1127 1128
  if (write_header && !error)
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
1129
    (void) sync_ddl_log();
1130
    if (write_ddl_log_header())
1131 1132
      error= TRUE;
  }
1133
  if (error)
1134
    release_ddl_log_memory_entry(*active_entry);
1135
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1136 1137 1138 1139
}


/*
1140
  Write final entry in the ddl log
unknown's avatar
unknown committed
1141
  SYNOPSIS
1142
    write_execute_ddl_log_entry()
1143 1144 1145 1146
    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.
1147 1148 1149
    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
1150 1151 1152
                                   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
1153
  RETURN VALUES
1154 1155 1156 1157
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
1158
    This is the last write in the ddl log. The previous log entries have
1159
    already been written but not yet synched to disk.
1160 1161 1162 1163
    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 
1164
*/ 
unknown's avatar
unknown committed
1165

1166 1167 1168
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
unknown's avatar
unknown committed
1169
{
1170
  bool write_header= FALSE;
1171 1172
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
1173

1174 1175 1176 1177
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
1178 1179
  if (!complete)
  {
1180 1181 1182 1183 1184 1185
    /*
      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
1186
    (void) sync_ddl_log();
1187
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
1188 1189
  }
  else
1190
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
1191 1192 1193 1194 1195 1196
  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;
1197
  if (!(*active_entry))
1198
  {
1199
    if (get_free_ddl_log_entry(active_entry, &write_header))
1200 1201 1202
    {
      DBUG_RETURN(TRUE);
    }
1203
  }
1204
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
1205
  {
1206
    sql_print_error("Error writing execute entry in ddl log");
1207
    release_ddl_log_memory_entry(*active_entry);
1208 1209
    DBUG_RETURN(TRUE);
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
1210
  (void) sync_ddl_log();
1211 1212
  if (write_header)
  {
1213
    if (write_ddl_log_header())
1214
    {
1215
      release_ddl_log_memory_entry(*active_entry);
1216 1217 1218
      DBUG_RETURN(TRUE);
    }
  }
unknown's avatar
unknown committed
1219 1220 1221 1222
  DBUG_RETURN(FALSE);
}


1223
/*
1224
  For complex rename operations we need to deactivate individual entries.
1225
  SYNOPSIS
1226
    deactivate_ddl_log_entry()
1227 1228 1229 1230 1231 1232 1233 1234
    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
1235
    do in a safe manner unless the ddl log is informed of the phases in
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
    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.
*/

1247
bool deactivate_ddl_log_entry(uint entry_no)
1248
{
1249 1250
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
1251

1252
  if (!read_ddl_log_file_entry(entry_no))
1253
  {
1254
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
1255
    {
1256 1257 1258 1259 1260 1261
      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)
1262
      {
1263 1264
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
1265 1266 1267 1268 1269
      }
      else
      {
        DBUG_ASSERT(0);
      }
1270 1271 1272 1273 1274 1275
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
1276 1277
    }
  }
1278 1279 1280 1281 1282 1283
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
1284 1285 1286 1287
}


/*
1288
  Sync ddl log file
1289
  SYNOPSIS
1290
    sync_ddl_log()
1291 1292 1293 1294 1295
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

1296
bool sync_ddl_log()
1297 1298
{
  bool error= FALSE;
1299
  DBUG_ENTER("sync_ddl_log");
1300

1301 1302
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
1303 1304 1305
  {
    DBUG_RETURN(TRUE);
  }
Marc Alff's avatar
Marc Alff committed
1306
  if (mysql_file_sync(global_ddl_log.file_id, MYF(0)))
1307 1308
  {
    /* Write to error log */
1309
    sql_print_error("Failed to sync ddl log");
1310 1311 1312 1313 1314 1315
    error= TRUE;
  }
  DBUG_RETURN(error);
}


1316 1317 1318
/*
  Release a log memory entry
  SYNOPSIS
1319
    release_ddl_log_memory_entry()
1320 1321 1322 1323 1324
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

1325
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
1326
{
1327 1328 1329 1330
  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");
1331

1332
  global_ddl_log.first_free= log_entry;
1333 1334 1335 1336 1337
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1338
    global_ddl_log.first_used= next_log_entry;
1339 1340
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1341
  DBUG_VOID_RETURN;
1342 1343 1344
}


unknown's avatar
unknown committed
1345
/*
1346
  Execute one entry in the ddl log. Executing an entry means executing
unknown's avatar
unknown committed
1347 1348
  a linked list of actions.
  SYNOPSIS
1349
    execute_ddl_log_entry()
unknown's avatar
unknown committed
1350 1351 1352 1353 1354 1355
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1356
bool execute_ddl_log_entry(THD *thd, uint first_entry)
unknown's avatar
unknown committed
1357
{
1358
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
unknown committed
1359
  uint read_entry= first_entry;
1360
  DBUG_ENTER("execute_ddl_log_entry");
unknown's avatar
unknown committed
1361

Marc Alff's avatar
Marc Alff committed
1362
  mysql_mutex_lock(&LOCK_gdl);
unknown's avatar
unknown committed
1363 1364
  do
  {
1365
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1366 1367
    {
      /* Write to error log and continue with next log entry */
1368 1369
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1370 1371
      break;
    }
1372 1373
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1374

1375
    if (execute_ddl_log_action(thd, &ddl_log_entry))
unknown's avatar
unknown committed
1376
    {
1377
      /* Write to error log and continue with next log entry */
1378 1379
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1380
      break;
unknown's avatar
unknown committed
1381
    }
1382
    read_entry= ddl_log_entry.next_entry;
unknown's avatar
unknown committed
1383
  } while (read_entry);
Marc Alff's avatar
Marc Alff committed
1384
  mysql_mutex_unlock(&LOCK_gdl);
unknown's avatar
unknown committed
1385 1386 1387
  DBUG_RETURN(FALSE);
}

1388

1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
/*
  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)
  {
Marc Alff's avatar
Marc Alff committed
1402
    (void) mysql_file_close(global_ddl_log.file_id, MYF(MY_WME));
1403 1404 1405 1406 1407 1408
    global_ddl_log.file_id= (File) -1;
  }
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
1409
/*
1410
  Execute the ddl log at recovery of MySQL Server
unknown's avatar
unknown committed
1411
  SYNOPSIS
1412
    execute_ddl_log_recovery()
unknown's avatar
unknown committed
1413 1414 1415 1416
  RETURN VALUES
    NONE
*/

1417
void execute_ddl_log_recovery()
unknown's avatar
unknown committed
1418
{
1419
  uint num_entries, i;
unknown's avatar
Fixes  
unknown committed
1420
  THD *thd;
1421
  DDL_LOG_ENTRY ddl_log_entry;
unknown's avatar
Fixes  
unknown committed
1422
  char file_name[FN_REFLEN];
1423
  DBUG_ENTER("execute_ddl_log_recovery");
unknown's avatar
unknown committed
1424

1425 1426 1427 1428 1429 1430 1431
  /*
    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
1432
  global_ddl_log.file_id= (File) -1;
1433

1434 1435 1436 1437 1438 1439 1440 1441
  /*
    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();

1442
  num_entries= read_ddl_log_header();
1443
  for (i= 1; i < num_entries + 1; i++)
unknown's avatar
unknown committed
1444
  {
1445
    if (read_ddl_log_entry(i, &ddl_log_entry))
1446
    {
1447 1448 1449
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1450
    }
1451
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
unknown's avatar
unknown committed
1452
    {
unknown's avatar
Fixes  
unknown committed
1453
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
unknown's avatar
unknown committed
1454
      {
1455 1456
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
unknown's avatar
unknown committed
1457 1458 1459
      }
    }
  }
1460
  close_ddl_log();
1461
  create_ddl_log_file_name(file_name);
Marc Alff's avatar
Marc Alff committed
1462
  (void) mysql_file_delete(key_file_global_ddl_log, file_name, MYF(0));
1463 1464 1465 1466
  global_ddl_log.recovery_phase= FALSE;
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
1467
  DBUG_VOID_RETURN;
1468 1469 1470 1471
}


/*
1472
  Release all memory allocated to the ddl log
1473
  SYNOPSIS
1474
    release_ddl_log()
1475 1476 1477 1478
  RETURN VALUES
    NONE
*/

1479
void release_ddl_log()
1480
{
1481 1482 1483
  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");
1484

1485 1486 1487
  if (!global_ddl_log.do_release)
    DBUG_VOID_RETURN;

Marc Alff's avatar
Marc Alff committed
1488
  mysql_mutex_lock(&LOCK_gdl);
1489 1490
  while (used_list)
  {
1491
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1492
    my_free(used_list, MYF(0));
unknown's avatar
unknown committed
1493
    used_list= tmp;
1494 1495 1496
  }
  while (free_list)
  {
1497
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1498
    my_free(free_list, MYF(0));
unknown's avatar
unknown committed
1499
    free_list= tmp;
1500
  }
1501
  close_ddl_log();
unknown's avatar
unknown committed
1502
  global_ddl_log.inited= 0;
Marc Alff's avatar
Marc Alff committed
1503 1504
  mysql_mutex_unlock(&LOCK_gdl);
  mysql_mutex_destroy(&LOCK_gdl);
1505
  global_ddl_log.do_release= false;
1506
  DBUG_VOID_RETURN;
1507 1508 1509
}


unknown's avatar
unknown committed
1510 1511 1512
/*
---------------------------------------------------------------------------

1513
  END MODULE DDL log
unknown's avatar
unknown committed
1514 1515 1516 1517 1518
  --------------------

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

unknown's avatar
unknown committed
1519

1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544
/**
   @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
1545 1546 1547 1548 1549 1550 1551 1552
/*
  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
1553 1554 1555 1556 1557 1558
      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
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580
      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];
1581 1582
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
unknown's avatar
unknown committed
1583
  char frm_name[FN_REFLEN+1];
1584 1585 1586 1587
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char *part_syntax_buf;
  uint syntax_len;
#endif
unknown's avatar
unknown committed
1588 1589
  DBUG_ENTER("mysql_write_frm");

1590 1591 1592
  /*
    Build shadow frm file name
  */
1593
  build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
1594
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1595
  if (flags & WFRM_WRITE_SHADOW)
unknown's avatar
unknown committed
1596
  {
1597 1598 1599 1600 1601 1602 1603 1604
    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
1605 1606 1607 1608 1609
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1610 1611
      partition_info *part_info= lpt->table->part_info;
      if (part_info)
unknown's avatar
unknown committed
1612
      {
1613 1614
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1615 1616 1617
                                                         TRUE, TRUE,
                                                         lpt->create_info,
                                                         lpt->alter_info)))
1618 1619 1620 1621
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
1622
        part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
1623 1624 1625
      }
    }
#endif
1626 1627 1628 1629
    /* 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,
1630
                          lpt->alter_info->create_list, lpt->key_count,
1631
                          lpt->key_info_buffer, lpt->table->file)) ||
1632 1633 1634
        lpt->table->file->ha_create_handler_files(shadow_path, NULL,
                                                  CHF_CREATE_FLAG,
                                                  lpt->create_info))
1635
    {
Marc Alff's avatar
Marc Alff committed
1636
      mysql_file_delete(key_file_frm, shadow_frm_name, MYF(0));
1637 1638 1639
      error= 1;
      goto end;
    }
unknown's avatar
unknown committed
1640 1641 1642 1643 1644 1645 1646 1647 1648
  }
  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.
    */
1649 1650
    uchar *data;
    size_t length;
1651
    if (readfrm(shadow_path, &data, &length) ||
unknown's avatar
unknown committed
1652 1653
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
1654 1655
      my_free(data, MYF(MY_ALLOW_ZERO_PTR));
      my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1656 1657 1658 1659
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
Marc Alff's avatar
Marc Alff committed
1660
    error= mysql_file_delete(key_file_frm, shadow_frm_name, MYF(MY_WME));
unknown's avatar
unknown committed
1661
  }
1662 1663
  if (flags & WFRM_INSTALL_SHADOW)
  {
1664 1665 1666
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1667 1668 1669
    /*
      Build frm file name
    */
1670
    build_table_filename(path, sizeof(path) - 1, lpt->db,
1671
                         lpt->table_name, "", 0);
1672 1673 1674 1675
    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.
1676 1677 1678 1679 1680 1681
      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.
1682
    */
Marc Alff's avatar
Marc Alff committed
1683
    mysql_mutex_lock(&LOCK_open);
Marc Alff's avatar
Marc Alff committed
1684
    if (mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) ||
1685
#ifdef WITH_PARTITION_STORAGE_ENGINE
1686 1687
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_DELETE_FLAG, NULL) ||
1688 1689
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1690
#endif
1691
#ifdef WITH_PARTITION_STORAGE_ENGINE
Marc Alff's avatar
Marc Alff committed
1692 1693
        mysql_file_rename(key_file_frm,
                          shadow_frm_name, frm_name, MYF(MY_WME)) ||
1694 1695
        lpt->table->file->ha_create_handler_files(path, shadow_path,
                                                  CHF_RENAME_FLAG, NULL))
1696
#else
Marc Alff's avatar
Marc Alff committed
1697 1698
        mysql_file_rename(key_file_frm,
                          shadow_frm_name, frm_name, MYF(MY_WME)))
1699
#endif
1700 1701
    {
      error= 1;
1702
      goto err;
1703
    }
1704
#ifdef WITH_PARTITION_STORAGE_ENGINE
1705
    if (part_info && (flags & WFRM_KEEP_SHARE))
1706 1707 1708 1709 1710
    {
      TABLE_SHARE *share= lpt->table->s;
      char *tmp_part_syntax_str;
      if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                       &syntax_len,
1711 1712 1713
                                                       TRUE, TRUE,
                                                       lpt->create_info,
                                                       lpt->alter_info)))
1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733
      {
        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;
1734
    }
1735 1736 1737
#endif

err:
Marc Alff's avatar
Marc Alff committed
1738
    mysql_mutex_unlock(&LOCK_open);
1739
#ifdef WITH_PARTITION_STORAGE_ENGINE
1740
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1741
    part_info->frm_log_entry= NULL;
Konstantin Osipov's avatar
Konstantin Osipov committed
1742
    (void) sync_ddl_log();
1743
#endif
unknown's avatar
unknown committed
1744
  }
1745

unknown's avatar
unknown committed
1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766
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
*/

1767
int write_bin_log(THD *thd, bool clear_error,
1768
                  char const *query, ulong query_length, bool is_trans)
unknown's avatar
unknown committed
1769
{
1770
  int error= 0;
unknown's avatar
unknown committed
1771 1772
  if (mysql_bin_log.is_open())
  {
1773
    int errcode= 0;
unknown's avatar
unknown committed
1774 1775
    if (clear_error)
      thd->clear_error();
1776 1777
    else
      errcode= query_error_code(thd, TRUE);
1778
    error= thd->binlog_query(THD::STMT_QUERY_TYPE,
1779 1780
                             query, query_length, is_trans, FALSE, FALSE,
                             errcode);
unknown's avatar
unknown committed
1781
  }
1782
  return error;
unknown's avatar
unknown committed
1783 1784
}

1785

unknown's avatar
unknown committed
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800
/*
 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
1801 1802
    Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set, but
    not if under LOCK TABLES.
unknown's avatar
unknown committed
1803 1804

  RETURN
unknown's avatar
unknown committed
1805 1806
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
1807 1808

*/
unknown's avatar
unknown committed
1809

unknown's avatar
unknown committed
1810 1811
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
1812
{
1813
  bool error;
1814
  Drop_table_error_handler err_handler(thd->get_internal_handler());
1815

unknown's avatar
unknown committed
1816 1817 1818 1819
  DBUG_ENTER("mysql_rm_table");

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

1820
  if (!drop_temporary)
unknown's avatar
unknown committed
1821
  {
1822 1823
    if (!thd->locked_tables_mode &&
       thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
1824
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1825
  }
1826

1827
  thd->push_internal_handler(&err_handler);
unknown's avatar
VIEW  
unknown committed
1828
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1829 1830
  thd->pop_internal_handler();

1831 1832
  if (thd->global_read_lock.has_protection())
    thd->global_read_lock.start_waiting_global_read_lock(thd);
1833

1834
  if (error)
unknown's avatar
unknown committed
1835
    DBUG_RETURN(TRUE);
1836
  my_ok(thd);
unknown's avatar
unknown committed
1837
  DBUG_RETURN(FALSE);
1838 1839
}

1840
/*
1841 1842 1843 1844 1845 1846 1847 1848 1849
  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
1850
    drop_view		Allow to delete VIEW .frm
1851 1852
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1853

1854 1855 1856 1857 1858 1859 1860 1861 1862
  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.
1863 1864 1865 1866 1867

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1868
*/
1869 1870

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
unknown's avatar
VIEW  
unknown committed
1871 1872
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1873 1874
{
  TABLE_LIST *table;
1875
  char path[FN_REFLEN + 1], *alias;
1876
  uint path_length;
1877
  String wrong_tables;
1878
  int error= 0;
1879
  int non_temp_tables_count= 0;
unknown's avatar
unknown committed
1880
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1881
  String built_query;
1882
  String built_tmp_query;
1883 1884
  DBUG_ENTER("mysql_rm_table_part2");

unknown's avatar
unknown committed
1885 1886 1887
  LINT_INIT(alias);
  LINT_INIT(path_length);

1888
  if (thd->is_current_stmt_binlog_format_row() && !dont_log_query)
1889 1890 1891 1892 1893 1894 1895
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1896

1897
  mysql_ha_rm_tables(thd, tables);
1898

unknown's avatar
unknown committed
1899 1900 1901 1902 1903
  /*
    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.
  */
1904
  mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1905 1906 1907
  for (table= tables; table; table= table->next_local)
  {
    TABLE_SHARE *share;
unknown's avatar
unknown committed
1908
    table->db_type= NULL;
unknown's avatar
unknown committed
1909
    if ((share= get_cached_table_share(table->db, table->table_name)))
unknown's avatar
WL#2936  
unknown committed
1910
      table->db_type= share->db_type();
1911 1912

    /* Disable drop of enabled log tables */
Marc Alff's avatar
Marc Alff committed
1913
    if (share && (share->table_category == TABLE_CATEGORY_LOG) &&
1914 1915
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1916
    {
Marc Alff's avatar
Marc Alff committed
1917
      mysql_mutex_unlock(&LOCK_open);
1918
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1919 1920
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
1921
  }
1922
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1923

1924
  if (!drop_temporary)
1925
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
1926
    if (!thd->locked_tables_mode)
1927 1928 1929
    {
      if (lock_table_names(thd, tables))
        DBUG_RETURN(1);
1930
      mysql_mutex_lock(&LOCK_open);
1931
      for (table= tables; table; table= table->next_local)
Konstantin Osipov's avatar
Konstantin Osipov committed
1932
        tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
1933
      mysql_mutex_unlock(&LOCK_open);
1934
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
1935
    else
1936 1937
    {
      for (table= tables; table; table= table->next_local)
Konstantin Osipov's avatar
Konstantin Osipov committed
1938 1939 1940
        if (find_temporary_table(thd, table->db, table->table_name))
        {
          /*
Konstantin Osipov's avatar
Konstantin Osipov committed
1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951
            A temporary table.

            Don't try to find a corresponding MDL lock or assign it
            to table->mdl_request.ticket. There can't be metadata
            locks for temporary tables: they are local to the session.

            Later in this function we release the MDL lock only if
            table->mdl_requeset.ticket is not NULL. Thus here we
            ensure that we won't release the metadata lock on the base
            table locked with LOCK TABLES as a side effect of temporary
            table drop.
Konstantin Osipov's avatar
Konstantin Osipov committed
1952
          */
Konstantin Osipov's avatar
Konstantin Osipov committed
1953
          DBUG_ASSERT(table->mdl_request.ticket == NULL);
Konstantin Osipov's avatar
Konstantin Osipov committed
1954 1955 1956 1957
        }
        else
        {
          /*
Konstantin Osipov's avatar
Konstantin Osipov committed
1958 1959
            Not a temporary table.

Konstantin Osipov's avatar
Konstantin Osipov committed
1960 1961 1962 1963
            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.
          */
1964 1965
          table->table= find_table_for_mdl_upgrade(thd->open_tables, table->db,
                                                   table->table_name, FALSE);
Konstantin Osipov's avatar
Konstantin Osipov committed
1966 1967
          if (!table->table)
            DBUG_RETURN(1);
Konstantin Osipov's avatar
Konstantin Osipov committed
1968
          table->mdl_request.ticket= table->table->mdl_ticket;
Konstantin Osipov's avatar
Konstantin Osipov committed
1969
        }
1970
    }
1971
  }
1972

unknown's avatar
VIEW  
unknown committed
1973
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
1974
  {
1975
    char *db=table->db;
unknown's avatar
unknown committed
1976 1977
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1978

1979 1980 1981
    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));
1982 1983 1984 1985 1986 1987 1988

    error= drop_temporary_table(thd, table);

    switch (error) {
    case  0:
      // removed temporary table
      tmp_table_deleted= 1;
1989
      if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
1990
          thd->is_current_stmt_binlog_format_row())
1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
      {
        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("`,");
      }

2008 2009
      continue;
    case -1:
2010
      DBUG_ASSERT(thd->in_sub_stmt);
2011
      error= 1;
Konstantin Osipov's avatar
Konstantin Osipov committed
2012
      goto err;
2013 2014 2015
    default:
      // temporary table not found
      error= 0;
unknown's avatar
unknown committed
2016
    }
unknown's avatar
unknown committed
2017

Konstantin Osipov's avatar
Konstantin Osipov committed
2018
    /* Probably a non-temporary table. */
2019 2020
    if (!drop_temporary)
      non_temp_tables_count++;
Konstantin Osipov's avatar
Konstantin Osipov committed
2021

2022 2023 2024 2025 2026 2027
    /*
      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.
      */
2028
    if (!drop_temporary && thd->is_current_stmt_binlog_format_row() && !dont_log_query)
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044
    {
      /*
        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
2045
    table_type= table->db_type;
2046
    if (!drop_temporary)
unknown's avatar
unknown committed
2047
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
2048
      if (thd->locked_tables_mode)
2049
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
2050
        if (wait_while_table_is_used(thd, table->table, HA_EXTRA_FORCE_REOPEN))
2051 2052
        {
          error= -1;
Konstantin Osipov's avatar
Konstantin Osipov committed
2053
          goto err;
2054
        }
Konstantin Osipov's avatar
Konstantin Osipov committed
2055
        close_all_tables_for_name(thd, table->table->s, TRUE);
Konstantin Osipov's avatar
Konstantin Osipov committed
2056
        table->table= 0;
2057
      }
unknown's avatar
unknown committed
2058

2059
      if (thd->killed)
2060
      {
2061
        error= -1;
Konstantin Osipov's avatar
Konstantin Osipov committed
2062
        goto err;
2063
      }
2064
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
unknown's avatar
unknown committed
2065
      /* remove .frm file and engine files */
2066 2067
      path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
                                        reg_ext,
2068 2069
                                        table->internal_tmp_table ?
                                        FN_IS_TMP : 0);
unknown's avatar
unknown committed
2070
    }
2071 2072 2073 2074
    /*
      TODO: Investigate what should be done to remove this lock completely.
            Is exclusive meta-data lock enough ?
    */
2075
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2076
    if (drop_temporary ||
Staale Smedseng's avatar
Staale Smedseng committed
2077 2078
        ((table_type == NULL &&        
         access(path, F_OK) &&
unknown's avatar
unknown committed
2079 2080
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
unknown's avatar
unknown committed
2081
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
unknown's avatar
unknown committed
2082
    {
2083
      // Table was not found on disk and table can't be created from engine
2084
      if (if_exists)
2085 2086
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
2087
			    table->table_name);
2088
      else
2089
        error= 1;
unknown's avatar
unknown committed
2090 2091 2092
    }
    else
    {
unknown's avatar
unknown committed
2093
      char *end;
unknown's avatar
unknown committed
2094 2095 2096 2097 2098
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
2099 2100
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
unknown's avatar
unknown committed
2101
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
2102
                             !dont_log_query);
2103
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
unknown's avatar
unknown committed
2104
	  (if_exists || table_type == NULL))
2105
      {
2106
	error= 0;
2107 2108
        thd->clear_error();
      }
unknown's avatar
unknown committed
2109
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
2110 2111
      {
	/* the table is referenced by a foreign key constraint */
2112
	foreign_key_error=1;
unknown's avatar
unknown committed
2113
      }
2114
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
2115
      {
2116
        int new_error;
unknown's avatar
unknown committed
2117 2118
	/* Delete the table definition file */
	strmov(end,reg_ext);
Marc Alff's avatar
Marc Alff committed
2119
        if (!(new_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
2120
        {
unknown's avatar
unknown committed
2121
	  some_tables_deleted=1;
2122 2123
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
2124
        }
2125
        error|= new_error;
2126
      }
unknown's avatar
unknown committed
2127
    }
2128
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2129 2130 2131 2132
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
2133
      wrong_tables.append(String(table->table_name,system_charset_info));
unknown's avatar
unknown committed
2134
    }
2135 2136
    DBUG_PRINT("table", ("table: 0x%lx  s: 0x%lx", (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
2137 2138 2139 2140 2141 2142

    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
2143
  }
unknown's avatar
unknown committed
2144
  thd->thread_specific_used|= tmp_table_deleted;
2145 2146 2147 2148
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
2149
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
2150
                      wrong_tables.c_ptr());
2151
    else
unknown's avatar
unknown committed
2152
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
2153 2154 2155 2156
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
2157
  {
unknown's avatar
unknown committed
2158
    query_cache_invalidate3(thd, tables, 0);
2159
    if (!dont_log_query)
2160
    {
2161
      if (!thd->is_current_stmt_binlog_format_row() ||
Staale Smedseng's avatar
Staale Smedseng committed
2162
          (non_temp_tables_count > 0 && !tmp_table_deleted))
2163 2164 2165 2166 2167 2168 2169 2170
      {
        /*
          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.
         */
2171
        error |= write_bin_log(thd, !error, thd->query(), thd->query_length());
2172
      }
2173
      else if (thd->is_current_stmt_binlog_format_row() &&
2174 2175
               tmp_table_deleted)
      {
2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192
        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 */");
2193
          error|= write_bin_log(thd, !error, built_query.ptr(), built_query.length());
2194 2195
        }

2196
        /*
2197 2198 2199
          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.
2200
        */
2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211
        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 */");
2212 2213
          error|= write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length(),
                                thd->in_multi_stmt_transaction());
2214
        }
2215
      }
2216

2217 2218
      /*
        The remaining cases are:
2219 2220
        - no tables were deleted and
        - only temporary tables were deleted and row-based
2221 2222 2223 2224
          replication is used.
        In both these cases, nothing should be written to the binary
        log.
      */
2225
    }
unknown's avatar
unknown committed
2226
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
2227
err:
2228 2229 2230 2231 2232 2233
  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
2234
      false positives in try_acquire_lock() function. So
2235
      it makes sense to remove exclusive meta-data locks in all cases.
Konstantin Osipov's avatar
Konstantin Osipov committed
2236 2237 2238 2239

      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.
2240
    */
2241 2242 2243
    if (! thd->locked_tables_mode)
      unlock_table_names(thd);
    else
Konstantin Osipov's avatar
Konstantin Osipov committed
2244
    {
2245
      if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
Konstantin Osipov's avatar
Konstantin Osipov committed
2246
      {
2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260
        thd->locked_tables_list.unlock_locked_tables(thd);
        goto end;
      }
      for (table= tables; table; table= table->next_local)
      {
        if (table->mdl_request.ticket)
        {
          /*
            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.
          */
          thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket);
        }
Konstantin Osipov's avatar
Konstantin Osipov committed
2261 2262
      }
    }
2263 2264
  }

Konstantin Osipov's avatar
Konstantin Osipov committed
2265
end:
2266
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2267 2268 2269
}


2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284
/*
  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
2285
bool quick_rm_table(handlerton *base,const char *db,
2286
                    const char *table_name, uint flags)
unknown's avatar
unknown committed
2287
{
2288
  char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
2289 2290 2291
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

2292
  uint path_length= build_table_filename(path, sizeof(path) - 1,
2293
                                         db, table_name, reg_ext, flags);
Marc Alff's avatar
Marc Alff committed
2294
  if (mysql_file_delete(key_file_frm, path, MYF(0)))
unknown's avatar
unknown committed
2295
    error= 1; /* purecov: inspected */
2296
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
2297 2298 2299
  if (!(flags & FRM_ONLY))
    error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2300 2301
}

2302 2303 2304
/*
  Sort keys in the following order:
  - PRIMARY KEY
2305 2306
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
2307 2308 2309 2310 2311 2312 2313 2314 2315 2316
  - 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)
{
2317 2318 2319
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
2320
  {
2321
    if (!(b_flags & HA_NOSAME))
2322
      return -1;
2323
    if ((a_flags ^ b_flags) & HA_NULL_PART_KEY)
2324 2325
    {
      /* Sort NOT NULL keys before other keys */
2326
      return (a_flags & HA_NULL_PART_KEY) ? 1 : -1;
2327 2328 2329 2330 2331
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
2332 2333 2334
    /* 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;
2335
  }
2336
  else if (b_flags & HA_NOSAME)
2337 2338
    return 1;					// Prefer b

2339
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
2340
  {
2341
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
2342
  }
unknown's avatar
unknown committed
2343
  /*
2344
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
2345 2346 2347 2348 2349
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
2350 2351
}

2352 2353
/*
  Check TYPELIB (set or enum) for duplicates
2354

2355 2356 2357
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
2358 2359
    name	  name of the checked column
    typelib	  list of values for the column
2360
    dup_val_count  returns count of duplicate elements
2361 2362

  DESCRIPTION
2363
    This function prints an warning for each value in list
2364 2365 2366
    which has some duplicates on its right

  RETURN VALUES
2367 2368
    0             ok
    1             Error
2369 2370
*/

2371
bool check_duplicates_in_interval(const char *set_or_name,
2372
                                  const char *name, TYPELIB *typelib,
2373
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
2374
{
2375
  TYPELIB tmp= *typelib;
2376
  const char **cur_value= typelib->type_names;
2377
  unsigned int *cur_length= typelib->type_lengths;
2378
  *dup_val_count= 0;  
2379 2380
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
2381
  {
2382 2383 2384 2385
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
2386
    {
2387 2388
      THD *thd= current_thd;
      ErrConvString err(*cur_value, *cur_length, cs);
2389 2390 2391 2392
      if ((current_thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
2393
                 name, err.ptr(), set_or_name);
2394 2395
        return 1;
      }
2396 2397 2398 2399
      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);
2400
      (*dup_val_count)++;
2401 2402
    }
  }
2403
  return 0;
2404
}
2405

2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433

/*
  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++)
  {
2434
    size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
2435 2436 2437 2438 2439 2440
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


unknown's avatar
unknown committed
2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
/*
  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
2452
    This function prepares a Create_field instance.
unknown's avatar
unknown committed
2453 2454 2455 2456 2457 2458 2459
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

unknown's avatar
unknown committed
2460
int prepare_create_field(Create_field *sql_field, 
unknown's avatar
unknown committed
2461 2462
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2463
			 longlong table_flags)
unknown's avatar
unknown committed
2464
{
2465
  unsigned int dup_val_count;
unknown's avatar
unknown committed
2466
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
2467 2468

  /*
2469
    This code came from mysql_prepare_create_table.
unknown's avatar
unknown committed
2470 2471 2472 2473 2474
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
2475 2476 2477 2478
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
2479 2480 2481 2482 2483 2484 2485
    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;
2486
    (*blob_columns)++;
unknown's avatar
unknown committed
2487
    break;
2488
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
2489
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
2490 2491 2492 2493
    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
2494
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2495 2496 2497 2498 2499 2500 2501 2502
    }
    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;
2503
    (*blob_columns)++;
unknown's avatar
unknown committed
2504 2505 2506 2507 2508
    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
2509
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
2510
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
2511
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
2512 2513 2514 2515 2516 2517 2518 2519
    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
2520
      {
unknown's avatar
unknown committed
2521 2522
        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
2523 2524
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
2525 2526 2527
    }
#endif
    /* fall through */
2528
  case MYSQL_TYPE_STRING:
unknown's avatar
unknown committed
2529 2530 2531 2532
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2533
  case MYSQL_TYPE_ENUM:
unknown's avatar
unknown committed
2534 2535 2536 2537 2538
    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;
2539 2540 2541 2542
    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
2543
    break;
2544
  case MYSQL_TYPE_SET:
unknown's avatar
unknown committed
2545 2546 2547 2548 2549
    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;
2550 2551 2552 2553
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2554 2555 2556 2557 2558 2559
    /* 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
2560
    break;
2561 2562 2563 2564 2565
  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
2566 2567
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2568
  case MYSQL_TYPE_BIT:
unknown's avatar
unknown committed
2569
    /* 
2570 2571
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
unknown's avatar
unknown committed
2572
    */
unknown's avatar
unknown committed
2573
    break;
2574
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
2575 2576 2577 2578 2579 2580 2581
    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;
2582
  case MYSQL_TYPE_TIMESTAMP:
unknown's avatar
unknown committed
2583 2584 2585
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2586
      if (!*timestamps)
unknown's avatar
unknown committed
2587
      {
unknown's avatar
unknown committed
2588
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2589
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2590
      }
unknown's avatar
unknown committed
2591 2592 2593 2594
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2595
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2596

2597
    (*timestamps)++;
unknown's avatar
unknown committed
2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612
    /* 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
2613 2614 2615
  DBUG_RETURN(0);
}

2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648

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


2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663
bool check_duplicate_warning(THD *thd, char *msg, ulong length)
{
  List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
  MYSQL_ERROR *err;
  while ((err= it++))
  {
    if (strncmp(msg, err->get_message_text(), length) == 0)
    {
      return true;
    }
  }
  return false;
}


unknown's avatar
unknown committed
2664
/*
2665
  Preparation for table creation
unknown's avatar
unknown committed
2666 2667

  SYNOPSIS
2668
    mysql_prepare_create_table()
2669 2670
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2671
      alter_info                List of columns and indexes to create
2672 2673 2674 2675 2676 2677
      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
2678

2679
  DESCRIPTION
2680
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
2681

2682
  NOTES
2683
    sets create_info->varchar if the table has a varchar
2684

unknown's avatar
unknown committed
2685
  RETURN VALUES
2686 2687
    FALSE    OK
    TRUE     error
unknown's avatar
unknown committed
2688
*/
unknown's avatar
unknown committed
2689

unknown's avatar
unknown committed
2690
static int
2691 2692 2693 2694 2695 2696
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
2697
{
2698
  const char	*key_name;
unknown's avatar
unknown committed
2699
  Create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
2700
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
2701
  ulong		record_offset= 0;
2702
  KEY		*key_info;
unknown's avatar
unknown committed
2703
  KEY_PART_INFO *key_part_info;
2704 2705 2706
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
unknown's avatar
unknown committed
2707 2708
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
unknown's avatar
unknown committed
2709
  uint total_uneven_bit_length= 0;
2710
  DBUG_ENTER("mysql_prepare_create_table");
unknown's avatar
unknown committed
2711

2712
  select_field_pos= alter_info->create_list.elements - select_field_count;
unknown's avatar
unknown committed
2713
  null_fields=blob_columns=0;
2714
  create_info->varchar= 0;
unknown's avatar
unknown committed
2715
  max_key_length= file->max_key_length();
2716

2717
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
2718
  {
2719 2720
    CHARSET_INFO *save_cs;

2721 2722 2723 2724 2725 2726
    /*
      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;
2727 2728
    save_cs= sql_field->charset= get_sql_field_charset(sql_field,
                                                       create_info);
2729 2730 2731 2732
    if ((sql_field->flags & BINCMP_FLAG) &&
	!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
						    MY_CS_BINSORT,MYF(0))))
    {
Davi Arnaut's avatar
Davi Arnaut committed
2733
      char tmp[65];
2734 2735
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2736
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2737
      DBUG_RETURN(TRUE);
2738
    }
2739

2740
    /*
2741
      Convert the default value from client character
2742 2743 2744
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2745
        save_cs != sql_field->def->collation.collation &&
2746 2747 2748 2749
        (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))
2750
    {
2751
      /*
unknown's avatar
unknown committed
2752
        Starting from 5.1 we work here with a copy of Create_field
2753 2754 2755 2756 2757 2758 2759 2760
        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.
      */
2761
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2762 2763 2764 2765 2766

      if (sql_field->def == NULL)
      {
        /* Could not convert */
        my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2767
        DBUG_RETURN(TRUE);
2768 2769 2770
      }
    }

2771 2772
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2773 2774 2775
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2776
      TYPELIB *interval= sql_field->interval;
2777 2778 2779 2780 2781 2782

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2783
      if (!interval)
2784
      {
2785
        /*
2786 2787 2788
          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.
2789
        */
2790
        interval= sql_field->interval= typelib(thd->mem_root,
2791
                                               sql_field->interval_list);
2792
        List_iterator<String> int_it(sql_field->interval_list);
2793
        String conv, *tmp;
2794 2795 2796 2797 2798
        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);
2799
        for (uint i= 0; (tmp= int_it++); i++)
2800
        {
2801
          size_t lengthsp;
2802 2803 2804 2805 2806
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2807
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
unknown's avatar
unknown committed
2808
                                                  conv.length());
2809 2810
            interval->type_lengths[i]= conv.length();
          }
2811

2812
          // Strip trailing spaces.
unknown's avatar
unknown committed
2813 2814
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2815 2816
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2817
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2818 2819 2820 2821 2822
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2823 2824
              ErrConvString err(tmp->ptr(), tmp->length(), cs);
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
2825
              DBUG_RETURN(TRUE);
2826 2827
            }
          }
2828
        }
2829
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2830 2831
      }

2832
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2833
      {
2834
        uint32 field_length;
2835
        if (sql_field->def != NULL)
2836 2837 2838 2839 2840
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2841 2842 2843 2844 2845
          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);
2846
              DBUG_RETURN(TRUE);
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858
            }

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

2859 2860 2861
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2862
            DBUG_RETURN(TRUE);
2863 2864
          }
        }
2865 2866
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2867
      }
2868
      else  /* MYSQL_TYPE_ENUM */
2869
      {
2870
        uint32 field_length;
2871
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2872
        if (sql_field->def != NULL)
2873 2874
        {
          String str, *def= sql_field->def->val_str(&str);
2875
          if (def == NULL) /* SQL "NULL" maps to NULL */
2876
          {
2877 2878 2879
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2880
              DBUG_RETURN(TRUE);
2881 2882 2883 2884 2885 2886 2887 2888 2889 2890
            }

            /* 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);
2891
              DBUG_RETURN(TRUE);
2892
            }
2893 2894
          }
        }
2895 2896
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2897 2898 2899 2900
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2901
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2902
    { 
unknown's avatar
unknown committed
2903
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2904
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2905 2906 2907 2908 2909
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2910
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
2911
    if (prepare_blob_field(thd, sql_field))
2912
      DBUG_RETURN(TRUE);
2913

unknown's avatar
unknown committed
2914 2915
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
2916

unknown's avatar
unknown committed
2917 2918
    if (check_column_name(sql_field->field_name))
    {
unknown's avatar
unknown committed
2919
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2920
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2921
    }
unknown's avatar
unknown committed
2922

2923 2924
    /* 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
2925
    {
unknown's avatar
unknown committed
2926
      if (my_strcasecmp(system_charset_info,
2927 2928
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
2929
      {
2930 2931 2932 2933
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2934 2935
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2936
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2937
	  DBUG_RETURN(TRUE);
2938 2939 2940
	}
	else
	{
2941
	  /* Field redefined */
2942
	  sql_field->def=		dup_field->def;
2943
	  sql_field->sql_type=		dup_field->sql_type;
2944 2945 2946
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2947
	  sql_field->length=		dup_field->char_length;
2948
          sql_field->pack_length=	dup_field->pack_length;
2949
          sql_field->key_length=	dup_field->key_length;
2950
	  sql_field->decimals=		dup_field->decimals;
2951
	  sql_field->create_length_to_internal_length();
2952
	  sql_field->unireg_check=	dup_field->unireg_check;
2953 2954 2955 2956 2957 2958 2959 2960
          /* 
            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
2961
          sql_field->interval=          dup_field->interval;
2962 2963 2964
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2965
	}
unknown's avatar
unknown committed
2966 2967
      }
    }
2968 2969
    /* 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
2970 2971
	(sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
2972
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
2973 2974
    it2.rewind();
  }
2975 2976 2977

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
2978
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
2979 2980 2981 2982

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

unknown's avatar
unknown committed
2985 2986
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2987
			     file->ha_table_flags()))
2988
      DBUG_RETURN(TRUE);
2989
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2990
      create_info->varchar= TRUE;
2991
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
2992 2993
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2994
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
2995
  }
2996 2997
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
2998 2999
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
3000
    DBUG_RETURN(TRUE);
3001
  }
unknown's avatar
unknown committed
3002 3003
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
3004
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3005
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3006 3007
  }
  if (auto_increment &&
3008
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
3009
  {
unknown's avatar
unknown committed
3010 3011
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
3012
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3013 3014
  }

3015
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
3016
  {
unknown's avatar
unknown committed
3017 3018
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
3019
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3020 3021 3022
  }

  /* Create keys */
3023

3024 3025
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
3026
  uint key_parts=0, fk_key_count=0;
3027
  bool primary_key=0,unique_key=0;
3028
  Key *key, *key2;
unknown's avatar
unknown committed
3029
  uint tmp, key_number;
3030 3031
  /* special marker for keys to be ignored */
  static char ignore_key[1];
3032

3033
  /* Calculate number of key segements */
3034
  *key_count= 0;
3035

unknown's avatar
unknown committed
3036 3037
  while ((key=key_iterator++))
  {
3038
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name.str ? key->name.str :
3039
                        "(none)" , key->type));
3040 3041 3042
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
unknown's avatar
unknown committed
3043
      Foreign_key *fk_key= (Foreign_key*) key;
3044 3045 3046
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
3047
        my_error(ER_WRONG_FK_DEF, MYF(0),
3048 3049
                 (fk_key->name.str ? fk_key->name.str :
                                     "foreign key without name"),
3050
                 ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
3051
	DBUG_RETURN(TRUE);
3052 3053 3054
      }
      continue;
    }
3055
    (*key_count)++;
unknown's avatar
unknown committed
3056
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
3057 3058 3059
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
3060
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3061
    }
3062
    if (check_string_char_length(&key->name, "", NAME_CHAR_LEN,
3063
                                 system_charset_info, 1))
unknown's avatar
unknown committed
3064
    {
3065
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str);
3066
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3067
    }
3068
    key_iterator2.rewind ();
3069
    if (key->type != Key::FOREIGN_KEY)
3070
    {
3071
      while ((key2 = key_iterator2++) != key)
3072
      {
unknown's avatar
unknown committed
3073
	/*
3074 3075 3076
          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
3077
        */
3078
        if ((key2->type != Key::FOREIGN_KEY &&
3079
             key2->name.str != ignore_key &&
3080
             !foreign_key_prefix(key, key2)))
3081
        {
3082
          /* TODO: issue warning message */
3083 3084 3085 3086
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
3087
            key->name.str= ignore_key;
3088 3089
          else
          {
3090
            key2->name.str= ignore_key;
3091 3092
            key_parts-= key2->columns.elements;
            (*key_count)--;
3093 3094 3095
          }
          break;
        }
3096 3097
      }
    }
3098
    if (key->name.str != ignore_key)
3099 3100 3101
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
3102 3103
    if (key->name.str && !tmp_table && (key->type != Key::PRIMARY) &&
	!my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
3104
    {
3105
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
3106
      DBUG_RETURN(TRUE);
3107
    }
3108
  }
unknown's avatar
unknown committed
3109
  tmp=file->max_keys();
3110
  if (*key_count > tmp)
3111 3112
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
3113
    DBUG_RETURN(TRUE);
3114
  }
3115

3116
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
3117
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
3118
  if (!*key_info_buffer || ! key_part_info)
3119
    DBUG_RETURN(TRUE);				// Out of memory
3120

3121
  key_iterator.rewind();
unknown's avatar
unknown committed
3122
  key_number=0;
unknown's avatar
unknown committed
3123
  for (; (key=key_iterator++) ; key_number++)
3124 3125
  {
    uint key_length=0;
unknown's avatar
unknown committed
3126
    Key_part_spec *column;
3127

3128
    if (key->name.str == ignore_key)
3129 3130 3131 3132
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
3133
      while (key && key->name.str == ignore_key);
3134 3135 3136 3137
      if (!key)
	break;
    }

3138
    switch (key->type) {
unknown's avatar
unknown committed
3139
    case Key::MULTIPLE:
3140
	key_info->flags= 0;
3141
	break;
unknown's avatar
unknown committed
3142
    case Key::FULLTEXT:
3143
	key_info->flags= HA_FULLTEXT;
unknown's avatar
unknown committed
3144
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
3145
          key_info->flags|= HA_USES_PARSER;
3146 3147
        else
          key_info->parser_name= 0;
3148
	break;
unknown's avatar
unknown committed
3149
    case Key::SPATIAL:
unknown's avatar
SCRUM:  
unknown committed
3150
#ifdef HAVE_SPATIAL
3151
	key_info->flags= HA_SPATIAL;
3152
	break;
unknown's avatar
SCRUM:  
unknown committed
3153
#else
3154 3155
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
3156
	DBUG_RETURN(TRUE);
unknown's avatar
SCRUM:  
unknown committed
3157
#endif
3158 3159 3160 3161
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
3162 3163
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
3164
    }
3165 3166
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
3167

unknown's avatar
unknown committed
3168 3169
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
3170
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
3171
    key_info->algorithm= key->key_create_info.algorithm;
unknown's avatar
unknown committed
3172

3173 3174
    if (key->type == Key::FULLTEXT)
    {
3175
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
3176
      {
unknown's avatar
unknown committed
3177 3178
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
3179
	DBUG_RETURN(TRUE);
3180 3181
      }
    }
unknown's avatar
unknown committed
3182 3183 3184
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
3185
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
3186 3187 3188
       in near future when new frm file is ready
       checking for proper key parts number:
    */
3189

3190
    /* TODO: Add proper checks if handler supports key_type and algorithm */
3191
    if (key_info->flags & HA_SPATIAL)
unknown's avatar
unknown committed
3192
    {
3193
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
unknown's avatar
unknown committed
3194 3195 3196
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
3197
        DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3198
      }
unknown's avatar
unknown committed
3199 3200
      if (key_info->key_parts != 1)
      {
3201
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
3202
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3203
      }
unknown's avatar
unknown committed
3204
    }
unknown's avatar
unknown committed
3205
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
3206
    {
unknown's avatar
SCRUM:  
unknown committed
3207
#ifdef HAVE_RTREE_KEYS
unknown's avatar
unknown committed
3208 3209
      if ((key_info->key_parts & 1) == 1)
      {
3210
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
3211
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3212
      }
3213
      /* TODO: To be deleted */
3214
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
3215
      DBUG_RETURN(TRUE);
unknown's avatar
SCRUM:  
unknown committed
3216
#else
3217 3218
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
3219
      DBUG_RETURN(TRUE);
unknown's avatar
SCRUM:  
unknown committed
3220
#endif
unknown's avatar
unknown committed
3221
    }
3222

3223 3224 3225 3226 3227
    /* 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
3228 3229
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
3230 3231 3232 3233 3234
                           create_info->key_block_size);

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

unknown's avatar
unknown committed
3235
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
3236
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
3237 3238
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
3239
      uint length;
unknown's avatar
unknown committed
3240
      Key_part_spec *dup_column;
unknown's avatar
unknown committed
3241

unknown's avatar
unknown committed
3242 3243 3244
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
3245
	     my_strcasecmp(system_charset_info,
3246
			   column->field_name.str,
3247
			   sql_field->field_name))
unknown's avatar
unknown committed
3248 3249 3250
	field++;
      if (!sql_field)
      {
3251
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
3252
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3253
      }
unknown's avatar
unknown committed
3254
      while ((dup_column= cols2++) != column)
3255 3256
      {
        if (!my_strcasecmp(system_charset_info,
3257
	     	           column->field_name.str, dup_column->field_name.str))
3258 3259 3260
	{
	  my_printf_error(ER_DUP_FIELDNAME,
			  ER(ER_DUP_FIELDNAME),MYF(0),
3261
			  column->field_name.str);
3262
	  DBUG_RETURN(TRUE);
3263 3264 3265
	}
      }
      cols2.rewind();
3266
      if (key->type == Key::FULLTEXT)
3267
      {
3268 3269
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
3270 3271
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
3272
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
3273 3274
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
3275
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286
	    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));
3287
      }
3288
      else
3289
      {
3290 3291
	column->length*= sql_field->charset->mbmaxlen;

3292
        if (key->type == Key::SPATIAL)
unknown's avatar
unknown committed
3293
        {
3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304
          if (column->length)
          {
            my_error(ER_WRONG_SUB_KEY, MYF(0));
            DBUG_RETURN(TRUE);
          }
          if (!f_is_geom(sql_field->pack_flag))
          {
            my_error(ER_SPATIAL_MUST_HAVE_GEOM_COL, MYF(0));
            DBUG_RETURN(TRUE);
          }
        }
unknown's avatar
unknown committed
3305

3306 3307
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
3308
	{
3309
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
3310
	  {
3311
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str);
3312
	    DBUG_RETURN(TRUE);
3313
	  }
3314 3315
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
3316
            column->length= 25;
3317 3318
	  if (!column->length)
	  {
3319
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
3320
	    DBUG_RETURN(TRUE);
3321 3322
	  }
	}
unknown's avatar
SCRUM:  
unknown committed
3323
#ifdef HAVE_SPATIAL
3324
	if (key->type == Key::SPATIAL)
3325
	{
3326
	  if (!column->length)
3327 3328
	  {
	    /*
3329 3330
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
3331
	    */
3332
	    column->length= 4*sizeof(double);
3333 3334
	  }
	}
unknown's avatar
SCRUM:  
unknown committed
3335
#endif
3336 3337 3338 3339 3340 3341 3342
	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
3343
            null_fields--;
3344 3345
	  }
	  else
3346 3347 3348 3349
          {
            key_info->flags|= HA_NULL_PART_KEY;
            if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
            {
3350
              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
3351
              DBUG_RETURN(TRUE);
3352 3353 3354 3355 3356
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
3357
              DBUG_RETURN(TRUE);
3358 3359
            }
          }
3360 3361 3362
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
3363
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
3364 3365
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
3366
      }
3367

unknown's avatar
unknown committed
3368 3369 3370
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
3371 3372
      length= sql_field->key_length;

unknown's avatar
unknown committed
3373 3374 3375 3376
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
3377
	  if ((length=column->length) > max_key_length ||
3378
	      length > file->max_key_part_length())
3379
	  {
unknown's avatar
unknown committed
3380
	    length=min(max_key_length, file->max_key_part_length());
3381 3382 3383 3384 3385 3386 3387 3388
	    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);
3389 3390
              /* Align key length to multibyte char boundary */
              length-= length % sql_field->charset->mbmaxlen;
3391 3392 3393 3394
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
3395
	      DBUG_RETURN(TRUE);
3396 3397
	    }
	  }
unknown's avatar
unknown committed
3398
	}
3399
        // Catch invalid use of partial keys 
3400
	else if (!f_is_geom(sql_field->pack_flag) &&
3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413
                 // is the key partial? 
                 column->length != length &&
                 // is prefix length bigger than field length? 
                 (column->length > length ||
                  // can the field have a partial key? 
                  !Field::type_can_have_key_part (sql_field->sql_type) ||
                  // a packed field can't be used in a partial key
                  f_is_packed(sql_field->pack_flag) ||
                  // does the storage engine allow prefixed search?
                  ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
                   // and is this a 'unique' key?
                   (key_info->flags & HA_NOSAME))))
        {         
unknown's avatar
unknown committed
3414
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
3415
	  DBUG_RETURN(TRUE);
3416
	}
3417
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
3418
	  length=column->length;
unknown's avatar
unknown committed
3419 3420 3421
      }
      else if (length == 0)
      {
3422
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str);
3423
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3424
      }
3425
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
3426
      {
3427
        length= file->max_key_part_length();
3428 3429 3430 3431 3432 3433 3434 3435
	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);
3436 3437
          /* Align key length to multibyte char boundary */
          length-= length % sql_field->charset->mbmaxlen;
3438 3439 3440 3441
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
3442
	  DBUG_RETURN(TRUE);
3443
	}
3444 3445
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
3446
      /* Use packed keys for long strings on the first column */
3447
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
3448
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
3449 3450
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
3451 3452
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
Staale Smedseng's avatar
Staale Smedseng committed
3453
	if ((column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB)) ||
3454 3455
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
unknown's avatar
unknown committed
3456 3457 3458
	else
	  key_info->flags|= HA_PACK_KEY;
      }
3459 3460 3461 3462
      /* 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
3463 3464 3465 3466 3467 3468 3469
      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)
3470 3471 3472
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
3473 3474
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
3475
	    DBUG_RETURN(TRUE);
3476 3477 3478 3479
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
3480
	else if (!(key_name= key->name.str))
unknown's avatar
unknown committed
3481
	  key_name=make_unique_key_name(sql_field->field_name,
3482 3483
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
3484
	{
3485
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
3486
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3487 3488 3489 3490
	}
	key_info->name=(char*) key_name;
      }
    }
3491 3492
    if (!key_info->name || check_column_name(key_info->name))
    {
3493
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
3494
      DBUG_RETURN(TRUE);
3495
    }
3496 3497
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
3498
    key_info->key_length=(uint16) key_length;
3499
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
3500
    {
3501
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
3502
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3503
    }
3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537

    uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
                                           key->key_create_info.comment.str,
                                           key->key_create_info.comment.str +
                                           key->key_create_info.comment.length,
                                           INDEX_COMMENT_MAXLEN);

    if (tmp_len < key->key_create_info.comment.length)
    {
      if ((thd->variables.sql_mode &
           (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_TOO_LONG_INDEX_COMMENT, MYF(0),
                 key_info->name, (uint) INDEX_COMMENT_MAXLEN);
        DBUG_RETURN(-1);
      }
      char warn_buff[MYSQL_ERRMSG_SIZE];
      my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT),
                  key_info->name, (uint) INDEX_COMMENT_MAXLEN);
      /* do not push duplicate warnings */
      if (!check_duplicate_warning(thd, warn_buff, strlen(warn_buff)))
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                     ER_TOO_LONG_INDEX_COMMENT, warn_buff);

      key->key_create_info.comment.length= tmp_len;
    }

    key_info->comment.length= key->key_create_info.comment.length;
    if (key_info->comment.length > 0)
    {
      key_info->flags|= HA_USES_COMMENT;
      key_info->comment.str= key->key_create_info.comment.str;
    }

unknown's avatar
unknown committed
3538
    key_info++;
unknown's avatar
unknown committed
3539
  }
3540
  if (!unique_key && !primary_key &&
3541
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
3542
  {
unknown's avatar
unknown committed
3543
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
3544
    DBUG_RETURN(TRUE);
3545
  }
unknown's avatar
unknown committed
3546 3547
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
3548
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3549
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3550
  }
3551
  /* Sort keys in optimized order */
3552 3553
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
unknown's avatar
unknown committed
3554
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
3555

3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586
  /* 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);
    }
  }

3587
  DBUG_RETURN(FALSE);
3588 3589
}

3590

unknown's avatar
unknown committed
3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606
/*
  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)
{
3607 3608 3609 3610 3611
  /*
    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
3612 3613 3614
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3615 3616 3617

    load_db_opt_by_name(thd, db, &db_info);

unknown's avatar
unknown committed
3618 3619 3620 3621 3622
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


unknown's avatar
unknown committed
3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635
/*
  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
3636
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
unknown's avatar
unknown committed
3637 3638 3639 3640 3641 3642 3643 3644 3645
{
  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];

3646 3647
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
3648 3649 3650 3651 3652
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3653
    sql_field->sql_type= MYSQL_TYPE_BLOB;
unknown's avatar
unknown committed
3654
    sql_field->flags|= BLOB_FLAG;
Sergei Golubchik's avatar
Sergei Golubchik committed
3655
    my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name,
3656
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
3657 3658 3659 3660
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
3661

unknown's avatar
unknown committed
3662 3663
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
3664 3665 3666
    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
3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677
    {
      /* 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);
}


3678
/*
unknown's avatar
unknown committed
3679
  Preparation of Create_field for SP function return values.
3680 3681
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692

  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
3693
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3694
{
3695 3696
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3697 3698
  {
    uint32 field_length, dummy;
3699
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3700 3701 3702 3703 3704 3705 3706
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3707
    else /* MYSQL_TYPE_ENUM */
3708 3709 3710 3711 3712 3713 3714 3715 3716
    {
      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);
  }

3717
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3718 3719 3720 3721 3722
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
3723 3724 3725 3726
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3727 3728


3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744
/*
  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
 */
3745 3746 3747
static inline int write_create_table_bin_log(THD *thd,
                                             const HA_CREATE_INFO *create_info,
                                             bool internal_tmp_table)
3748 3749 3750 3751 3752 3753 3754 3755 3756
{
  /*
    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 &&
3757 3758
      (!thd->is_current_stmt_binlog_format_row() ||
       (thd->is_current_stmt_binlog_format_row() &&
3759
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3760 3761
    return write_bin_log(thd, TRUE, thd->query(), thd->query_length());
  return 0;
3762 3763 3764
}


3765 3766 3767 3768
/*
  Create a table

  SYNOPSIS
unknown's avatar
unknown committed
3769
    mysql_create_table_no_lock()
3770 3771 3772
    thd			Thread object
    db			Database
    table_name		Table name
3773
    create_info	        Create information (like MAX_ROWS)
3774 3775
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
3776
    internal_tmp_table  Set to 1 if this is an internal temporary table
3777
			(From ALTER TABLE)
3778
    select_field_count
3779 3780

  DESCRIPTION
3781
    If one creates a temporary table, this is automatically opened
3782

unknown's avatar
unknown committed
3783
    Note that this function assumes that caller already have taken
Konstantin Osipov's avatar
Konstantin Osipov committed
3784 3785 3786
    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
3787

3788 3789 3790 3791 3792 3793
    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
3794 3795
    FALSE OK
    TRUE  error
3796 3797
*/

unknown's avatar
unknown committed
3798
bool mysql_create_table_no_lock(THD *thd,
unknown's avatar
unknown committed
3799
                                const char *db, const char *table_name,
3800 3801 3802 3803
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3804
{
3805
  char		path[FN_REFLEN + 1];
3806
  uint          path_length;
3807 3808 3809 3810
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
3811
  bool		error= TRUE;
unknown's avatar
unknown committed
3812
  DBUG_ENTER("mysql_create_table_no_lock");
3813 3814
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3815

3816

3817
  /* Check for duplicate fields and check type of table to create */
3818
  if (!alter_info->create_list.elements)
3819
  {
unknown's avatar
unknown committed
3820 3821
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
3822
    DBUG_RETURN(TRUE);
3823
  }
3824
  if (check_engine(thd, table_name, create_info))
3825
    DBUG_RETURN(TRUE);
3826 3827 3828

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

3829
  db_options= create_info->table_options;
3830 3831 3832
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3833 3834
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3835
  {
unknown's avatar
unknown committed
3836
    mem_alloc_error(sizeof(handler));
3837 3838
    DBUG_RETURN(TRUE);
  }
3839
#ifdef WITH_PARTITION_STORAGE_ENGINE
3840 3841
  partition_info *part_info= thd->work_part_info;

unknown's avatar
unknown committed
3842 3843 3844 3845 3846 3847 3848 3849
  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
3850
    thd->work_part_info= part_info= new partition_info();
unknown's avatar
unknown committed
3851 3852 3853 3854 3855 3856
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
unknown's avatar
unknown committed
3857
    part_info->default_engine_type= create_info->db_type;
3858
    part_info->is_auto_partitioned= TRUE;
unknown's avatar
unknown committed
3859
  }
3860 3861 3862
  if (part_info)
  {
    /*
unknown's avatar
unknown committed
3863 3864 3865 3866 3867 3868 3869 3870 3871
      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.
3872
    */
3873
    List_iterator<Key> key_iterator(alter_info->key_list);
unknown's avatar
unknown committed
3874
    Key *key;
unknown's avatar
unknown committed
3875
    handlerton *part_engine_type= create_info->db_type;
3876 3877
    char *part_syntax_buf;
    uint syntax_len;
unknown's avatar
unknown committed
3878
    handlerton *engine_type;
3879 3880 3881 3882 3883
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
unknown's avatar
unknown committed
3884 3885
    while ((key= key_iterator++))
    {
3886 3887
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
unknown's avatar
unknown committed
3888
      {
3889
        my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
unknown's avatar
unknown committed
3890 3891 3892
        goto err;
      }
    }
3893
    if ((part_engine_type == partition_hton) &&
3894
        part_info->default_engine_type)
3895 3896 3897 3898 3899 3900
    {
      /*
        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
3901
      ;
3902
    }
3903 3904
    else
    {
unknown's avatar
unknown committed
3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916
      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);
        }
      }
3917
    }
unknown's avatar
unknown committed
3918 3919 3920
    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)));
3921
    if (part_info->check_partition_info(thd, &engine_type, file,
3922
                                        create_info, FALSE))
unknown's avatar
unknown committed
3923
      goto err;
unknown's avatar
unknown committed
3924
    part_info->default_engine_type= engine_type;
unknown's avatar
unknown committed
3925

3926 3927 3928 3929 3930 3931
    /*
      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,
3932 3933 3934
                                                     TRUE, TRUE,
                                                     create_info,
                                                     alter_info)))
unknown's avatar
unknown committed
3935
      goto err;
3936 3937
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
3938 3939
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3940
        create_info->db_type == partition_hton)
3941 3942 3943 3944 3945
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
unknown's avatar
unknown committed
3946
      DBUG_PRINT("info", ("db_type: %s",
unknown's avatar
unknown committed
3947
                        ha_resolve_storage_engine_name(create_info->db_type)));
3948
      delete file;
3949
      create_info->db_type= partition_hton;
3950 3951 3952 3953
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3954 3955 3956 3957 3958 3959
      /*
        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.
      */
3960 3961 3962
      if (part_info->use_default_num_partitions &&
          part_info->num_parts &&
          (int)part_info->num_parts !=
3963
          file->get_default_no_partitions(create_info))
3964
      {
3965
        uint i;
3966
        List_iterator<partition_element> part_it(part_info->partitions);
3967 3968 3969 3970
        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;
3971 3972
      }
      else if (part_info->is_sub_partitioned() &&
3973 3974 3975
               part_info->use_default_num_subpartitions &&
               part_info->num_subparts &&
               (int)part_info->num_subparts !=
3976
                 file->get_default_no_partitions(create_info))
3977
      {
3978
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3979
        part_info->num_subparts= file->get_default_no_partitions(create_info);
3980 3981 3982 3983
      }
    }
    else if (create_info->db_type != engine_type)
    {
unknown's avatar
unknown committed
3984 3985 3986 3987 3988 3989
      /*
        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.
      */
3990
      delete file;
3991 3992
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3993 3994 3995 3996
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3997 3998 3999
    }
  }
#endif
4000

4001 4002 4003 4004 4005
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
4006
    goto err;
unknown's avatar
unknown committed
4007 4008 4009 4010

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
4011
    path_length= build_tmptable_filename(thd, path, sizeof(path));
unknown's avatar
unknown committed
4012 4013
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
4014 4015
  else  
  {
4016
    path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,
4017
                                      internal_tmp_table ? FN_IS_TMP : 0);
4018
  }
4019

unknown's avatar
unknown committed
4020
  /* Check if table already exists */
unknown's avatar
unknown committed
4021 4022
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
unknown's avatar
unknown committed
4023
  {
4024
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
4025 4026
    {
      create_info->table_existed= 1;		// Mark that table existed
4027 4028 4029
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
4030
      error= 0;
unknown's avatar
unknown committed
4031
      goto err;
4032
    }
unknown's avatar
unknown committed
4033
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
4034
    goto err;
unknown's avatar
unknown committed
4035
  }
4036

Marc Alff's avatar
Marc Alff committed
4037
  mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4038
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
4039 4040 4041 4042
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
4043 4044
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
4045
      goto unlock_and_end;
unknown's avatar
unknown committed
4046
    }
4047 4048 4049 4050 4051 4052 4053 4054
    /*
      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.
    */
4055
    if (get_cached_table_share(db, table_name))
4056 4057 4058 4059
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
unknown's avatar
unknown committed
4060 4061
  }

unknown's avatar
unknown committed
4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074
  /*
    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;
4075 4076 4077
    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
4078
    {
4079 4080 4081 4082 4083
      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
4084

4085 4086 4087 4088 4089 4090 4091 4092 4093
        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
4094 4095 4096
    }
  }

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

4100
#ifdef HAVE_READLINK
4101
  {
4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137
    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;
      }
    }
4138 4139 4140 4141 4142 4143 4144 4145 4146
  }

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

4147
  if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
4148
#endif /* HAVE_READLINK */
4149 4150
  {
    if (create_info->data_file_name)
4151 4152 4153
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "DATA DIRECTORY");
4154
    if (create_info->index_file_name)
4155 4156 4157
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "INDEX DIRECTORY");
4158
    create_info->data_file_name= create_info->index_file_name= 0;
4159
  }
unknown's avatar
unknown committed
4160
  create_info->table_options=db_options;
4161

4162
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
4163 4164
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
unknown's avatar
unknown committed
4165
                       key_count, key_info_buffer, file))
unknown's avatar
unknown committed
4166
    goto unlock_and_end;
unknown's avatar
unknown committed
4167

unknown's avatar
unknown committed
4168 4169 4170 4171 4172 4173
  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
4174
      goto unlock_and_end;
unknown's avatar
unknown committed
4175
    }
unknown's avatar
unknown committed
4176
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
4177
  }
unknown's avatar
unknown committed
4178

4179
  error= FALSE;
unknown's avatar
unknown committed
4180
unlock_and_end:
Marc Alff's avatar
Marc Alff committed
4181
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4182 4183

err:
4184
  thd_proc_info(thd, "After create");
unknown's avatar
unknown committed
4185
  delete file;
unknown's avatar
unknown committed
4186
  DBUG_RETURN(error);
4187 4188

warn:
unknown's avatar
unknown committed
4189
  error= FALSE;
4190 4191 4192 4193
  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                      ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                      alias);
  create_info->table_existed= 1;		// Mark that table existed
unknown's avatar
unknown committed
4194
  goto unlock_and_end;
unknown's avatar
unknown committed
4195 4196
}

unknown's avatar
unknown committed
4197 4198

/*
unknown's avatar
unknown committed
4199
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
unknown's avatar
unknown committed
4200 4201
*/

4202
bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
unknown's avatar
unknown committed
4203
                        HA_CREATE_INFO *create_info,
4204
                        Alter_info *alter_info)
unknown's avatar
unknown committed
4205 4206 4207 4208 4209
{
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
Marc Alff's avatar
Marc Alff committed
4210
  mysql_mutex_lock(&LOCK_lock_db);
unknown's avatar
unknown committed
4211
  while (!thd->killed &&
4212 4213
         my_hash_search(&lock_db_cache, (uchar*)create_table->db,
                        create_table->db_length))
unknown's avatar
unknown committed
4214 4215
  {
    wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
Marc Alff's avatar
Marc Alff committed
4216
    mysql_mutex_lock(&LOCK_lock_db);
unknown's avatar
unknown committed
4217 4218 4219 4220
  }

  if (thd->killed)
  {
Marc Alff's avatar
Marc Alff committed
4221
    mysql_mutex_unlock(&LOCK_lock_db);
unknown's avatar
unknown committed
4222 4223 4224
    DBUG_RETURN(TRUE);
  }
  creating_table++;
Marc Alff's avatar
Marc Alff committed
4225
  mysql_mutex_unlock(&LOCK_lock_db);
unknown's avatar
unknown committed
4226

4227 4228 4229 4230
  /*
    Open or obtain an exclusive metadata lock on table being created.
  */
  if (open_and_lock_tables_derived(thd, thd->lex->query_tables, FALSE,
4231
                                   0))
unknown's avatar
unknown committed
4232
  {
4233 4234
    result= TRUE;
    goto unlock;
unknown's avatar
unknown committed
4235
  }
unknown's avatar
unknown committed
4236

4237 4238
  /* Got lock. */
  DEBUG_SYNC(thd, "locked_table_name");
unknown's avatar
unknown committed
4239

4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250
  result= mysql_create_table_no_lock(thd, create_table->db,
                                     create_table->table_name, create_info,
                                     alter_info, FALSE, 0);

  /*
    Don't write statement if:
    - Table creation has failed
    - Row-based logging is used and we are creating a temporary table
    Otherwise, the statement shall be binlogged.
  */
  if (!result &&
4251 4252
      (!thd->is_current_stmt_binlog_format_row() ||
       (thd->is_current_stmt_binlog_format_row() &&
4253
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
4254
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4255 4256 4257 4258 4259 4260 4261 4262 4263

  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    /*
      close_thread_tables() takes care about both closing open tables (which
      might be still around in case of error) and releasing metadata locks.
    */
    close_thread_tables(thd);
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4264

4265
unlock:
Marc Alff's avatar
Marc Alff committed
4266
  mysql_mutex_lock(&LOCK_lock_db);
unknown's avatar
unknown committed
4267
  if (!--creating_table && creating_database)
Marc Alff's avatar
Marc Alff committed
4268 4269
    mysql_cond_signal(&COND_refresh);
  mysql_mutex_unlock(&LOCK_lock_db);
unknown's avatar
unknown committed
4270 4271 4272 4273
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
4274 4275 4276 4277 4278 4279 4280 4281
/*
** 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++)
4282
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
4283 4284 4285 4286 4287 4288 4289 4290 4291 4292
      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;

4293 4294
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
4295
    return (char*) field_name;			// Use fieldname
4296 4297 4298 4299 4300 4301
  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
  */
4302
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
4303
  {
4304 4305
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
4306 4307 4308
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
4309
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
4310 4311
}

4312

unknown's avatar
unknown committed
4313 4314 4315 4316
/****************************************************************************
** Alter a table definition
****************************************************************************/

4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330

/*
  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.
4331 4332
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
4333 4334

  RETURN
4335 4336
    FALSE   OK
    TRUE    Error
4337 4338
*/

4339
bool
4340 4341 4342
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
4343
{
4344
  THD *thd= current_thd;
4345 4346
  char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
    lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
4347 4348
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
unknown's avatar
unknown committed
4349
  handler *file;
unknown's avatar
unknown committed
4350
  int error=0;
unknown's avatar
unknown committed
4351
  DBUG_ENTER("mysql_rename_table");
4352 4353
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
unknown's avatar
unknown committed
4354

unknown's avatar
unknown committed
4355
  file= (base == NULL ? 0 :
unknown's avatar
unknown committed
4356 4357
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

4358
  build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
4359
                       flags & FN_FROM_IS_TMP);
4360
  build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
4361
                       flags & FN_TO_IS_TMP);
4362 4363 4364 4365 4366 4367

  /*
    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.
   */
4368
  if (lower_case_table_names == 2 && file &&
4369
      !(file->ha_table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
4370
  {
4371 4372
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
4373
    build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
4374
                         flags & FN_FROM_IS_TMP);
4375
    from_base= lc_from;
unknown's avatar
unknown committed
4376

4377 4378
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
4379
    build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
4380
                         flags & FN_TO_IS_TMP);
4381
    to_base= lc_to;
unknown's avatar
unknown committed
4382 4383
  }

4384
  if (!file || !(error=file->ha_rename_table(from_base, to_base)))
4385
  {
4386
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
4387
    {
unknown's avatar
unknown committed
4388
      error=my_errno;
4389
      /* Restore old file name */
4390
      if (file)
4391
        file->ha_rename_table(to_base, from_base);
4392 4393
    }
  }
unknown's avatar
unknown committed
4394
  delete file;
4395 4396 4397
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
4398 4399
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
4400 4401
}

unknown's avatar
unknown committed
4402

unknown's avatar
unknown committed
4403
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
4404
			     const char* operator_name, const char* errmsg)
4405

unknown's avatar
unknown committed
4406
{
unknown's avatar
unknown committed
4407 4408
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
4409 4410
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
4411
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4412
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
4413
  thd->clear_error();
unknown's avatar
unknown committed
4414
  if (protocol->write())
unknown's avatar
unknown committed
4415 4416 4417 4418
    return -1;
  return 1;
}

4419

unknown's avatar
unknown committed
4420
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
4421
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
4422
{
unknown's avatar
unknown committed
4423 4424
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
4425
  TABLE_SHARE *share;
Konstantin Osipov's avatar
Konstantin Osipov committed
4426
  bool has_mdl_lock= FALSE;
unknown's avatar
unknown committed
4427 4428 4429
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
Konstantin Osipov's avatar
Konstantin Osipov committed
4430
  Open_table_context ot_ctx_unused(thd);
unknown's avatar
unknown committed
4431
  DBUG_ENTER("prepare_for_repair");
Konstantin Osipov's avatar
Konstantin Osipov committed
4432 4433
  uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH |
                                 MYSQL_OPEN_HAS_MDL_LOCK);
unknown's avatar
unknown committed
4434 4435 4436

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

Konstantin Osipov's avatar
Konstantin Osipov committed
4438
  if (!(table= table_list->table))
unknown's avatar
unknown committed
4439
  {
4440 4441 4442 4443
    char key[MAX_DBKEY_LENGTH];
    uint key_length;
    MDL_request mdl_global_request;
    MDL_request_list mdl_requests;
4444 4445 4446 4447 4448 4449 4450 4451
    /*
      If the table didn't exist, we have a shared metadata lock
      on it that is left from mysql_admin_table()'s attempt to 
      open it. Release the shared metadata lock before trying to
      acquire the exclusive lock to satisfy MDL asserts and avoid
      deadlocks.
    */
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4452 4453 4454 4455
    /*
      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.
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
4456
    my_hash_value_type hash_value;
unknown's avatar
unknown committed
4457 4458

    key_length= create_table_def_key(thd, key, table_list, 0);
Konstantin Osipov's avatar
Konstantin Osipov committed
4459 4460
    table_list->mdl_request.init(MDL_key::TABLE,
                                 table_list->db, table_list->table_name,
Konstantin Osipov's avatar
Konstantin Osipov committed
4461
                                 MDL_EXCLUSIVE);
4462 4463

    mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
4464 4465
    mdl_requests.push_front(&table_list->mdl_request);
    mdl_requests.push_front(&mdl_global_request);
4466

4467 4468
    if (thd->mdl_context.acquire_locks(&mdl_requests,
                                       thd->variables.lock_wait_timeout))
4469
      DBUG_RETURN(0);
Konstantin Osipov's avatar
Konstantin Osipov committed
4470
    has_mdl_lock= TRUE;
4471

Konstantin Osipov's avatar
Konstantin Osipov committed
4472
    hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
Marc Alff's avatar
Marc Alff committed
4473
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4474
    if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
Konstantin Osipov's avatar
Konstantin Osipov committed
4475
                                  &error, hash_value))))
unknown's avatar
unknown committed
4476
    {
Marc Alff's avatar
Marc Alff committed
4477
      mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4478
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
4479 4480
    }

unknown's avatar
unknown committed
4481
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
unknown's avatar
unknown committed
4482
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4483
      release_table_share(share);
Marc Alff's avatar
Marc Alff committed
4484
      mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4485 4486
      DBUG_RETURN(0);                           // Out of memory
    }
Marc Alff's avatar
Marc Alff committed
4487
    mysql_mutex_unlock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
4488
    table= &tmp_table;
Konstantin Osipov's avatar
Konstantin Osipov committed
4489
  }
4490 4491

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

4494 4495 4496 4497 4498 4499 4500 4501 4502
  /*
    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
4503

unknown's avatar
unknown committed
4504 4505 4506 4507 4508 4509 4510 4511 4512
  /*
    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
4513

4514 4515 4516
  if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  {
    error= send_check_errmsg(thd, table_list, "repair",
Timothy Smith's avatar
Timothy Smith committed
4517
                             "Failed repairing incompatible .frm file");
4518 4519
    goto end;
  }
4520

unknown's avatar
unknown committed
4521 4522
  /*
    Check if this is a table type that stores index and data separately,
4523 4524 4525
    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
4526
  */
unknown's avatar
unknown committed
4527
  ext= table->file->bas_ext();
unknown's avatar
unknown committed
4528 4529
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
4530

unknown's avatar
unknown committed
4531 4532
  // Name of data file
  strxmov(from, table->s->normalized_path.str, ext[1], NullS);
Marc Alff's avatar
Marc Alff committed
4533
  if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
unknown's avatar
unknown committed
4534
    goto end;				// Can't use USE_FRM flag
unknown's avatar
unknown committed
4535

4536 4537
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
4538

4539 4540
  if (table_list->table)
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4541 4542 4543 4544
    /*
      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
4545
    if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
4546
      goto end;
Konstantin Osipov's avatar
Konstantin Osipov committed
4547
    close_all_tables_for_name(thd, table_list->table->s, FALSE);
4548
    table_list->table= 0;
unknown's avatar
unknown committed
4549
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4550 4551 4552 4553 4554
  /*
    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().
  */
4555

unknown's avatar
unknown committed
4556
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
4557
  {
unknown's avatar
unknown committed
4558 4559 4560 4561 4562 4563 4564 4565 4566 4567
    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;
  }
Marc Alff's avatar
Marc Alff committed
4568
  if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
unknown's avatar
unknown committed
4569 4570 4571 4572
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
unknown's avatar
unknown committed
4573 4574
  }

Konstantin Osipov's avatar
Konstantin Osipov committed
4575 4576 4577
  if (thd->locked_tables_list.reopen_tables(thd))
    goto end;

4578 4579 4580 4581
  /*
    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
4582
  if (open_table(thd, table_list, thd->mem_root,
Konstantin Osipov's avatar
Konstantin Osipov committed
4583
                 &ot_ctx_unused, reopen_for_repair_flags))
unknown's avatar
unknown committed
4584
  {
4585 4586 4587
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
4588
  }
unknown's avatar
unknown committed
4589 4590

end:
Konstantin Osipov's avatar
Konstantin Osipov committed
4591
  thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
unknown's avatar
unknown committed
4592
  if (table == &tmp_table)
unknown's avatar
unknown committed
4593
  {
Marc Alff's avatar
Marc Alff committed
4594
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4595
    closefrm(table, 1);				// Free allocated memory
Marc Alff's avatar
Marc Alff committed
4596
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4597
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4598
  /* In case of a temporary table there will be no metadata lock. */
Konstantin Osipov's avatar
Konstantin Osipov committed
4599
  if (error && has_mdl_lock)
4600
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4601

unknown's avatar
unknown committed
4602
  DBUG_RETURN(error);
unknown's avatar
unknown committed
4603
}
4604

4605

4606

unknown's avatar
unknown committed
4607 4608
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
4609 4610 4611
    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
4612
*/
unknown's avatar
unknown committed
4613 4614 4615 4616 4617
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,
4618
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
4619 4620 4621
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
4622 4623 4624
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
4625
{
4626
  TABLE_LIST *table;
4627
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
4628
  List<Item> field_list;
unknown's avatar
unknown committed
4629 4630
  Item *item;
  Protocol *protocol= thd->protocol;
4631
  LEX *lex= thd->lex;
4632
  int result_code;
4633
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
4634

4635
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
unknown's avatar
unknown committed
4636 4637 4638 4639 4640 4641 4642
  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;
4643
  if (protocol->send_result_set_metadata(&field_list,
4644
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4645
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4646

4647
  mysql_ha_rm_tables(thd, tables);
4648

unknown's avatar
VIEW  
unknown committed
4649
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
4650 4651
  {
    char table_name[NAME_LEN*2+2];
4652
    char* db = table->db;
4653
    bool fatal_error=0;
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
4654
    bool open_error;
unknown's avatar
unknown committed
4655

4656 4657
    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
4658
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
4659
    thd->open_options|= extra_open_options;
4660 4661
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4662
    {
4663 4664 4665 4666 4667
      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;
4668
      select->table_list.first= (uchar*)table;
4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680
      /*
        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;
4681

Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
4682 4683
      open_error= open_and_lock_tables_derived(thd, table, TRUE,
                                               MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
4684 4685 4686 4687
      thd->no_warnings_for_error= 0;
      table->next_global= save_next_global;
      table->next_local= save_next_local;
      thd->open_options&= ~extra_open_options;
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
4688 4689 4690 4691 4692 4693 4694 4695 4696 4697
      /*
        Under locked tables, we know that the table can be opened,
        so any errors opening the table are logical errors.
        In these cases it does not make sense to try to repair.
      */
      if (open_error && thd->locked_tables_mode)
      {
        result_code= HA_ADMIN_FAILED;
        goto send_result;
      }
4698
#ifdef WITH_PARTITION_STORAGE_ENGINE
4699
      if (table->table)
4700 4701 4702 4703
      {
        /*
          Set up which partitions that should be processed
          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
4704
          CACHE INDEX/LOAD INDEX for specified partitions
4705 4706 4707
        */
        Alter_info *alter_info= &lex->alter_info;

4708
        if (alter_info->flags & ALTER_ADMIN_PARTITION)
4709
        {
4710 4711 4712 4713 4714
          if (!table->table->part_info)
          {
            my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
            DBUG_RETURN(TRUE);
          }
4715 4716 4717
          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,
4718
                                          PART_ADMIN);
4719
          if (num_parts_found != num_parts_opt &&
4720 4721 4722
              (!(alter_info->flags & ALTER_ALL_PARTITION)))
          {
            char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4723
            size_t length;
4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740
            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
4741
    }
4742 4743
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

unknown's avatar
unknown committed
4744
    if (prepare_func)
4745
    {
4746
      DBUG_PRINT("admin", ("calling prepare_func"));
unknown's avatar
unknown committed
4747
      switch ((*prepare_func)(thd, table, check_opt)) {
4748
      case  1:           // error, message written to net
Konstantin Osipov's avatar
Konstantin Osipov committed
4749 4750
        trans_rollback_stmt(thd);
        trans_rollback(thd);
4751
        close_thread_tables(thd);
4752
        DBUG_PRINT("admin", ("simple error, admin next table"));
4753 4754
        continue;
      case -1:           // error, message could be written to net
4755 4756
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4757
        goto err;
4758
        /* purecov: end */
4759
      default:           // should be 0 otherwise
4760
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4761
        ;
unknown's avatar
unknown committed
4762
      }
4763
    }
4764

4765
    /*
unknown's avatar
unknown committed
4766 4767 4768 4769 4770 4771
      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)
4772
    */
unknown's avatar
unknown committed
4773 4774
    if (!table->table)
    {
4775
      DBUG_PRINT("admin", ("open table failed"));
Marc Alff's avatar
Marc Alff committed
4776 4777
      if (thd->warning_info->is_empty())
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4778
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4779 4780 4781
      /* 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
4782
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4783
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
Marc Alff's avatar
Marc Alff committed
4784 4785 4786
      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
4787 4788 4789 4790 4791
        /* 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;
4792
      goto send_result;
unknown's avatar
unknown committed
4793
    }
4794 4795 4796

    if (table->view)
    {
4797
      DBUG_PRINT("admin", ("calling view_operator_func"));
4798 4799 4800 4801
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4802
    if (table->schema_table)
unknown's avatar
unknown committed
4803
    {
4804 4805
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
unknown's avatar
unknown committed
4806 4807
    }

4808
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
4809
    {
4810
      /* purecov: begin inspected */
unknown's avatar
unknown committed
4811
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4812
      size_t length;
4813
      DBUG_PRINT("admin", ("sending error message"));
unknown's avatar
unknown committed
4814
      protocol->prepare_for_resend();
4815 4816
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4817
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4818 4819 4820
      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
4821 4822
      trans_commit_stmt(thd);
      trans_commit(thd);
4823
      close_thread_tables(thd);
4824
      thd->mdl_context.release_transactional_locks();
4825
      lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
4826
      table->table=0;				// For query cache
unknown's avatar
unknown committed
4827
      if (protocol->write())
unknown's avatar
unknown committed
4828
	goto err;
Marc Alff's avatar
Marc Alff committed
4829
      thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
4830
      continue;
4831
      /* purecov: end */
unknown's avatar
unknown committed
4832 4833
    }

4834
    /* Close all instances of the table to allow repair to rename files */
4835
    if (lock_type == TL_WRITE && table->table->s->version)
4836
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4837 4838 4839 4840 4841 4842 4843
      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;);
4844 4845 4846
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4847 4848
    }

unknown's avatar
unknown committed
4849
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4850
    {
4851 4852
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4853 4854 4855
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4856 4857 4858
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4859 4860
      if (protocol->write())
        goto err;
4861
      /* purecov: end */
4862 4863
    }

4864 4865
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
unknown's avatar
unknown committed
4866 4867 4868 4869 4870
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4871
        DBUG_PRINT("admin", ("recreating table"));
Konstantin Osipov's avatar
Konstantin Osipov committed
4872
        trans_rollback_stmt(thd);
4873
        trans_rollback(thd);
unknown's avatar
unknown committed
4874
        close_thread_tables(thd);
4875
        thd->mdl_context.release_transactional_locks();
unknown's avatar
unknown committed
4876
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4877
        result_code= mysql_recreate_table(thd, table);
unknown's avatar
unknown committed
4878
        reenable_binlog(thd);
4879 4880 4881 4882 4883 4884
        /*
          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
4885 4886
        if (thd->stmt_da->is_ok())
          thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
4887 4888 4889 4890
        goto send_result;
      }
    }

4891
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4892
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4893
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4894 4895 4896

send_result:

4897
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
4898
    thd->clear_error();  // these errors shouldn't get client
4899
    {
Marc Alff's avatar
Marc Alff committed
4900
      List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
4901 4902 4903 4904 4905 4906
      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
4907 4908
        protocol->store(warning_level_names[err->get_level()].str,
                        warning_level_names[err->get_level()].length,
4909
                        system_charset_info);
Marc Alff's avatar
Marc Alff committed
4910
        protocol->store(err->get_message_text(), system_charset_info);
4911 4912 4913
        if (protocol->write())
          goto err;
      }
Marc Alff's avatar
Marc Alff committed
4914
      thd->warning_info->clear_warning_info(thd->query_id);
4915
    }
unknown's avatar
unknown committed
4916
    protocol->prepare_for_resend();
4917 4918
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
4919

4920 4921 4922
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4923 4924
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4925
      {
4926
       char buf[MYSQL_ERRMSG_SIZE];
4927
       size_t length=my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
4928
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4929
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4930
	protocol->store(buf, length, system_charset_info);
4931
      }
unknown's avatar
unknown committed
4932 4933
      break;

unknown's avatar
unknown committed
4934 4935
    case HA_ADMIN_NOT_BASE_TABLE:
      {
4936
        char buf[MYSQL_ERRMSG_SIZE];
4937
        size_t length= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
4938
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4939
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4940
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
4941 4942 4943
      }
      break;

4944
    case HA_ADMIN_OK:
4945 4946
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
4947 4948
      break;

4949
    case HA_ADMIN_FAILED:
4950 4951 4952
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
4953 4954
      break;

unknown's avatar
unknown committed
4955
    case HA_ADMIN_REJECT:
4956 4957 4958
      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
4959
      open_for_modify= FALSE;
unknown's avatar
unknown committed
4960 4961
      break;

4962
    case HA_ADMIN_ALREADY_DONE:
4963 4964 4965
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4966 4967
      break;

4968
    case HA_ADMIN_CORRUPT:
4969 4970
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4971
      fatal_error=1;
unknown's avatar
unknown committed
4972 4973
      break;

unknown's avatar
unknown committed
4974
    case HA_ADMIN_INVALID:
4975 4976 4977
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
4978 4979
      break;

4980 4981 4982 4983 4984 4985
    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.
4986
        We have to end the row, so analyze could return more rows.
4987
      */
4988 4989 4990
      trans_commit_stmt(thd);
      trans_commit(thd);
      close_thread_tables(thd);
4991
      thd->mdl_context.release_transactional_locks();
4992
      DEBUG_SYNC(thd, "ha_admin_try_alter");
4993 4994 4995 4996 4997 4998 4999
      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
5000 5001 5002
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
5003
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
5004
      result_code= mysql_recreate_table(thd, table);
5005
      reenable_binlog(thd);
5006 5007 5008 5009 5010 5011
      /*
        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
5012 5013
      if (thd->stmt_da->is_ok())
        thd->stmt_da->reset_diagnostics_area();
Konstantin Osipov's avatar
Konstantin Osipov committed
5014
      trans_commit_stmt(thd);
5015
      trans_commit(thd);
unknown's avatar
unknown committed
5016
      close_thread_tables(thd);
5017
      thd->mdl_context.release_transactional_locks();
5018 5019
      if (!result_code) // recreation went ok
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
5020 5021
        /* Clear the ticket released in close_thread_tables(). */
        table->mdl_request.ticket= NULL;
5022
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
5023
            ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
5024
          result_code= 0; // analyze went ok
5025 5026
        if (result_code)  // analyze failed
          table->table->file->print_error(result_code, MYF(0));
5027
      }
5028 5029 5030 5031
      /* 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);
5032 5033
      if (result_code) // either mysql_recreate_table or analyze failed
      {
5034 5035
        DBUG_ASSERT(thd->is_error());
        if (thd->is_error())
5036
        {
Marc Alff's avatar
Marc Alff committed
5037
          const char *err_msg= thd->stmt_da->message();
5038 5039
          if (!thd->vio_ok())
          {
Staale Smedseng's avatar
Staale Smedseng committed
5040
            sql_print_error("%s", err_msg);
5041 5042 5043 5044
          }
          else
          {
            /* Hijack the row already in-progress. */
5045
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
5046
            protocol->store(err_msg, system_charset_info);
5047 5048
            if (protocol->write())
              goto err;
5049 5050 5051 5052 5053
            /* 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);
          }
5054
          thd->clear_error();
5055 5056
        }
      }
5057
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
5058 5059
      table->next_local= save_next_local;
      table->next_global= save_next_global;
5060 5061
      goto send_result_message;
    }
5062 5063
    case HA_ADMIN_WRONG_CHECKSUM:
    {
5064
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
5065 5066
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
5067 5068
      break;
    }
5069

unknown's avatar
unknown committed
5070 5071 5072
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
5073
      char buf[MYSQL_ERRMSG_SIZE];
5074
      size_t length;
unknown's avatar
unknown committed
5075 5076

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
5077 5078
      length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
                         table->table_name);
unknown's avatar
unknown committed
5079 5080 5081 5082 5083
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

5084
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
5085
      {
5086
        char buf[MYSQL_ERRMSG_SIZE];
5087
        size_t length=my_snprintf(buf, sizeof(buf),
5088 5089
                                "Unknown - internal error %d during operation",
                                result_code);
5090
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
5091 5092 5093 5094
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
5095
    }
unknown's avatar
unknown committed
5096
    if (table->table)
5097
    {
unknown's avatar
unknown committed
5098 5099
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
5100
      else if (open_for_modify)
unknown's avatar
unknown committed
5101
      {
unknown's avatar
unknown committed
5102
        if (table->table->s->tmp_table)
unknown's avatar
merging  
unknown committed
5103 5104 5105
          table->table->file->info(HA_STATUS_CONST);
        else
        {
Konstantin Osipov's avatar
Konstantin Osipov committed
5106 5107 5108 5109
          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
5110 5111
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
5112 5113
        query_cache_invalidate3(thd, table->table, 0);
      }
5114
    }
5115
    /* Error path, a admin command failed. */
Konstantin Osipov's avatar
Konstantin Osipov committed
5116 5117
    trans_commit_stmt(thd);
    trans_commit_implicit(thd);
unknown's avatar
unknown committed
5118
    close_thread_tables(thd);
5119
    thd->mdl_context.release_transactional_locks();
unknown's avatar
unknown committed
5120
    table->table=0;				// For query cache
Konstantin Osipov's avatar
Konstantin Osipov committed
5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137

    /*
      If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
      separate open_tables() for each CHECK TABLE argument.
      Right now we do not have a separate method to reset the prelocking
      state in the lex to the state after parsing, so each open will pollute
      this state: add elements to lex->srotuines_list, TABLE_LISTs to
      lex->query_tables. Below is a lame attempt to recover from this
      pollution.
      @todo: have a method to reset a prelocking context, or use separate
      contexts for each open.
    */
    for (Sroutine_hash_entry *rt=
           (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
         rt; rt= rt->next)
      rt->mdl_request.ticket= NULL;

unknown's avatar
unknown committed
5138
    if (protocol->write())
unknown's avatar
unknown committed
5139 5140 5141
      goto err;
  }

5142
  my_eof(thd);
unknown's avatar
unknown committed
5143
  DBUG_RETURN(FALSE);
5144

5145
err:
Konstantin Osipov's avatar
Konstantin Osipov committed
5146 5147
  trans_rollback_stmt(thd);
  trans_rollback(thd);
5148
  close_thread_tables(thd);			// Shouldn't be needed
5149
  thd->mdl_context.release_transactional_locks();
unknown's avatar
unknown committed
5150 5151
  if (table)
    table->table=0;
unknown's avatar
unknown committed
5152
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5153 5154
}

unknown's avatar
unknown committed
5155

unknown's avatar
unknown committed
5156
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5157 5158 5159
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5160 5161 5162
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
5163
				&prepare_for_repair,
unknown's avatar
unknown committed
5164
				&handler::ha_repair, 0));
5165 5166
}

5167

unknown's avatar
unknown committed
5168
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5169 5170 5171
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5172
				"optimize", TL_WRITE, 1,0,0,0,
5173
				&handler::ha_optimize, 0));
5174 5175 5176
}


unknown's avatar
unknown committed
5177 5178 5179 5180 5181
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
5182 5183
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5184 5185

  RETURN VALUES
unknown's avatar
unknown committed
5186 5187
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
5188 5189
*/

unknown's avatar
unknown committed
5190
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
5191
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
5192
{
5193
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
5194
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
5195
  DBUG_ENTER("mysql_assign_to_keycache");
5196 5197

  check_opt.init();
Marc Alff's avatar
Marc Alff committed
5198
  mysql_mutex_lock(&LOCK_global_system_variables);
5199 5200
  if (!(key_cache= get_key_cache(key_cache_name)))
  {
Marc Alff's avatar
Marc Alff committed
5201
    mysql_mutex_unlock(&LOCK_global_system_variables);
5202
    my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
unknown's avatar
unknown committed
5203
    DBUG_RETURN(TRUE);
5204
  }
Marc Alff's avatar
Marc Alff committed
5205
  mysql_mutex_unlock(&LOCK_global_system_variables);
5206 5207
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
5208
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
5209
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
5210 5211
}

unknown's avatar
unknown committed
5212

unknown's avatar
unknown committed
5213 5214 5215 5216 5217
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
5218 5219
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5220 5221

  RETURN VALUES
unknown's avatar
unknown committed
5222 5223
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
5224 5225
*/

unknown's avatar
unknown committed
5226
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
5227 5228
{
  DBUG_ENTER("mysql_preload_keys");
5229 5230 5231 5232 5233
  /*
    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
5234
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
5235
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
5236
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
5237 5238 5239
}


unknown's avatar
unknown committed
5240 5241 5242 5243 5244
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
5245
    thd		Thread object
unknown's avatar
unknown committed
5246 5247
    table       Table list element for target table
    src_table   Table list element for source table
unknown's avatar
unknown committed
5248 5249 5250
    create_info Create info

  RETURN VALUES
unknown's avatar
unknown committed
5251 5252
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
5253 5254
*/

unknown's avatar
unknown committed
5255
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
5256
                             HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
5257
{
5258 5259
  HA_CREATE_INFO local_create_info;
  Alter_info local_alter_info;
unknown's avatar
unknown committed
5260
  bool res= TRUE;
unknown's avatar
unknown committed
5261
  uint not_used;
unknown's avatar
unknown committed
5262
  DBUG_ENTER("mysql_create_like_table");
5263

5264

unknown's avatar
unknown committed
5265
  /*
5266 5267 5268 5269 5270 5271 5272 5273 5274
    We the open source table to get its description in HA_CREATE_INFO
    and Alter_info objects. This also acquires a shared metadata lock
    on this table which ensures that no concurrent DDL operation will
    mess with it.
    Also in case when we create non-temporary table open_tables()
    call obtains an exclusive metadata lock on target table ensuring
    that we can safely perform table creation.
    Thus by holding both these locks we ensure that our statement is
    properly isolated from all concurrent operations which matter.
5275
  */
5276 5277 5278
  if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))
    goto err;
  src_table->table->use_all_columns();
unknown's avatar
unknown committed
5279

5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291
  /* Fill HA_CREATE_INFO and Alter_info with description of source table. */
  bzero((char*) &local_create_info, sizeof(local_create_info));
  local_create_info.db_type= src_table->table->s->db_type();
  local_create_info.row_type= src_table->table->s->row_type;
  if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info,
                                &local_alter_info))
    goto err;
#ifdef WITH_PARTITION_STORAGE_ENGINE
  /* Partition info is not handled by mysql_prepare_alter_table() call. */
  if (src_table->table->part_info)
    thd->work_part_info= src_table->table->part_info->get_clone();
#endif
unknown's avatar
unknown committed
5292

unknown's avatar
unknown committed
5293
  /*
5294 5295
    Adjust description of source table before using it for creation of
    target table.
Konstantin Osipov's avatar
Konstantin Osipov committed
5296

5297 5298
    Similarly to SHOW CREATE TABLE we ignore MAX_ROWS attribute of
    temporary table which represents I_S table.
5299
  */
5300
  if (src_table->schema_table)
5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311
    local_create_info.max_rows= 0;
  /* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */
  local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS;
  /* Replace type of source table with one specified in the statement. */
  local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
  local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE;
  /* Reset auto-increment counter for the new table. */
  local_create_info.auto_increment_value= 0;

  if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name,
                                       &local_create_info, &local_alter_info,
5312
                                       FALSE, 0)))
5313
    goto err;
unknown's avatar
unknown committed
5314 5315

  /*
5316 5317
    Ensure that we have an exclusive lock on target table if we are creating
    non-temporary table.
unknown's avatar
unknown committed
5318
  */
5319
  DBUG_ASSERT((create_info->options & HA_LEX_CREATE_TMP_TABLE) ||
5320
              local_create_info.table_existed ||
5321 5322 5323
              thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
                                             table->table_name,
                                             MDL_EXCLUSIVE));
unknown's avatar
unknown committed
5324

5325 5326 5327
  /*
    We have to write the query before we unlock the tables.
  */
5328
  if (thd->is_current_stmt_binlog_format_row())
5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346
  {
    /*
       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
5347
      if (src_table->table->s->tmp_table)               // Case 2
5348 5349 5350 5351
      {
        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
5352
        Open_table_context ot_ctx_unused(thd);
5353

5354
        /*
5355 5356 5357
          The condition avoids a crash as described in BUG#48506. Other
          binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE
          when the existing object is a view will be solved by BUG 47442.
5358
        */
5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369
        if (!table->view)
        {
          /*
            Here we open the destination table, on which we already have
            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.
          */
          if (open_table(thd, table, thd->mem_root, &ot_ctx_unused,
                         MYSQL_OPEN_REOPEN))
            goto err;
5370

5371 5372 5373
          int result __attribute__((unused))=
            store_create_info(thd, table, &query,
                              create_info, FALSE /* show_database */);
5374

5375 5376 5377
          DBUG_ASSERT(result == 0); // store_create_info() always return 0
          if (write_bin_log(thd, TRUE, query.ptr(), query.length()))
            goto err;
5378

5379 5380 5381 5382 5383 5384 5385 5386 5387 5388
          DBUG_ASSERT(thd->open_tables == table->table);
          mysql_mutex_lock(&LOCK_open);
          /*
            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);
          mysql_mutex_unlock(&LOCK_open);
        }
5389 5390
      }
      else                                      // Case 1
5391 5392
        if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
          goto err;
5393 5394 5395 5396 5397
    }
    /*
      Case 3 and 4 does nothing under RBR
    */
  }
5398 5399
  else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
    goto err;
5400

5401 5402
err:
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5403 5404 5405
}


unknown's avatar
unknown committed
5406
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5407
{
unknown's avatar
unknown committed
5408 5409
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5410 5411
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5412
				"analyze", lock_type, 1, 0, 0, 0,
5413
				&handler::ha_analyze, 0));
5414 5415 5416
}


unknown's avatar
unknown committed
5417
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
5418
{
unknown's avatar
unknown committed
5419 5420
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5421 5422
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
5423
				"check", lock_type,
5424
				0, 0, HA_OPEN_FOR_REPAIR, 0,
unknown's avatar
unknown committed
5425
				&handler::ha_check, &view_checksum));
5426 5427
}

unknown's avatar
unknown committed
5428

unknown's avatar
unknown committed
5429
/* table_list should contain just one table */
unknown's avatar
unknown committed
5430 5431 5432 5433
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
5434 5435 5436 5437 5438 5439
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
5440 5441 5442 5443
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
5444

5445
  thd_proc_info(thd, "discard_or_import_tablespace");
unknown's avatar
unknown committed
5446

unknown's avatar
unknown committed
5447
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
5448

unknown's avatar
unknown committed
5449 5450 5451 5452 5453
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
5454
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
unknown's avatar
unknown committed
5455 5456 5457 5458
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
5459

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

5462
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
5463 5464 5465 5466

  if (error)
    goto err;

unknown's avatar
unknown committed
5467 5468 5469 5470
  /*
    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
5471 5472 5473
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
Konstantin Osipov's avatar
Konstantin Osipov committed
5474 5475
  error= trans_commit_stmt(thd);
  if (trans_commit_implicit(thd))
unknown's avatar
unknown committed
5476 5477 5478
    error=1;
  if (error)
    goto err;
5479
  error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
5480

unknown's avatar
unknown committed
5481
err:
Konstantin Osipov's avatar
Konstantin Osipov committed
5482
  trans_rollback_stmt(thd);
unknown's avatar
unknown committed
5483
  thd->tablespace_op=FALSE;
5484

unknown's avatar
unknown committed
5485 5486
  if (error == 0)
  {
5487
    my_ok(thd);
unknown's avatar
unknown committed
5488
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5489
  }
unknown's avatar
unknown committed
5490

5491
  table->file->print_error(error, MYF(0));
5492

unknown's avatar
unknown committed
5493
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
5494
}
unknown's avatar
unknown committed
5495

5496

unknown's avatar
unknown committed
5497 5498
/*
  SYNOPSIS
5499 5500
    compare_tables()
      table                     The original table.
5501 5502
      alter_info                Alter options, fields and keys for the new
                                table.
5503 5504
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
5505 5506 5507 5508 5509 5510 5511 5512
      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
5513 5514 5515 5516
      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.
5517
      candidate_key_count OUT   The number of candidate keys in original table.
unknown's avatar
unknown committed
5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528

  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.

5529 5530 5531 5532 5533
    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
5534
  RETURN VALUES
5535 5536
    TRUE   error
    FALSE  success
unknown's avatar
unknown committed
5537 5538
*/

5539 5540 5541 5542 5543 5544
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
unknown's avatar
unknown committed
5545
               enum_alter_table_change_level *need_copy_table,
5546 5547
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
5548 5549
               uint **index_add_buffer, uint *index_add_count,
               uint *candidate_key_count)
unknown's avatar
unknown committed
5550 5551 5552
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
5553
  uint key_count;
5554 5555
  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
  Create_field *new_field, *tmp_new_field;
5556 5557
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5558
  THD *thd= table->in_use;
5559 5560 5561 5562 5563
  /*
    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;
5564 5565 5566
  bool not_nullable= true;
  DBUG_ENTER("compare_tables");

5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585
  /*
    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,
5586 5587 5588 5589 5590
                                 &tmp_alter_info,
                                 (table->s->tmp_table != NO_TMP_TABLE),
                                 &db_options,
                                 table->file, key_info_buffer,
                                 &key_count, 0))
5591 5592 5593
    DBUG_RETURN(1);
  /* Allocate result buffers. */
  if (! (*index_drop_buffer=
5594
         (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
5595
      ! (*index_add_buffer=
5596
         (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
5597
    DBUG_RETURN(1);
5598
  
unknown's avatar
unknown committed
5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615
  /*
    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
5616 5617 5618 5619 5620 5621 5622 5623

    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
5624
  */
5625
  if (table->s->fields != alter_info->create_list.elements ||
unknown's avatar
WL#2936  
unknown committed
5626
      table->s->db_type() != create_info->db_type ||
unknown's avatar
unknown committed
5627 5628 5629 5630
      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 ||
5631
      (table->s->row_type != create_info->row_type) ||
5632 5633
      create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
      create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
5634
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5635
      order_num ||
unknown's avatar
unknown committed
5636
      !table->s->mysql_version ||
5637
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5638 5639 5640 5641
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5642

5643 5644 5645 5646 5647 5648 5649
  /*
    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
5650 5651 5652 5653
  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
5654 5655 5656 5657 5658
  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
5659 5660 5661 5662
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5663

unknown's avatar
unknown committed
5664
    /* Check that NULL behavior is same for old and new fields */
5665
    if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
unknown's avatar
unknown committed
5666
	(uint) (field->flags & NOT_NULL_FLAG))
5667 5668 5669 5670
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
5671 5672 5673

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

5679
    /* Check if field was renamed */
5680
    field->flags&= ~FIELD_IS_RENAMED;
5681 5682
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
5683
		      tmp_new_field->field_name))
5684
      field->flags|= FIELD_IS_RENAMED;      
5685

unknown's avatar
unknown committed
5686
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
5687
    if (!(tmp= field->is_equal(tmp_new_field)))
5688 5689 5690 5691
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5692
    // Clear indexed marker
5693
    field->flags&= ~FIELD_IN_ADD_INDEX;
unknown's avatar
unknown committed
5694 5695 5696 5697 5698 5699 5700
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5701 5702 5703
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5704
  KEY *new_key_end= *key_info_buffer + key_count;
unknown's avatar
unknown committed
5705

5706 5707 5708 5709 5710 5711 5712
  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;
5713
  *candidate_key_count= 0;
5714
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
unknown's avatar
unknown committed
5715
  {
5716 5717 5718 5719
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735
   /*
      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)++;

5736
    /* Search a new key with the same name. */
5737
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5738 5739 5740 5741 5742 5743 5744
    {
      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. */
5745
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5746 5747 5748 5749 5750 5751 5752 5753 5754 5755
      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
5756 5757 5758 5759 5760

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5761 5762 5763
    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
5764 5765 5766
    {
      /*
	Key definition has changed if we are using a different field or
5767 5768
	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
5769
      */
5770 5771 5772
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
unknown's avatar
unknown committed
5773
    }
5774 5775 5776 5777
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5778 5779
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5780 5781 5782 5783 5784 5785
    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];
5786
      field->flags|= FIELD_IN_ADD_INDEX;
5787
    }
5788
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
unknown's avatar
unknown committed
5789
  }
5790
  /*end of for (; table_key < table_key_end;) */
unknown's avatar
unknown committed
5791

5792 5793 5794
  /*
    Step through all keys of the new table and find matching old keys.
  */
5795
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5796 5797 5798 5799 5800 5801 5802 5803 5804 5805
  {
    /* 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. */
5806
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5807 5808 5809 5810 5811 5812
      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];
5813
        field->flags|= FIELD_IN_ADD_INDEX;
5814
      }
unknown's avatar
unknown committed
5815
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5816 5817
    }
  }
5818 5819 5820

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5821 5822 5823 5824
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5825

5826
  if (*index_drop_count || *index_add_count)
5827 5828 5829 5830
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5831

5832 5833
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5834 5835 5836
}


unknown's avatar
unknown committed
5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862
/*
  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:
5863
    error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5864 5865 5866 5867 5868 5869
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
5870
    error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5871 5872 5873 5874 5875
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5876 5877
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
unknown's avatar
unknown committed
5878 5879 5880 5881 5882 5883 5884
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}

5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913
/**
  maximum possible length for certain blob types.

  @param[in]      type        Blob type (e.g. MYSQL_TYPE_TINY_BLOB)

  @return
    length
*/

static uint
blob_length_by_type(enum_field_types type)
{
  switch (type)
  {
  case MYSQL_TYPE_TINY_BLOB:
    return 255;
  case MYSQL_TYPE_BLOB:
    return 65535;
  case MYSQL_TYPE_MEDIUM_BLOB:
    return 16777215;
  case MYSQL_TYPE_LONG_BLOB:
    return 4294967295U;
  default:
    DBUG_ASSERT(0); // we should never go here
    return 0;
  }
}


5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952
/**
  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
5953
*/
5954

5955 5956 5957 5958
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
unknown's avatar
unknown committed
5959
{
5960
  /* New column definitions are added here */
unknown's avatar
unknown committed
5961
  List<Create_field> new_create_list;
5962 5963 5964
  /* 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
5965
  List_iterator<Create_field> def_it(alter_info->create_list);
5966 5967
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
unknown's avatar
unknown committed
5968 5969 5970
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5971 5972 5973 5974 5975
  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;
5976

5977
  DBUG_ENTER("mysql_prepare_alter_table");
5978

5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989
  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)
5990
  {
5991 5992 5993
    /* 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;
5994
  }
5995 5996
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
unknown's avatar
unknown committed
5997

5998
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
5999
  {
6000
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
6001
    /*
6002 6003
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
6004
    */
6005 6006 6007 6008 6009
    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
6010
  Create_field *def;
6011

6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023
  /*
    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++))
6024
    {
6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036
      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;
      }
6037
    }
6038
    if (drop)
6039
    {
6040 6041
      drop_it.remove();
      continue;
6042
    }
6043 6044 6045
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
6046
    {
6047 6048 6049
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
6050
    }
6051 6052 6053 6054
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
unknown's avatar
unknown committed
6055
      {
6056 6057
	new_create_list.push_back(def);
	def_it.remove();
unknown's avatar
unknown committed
6058 6059
      }
    }
6060
    else
6061 6062
    {
      /*
6063 6064
        This field was not dropped and not changed, add it to the list
        for the new table.
6065
      */
unknown's avatar
unknown committed
6066
      def= new Create_field(field, field);
6067 6068 6069 6070
      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
6071
      {
6072 6073
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
6074
      }
6075
      if (alter)
unknown's avatar
unknown committed
6076
      {
6077
	if (def->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
6078
	{
6079
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
6080
          goto err;
unknown's avatar
unknown committed
6081
	}
6082 6083 6084 6085 6086
	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
6087 6088 6089
      }
    }
  }
6090 6091
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
6092
  {
6093
    if (def->change && ! def->field)
6094
    {
6095
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
6096
      goto err;
6097
    }
unknown's avatar
unknown committed
6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114
    /*
      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;
    }
6115 6116 6117 6118
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
6119
    else
6120
    {
unknown's avatar
unknown committed
6121
      Create_field *find;
6122 6123 6124 6125 6126 6127 6128 6129
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
6130
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
6131 6132 6133
        goto err;
      }
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
6134
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
6135
    }
6136
  }
6137
  if (alter_info->alter_list.elements)
6138
  {
6139
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
6140
             alter_info->alter_list.head()->name, table->s->table_name.str);
unknown's avatar
unknown committed
6141
    goto err;
6142
  }
6143
  if (!new_create_list.elements)
unknown's avatar
unknown committed
6144
  {
unknown's avatar
unknown committed
6145 6146
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
6147
    goto err;
unknown's avatar
unknown committed
6148 6149 6150
  }

  /*
6151 6152
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
6153 6154
  */

6155
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
6156
  {
6157
    char *key_name= key_info->name;
unknown's avatar
unknown committed
6158 6159 6160 6161 6162
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
6163
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178
	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
6179
      Create_field *cfield;
unknown's avatar
unknown committed
6180 6181 6182 6183 6184
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
6185 6186
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
6187 6188
	    break;
	}
6189
	else if (!my_strcasecmp(system_charset_info,
6190
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
6191
	  break;
unknown's avatar
unknown committed
6192 6193 6194 6195 6196
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
6197 6198 6199 6200 6201 6202 6203
      {
        /*
          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.

6204 6205 6206
          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.

6207 6208
          BLOBs may have cfield->length == 0, which is why we test it before
          checking whether cfield->length < key_part_length (in chars).
6209 6210 6211 6212 6213 6214 6215 6216
          
          In case of TEXTs we check the data type maximum length *in bytes*
          to key part length measured *in characters* (i.e. key_part_length
          devided to mbmaxlen). This is because it's OK to have:
          CREATE TABLE t1 (a tinytext, key(a(254)) character set utf8);
          In case of this example:
          - data type maximum length is 255.
          - key_part_length is 1016 (=254*4, where 4 is mbmaxlen)
6217 6218
         */
        if (!Field::type_can_have_key_part(cfield->field->type()) ||
unknown's avatar
unknown committed
6219
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
6220 6221
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
unknown's avatar
unknown committed
6222 6223
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
6224 6225 6226 6227 6228
            (cfield->length && (((cfield->sql_type >= MYSQL_TYPE_TINY_BLOB &&
                                  cfield->sql_type <= MYSQL_TYPE_BLOB) ? 
                                blob_length_by_type(cfield->sql_type) :
                                cfield->length) <
	     key_part_length / key_part->field->charset()->mbmaxlen)))
6229
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
6230
      }
6231
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
6232
      key_parts.push_back(new Key_part_spec(cfield->field_name,
6233
                                            strlen(cfield->field_name),
unknown's avatar
unknown committed
6234 6235 6236
					    key_part_length));
    }
    if (key_parts.elements)
6237 6238
    {
      KEY_CREATE_INFO key_create_info;
6239 6240
      Key *key;
      enum Key::Keytype key_type;
6241 6242 6243 6244 6245 6246
      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)
6247
        key_create_info.parser_name= *plugin_name(key_info->parser);
6248 6249
      if (key_info->flags & HA_USES_COMMENT)
        key_create_info.comment= key_info->comment;
6250

6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264
      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;

6265
      key= new Key(key_type, key_name, strlen(key_name),
6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277
                   &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);
6278 6279
      if (key->name.str &&
	  !my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
6280
      {
6281
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 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
        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)
{
6379
  TABLE *table, *new_table= 0;
Konstantin Osipov's avatar
Konstantin Osipov committed
6380
  MDL_ticket *mdl_ticket;
Konstantin Osipov's avatar
Konstantin Osipov committed
6381 6382
  MDL_request target_mdl_request;
  bool has_target_mdl_lock= FALSE;
6383
  int error= 0;
6384
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
6385 6386
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
6387
  char path[FN_REFLEN + 1];
6388
  char reg_path[FN_REFLEN+1];
unknown's avatar
unknown committed
6389
  ha_rows copied,deleted;
6390
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
6391
  legacy_db_type table_type;
6392
  frm_type_enum frm_type;
unknown's avatar
unknown committed
6393
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
6394
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6395
  uint fast_alter_partition= 0;
6396 6397
  bool partition_changed= FALSE;
#endif
6398 6399
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
Staale Smedseng's avatar
Staale Smedseng committed
6400 6401 6402 6403 6404
  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
6405
  bool committed= 0;
6406
  bool no_pk;
unknown's avatar
unknown committed
6407 6408
  DBUG_ENTER("mysql_alter_table");

6409 6410 6411 6412 6413 6414
  /*
    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.
  */
6415
  if (table_list && table_list->db && table_list->table_name)
6416
  {
6417
    int table_kind= 0;
6418

6419 6420 6421
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
6422

6423
    if (table_kind)
6424
    {
6425 6426 6427 6428 6429 6430
      /* 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);
      }
6431

6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443
      /* 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)
      {
6444
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
6445 6446 6447
        DBUG_RETURN(TRUE);
      }
#endif
6448 6449 6450
    }
  }

6451 6452 6453 6454 6455
  /*
    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.
  */
6456
  thd_proc_info(thd, "init");
6457
  table_name=table_list->table_name;
unknown's avatar
unknown committed
6458
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
unknown's avatar
unknown committed
6459
  db=table_list->db;
unknown's avatar
unknown committed
6460
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
6461
    new_db= db;
6462 6463
  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);
6464

6465
  mysql_ha_rm_tables(thd, table_list);
unknown's avatar
unknown committed
6466

unknown's avatar
unknown committed
6467
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
6468
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
6469
    /* Conditionally writes to binlog. */
unknown's avatar
unknown committed
6470
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
6471
						   alter_info->tablespace_op));
6472 6473 6474
  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);
6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487
  /*
    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.
  */
6488 6489
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
6490
  /* Sic: there is a race here */
6491 6492
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506
    /*
      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);
    }

6507 6508 6509 6510 6511
    /*
      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
6512
    if (thd->locked_tables_mode || thd->active_transaction())
6513 6514 6515
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
6516
      DBUG_RETURN(TRUE);
6517 6518
    }

6519
    if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
6520
      DBUG_RETURN(TRUE);
6521
    if (lock_table_names(thd, table_list))
6522
    {
unknown's avatar
unknown committed
6523
      error= 1;
6524
      goto view_err;
6525
    }
6526

6527
    mysql_mutex_lock(&LOCK_open);
6528

6529 6530 6531 6532 6533
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
6534
        Query_log_event qinfo(thd, thd->query(), thd->query_length(),
6535
                              FALSE, TRUE, FALSE, 0);
6536
        if ((error= mysql_bin_log.write(&qinfo)))
6537
          goto view_err_unlock;
6538
      }
6539
      my_ok(thd);
6540 6541
    }

6542
view_err_unlock:
6543
    mysql_mutex_unlock(&LOCK_open);
6544
    unlock_table_names(thd);
6545 6546

view_err:
6547
    thd->global_read_lock.start_waiting_global_read_lock(thd);
6548 6549
    DBUG_RETURN(error);
  }
6550

Konstantin Osipov's avatar
Konstantin Osipov committed
6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566

  /*
    Code below can handle only base tables so ensure that we won't open a view.
    Note that RENAME TABLE the only ALTER clause which is supported for views
    has been already processed.
  */
  table_list->required_type= FRMTYPE_TABLE;

  Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);

  error= open_and_lock_tables_derived(thd, table_list, FALSE,
                                      MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
                                      &alter_prelocking_strategy);

  if (error)
  {
unknown's avatar
unknown committed
6567
    DBUG_RETURN(TRUE);
Konstantin Osipov's avatar
Konstantin Osipov committed
6568 6569 6570
  }

  table= table_list->table;
6571
  table->use_all_columns();
Konstantin Osipov's avatar
Konstantin Osipov committed
6572
  mdl_ticket= table->mdl_ticket;
unknown's avatar
unknown committed
6573

6574 6575 6576 6577 6578 6579
  /*
    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
6580 6581
  if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
       thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) &&
6582 6583 6584 6585 6586 6587 6588
      (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
6589 6590 6591
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
6592
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
unknown's avatar
unknown committed
6593
    strmov(new_name_buff,new_name);
unknown's avatar
unknown committed
6594
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
6595
    if (lower_case_table_names)
unknown's avatar
unknown committed
6596 6597 6598
    {
      if (lower_case_table_names != 2)
      {
6599
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
6600 6601
	new_alias= new_name;			// Create lower case table name
      }
6602
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
6603
    }
6604
    if (new_db == db &&
unknown's avatar
unknown committed
6605
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
6606 6607
    {
      /*
6608 6609
	Source and destination table names are equal: make later check
	easier.
6610
      */
unknown's avatar
unknown committed
6611
      new_alias= new_name= table_name;
6612
    }
unknown's avatar
unknown committed
6613 6614
    else
    {
unknown's avatar
unknown committed
6615
      if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6616 6617 6618
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
6619
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
6620
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6621 6622 6623 6624
	}
      }
      else
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6625 6626
        target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
                                MDL_EXCLUSIVE);
6627 6628 6629 6630
        /*
          Global intention exclusive lock must have been already acquired when
          table to be altered was open, so there is no need to do it here.
        */
6631 6632 6633
        DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL,
                                                   "", "",
                                                   MDL_INTENTION_EXCLUSIVE));
6634

6635
        if (thd->mdl_context.try_acquire_lock(&target_mdl_request))
unknown's avatar
unknown committed
6636
          DBUG_RETURN(TRUE);
Konstantin Osipov's avatar
Konstantin Osipov committed
6637
        if (target_mdl_request.ticket == NULL)
unknown's avatar
unknown committed
6638
        {
Konstantin Osipov's avatar
Konstantin Osipov committed
6639
          /* Table exists and is locked by some thread. */
unknown's avatar
unknown committed
6640 6641 6642
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
	  DBUG_RETURN(TRUE);
        }
Konstantin Osipov's avatar
Konstantin Osipov committed
6643 6644 6645 6646 6647 6648
        DEBUG_SYNC(thd, "locked_table_name");
        has_target_mdl_lock= TRUE;
        /*
          Table maybe does not exist, but we got an exclusive lock
          on the name, now we can safely try to find out for sure.
        */
6649
        build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
6650 6651
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
unknown's avatar
unknown committed
6652 6653
	{
	  /* Table will be closed in do_command() */
6654
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
6655
          goto err;
unknown's avatar
unknown committed
6656 6657 6658 6659 6660
	}
      }
    }
  }
  else
6661 6662 6663 6664
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
6665

unknown's avatar
WL#2936  
unknown committed
6666
  old_db_type= table->s->db_type();
unknown's avatar
unknown committed
6667
  if (!create_info->db_type)
6668
  {
6669
#ifdef WITH_PARTITION_STORAGE_ENGINE
6670 6671
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
6672 6673 6674 6675
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
6676 6677 6678
        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.
6679
      */
6680
      create_info->db_type= table->part_info->default_engine_type;
6681
    }
6682
    else
6683
#endif
6684
      create_info->db_type= old_db_type;
6685
  }
unknown's avatar
unknown committed
6686

6687
  if (check_engine(thd, new_name, create_info))
unknown's avatar
unknown committed
6688
    goto err;
6689
  new_db_type= create_info->db_type;
6690

6691 6692
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
6693
      !table->file->can_switch_engines())
unknown's avatar
unknown committed
6694
  {
6695
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
unknown's avatar
unknown committed
6696 6697 6698
    goto err;
  }

6699 6700 6701 6702 6703
  /*
   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.
  */
6704
  if (create_info->row_type == ROW_TYPE_NOT_USED)
6705
  {
6706
    /* ALTER TABLE without explicit row type */
6707
    create_info->row_type= table->s->row_type;
6708 6709 6710 6711
  }
  else
  {
    /* ALTER TABLE with specific row type */
6712 6713
    create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
  }
unknown's avatar
unknown committed
6714

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

    if (!error && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6760
    {
6761
      thd_proc_info(thd, "rename");
unknown's avatar
unknown committed
6762 6763 6764
      /*
        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
6765
        Note that if wait_while_table_is_used() returns error here (i.e. if
6766
        this thread was killed) then it must be that previous step of
Konstantin Osipov's avatar
Konstantin Osipov committed
6767
        simple rename did nothing and therefore we can safely return
6768
        without additional clean-up.
unknown's avatar
unknown committed
6769
      */
Konstantin Osipov's avatar
Konstantin Osipov committed
6770
      if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
6771
        goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
6772
      close_all_tables_for_name(thd, table->s, TRUE);
unknown's avatar
unknown committed
6773 6774 6775
      /*
        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
6776 6777 6778
        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
6779 6780
        TODO: Investigate if we need this access() check at all.
      */
6781 6782
      if (!access(new_name_buff,F_OK))
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6783 6784
        my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
        error= -1;
6785 6786 6787
      }
      else
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6788
        *fn_ext(new_name)=0;
6789
        mysql_mutex_lock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
6790 6791
        if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
          error= -1;
6792 6793 6794
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
Konstantin Osipov's avatar
Konstantin Osipov committed
6795
          (void) mysql_rename_table(old_db_type, new_db, new_alias, db,
Konstantin Osipov's avatar
Konstantin Osipov committed
6796
                                    table_name, 0);
6797 6798
          error= -1;
        }
6799
        mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6800 6801
      }
    }
6802

6803 6804 6805 6806
    if (error == HA_ERR_WRONG_COMMAND)
    {
      error= 0;
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
Konstantin Osipov's avatar
Konstantin Osipov committed
6807 6808
                          ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                          table->alias);
6809
    }
6810

6811 6812
    if (!error)
    {
6813 6814 6815
      error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
      if (!error)
        my_ok(thd);
6816
    }
6817
    else if (error > 0)
6818
    {
6819 6820
      table->file->print_error(error, MYF(0));
      error= -1;
6821
    }
6822 6823
    table_list->table= NULL;                    // For query cache
    query_cache_invalidate3(thd, table_list, 0);
6824

Konstantin Osipov's avatar
Konstantin Osipov committed
6825 6826
    if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
         thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
6827 6828 6829 6830 6831 6832 6833
    {
      /*
        Under LOCK TABLES we should adjust meta-data locks before finishing
        statement. Otherwise we can rely on close_thread_tables() releasing
        them.
      */
      if (new_name != table_name || new_db != db)
Konstantin Osipov's avatar
Konstantin Osipov committed
6834
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
6835
        thd->mdl_context.release_lock(target_mdl_request.ticket);
Konstantin Osipov's avatar
Konstantin Osipov committed
6836
        thd->mdl_context.release_all_locks_for_name(mdl_ticket);
Konstantin Osipov's avatar
Konstantin Osipov committed
6837
      }
6838
      else
6839
        mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
6840
    }
6841
    DBUG_RETURN(error);
unknown's avatar
unknown committed
6842 6843
  }

6844
  /* We have to do full alter table. */
unknown's avatar
unknown committed
6845

6846 6847 6848
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6849
    goto err;
6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862
#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
6863

6864 6865
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
6866

unknown's avatar
unknown committed
6867
  need_copy_table= alter_info->change_level;
unknown's avatar
unknown committed
6868

unknown's avatar
unknown committed
6869 6870
  set_table_default_charset(thd, create_info, db);

6871
  if (thd->variables.old_alter_table
unknown's avatar
WL#2936  
unknown committed
6872
      || (table->s->db_type() != create_info->db_type)
6873
#ifdef WITH_PARTITION_STORAGE_ENGINE
6874
      || partition_changed
6875
#endif
6876
     )
6877
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
unknown's avatar
unknown committed
6878
  else
6879
  {
unknown's avatar
unknown committed
6880
    enum_alter_table_change_level need_copy_table_res;
6881
    /* Check how much the tables differ. */
6882 6883
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6884
                       &need_copy_table_res,
6885 6886
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
6887 6888
                       &index_add_buffer, &index_add_count,
                       &candidate_key_count))
6889
      goto err;
Konstantin Osipov's avatar
Konstantin Osipov committed
6890

6891 6892
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6893 6894 6895 6896 6897 6898 6899 6900 6901 6902
  }

  /*
    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
6903
    ulong alter_flags= 0;
6904 6905 6906 6907 6908 6909
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

6910
    alter_flags= table->file->alter_table_flags(alter_info->flags);
unknown's avatar
unknown committed
6911
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6912 6913 6914 6915 6916 6917 6918 6919 6920
    /* 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)
      {
6921 6922 6923 6924 6925
        /* 
           Unique key. Check for "PRIMARY". 
           or if dropping last unique key
        */
        if ((uint) (key - table->key_info) == table->s->primary_key)
6926
        {
6927
          DBUG_PRINT("info", ("Dropping primary key"));
6928 6929 6930 6931
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
6932
          candidate_key_count--;
6933 6934 6935
        }
        else
        {
6936 6937 6938
          KEY_PART_INFO *part_end= key->key_part + key->key_parts;
          bool is_candidate_key= true;

6939 6940 6941
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954

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

7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036
    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));
7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047
    /*
      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. */
7048
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
7049 7050 7051 7052 7053
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
7054
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
7055 7056 7057 7058 7059
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
unknown's avatar
unknown committed
7060

7061 7062
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
7063
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
7064 7065
    so that ALTER TABLE won't break when somebody will add new flag
  */
7066
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7067
    create_info->frm_only= 1;
7068

7069
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
7070
  if (fast_alter_partition)
unknown's avatar
unknown committed
7071
  {
unknown's avatar
unknown committed
7072 7073 7074 7075
    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
7076
  }
7077
#endif
unknown's avatar
unknown committed
7078

7079 7080 7081 7082 7083 7084
  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
7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099
  /*
    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.
7100 7101
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
unknown's avatar
unknown committed
7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127
      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);
    }
  }
7128 7129
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
7130

7131 7132 7133 7134 7135 7136
  /*
    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
7137
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
7138 7139 7140
                                    create_info,
                                    alter_info,
                                    1, 0);
7141 7142
  reenable_binlog(thd);
  if (error)
unknown's avatar
unknown committed
7143
    goto err;
7144 7145

  /* Open the table if we need to copy the data. */
7146
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
7147
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7148
  {
7149
    if (table->s->tmp_table)
7150
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7151
      Open_table_context ot_ctx_unused(thd);
7152 7153 7154
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
7155
      tbl.table_name= tbl.alias= tmp_name;
unknown's avatar
unknown committed
7156
      /* Table is in thd->temporary_tables */
Konstantin Osipov's avatar
Konstantin Osipov committed
7157
      (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx_unused,
Konstantin Osipov's avatar
Konstantin Osipov committed
7158 7159
                        MYSQL_LOCK_IGNORE_FLUSH);
      new_table= tbl.table;
7160 7161 7162
    }
    else
    {
7163
      char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
7164
      /* table is a normal table: Create temporary table in same directory */
7165
      build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
7166
                           FN_IS_TMP);
7167
      /* Open our intermediate table */
7168
      new_table= open_temporary_table(thd, path, new_db, tmp_name, 1);
7169 7170
    }
    if (!new_table)
7171
      goto err_new_table_cleanup;
7172 7173 7174 7175
    /*
      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
7176 7177
  }

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

7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228
  /* 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"));

7229
    table->file->ha_prepare_for_alter();
7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256
    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;
7257
        goto err_new_table_cleanup;
7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279
      }
    }
    /*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));
7280
        goto err_new_table_cleanup;
7281 7282 7283 7284 7285 7286
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
7287
        goto err_new_table_cleanup;
7288 7289 7290 7291
      }
    }
    /*end of if (index_drop_count)*/

7292 7293 7294 7295
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
7296

7297 7298
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
Konstantin Osipov's avatar
Konstantin Osipov committed
7299
    if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
7300
      goto err_new_table_cleanup;
7301
    committed= 1;
7302 7303 7304
  }
  /*end of if (! new_table) for add/drop index*/

7305 7306 7307
  if (error)
    goto err_new_table_cleanup;

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

7330 7331 7332 7333 7334
  /*
    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().
  */
7335 7336
  if (new_table)
  {
7337 7338
    close_temporary_table(thd, new_table, 1, 0);
    new_table= 0;
unknown's avatar
unknown committed
7339
  }
7340

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

7358
  thd_proc_info(thd, "rename result table");
7359 7360
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
7361 7362
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
unknown's avatar
unknown committed
7363

7364 7365 7366 7367
  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
7368 7369
  close_all_tables_for_name(thd, table->s,
                            new_name != table_name || new_db != db);
unknown's avatar
unknown committed
7370

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

Konstantin Osipov's avatar
Konstantin Osipov committed
7418 7419 7420
  if (! error)
    (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);

7421
  mysql_mutex_unlock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
7422

unknown's avatar
unknown committed
7423 7424
  if (error)
  {
7425
    /* This shouldn't happen. But let us play it safe. */
Konstantin Osipov's avatar
Konstantin Osipov committed
7426
    goto err_with_mdl;
unknown's avatar
unknown committed
7427
  }
7428

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

Konstantin Osipov's avatar
Konstantin Osipov committed
7463 7464 7465 7466 7467
    /* 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);
7468
    mysql_mutex_lock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
7469
    close_thread_table(thd, &thd->open_tables);
7470
    mysql_mutex_unlock(&LOCK_open);
Konstantin Osipov's avatar
Konstantin Osipov committed
7471
    table_list->table= 0;
7472

unknown's avatar
unknown committed
7473
    if (error)
Konstantin Osipov's avatar
Konstantin Osipov committed
7474
      goto err_with_mdl;
unknown's avatar
unknown committed
7475
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
7476 7477
  if (thd->locked_tables_list.reopen_tables(thd))
    goto err_with_mdl;
7478

7479
  thd_proc_info(thd, "end");
7480

7481 7482
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

7483
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
7484
                      thd->query(), thd->query_length(),
7485 7486
                      db, table_name);

unknown's avatar
unknown committed
7487
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
7488
                thd->is_current_stmt_binlog_format_row() &&
7489
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
7490 7491
  if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
    DBUG_RETURN(TRUE);
7492

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

Konstantin Osipov's avatar
Konstantin Osipov committed
7517 7518
  if (thd->locked_tables_mode == LTM_LOCK_TABLES ||
      thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
unknown's avatar
unknown committed
7519
  {
7520 7521
    if ((new_name != table_name || new_db != db))
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7522
      thd->mdl_context.release_lock(target_mdl_request.ticket);
Konstantin Osipov's avatar
Konstantin Osipov committed
7523
      thd->mdl_context.release_all_locks_for_name(mdl_ticket);
7524 7525
    }
    else
7526
      mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
unknown's avatar
unknown committed
7527 7528
  }

unknown's avatar
unknown committed
7529
end_temporary:
7530 7531
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
7532
	      (ulong) thd->warning_info->statement_warn_count());
7533
  my_ok(thd, copied + deleted, 0L, tmp_name);
unknown's avatar
unknown committed
7534
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7535

7536
err_new_table_cleanup:
7537 7538 7539 7540 7541 7542
  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
7543 7544
    (void) quick_rm_table(new_db_type, new_db, tmp_name,
                          create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
7545

7546
err:
7547 7548 7549 7550 7551 7552
  /*
    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
7553 7554
  if (alter_info->error_if_not_empty &&
      thd->warning_info->current_row_for_warning())
7555
  {
7556 7557
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
unknown's avatar
unknown committed
7558
    switch (alter_info->datetime_field->sql_type)
7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574
    {
      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
7575
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
unknown's avatar
unknown committed
7576
                                 f_val, strlength(f_val), t_type,
unknown's avatar
unknown committed
7577
                                 alter_info->datetime_field->field_name);
7578 7579
    thd->abort_on_warning= save_abort_on_warning;
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
7580 7581 7582
  if (has_target_mdl_lock)
    thd->mdl_context.release_lock(target_mdl_request.ticket);

unknown's avatar
unknown committed
7583
  DBUG_RETURN(TRUE);
7584

Konstantin Osipov's avatar
Konstantin Osipov committed
7585
err_with_mdl:
7586
  /*
Konstantin Osipov's avatar
Konstantin Osipov committed
7587
    An error happened while we were holding exclusive name metadata lock
Konstantin Osipov's avatar
Konstantin Osipov committed
7588 7589 7590
    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.
7591
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
7592
  thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
Konstantin Osipov's avatar
Konstantin Osipov committed
7593 7594 7595
  if (has_target_mdl_lock)
    thd->mdl_context.release_lock(target_mdl_request.ticket);

Konstantin Osipov's avatar
Konstantin Osipov committed
7596
  thd->mdl_context.release_all_locks_for_name(mdl_ticket);
unknown's avatar
unknown committed
7597
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7598
}
unknown's avatar
unknown committed
7599
/* mysql_alter_table */
unknown's avatar
unknown committed
7600 7601

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

7627 7628 7629 7630 7631 7632
  /*
    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
  */
7633
  error= ha_enable_transaction(thd, FALSE);
7634 7635
  if (error)
    DBUG_RETURN(-1);
7636
  
7637
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
7638 7639
    DBUG_RETURN(-1);				/* purecov: inspected */

7640
  if (to->file->ha_external_lock(thd, F_WRLCK))
7641
    DBUG_RETURN(-1);
7642

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

7646 7647 7648 7649 7650
  /* 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));

7651
  from->file->info(HA_STATUS_VARIABLE);
7652
  to->file->ha_start_bulk_insert(from->file->stats.records);
unknown's avatar
unknown committed
7653

7654 7655
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
7656 7657
  List_iterator<Create_field> it(create);
  Create_field *def;
unknown's avatar
unknown committed
7658 7659 7660 7661 7662
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
7663 7664
    {
      if (*ptr == to->next_number_field)
7665
      {
7666
        auto_increment_field_copied= TRUE;
7667 7668 7669 7670 7671 7672 7673 7674 7675
        /*
          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
7676
      (copy_end++)->set(*ptr,def->field,0);
7677 7678
    }

unknown's avatar
unknown committed
7679 7680
  }

7681 7682
  found_count=delete_count=0;

unknown's avatar
unknown committed
7683 7684
  if (order)
  {
7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702
    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
7703

7704 7705 7706 7707 7708 7709 7710 7711 7712 7713
      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
7714 7715
  };

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

unknown's avatar
unknown committed
7772 7773 7774
	to->file->print_error(error,MYF(0));
	break;
      }
7775
      to->file->restore_auto_increment(prev_insert_id);
unknown's avatar
unknown committed
7776 7777 7778
      delete_count++;
    }
    else
7779
      found_count++;
Marc Alff's avatar
Marc Alff committed
7780
    thd->warning_info->inc_current_row_for_warning();
unknown's avatar
unknown committed
7781 7782
  }
  end_read_record(&info);
unknown's avatar
unknown committed
7783
  free_io_cache(from);
7784
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
7785

7786
  if (to->file->ha_end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
7787
  {
unknown's avatar
unknown committed
7788
    to->file->print_error(my_errno,MYF(0));
Konstantin Osipov's avatar
Konstantin Osipov committed
7789
    error= 1;
unknown's avatar
unknown committed
7790
  }
unknown's avatar
unknown committed
7791
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7792

7793 7794 7795 7796 7797 7798
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7799 7800 7801 7802
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
7803
  if (trans_commit_stmt(thd))
7804
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7805
  if (trans_commit_implicit(thd))
7806
    error=1;
7807

unknown's avatar
unknown committed
7808
 err:
7809
  thd->variables.sql_mode= save_sql_mode;
7810
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
7811
  free_io_cache(from);
unknown's avatar
unknown committed
7812 7813
  *copied= found_count;
  *deleted=delete_count;
7814
  to->file->ha_release_auto_increment();
7815
  if (to->file->ha_external_lock(thd,F_UNLCK))
7816
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7817 7818
  if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
    error= 1;
unknown's avatar
unknown committed
7819 7820
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7821

unknown's avatar
unknown committed
7822

7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7834
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7835 7836
{
  HA_CREATE_INFO create_info;
7837 7838 7839
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7840 7841 7842 7843 7844 7845
  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;
Konstantin Osipov's avatar
Konstantin Osipov committed
7846 7847
  /* Same applies to MDL ticket. */
  table_list->mdl_request.ticket= NULL;
7848 7849

  bzero((char*) &create_info, sizeof(create_info));
unknown's avatar
unknown committed
7850
  create_info.row_type=ROW_TYPE_NOT_USED;
7851
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
7852
  /* Force alter table to recreate table */
7853
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7854
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7855 7856
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7857 7858 7859
}


7860 7861
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7862 7863 7864 7865 7866
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
7867
  DBUG_ENTER("mysql_checksum_table");
7868 7869 7870

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7871 7872
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7873
  item->maybe_null= 1;
7874
  if (protocol->send_result_set_metadata(&field_list,
7875
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
7876
    DBUG_RETURN(TRUE);
7877

7878
  /* Open one table after the other to keep lock time as short as possible. */
unknown's avatar
VIEW  
unknown committed
7879
  for (table= tables; table; table= table->next_local)
7880 7881
  {
    char table_name[NAME_LEN*2+2];
7882
    TABLE *t;
7883

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

Konstantin Osipov's avatar
Konstantin Osipov committed
7886
    t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
unknown's avatar
unknown committed
7887
    thd->clear_error();			// these errors shouldn't get client
7888 7889 7890 7891

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

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

7912
        t->use_all_columns();
7913

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

unknown's avatar
unknown committed
7945 7946
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7947

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

7969 7970 7971
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
7972
          t->file->ha_rnd_end();
7973
	}
7974
      }
unknown's avatar
unknown committed
7975
      thd->clear_error();
7976 7977 7978 7979 7980 7981 7982
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

7983
  my_eof(thd);
unknown's avatar
unknown committed
7984
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7985

7986 7987 7988 7989
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
7990
  DBUG_RETURN(TRUE);
7991
}
7992 7993

static bool check_engine(THD *thd, const char *table_name,
7994
                         HA_CREATE_INFO *create_info)
7995
{
7996
  handlerton **new_engine= &create_info->db_type;
unknown's avatar
unknown committed
7997
  handlerton *req_engine= *new_engine;
unknown's avatar
unknown committed
7998
  bool no_substitution=
7999
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
8000 8001
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
8002 8003
    return TRUE;

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