sql_table.cc 253 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"
19
#include <hash.h>
unknown's avatar
unknown committed
20
#include <myisam.h>
21
#include <my_dir.h>
22 23
#include "sp_head.h"
#include "sql_trigger.h"
24
#include "sql_show.h"
25
#include "keycaches.h"
unknown's avatar
unknown committed
26 27 28 29 30

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

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

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

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

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

58
#ifndef DBUG_OFF
unknown's avatar
unknown committed
59

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

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

71

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

  DBUG_ENTER("add_identifier");
  if (!name[name_len])
    conv_name= name;
  else
  {
    strnmov(tmp_name, name, name_len);
    tmp_name[name_len]= 0;
    conv_name= tmp_name;
  }
  res= strconvert(&my_charset_filename, conv_name, system_charset_info,
                  conv_string, FN_REFLEN, &errors);
  if (!res || errors)
102 103
  {
    DBUG_PRINT("error", ("strconvert of '%s' failed with %u (errors: %u)", conv_name, res, errors));
104
    conv_name= name;
105
  }
106 107 108 109 110 111
  else
  {
    DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string));
    conv_name= conv_string;
  }

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

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


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

161
   @param      thd          Thread handle
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
   @param      from         Path name in my_charset_filename
                            Null terminated in my_charset_filename, normalized
                            to use '/' as directory separation character.
   @param      to           Explained name in system_charset_info
   @param      to_length    Size of to buffer
   @param      explain_mode Requested output format.
                            EXPLAIN_ALL_VERBOSE ->
                            [Database `db`, ]Table `tbl`[,[ Temporary| Renamed]
                            Partition `p` [, Subpartition `sp`]]
                            EXPLAIN_PARTITIONS_VERBOSE -> `db`.`tbl`
                            [[ Temporary| Renamed] Partition `p`
                            [, Subpartition `sp`]]
                            EXPLAIN_PARTITIONS_AS_COMMENT -> `db`.`tbl` |*
                            [,[ Temporary| Renamed] Partition `p`
                            [, Subpartition `sp`]] *|
                            (| is really a /, and it is all in one line)

   @retval     Length of returned string
*/

182 183
uint explain_filename(THD* thd,
		      const char *from,
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
                      char *to,
                      uint to_length,
                      enum_explain_filename_mode explain_mode)
{
  uint res= 0;
  char *to_p= to;
  char *end_p= to_p + to_length;
  const char *db_name= NULL;
  int  db_name_len= 0;
  const char *table_name;
  int  table_name_len= 0;
  const char *part_name= NULL;
  int  part_name_len= 0;
  const char *subpart_name= NULL;
  int  subpart_name_len= 0;
  enum enum_file_name_type {NORMAL, TEMP, RENAMED} name_type= NORMAL;
  const char *tmp_p;
  DBUG_ENTER("explain_filename");
  DBUG_PRINT("enter", ("from '%s'", from));
  tmp_p= from;
  table_name= from;
  /*
    If '/' then take last directory part as database.
    '/' is the directory separator, not FN_LIB_CHAR
  */
  while ((tmp_p= strchr(tmp_p, '/')))
  {
    db_name= table_name;
    /* calculate the length */
    db_name_len= tmp_p - db_name;
    tmp_p++;
    table_name= tmp_p;
  }
  tmp_p= table_name;
  while (!res && (tmp_p= strchr(tmp_p, '#')))
  {
    tmp_p++;
    switch (tmp_p[0]) {
    case 'P':
    case 'p':
      if (tmp_p[1] == '#')
        part_name= tmp_p + 2;
      else
        res= 1;
      tmp_p+= 2;
      break;
    case 'S':
    case 's':
      if ((tmp_p[1] == 'P' || tmp_p[1] == 'p') && tmp_p[2] == '#')
      {
        part_name_len= tmp_p - part_name - 1;
        subpart_name= tmp_p + 3;
      }
      else
        res= 2;
      tmp_p+= 3;
      break;
    case 'T':
    case 't':
      if ((tmp_p[1] == 'M' || tmp_p[1] == 'm') &&
          (tmp_p[2] == 'P' || tmp_p[2] == 'p') &&
          tmp_p[3] == '#' && !tmp_p[4])
        name_type= TEMP;
      else
        res= 3;
      tmp_p+= 4;
      break;
    case 'R':
    case 'r':
      if ((tmp_p[1] == 'E' || tmp_p[1] == 'e') &&
          (tmp_p[2] == 'N' || tmp_p[2] == 'n') &&
          tmp_p[3] == '#' && !tmp_p[4])
        name_type= RENAMED;
      else
        res= 4;
      tmp_p+= 4;
      break;
    default:
      res= 5;
    }
  }
  if (res)
  {
    /* Better to give something back if we fail parsing, than nothing at all */
    DBUG_PRINT("info", ("Error in explain_filename: %u", res));
    sql_print_warning("Invalid (old?) table or database name '%s'", from);
    DBUG_RETURN(my_snprintf(to, to_length,
                            "<result %u when explaining filename '%s'>",
                            res, from));
  }
  if (part_name)
  {
    table_name_len= part_name - table_name - 3;
    if (subpart_name)
      subpart_name_len= strlen(subpart_name);
    else
      part_name_len= strlen(part_name);
    if (name_type != NORMAL)
    {
      if (subpart_name)
        subpart_name_len-= 5;
      else
        part_name_len-= 5;
    }
  }
289 290
  else
    table_name_len= strlen(table_name);
291 292 293 294
  if (db_name)
  {
    if (explain_mode == EXPLAIN_ALL_VERBOSE)
    {
295 296
      to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_DATABASE_NAME),
                                            end_p - to_p);
297
      *(to_p++)= ' ';
298
      to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
299 300 301 302
      to_p= strnmov(to_p, ", ", end_p - to_p);
    }
    else
    {
303
      to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
304 305 306 307
      to_p= strnmov(to_p, ".", end_p - to_p);
    }
  }
  if (explain_mode == EXPLAIN_ALL_VERBOSE)
308
  {
309
    to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_TABLE_NAME), end_p - to_p);
310
    *(to_p++)= ' ';
311
    to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
312
  }
313
  else
314
    to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
315 316
  if (part_name)
  {
317
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
318 319 320 321 322 323 324 325
      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)
326 327
        to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_TEMPORARY_NAME),
                      end_p - to_p);
328
      else
329 330
        to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_RENAMED_NAME),
                      end_p - to_p);
331 332
      to_p= strnmov(to_p, " ", end_p - to_p);
    }
333 334
    to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_PARTITION_NAME),
                  end_p - to_p);
335
    *(to_p++)= ' ';
336
    to_p= add_identifier(thd, to_p, end_p, part_name, part_name_len);
337 338 339
    if (subpart_name)
    {
      to_p= strnmov(to_p, ", ", end_p - to_p);
340 341
      to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_SUBPARTITION_NAME),
                    end_p - to_p);
342
      *(to_p++)= ' ';
343
      to_p= add_identifier(thd, to_p, end_p, subpart_name, subpart_name_len);
344
    }
345
    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
346 347 348 349 350 351 352
      to_p= strnmov(to_p, " */", end_p - to_p);
  }
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(to_p - to);
}


353 354 355 356 357 358 359 360 361 362 363 364 365
/*
  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.
*/

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

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

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
396 397 398
}


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


423 424 425 426 427 428 429 430 431 432 433 434 435
/*
  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.
*/

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

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


457
/*
458
  Creates path to a file: mysql_data_dir/db/table.ext
459 460

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

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

480 481 482 483 484
    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.
485

486 487
  RETURN
    path length
488 489 490
*/

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

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

Konstantin Osipov's avatar
Konstantin Osipov committed
504
  (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff));
505 506 507 508

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

520
  DBUG_PRINT("exit", ("buff: '%s'", buff));
521
  DBUG_RETURN(pos - buff);
522 523 524
}


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

543
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
544
{
545 546
  DBUG_ENTER("build_tmptable_filename");

547 548 549 550
  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);
551

552 553 554 555 556
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }
557

558
  size_t length= unpack_filename(buff, buff);
559 560
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
561 562
}

unknown's avatar
unknown committed
563 564 565
/*
--------------------------------------------------------------------------

566
   MODULE: DDL log
unknown's avatar
unknown committed
567 568 569 570 571 572 573 574
   -----------------

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

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

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

584 585
   History:
   First version written in 2006 by Mikael Ronstrom
unknown's avatar
unknown committed
586 587 588 589
--------------------------------------------------------------------------
*/


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

613
st_global_ddl_log global_ddl_log;
unknown's avatar
unknown committed
614

Marc Alff's avatar
Marc Alff committed
615
mysql_mutex_t LOCK_gdl;
unknown's avatar
unknown committed
616

617 618 619 620 621
#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
622

623 624
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
625
#define DDL_LOG_IO_SIZE_POS 8
unknown's avatar
unknown committed
626

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

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

Marc Alff's avatar
Marc Alff committed
645 646
  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
647 648 649 650 651 652
    error= TRUE;
  DBUG_RETURN(error);
}


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

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

Marc Alff's avatar
Marc Alff committed
669 670
  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
671 672 673 674 675 676
    error= TRUE;
  DBUG_RETURN(error);
}


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

685
static bool write_ddl_log_header()
unknown's avatar
unknown committed
686 687
{
  uint16 const_var;
688
  bool error= FALSE;
689
  DBUG_ENTER("write_ddl_log_header");
unknown's avatar
unknown committed
690

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


709
/*
710
  Create ddl log file name
711
  SYNOPSIS
712
    create_ddl_log_file_name()
713 714 715 716 717
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

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


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

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

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


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

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

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


/*
820
  Initialise ddl log
unknown's avatar
unknown committed
821
  SYNOPSIS
822
    init_ddl_log()
unknown's avatar
unknown committed
823

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

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
unknown's avatar
unknown committed
831 832
*/

833
static bool init_ddl_log()
unknown's avatar
unknown committed
834
{
835
  char file_name[FN_REFLEN];
836
  DBUG_ENTER("init_ddl_log");
unknown's avatar
unknown committed
837

838
  if (global_ddl_log.inited)
unknown's avatar
unknown committed
839 840
    goto end;

841
  global_ddl_log.io_size= IO_SIZE;
842
  global_ddl_log.name_len= FN_LEN;
843
  create_ddl_log_file_name(file_name);
Marc Alff's avatar
Marc Alff committed
844 845 846 847
  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)
848
  {
849
    /* Couldn't create ddl log file, this is serious error */
850 851
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
852
  }
unknown's avatar
Fixes  
unknown committed
853
  global_ddl_log.inited= TRUE;
854
  if (write_ddl_log_header())
855
  {
Marc Alff's avatar
Marc Alff committed
856
    (void) mysql_file_close(global_ddl_log.file_id, MYF(MY_WME));
unknown's avatar
Fixes  
unknown committed
857
    global_ddl_log.inited= FALSE;
858
    DBUG_RETURN(TRUE);
859
  }
unknown's avatar
unknown committed
860 861

end:
862
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
863 864 865 866
}


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

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

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


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

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

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

  *active_entry= used_entry;
1048
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
1049 1050 1051 1052
}


/*
1053
  External interface methods for the DDL log Module
1054 1055 1056 1057
  ---------------------------------------------------
*/

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

unknown's avatar
unknown committed
1063
  RETURN VALUES
1064 1065 1066 1067
    TRUE                      Error
    FALSE                     Success

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

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

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


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

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

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

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


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

1244
bool deactivate_ddl_log_entry(uint entry_no)
1245
{
1246 1247
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
1248

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


/*
1285
  Sync ddl log file
1286
  SYNOPSIS
1287
    sync_ddl_log()
1288 1289 1290 1291 1292
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

1293
bool sync_ddl_log()
1294 1295
{
  bool error= FALSE;
1296
  DBUG_ENTER("sync_ddl_log");
1297

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


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

1322
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
1323
{
1324 1325 1326 1327
  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");
1328

1329
  global_ddl_log.first_free= log_entry;
1330 1331 1332 1333 1334
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1335
    global_ddl_log.first_used= next_log_entry;
1336 1337
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1338
  DBUG_VOID_RETURN;
1339 1340 1341
}


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

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

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

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

1385

1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
/*
  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
1399
    (void) mysql_file_close(global_ddl_log.file_id, MYF(MY_WME));
1400 1401 1402 1403 1404 1405
    global_ddl_log.file_id= (File) -1;
  }
  DBUG_VOID_RETURN;
}


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

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

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

1431 1432 1433 1434 1435 1436 1437 1438
  /*
    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();

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


/*
1469
  Release all memory allocated to the ddl log
1470
  SYNOPSIS
1471
    release_ddl_log()
1472 1473 1474 1475
  RETURN VALUES
    NONE
*/

1476
void release_ddl_log()
1477
{
1478 1479 1480
  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");
1481

1482 1483 1484
  if (!global_ddl_log.do_release)
    DBUG_VOID_RETURN;

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


unknown's avatar
unknown committed
1507 1508 1509
/*
---------------------------------------------------------------------------

1510
  END MODULE DDL log
unknown's avatar
unknown committed
1511 1512 1513 1514 1515
  --------------------

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

unknown's avatar
unknown committed
1516

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

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

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

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

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

1782

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

  RETURN
unknown's avatar
unknown committed
1802 1803
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
1804 1805

*/
unknown's avatar
unknown committed
1806

unknown's avatar
unknown committed
1807 1808
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
1809
{
Konstantin Osipov's avatar
Konstantin Osipov committed
1810
  bool error= FALSE, need_start_waiting= FALSE;
1811
  Drop_table_error_handler err_handler(thd->get_internal_handler());
unknown's avatar
unknown committed
1812 1813 1814 1815
  DBUG_ENTER("mysql_rm_table");

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

1816
  if (!drop_temporary)
unknown's avatar
unknown committed
1817
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
1818 1819
    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
1820
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1821
  }
1822 1823 1824 1825 1826 1827

  /*
    Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
    LOCK_open during wait_if_global_read_lock(), other threads could not
    close their tables. This would make a pretty deadlock.
  */
1828
  thd->push_internal_handler(&err_handler);
unknown's avatar
VIEW  
unknown committed
1829
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1830 1831
  thd->pop_internal_handler();

1832

Konstantin Osipov's avatar
Konstantin Osipov committed
1833
  if (need_start_waiting)
1834 1835
    start_waiting_global_read_lock(thd);

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

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

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

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1870
*/
1871 1872

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

1887 1888 1889
  LINT_INIT(alias);
  LINT_INIT(path_length);

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

1899
  mysql_ha_rm_tables(thd, tables, FALSE);
1900

Marc Alff's avatar
Marc Alff committed
1901
  mysql_mutex_lock(&LOCK_open);
1902

unknown's avatar
unknown committed
1903 1904 1905 1906 1907 1908 1909 1910 1911
  /*
    If we have the table in the definition cache, we don't have to check the
    .frm file to find if the table is a normal table (not view) and what
    engine to use.
  */

  for (table= tables; table; table= table->next_local)
  {
    TABLE_SHARE *share;
unknown's avatar
unknown committed
1912
    table->db_type= NULL;
unknown's avatar
unknown committed
1913
    if ((share= get_cached_table_share(table->db, table->table_name)))
unknown's avatar
unknown committed
1914
      table->db_type= share->db_type();
1915 1916

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

1927 1928
  if (!drop_temporary && lock_table_names_exclusively(thd, tables))
  {
Marc Alff's avatar
Marc Alff committed
1929
    mysql_mutex_unlock(&LOCK_open);
1930
    DBUG_RETURN(1);
1931
  }
1932

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

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

    error= drop_temporary_table(thd, table);

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

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

1978 1979 1980 1981 1982 1983
    /*
      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.
      */
1984
    if (!drop_temporary && thd->is_current_stmt_binlog_format_row() && !dont_log_query)
1985
    {
1986
      non_temp_tables_count++;
1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001
      /*
        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
2002
    table_type= table->db_type;
2003
    if (!drop_temporary)
unknown's avatar
unknown committed
2004
    {
2005
      TABLE *locked_table;
unknown's avatar
unknown committed
2006 2007 2008 2009
      abort_locked_tables(thd, db, table->table_name);
      remove_table_from_cache(thd, db, table->table_name,
	                      RTFC_WAIT_OTHER_THREAD_FLAG |
			      RTFC_CHECK_KILLED_FLAG);
2010 2011 2012 2013 2014 2015 2016
      /*
        If the table was used in lock tables, remember it so that
        unlock_table_names can free it
      */
      if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
        table->table= locked_table;

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

    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
2095
  }
2096 2097 2098 2099
  /*
    It's safe to unlock LOCK_open: we have an exclusive lock
    on the table name.
  */
Marc Alff's avatar
Marc Alff committed
2100
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2101
  thd->thread_specific_used|= tmp_table_deleted;
2102 2103 2104 2105
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
2106
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
2107
                      wrong_tables.c_ptr());
2108
    else
unknown's avatar
unknown committed
2109
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
2110 2111 2112 2113
    error= 1;
  }

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

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

2174 2175
      /*
        The remaining cases are:
2176 2177
        - no tables were deleted and
        - only temporary tables were deleted and row-based
2178 2179 2180 2181
          replication is used.
        In both these cases, nothing should be written to the binary
        log.
      */
2182
    }
unknown's avatar
unknown committed
2183
  }
Marc Alff's avatar
Marc Alff committed
2184
  mysql_mutex_lock(&LOCK_open);
2185
err_with_placeholders:
2186
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
Marc Alff's avatar
Marc Alff committed
2187
  mysql_mutex_unlock(&LOCK_open);
2188
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2189 2190 2191
}


2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206
/*
  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
2207
bool quick_rm_table(handlerton *base,const char *db,
2208
                    const char *table_name, uint flags)
unknown's avatar
unknown committed
2209
{
2210
  char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
2211 2212 2213
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

2214
  uint path_length= build_table_filename(path, sizeof(path) - 1,
2215
                                         db, table_name, reg_ext, flags);
Marc Alff's avatar
Marc Alff committed
2216
  if (mysql_file_delete(key_file_frm, path, MYF(0)))
unknown's avatar
unknown committed
2217
    error= 1; /* purecov: inspected */
2218
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
2219 2220 2221
  if (!(flags & FRM_ONLY))
    error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2222 2223
}

2224 2225 2226
/*
  Sort keys in the following order:
  - PRIMARY KEY
2227 2228
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
2229 2230 2231 2232 2233 2234 2235 2236 2237 2238
  - 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)
{
2239 2240 2241
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
2242
  {
2243
    if (!(b_flags & HA_NOSAME))
2244
      return -1;
2245
    if ((a_flags ^ b_flags) & HA_NULL_PART_KEY)
2246 2247
    {
      /* Sort NOT NULL keys before other keys */
2248
      return (a_flags & HA_NULL_PART_KEY) ? 1 : -1;
2249 2250 2251 2252 2253
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
2254 2255 2256
    /* 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;
2257
  }
2258
  else if (b_flags & HA_NOSAME)
2259 2260
    return 1;					// Prefer b

2261
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
2262
  {
2263
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
2264
  }
unknown's avatar
unknown committed
2265
  /*
2266
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
2267 2268 2269 2270 2271
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
2272 2273
}

2274 2275
/*
  Check TYPELIB (set or enum) for duplicates
2276

2277 2278 2279
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
2280 2281
    name	  name of the checked column
    typelib	  list of values for the column
2282
    dup_val_count  returns count of duplicate elements
2283 2284

  DESCRIPTION
2285
    This function prints an warning for each value in list
2286 2287 2288
    which has some duplicates on its right

  RETURN VALUES
2289 2290
    0             ok
    1             Error
2291 2292
*/

2293
bool check_duplicates_in_interval(const char *set_or_name,
2294
                                  const char *name, TYPELIB *typelib,
2295
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
2296
{
2297
  TYPELIB tmp= *typelib;
2298
  const char **cur_value= typelib->type_names;
2299
  unsigned int *cur_length= typelib->type_lengths;
2300
  *dup_val_count= 0;  
2301 2302
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
2303
  {
2304 2305 2306 2307
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
2308
    {
2309 2310
      THD *thd= current_thd;
      ErrConvString err(*cur_value, *cur_length, cs);
2311 2312 2313 2314
      if ((current_thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
2315
                 name, err.ptr(), set_or_name);
2316 2317
        return 1;
      }
2318 2319 2320 2321
      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);
2322
      (*dup_val_count)++;
2323 2324
    }
  }
2325
  return 0;
2326
}
2327

2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355

/*
  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++)
  {
2356
    size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
2357 2358 2359 2360 2361 2362
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


unknown's avatar
unknown committed
2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373
/*
  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
2374
    This function prepares a Create_field instance.
unknown's avatar
unknown committed
2375 2376 2377 2378 2379 2380 2381
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

unknown's avatar
unknown committed
2382
int prepare_create_field(Create_field *sql_field, 
unknown's avatar
unknown committed
2383 2384
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
2385
			 longlong table_flags)
unknown's avatar
unknown committed
2386
{
2387
  unsigned int dup_val_count;
unknown's avatar
unknown committed
2388
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
2389 2390

  /*
2391
    This code came from mysql_prepare_create_table.
unknown's avatar
unknown committed
2392 2393 2394 2395 2396
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
2397 2398 2399 2400
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
2401 2402 2403 2404 2405 2406 2407
    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;
2408
    (*blob_columns)++;
unknown's avatar
unknown committed
2409
    break;
2410
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
2411
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
2412 2413 2414 2415
    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
2416
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2417 2418 2419 2420 2421 2422 2423 2424
    }
    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;
2425
    (*blob_columns)++;
unknown's avatar
unknown committed
2426 2427 2428 2429 2430
    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
2431
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
2432
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
2433
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
2434 2435 2436 2437 2438 2439 2440 2441
    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
2442
      {
unknown's avatar
unknown committed
2443 2444
        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
2445 2446
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
2447 2448 2449
    }
#endif
    /* fall through */
2450
  case MYSQL_TYPE_STRING:
unknown's avatar
unknown committed
2451 2452 2453 2454
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2455
  case MYSQL_TYPE_ENUM:
unknown's avatar
unknown committed
2456 2457 2458 2459 2460
    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;
2461 2462 2463 2464
    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
2465
    break;
2466
  case MYSQL_TYPE_SET:
unknown's avatar
unknown committed
2467 2468 2469 2470 2471
    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;
2472 2473 2474 2475
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2476 2477 2478 2479 2480 2481
    /* 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
2482
    break;
2483 2484 2485 2486 2487
  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
2488 2489
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2490
  case MYSQL_TYPE_BIT:
unknown's avatar
unknown committed
2491
    /* 
2492 2493
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
unknown's avatar
unknown committed
2494
    */
unknown's avatar
unknown committed
2495
    break;
2496
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
2497 2498 2499 2500 2501 2502 2503
    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;
2504
  case MYSQL_TYPE_TIMESTAMP:
unknown's avatar
unknown committed
2505 2506 2507
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2508
      if (!*timestamps)
unknown's avatar
unknown committed
2509
      {
unknown's avatar
unknown committed
2510
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2511
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2512
      }
unknown's avatar
unknown committed
2513 2514 2515 2516
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2517
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
2518

2519
    (*timestamps)++;
unknown's avatar
unknown committed
2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534
    /* 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
2535 2536 2537
  DBUG_RETURN(0);
}

2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570

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

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

  RETURN VALUES
    cs                        Character set
*/

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

  if (!cs)
    cs= create_info->default_table_charset;
  /*
    table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname
    if we want change character set for all varchar/char columns.
    But the table charset must not affect the BLOB fields, so don't
    allow to change my_charset_bin to somethig else.
  */
  if (create_info->table_charset && cs != &my_charset_bin)
    cs= create_info->table_charset;
  return cs;
}


unknown's avatar
unknown committed
2571
/*
2572
  Preparation for table creation
unknown's avatar
unknown committed
2573 2574

  SYNOPSIS
2575
    mysql_prepare_create_table()
2576 2577
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2578
      alter_info                List of columns and indexes to create
2579 2580 2581 2582 2583 2584
      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
2585

2586
  DESCRIPTION
2587
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
2588

2589
  NOTES
2590
    sets create_info->varchar if the table has a varchar
2591

unknown's avatar
unknown committed
2592
  RETURN VALUES
2593 2594
    FALSE    OK
    TRUE     error
unknown's avatar
unknown committed
2595
*/
unknown's avatar
unknown committed
2596

unknown's avatar
unknown committed
2597
static int
2598 2599 2600 2601 2602 2603
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
2604
{
2605
  const char	*key_name;
unknown's avatar
unknown committed
2606
  Create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
2607
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
2608
  ulong		record_offset= 0;
2609
  KEY		*key_info;
unknown's avatar
unknown committed
2610
  KEY_PART_INFO *key_part_info;
2611 2612 2613
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
unknown's avatar
unknown committed
2614 2615
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
unknown's avatar
unknown committed
2616
  uint total_uneven_bit_length= 0;
2617
  DBUG_ENTER("mysql_prepare_create_table");
unknown's avatar
unknown committed
2618

2619
  select_field_pos= alter_info->create_list.elements - select_field_count;
unknown's avatar
unknown committed
2620
  null_fields=blob_columns=0;
2621
  create_info->varchar= 0;
unknown's avatar
unknown committed
2622
  max_key_length= file->max_key_length();
2623

2624
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
2625
  {
2626 2627
    CHARSET_INFO *save_cs;

2628 2629 2630 2631 2632 2633
    /*
      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;
2634 2635
    save_cs= sql_field->charset= get_sql_field_charset(sql_field,
                                                       create_info);
2636 2637 2638 2639
    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
2640
      char tmp[65];
2641 2642
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2643
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2644
      DBUG_RETURN(TRUE);
2645
    }
2646

2647
    /*
2648
      Convert the default value from client character
2649 2650 2651
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2652
        save_cs != sql_field->def->collation.collation &&
2653 2654 2655 2656
        (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))
2657
    {
2658
      /*
unknown's avatar
unknown committed
2659
        Starting from 5.1 we work here with a copy of Create_field
2660 2661 2662 2663 2664 2665 2666 2667
        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.
      */
2668
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2669 2670 2671 2672 2673

      if (sql_field->def == NULL)
      {
        /* Could not convert */
        my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2674
        DBUG_RETURN(TRUE);
2675 2676 2677
      }
    }

2678 2679
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2680 2681 2682
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2683
      TYPELIB *interval= sql_field->interval;
2684 2685 2686 2687 2688 2689

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2690
      if (!interval)
2691
      {
2692
        /*
2693 2694 2695
          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.
2696
        */
2697
        interval= sql_field->interval= typelib(thd->mem_root,
2698
                                               sql_field->interval_list);
2699
        List_iterator<String> int_it(sql_field->interval_list);
2700
        String conv, *tmp;
2701 2702 2703 2704 2705
        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);
2706
        for (uint i= 0; (tmp= int_it++); i++)
2707
        {
2708
          size_t lengthsp;
2709 2710 2711 2712 2713
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2714
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
unknown's avatar
unknown committed
2715
                                                  conv.length());
2716 2717
            interval->type_lengths[i]= conv.length();
          }
2718

2719
          // Strip trailing spaces.
unknown's avatar
unknown committed
2720 2721
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2722 2723
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2724
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2725 2726 2727 2728 2729
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2730 2731
              ErrConvString err(tmp->ptr(), tmp->length(), cs);
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
2732
              DBUG_RETURN(TRUE);
2733 2734
            }
          }
2735
        }
2736
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2737 2738
      }

2739
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2740
      {
2741
        uint32 field_length;
2742
        if (sql_field->def != NULL)
2743 2744 2745 2746 2747
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2748 2749 2750 2751 2752
          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);
2753
              DBUG_RETURN(TRUE);
2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765
            }

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

2766 2767 2768
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2769
            DBUG_RETURN(TRUE);
2770 2771
          }
        }
2772 2773
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2774
      }
2775
      else  /* MYSQL_TYPE_ENUM */
2776
      {
2777
        uint32 field_length;
2778
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2779
        if (sql_field->def != NULL)
2780 2781
        {
          String str, *def= sql_field->def->val_str(&str);
2782
          if (def == NULL) /* SQL "NULL" maps to NULL */
2783
          {
2784 2785 2786
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2787
              DBUG_RETURN(TRUE);
2788 2789 2790 2791 2792 2793 2794 2795 2796 2797
            }

            /* 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);
2798
              DBUG_RETURN(TRUE);
2799
            }
2800 2801
          }
        }
2802 2803
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2804 2805 2806 2807
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2808
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2809
    { 
unknown's avatar
unknown committed
2810
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2811
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2812 2813 2814 2815 2816
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2817
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
2818
    if (prepare_blob_field(thd, sql_field))
2819
      DBUG_RETURN(TRUE);
2820

unknown's avatar
unknown committed
2821 2822
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
2823

unknown's avatar
unknown committed
2824 2825
    if (check_column_name(sql_field->field_name))
    {
2826
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2827
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2828
    }
unknown's avatar
unknown committed
2829

2830 2831
    /* 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
2832
    {
2833
      if (my_strcasecmp(system_charset_info,
2834 2835
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
2836
      {
2837 2838 2839 2840
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2841 2842
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2843
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2844
	  DBUG_RETURN(TRUE);
2845 2846 2847
	}
	else
	{
2848
	  /* Field redefined */
2849
	  sql_field->def=		dup_field->def;
2850
	  sql_field->sql_type=		dup_field->sql_type;
2851 2852 2853
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2854
	  sql_field->length=		dup_field->char_length;
2855
          sql_field->pack_length=	dup_field->pack_length;
2856
          sql_field->key_length=	dup_field->key_length;
2857
	  sql_field->decimals=		dup_field->decimals;
2858
	  sql_field->create_length_to_internal_length();
2859
	  sql_field->unireg_check=	dup_field->unireg_check;
2860 2861 2862 2863 2864 2865 2866 2867
          /* 
            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
2868
          sql_field->interval=          dup_field->interval;
2869 2870 2871
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2872
	}
unknown's avatar
unknown committed
2873 2874
      }
    }
2875 2876
    /* 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
2877 2878
	(sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED))
2879
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
2880 2881
    it2.rewind();
  }
2882 2883 2884

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
2885
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
2886 2887 2888 2889

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

unknown's avatar
unknown committed
2892 2893
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2894
			     file->ha_table_flags()))
2895
      DBUG_RETURN(TRUE);
2896
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2897
      create_info->varchar= TRUE;
2898
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
2899 2900
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2901
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
2902
  }
2903 2904
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
2905 2906
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2907
    DBUG_RETURN(TRUE);
2908
  }
unknown's avatar
unknown committed
2909 2910
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
2911
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2912
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2913 2914
  }
  if (auto_increment &&
2915
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
2916
  {
unknown's avatar
unknown committed
2917 2918
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2919
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2920 2921
  }

2922
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
2923
  {
unknown's avatar
unknown committed
2924 2925
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2926
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2927 2928 2929
  }

  /* Create keys */
2930

2931 2932
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2933
  uint key_parts=0, fk_key_count=0;
2934
  bool primary_key=0,unique_key=0;
2935
  Key *key, *key2;
unknown's avatar
unknown committed
2936
  uint tmp, key_number;
2937 2938
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2939

2940
  /* Calculate number of key segements */
2941
  *key_count= 0;
2942

unknown's avatar
unknown committed
2943 2944
  while ((key=key_iterator++))
  {
2945
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name.str ? key->name.str :
2946
                        "(none)" , key->type));
2947 2948 2949
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
unknown's avatar
unknown committed
2950
      Foreign_key *fk_key= (Foreign_key*) key;
2951 2952 2953
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2954
        my_error(ER_WRONG_FK_DEF, MYF(0),
2955 2956
                 (fk_key->name.str ? fk_key->name.str :
                                     "foreign key without name"),
2957
                 ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
2958
	DBUG_RETURN(TRUE);
2959 2960 2961
      }
      continue;
    }
2962
    (*key_count)++;
unknown's avatar
unknown committed
2963
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
2964 2965 2966
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2967
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2968
    }
2969
    if (check_string_char_length(&key->name, "", NAME_CHAR_LEN,
2970
                                 system_charset_info, 1))
unknown's avatar
unknown committed
2971
    {
2972
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str);
2973
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2974
    }
2975
    key_iterator2.rewind ();
2976
    if (key->type != Key::FOREIGN_KEY)
2977
    {
2978
      while ((key2 = key_iterator2++) != key)
2979
      {
unknown's avatar
unknown committed
2980
	/*
2981 2982 2983
          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
2984
        */
2985
        if ((key2->type != Key::FOREIGN_KEY &&
2986
             key2->name.str != ignore_key &&
2987
             !foreign_key_prefix(key, key2)))
2988
        {
2989
          /* TODO: issue warning message */
2990 2991 2992 2993
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
2994
            key->name.str= ignore_key;
2995 2996
          else
          {
2997
            key2->name.str= ignore_key;
2998 2999
            key_parts-= key2->columns.elements;
            (*key_count)--;
3000 3001 3002
          }
          break;
        }
3003 3004
      }
    }
3005
    if (key->name.str != ignore_key)
3006 3007 3008
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
3009 3010
    if (key->name.str && !tmp_table && (key->type != Key::PRIMARY) &&
	!my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
3011
    {
3012
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
3013
      DBUG_RETURN(TRUE);
3014
    }
3015
  }
unknown's avatar
unknown committed
3016
  tmp=file->max_keys();
3017
  if (*key_count > tmp)
3018 3019
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
3020
    DBUG_RETURN(TRUE);
3021
  }
3022

3023
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
3024
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
3025
  if (!*key_info_buffer || ! key_part_info)
3026
    DBUG_RETURN(TRUE);				// Out of memory
3027

3028
  key_iterator.rewind();
unknown's avatar
unknown committed
3029
  key_number=0;
unknown's avatar
unknown committed
3030
  for (; (key=key_iterator++) ; key_number++)
3031 3032
  {
    uint key_length=0;
unknown's avatar
unknown committed
3033
    Key_part_spec *column;
3034

3035
    if (key->name.str == ignore_key)
3036 3037 3038 3039
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
3040
      while (key && key->name.str == ignore_key);
3041 3042 3043 3044
      if (!key)
	break;
    }

3045
    switch (key->type) {
unknown's avatar
unknown committed
3046
    case Key::MULTIPLE:
3047
	key_info->flags= 0;
3048
	break;
unknown's avatar
unknown committed
3049
    case Key::FULLTEXT:
3050
	key_info->flags= HA_FULLTEXT;
unknown's avatar
unknown committed
3051
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
3052
          key_info->flags|= HA_USES_PARSER;
3053 3054
        else
          key_info->parser_name= 0;
3055
	break;
unknown's avatar
unknown committed
3056
    case Key::SPATIAL:
unknown's avatar
unknown committed
3057
#ifdef HAVE_SPATIAL
3058
	key_info->flags= HA_SPATIAL;
3059
	break;
unknown's avatar
unknown committed
3060
#else
3061 3062
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
3063
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3064
#endif
3065 3066 3067 3068
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
3069 3070
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
3071
    }
3072 3073
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
3074

unknown's avatar
unknown committed
3075 3076
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
3077
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
3078
    key_info->algorithm= key->key_create_info.algorithm;
unknown's avatar
unknown committed
3079

3080 3081
    if (key->type == Key::FULLTEXT)
    {
3082
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
3083
      {
unknown's avatar
unknown committed
3084 3085
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
3086
	DBUG_RETURN(TRUE);
3087 3088
      }
    }
unknown's avatar
unknown committed
3089 3090 3091
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
3092
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
3093 3094 3095
       in near future when new frm file is ready
       checking for proper key parts number:
    */
3096

3097
    /* TODO: Add proper checks if handler supports key_type and algorithm */
3098
    if (key_info->flags & HA_SPATIAL)
3099
    {
3100
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
3101 3102 3103
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
3104
        DBUG_RETURN(TRUE);
3105
      }
3106 3107
      if (key_info->key_parts != 1)
      {
3108
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
3109
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3110
      }
3111
    }
unknown's avatar
unknown committed
3112
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
3113
    {
unknown's avatar
unknown committed
3114
#ifdef HAVE_RTREE_KEYS
3115 3116
      if ((key_info->key_parts & 1) == 1)
      {
3117
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
3118
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3119
      }
3120
      /* TODO: To be deleted */
3121
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
3122
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3123
#else
3124 3125
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
3126
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3127
#endif
unknown's avatar
unknown committed
3128
    }
3129

3130 3131 3132 3133 3134
    /* 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
3135 3136
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
3137 3138 3139 3140 3141
                           create_info->key_block_size);

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

unknown's avatar
unknown committed
3142
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
3143
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
3144 3145
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
3146
      uint length;
unknown's avatar
unknown committed
3147
      Key_part_spec *dup_column;
unknown's avatar
unknown committed
3148

unknown's avatar
unknown committed
3149 3150 3151
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
3152
	     my_strcasecmp(system_charset_info,
3153
			   column->field_name.str,
3154
			   sql_field->field_name))
unknown's avatar
unknown committed
3155 3156 3157
	field++;
      if (!sql_field)
      {
3158
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
3159
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3160
      }
unknown's avatar
unknown committed
3161
      while ((dup_column= cols2++) != column)
3162 3163
      {
        if (!my_strcasecmp(system_charset_info,
3164
	     	           column->field_name.str, dup_column->field_name.str))
3165 3166 3167
	{
	  my_printf_error(ER_DUP_FIELDNAME,
			  ER(ER_DUP_FIELDNAME),MYF(0),
3168
			  column->field_name.str);
3169
	  DBUG_RETURN(TRUE);
3170 3171 3172
	}
      }
      cols2.rewind();
3173
      if (key->type == Key::FULLTEXT)
3174
      {
3175 3176
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
3177 3178
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
3179
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
3180 3181
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
3182
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193
	    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));
3194
      }
3195
      else
3196
      {
3197 3198
	column->length*= sql_field->charset->mbmaxlen;

unknown's avatar
unknown committed
3199 3200 3201
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
3202
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3203 3204
	}

3205 3206
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
3207
	{
3208
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
3209
	  {
3210
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str);
3211
	    DBUG_RETURN(TRUE);
3212
	  }
3213 3214
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
3215
            column->length= 25;
3216 3217
	  if (!column->length)
	  {
3218
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
3219
	    DBUG_RETURN(TRUE);
3220 3221
	  }
	}
unknown's avatar
unknown committed
3222
#ifdef HAVE_SPATIAL
3223
	if (key->type == Key::SPATIAL)
3224
	{
3225
	  if (!column->length)
3226 3227
	  {
	    /*
3228 3229
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
3230
	    */
3231
	    column->length= 4*sizeof(double);
3232 3233
	  }
	}
unknown's avatar
unknown committed
3234
#endif
3235 3236 3237 3238 3239 3240 3241
	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
3242
            null_fields--;
3243 3244
	  }
	  else
3245 3246 3247 3248
          {
            key_info->flags|= HA_NULL_PART_KEY;
            if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
            {
3249
              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
3250
              DBUG_RETURN(TRUE);
3251 3252 3253 3254 3255
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
3256
              DBUG_RETURN(TRUE);
3257 3258
            }
          }
3259 3260 3261
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
3262
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
3263 3264
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
3265
      }
3266

unknown's avatar
unknown committed
3267 3268 3269
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
3270 3271
      length= sql_field->key_length;

unknown's avatar
unknown committed
3272 3273 3274 3275
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
3276
	  if ((length=column->length) > max_key_length ||
3277
	      length > file->max_key_part_length())
3278
	  {
unknown's avatar
unknown committed
3279
	    length=min(max_key_length, file->max_key_part_length());
3280 3281 3282 3283 3284 3285 3286 3287
	    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);
3288 3289
              /* Align key length to multibyte char boundary */
              length-= length % sql_field->charset->mbmaxlen;
3290 3291 3292 3293
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
3294
	      DBUG_RETURN(TRUE);
3295 3296
	    }
	  }
unknown's avatar
unknown committed
3297
	}
3298
	else if (!f_is_geom(sql_field->pack_flag) &&
3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313
                 ((column->length > length &&
                   !Field::type_can_have_key_part (sql_field->sql_type)) ||
                  ((f_is_packed(sql_field->pack_flag) ||
                    ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
                     (key_info->flags & HA_NOSAME))) &&
                   column->length != length)))
        {
          /* Catch invalid uses of partial keys.
             A key is identified as 'partial' if column->length != length.
             A partial key is invalid if they data type does
             not allow it, or the field is packed (as in MyISAM),
             or the storage engine doesn't allow prefixed search and
             the key is primary key.
          */

unknown's avatar
unknown committed
3314
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
3315
	  DBUG_RETURN(TRUE);
3316
	}
3317
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
3318
	  length=column->length;
unknown's avatar
unknown committed
3319 3320 3321
      }
      else if (length == 0)
      {
3322
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str);
3323
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3324
      }
3325
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
3326
      {
3327
        length= file->max_key_part_length();
3328 3329 3330 3331 3332 3333 3334 3335
	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);
3336 3337
          /* Align key length to multibyte char boundary */
          length-= length % sql_field->charset->mbmaxlen;
3338 3339 3340 3341
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
3342
	  DBUG_RETURN(TRUE);
3343
	}
3344 3345
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
3346
      /* Use packed keys for long strings on the first column */
3347
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
3348
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
3349 3350
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
3351 3352
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
Staale Smedseng's avatar
Staale Smedseng committed
3353
	if ((column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB)) ||
3354 3355
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
unknown's avatar
unknown committed
3356 3357 3358
	else
	  key_info->flags|= HA_PACK_KEY;
      }
3359 3360 3361 3362
      /* 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
3363 3364 3365 3366 3367 3368 3369
      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)
3370 3371 3372
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
3373 3374
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
3375
	    DBUG_RETURN(TRUE);
3376 3377 3378 3379
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
3380
	else if (!(key_name= key->name.str))
unknown's avatar
unknown committed
3381
	  key_name=make_unique_key_name(sql_field->field_name,
3382 3383
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
3384
	{
3385
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
3386
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3387 3388 3389 3390
	}
	key_info->name=(char*) key_name;
      }
    }
3391 3392
    if (!key_info->name || check_column_name(key_info->name))
    {
3393
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
3394
      DBUG_RETURN(TRUE);
3395
    }
3396 3397
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
3398
    key_info->key_length=(uint16) key_length;
3399
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
3400
    {
3401
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
3402
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3403
    }
unknown's avatar
unknown committed
3404
    key_info++;
unknown's avatar
unknown committed
3405
  }
3406
  if (!unique_key && !primary_key &&
3407
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
3408
  {
unknown's avatar
unknown committed
3409
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
3410
    DBUG_RETURN(TRUE);
3411
  }
unknown's avatar
unknown committed
3412 3413
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
3414
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
3415
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3416
  }
3417
  /* Sort keys in optimized order */
3418 3419
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
unknown's avatar
unknown committed
3420
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
3421

3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452
  /* 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);
    }
  }

3453
  DBUG_RETURN(FALSE);
3454 3455
}

3456

unknown's avatar
unknown committed
3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472
/*
  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)
{
3473 3474 3475 3476 3477
  /*
    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
3478 3479 3480
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
3481 3482 3483

    load_db_opt_by_name(thd, db, &db_info);

unknown's avatar
unknown committed
3484 3485 3486 3487 3488
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


unknown's avatar
unknown committed
3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501
/*
  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
3502
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
unknown's avatar
unknown committed
3503 3504 3505 3506 3507 3508 3509 3510 3511
{
  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];

3512 3513
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
3514 3515 3516 3517 3518
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3519
    sql_field->sql_type= MYSQL_TYPE_BLOB;
unknown's avatar
unknown committed
3520
    sql_field->flags|= BLOB_FLAG;
Sergei Golubchik's avatar
Sergei Golubchik committed
3521
    my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name,
3522
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
3523 3524 3525 3526
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
3527

unknown's avatar
unknown committed
3528 3529
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
3530 3531 3532
    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
3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543
    {
      /* 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);
}


3544
/*
unknown's avatar
unknown committed
3545
  Preparation of Create_field for SP function return values.
3546 3547
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558

  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
3559
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3560
{
3561 3562
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3563 3564
  {
    uint32 field_length, dummy;
3565
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3566 3567 3568 3569 3570 3571 3572
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3573
    else /* MYSQL_TYPE_ENUM */
3574 3575 3576 3577 3578 3579 3580 3581 3582
    {
      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);
  }

3583
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3584 3585 3586 3587 3588
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
3589 3590 3591 3592
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3593 3594


3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610
/*
  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
 */
3611 3612 3613
static inline int write_create_table_bin_log(THD *thd,
                                             const HA_CREATE_INFO *create_info,
                                             bool internal_tmp_table)
3614 3615 3616 3617 3618 3619 3620 3621 3622
{
  /*
    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 &&
3623 3624
      (!thd->is_current_stmt_binlog_format_row() ||
       (thd->is_current_stmt_binlog_format_row() &&
3625
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3626
    return write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3627
  return 0;
3628 3629 3630
}


3631 3632 3633 3634
/*
  Create a table

  SYNOPSIS
unknown's avatar
unknown committed
3635
    mysql_create_table_no_lock()
3636 3637 3638
    thd			Thread object
    db			Database
    table_name		Table name
3639
    create_info	        Create information (like MAX_ROWS)
3640 3641
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
3642
    internal_tmp_table  Set to 1 if this is an internal temporary table
3643
			(From ALTER TABLE)
3644
    select_field_count
3645 3646

  DESCRIPTION
3647
    If one creates a temporary table, this is automatically opened
3648

unknown's avatar
unknown committed
3649 3650 3651 3652 3653
    Note that this function assumes that caller already have taken
    name-lock on table being created or used some other way to ensure
    that concurrent operations won't intervene. mysql_create_table()
    is a wrapper that can be used for this.

3654 3655 3656 3657 3658 3659
    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
3660 3661
    FALSE OK
    TRUE  error
3662 3663
*/

unknown's avatar
unknown committed
3664
bool mysql_create_table_no_lock(THD *thd,
unknown's avatar
unknown committed
3665
                                const char *db, const char *table_name,
3666 3667 3668 3669
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3670
{
3671
  char		path[FN_REFLEN + 1];
3672
  uint          path_length;
3673 3674 3675 3676
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
3677
  bool		error= TRUE;
unknown's avatar
unknown committed
3678
  DBUG_ENTER("mysql_create_table_no_lock");
3679 3680
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3681

3682

3683
  /* Check for duplicate fields and check type of table to create */
3684
  if (!alter_info->create_list.elements)
3685
  {
unknown's avatar
unknown committed
3686 3687
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
3688
    DBUG_RETURN(TRUE);
3689
  }
3690
  if (check_engine(thd, table_name, create_info))
3691
    DBUG_RETURN(TRUE);
3692 3693 3694

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

3695
  db_options= create_info->table_options;
3696 3697 3698
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3699 3700
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3701
  {
unknown's avatar
unknown committed
3702
    mem_alloc_error(sizeof(handler));
3703 3704
    DBUG_RETURN(TRUE);
  }
3705
#ifdef WITH_PARTITION_STORAGE_ENGINE
3706 3707
  partition_info *part_info= thd->work_part_info;

unknown's avatar
unknown committed
3708 3709 3710 3711 3712 3713 3714 3715
  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
3716
    thd->work_part_info= part_info= new partition_info();
unknown's avatar
unknown committed
3717 3718 3719 3720 3721 3722
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
unknown's avatar
unknown committed
3723
    part_info->default_engine_type= create_info->db_type;
3724
    part_info->is_auto_partitioned= TRUE;
unknown's avatar
unknown committed
3725
  }
3726 3727 3728
  if (part_info)
  {
    /*
unknown's avatar
unknown committed
3729 3730 3731 3732 3733 3734 3735 3736 3737
      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.
3738
    */
3739
    List_iterator<Key> key_iterator(alter_info->key_list);
unknown's avatar
unknown committed
3740
    Key *key;
unknown's avatar
unknown committed
3741
    handlerton *part_engine_type= create_info->db_type;
3742 3743
    char *part_syntax_buf;
    uint syntax_len;
unknown's avatar
unknown committed
3744
    handlerton *engine_type;
3745 3746 3747 3748 3749
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
unknown's avatar
unknown committed
3750 3751
    while ((key= key_iterator++))
    {
3752 3753
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
unknown's avatar
unknown committed
3754
      {
3755
        my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
unknown's avatar
unknown committed
3756 3757 3758
        goto err;
      }
    }
3759
    if ((part_engine_type == partition_hton) &&
3760
        part_info->default_engine_type)
3761 3762 3763 3764 3765 3766
    {
      /*
        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
3767
      ;
3768
    }
3769 3770
    else
    {
unknown's avatar
unknown committed
3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782
      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);
        }
      }
3783
    }
unknown's avatar
unknown committed
3784 3785 3786
    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)));
3787
    if (part_info->check_partition_info(thd, &engine_type, file,
3788
                                        create_info, FALSE))
unknown's avatar
unknown committed
3789
      goto err;
unknown's avatar
unknown committed
3790
    part_info->default_engine_type= engine_type;
unknown's avatar
unknown committed
3791

3792 3793 3794 3795 3796 3797
    /*
      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,
3798 3799 3800
                                                     TRUE, TRUE,
                                                     create_info,
                                                     alter_info)))
unknown's avatar
unknown committed
3801
      goto err;
3802 3803
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
unknown's avatar
unknown committed
3804 3805
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3806
        create_info->db_type == partition_hton)
3807 3808 3809 3810 3811
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
unknown's avatar
unknown committed
3812
      DBUG_PRINT("info", ("db_type: %s",
unknown's avatar
unknown committed
3813
                        ha_resolve_storage_engine_name(create_info->db_type)));
3814
      delete file;
3815
      create_info->db_type= partition_hton;
3816 3817 3818 3819
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3820 3821 3822 3823 3824 3825
      /*
        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.
      */
3826 3827 3828
      if (part_info->use_default_num_partitions &&
          part_info->num_parts &&
          (int)part_info->num_parts !=
3829
          file->get_default_no_partitions(create_info))
3830
      {
3831
        uint i;
3832
        List_iterator<partition_element> part_it(part_info->partitions);
3833 3834 3835 3836
        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;
3837 3838
      }
      else if (part_info->is_sub_partitioned() &&
3839 3840 3841
               part_info->use_default_num_subpartitions &&
               part_info->num_subparts &&
               (int)part_info->num_subparts !=
3842
                 file->get_default_no_partitions(create_info))
3843
      {
3844
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3845
        part_info->num_subparts= file->get_default_no_partitions(create_info);
3846 3847 3848 3849
      }
    }
    else if (create_info->db_type != engine_type)
    {
3850 3851 3852 3853 3854 3855
      /*
        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.
      */
3856
      delete file;
3857 3858
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3859 3860 3861 3862
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3863 3864 3865
    }
  }
#endif
3866

3867 3868 3869 3870 3871
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3872
    goto err;
unknown's avatar
unknown committed
3873 3874 3875 3876

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3877
    path_length= build_tmptable_filename(thd, path, sizeof(path));
unknown's avatar
unknown committed
3878 3879
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3880 3881
  else  
  {
3882
    path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,
3883
                                      internal_tmp_table ? FN_IS_TMP : 0);
3884
  }
3885

unknown's avatar
unknown committed
3886
  /* Check if table already exists */
unknown's avatar
unknown committed
3887 3888
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
unknown's avatar
unknown committed
3889
  {
3890
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3891 3892
    {
      create_info->table_existed= 1;		// Mark that table existed
3893 3894 3895
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
3896
      error= write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
3897
      goto err;
3898
    }
unknown's avatar
unknown committed
3899
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3900
    goto err;
unknown's avatar
unknown committed
3901
  }
3902

Marc Alff's avatar
Marc Alff committed
3903
  mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
3904
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
3905 3906 3907 3908
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3909 3910
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
3911
      goto unlock_and_end;
unknown's avatar
unknown committed
3912
    }
3913 3914 3915 3916 3917 3918 3919 3920
    /*
      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.
    */
3921
    if (get_cached_table_share(db, table_name))
3922 3923 3924 3925
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
unknown's avatar
unknown committed
3926 3927
  }

unknown's avatar
unknown committed
3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940
  /*
    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;
3941 3942 3943
    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
3944
    {
3945 3946 3947 3948 3949
      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
3950

3951 3952 3953 3954 3955 3956 3957 3958 3959
        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
3960 3961 3962
    }
  }

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

3966
#ifdef HAVE_READLINK
3967
  {
3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003
    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;
      }
    }
4004 4005 4006 4007 4008 4009 4010 4011 4012
  }

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

4013
  if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
4014
#endif /* HAVE_READLINK */
4015 4016
  {
    if (create_info->data_file_name)
4017 4018 4019
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "DATA DIRECTORY");
4020
    if (create_info->index_file_name)
4021 4022 4023
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                          "INDEX DIRECTORY");
4024
    create_info->data_file_name= create_info->index_file_name= 0;
4025
  }
unknown's avatar
unknown committed
4026
  create_info->table_options=db_options;
4027

4028
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
4029 4030
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
unknown's avatar
unknown committed
4031
                       key_count, key_info_buffer, file))
unknown's avatar
unknown committed
4032
    goto unlock_and_end;
unknown's avatar
unknown committed
4033

unknown's avatar
unknown committed
4034 4035 4036 4037 4038 4039
  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
4040
      goto unlock_and_end;
unknown's avatar
unknown committed
4041
    }
unknown's avatar
unknown committed
4042
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
4043
  }
unknown's avatar
unknown committed
4044

4045
  error= write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4046
unlock_and_end:
Marc Alff's avatar
Marc Alff committed
4047
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4048 4049

err:
4050
  thd_proc_info(thd, "After create");
unknown's avatar
unknown committed
4051
  delete file;
unknown's avatar
unknown committed
4052
  DBUG_RETURN(error);
4053 4054

warn:
4055
  error= FALSE;
4056 4057 4058 4059
  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
4060
  error= write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4061
  goto unlock_and_end;
unknown's avatar
unknown committed
4062 4063
}

unknown's avatar
unknown committed
4064 4065

/*
unknown's avatar
unknown committed
4066
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
unknown's avatar
unknown committed
4067 4068 4069 4070
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
4071 4072 4073
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
unknown's avatar
unknown committed
4074
{
unknown's avatar
unknown committed
4075
  TABLE *name_lock= 0;
unknown's avatar
unknown committed
4076 4077 4078 4079
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
Marc Alff's avatar
Marc Alff committed
4080
  mysql_mutex_lock(&LOCK_lock_db);
unknown's avatar
unknown committed
4081
  while (!thd->killed &&
Konstantin Osipov's avatar
Konstantin Osipov committed
4082
         my_hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
unknown's avatar
unknown committed
4083 4084
  {
    wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
Marc Alff's avatar
Marc Alff committed
4085
    mysql_mutex_lock(&LOCK_lock_db);
unknown's avatar
unknown committed
4086 4087 4088 4089
  }

  if (thd->killed)
  {
Marc Alff's avatar
Marc Alff committed
4090
    mysql_mutex_unlock(&LOCK_lock_db);
unknown's avatar
unknown committed
4091 4092 4093
    DBUG_RETURN(TRUE);
  }
  creating_table++;
Marc Alff's avatar
Marc Alff committed
4094
  mysql_mutex_unlock(&LOCK_lock_db);
unknown's avatar
unknown committed
4095

unknown's avatar
unknown committed
4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111
  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
    {
      result= TRUE;
      goto unlock;
    }
    if (!name_lock)
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                            ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                            table_name);
        create_info->table_existed= 1;
        result= FALSE;
4112
        write_create_table_bin_log(thd, create_info, internal_tmp_table);
unknown's avatar
unknown committed
4113 4114 4115 4116 4117 4118 4119 4120 4121
      }
      else
      {
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        result= TRUE;
      }
      goto unlock;
    }
  }
unknown's avatar
unknown committed
4122

unknown's avatar
unknown committed
4123
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
4124 4125 4126
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
unknown's avatar
unknown committed
4127

unknown's avatar
unknown committed
4128 4129 4130
unlock:
  if (name_lock)
  {
Marc Alff's avatar
Marc Alff committed
4131
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4132
    unlink_open_table(thd, name_lock, FALSE);
Marc Alff's avatar
Marc Alff committed
4133
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4134
  }
Marc Alff's avatar
Marc Alff committed
4135
  mysql_mutex_lock(&LOCK_lock_db);
unknown's avatar
unknown committed
4136
  if (!--creating_table && creating_database)
Marc Alff's avatar
Marc Alff committed
4137 4138
    mysql_cond_signal(&COND_refresh);
  mysql_mutex_unlock(&LOCK_lock_db);
unknown's avatar
unknown committed
4139 4140 4141 4142
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
4143 4144 4145 4146 4147 4148 4149 4150
/*
** 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++)
4151
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
4152 4153 4154 4155 4156 4157 4158 4159 4160 4161
      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;

4162 4163
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
4164
    return (char*) field_name;			// Use fieldname
4165 4166 4167 4168 4169 4170
  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
  */
4171
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
4172
  {
4173 4174
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
4175 4176 4177
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
4178
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
4179 4180
}

4181

unknown's avatar
unknown committed
4182 4183 4184 4185
/****************************************************************************
** Alter a table definition
****************************************************************************/

4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199

/*
  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.
4200 4201
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
4202 4203

  RETURN
4204 4205
    FALSE   OK
    TRUE    Error
4206 4207
*/

4208
bool
4209 4210 4211
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
4212
{
4213
  THD *thd= current_thd;
4214 4215
  char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
    lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
4216 4217
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
unknown's avatar
unknown committed
4218
  handler *file;
unknown's avatar
unknown committed
4219
  int error=0;
unknown's avatar
unknown committed
4220
  DBUG_ENTER("mysql_rename_table");
4221 4222
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
unknown's avatar
unknown committed
4223

unknown's avatar
unknown committed
4224
  file= (base == NULL ? 0 :
unknown's avatar
unknown committed
4225 4226
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

4227
  build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
4228
                       flags & FN_FROM_IS_TMP);
4229
  build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
4230
                       flags & FN_TO_IS_TMP);
4231 4232 4233 4234 4235 4236

  /*
    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.
   */
4237
  if (lower_case_table_names == 2 && file &&
4238
      !(file->ha_table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
4239
  {
4240 4241
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
4242
    build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
4243
                         flags & FN_FROM_IS_TMP);
4244
    from_base= lc_from;
unknown's avatar
unknown committed
4245

4246 4247
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
4248
    build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
4249
                         flags & FN_TO_IS_TMP);
4250
    to_base= lc_to;
unknown's avatar
unknown committed
4251 4252
  }

4253
  if (!file || !(error=file->ha_rename_table(from_base, to_base)))
4254
  {
4255
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
4256
    {
unknown's avatar
unknown committed
4257
      error=my_errno;
4258
      /* Restore old file name */
4259
      if (file)
4260
        file->ha_rename_table(to_base, from_base);
4261 4262
    }
  }
unknown's avatar
unknown committed
4263
  delete file;
4264 4265 4266
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
4267 4268
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
4269 4270
}

unknown's avatar
unknown committed
4271

unknown's avatar
unknown committed
4272
/*
4273 4274 4275 4276 4277 4278
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
4279 4280 4281
    function            HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
                        HA_EXTRA_FORCE_REOPEN if table is not be used
                        HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
4282 4283 4284 4285 4286 4287 4288
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

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

4291 4292
void wait_while_table_is_used(THD *thd, TABLE *table,
                              enum ha_extra_function function)
unknown's avatar
unknown committed
4293
{
4294
  DBUG_ENTER("wait_while_table_is_used");
unknown's avatar
unknown committed
4295
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %lu",
unknown's avatar
unknown committed
4296 4297
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
unknown's avatar
unknown committed
4298

Marc Alff's avatar
Marc Alff committed
4299
  mysql_mutex_assert_owner(&LOCK_open);
4300

Konstantin Osipov's avatar
Konstantin Osipov committed
4301
  (void) table->file->extra(function);
4302
  /* Mark all tables that are in use as 'old' */
unknown's avatar
unknown committed
4303
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
4304 4305

  /* Wait until all there are no other threads that has this table open */
unknown's avatar
unknown committed
4306 4307 4308
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
4309 4310
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
4311

4312 4313
/*
  Close a cached table
unknown's avatar
unknown committed
4314

4315
  SYNOPSIS
unknown's avatar
unknown committed
4316
    close_cached_table()
4317 4318 4319 4320 4321 4322
    thd			Thread handler
    table		Table to remove from cache

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

4324 4325 4326 4327
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
4328

4329
void close_cached_table(THD *thd, TABLE *table)
4330 4331
{
  DBUG_ENTER("close_cached_table");
4332

4333
  wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
4334 4335
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
4336
  {
4337 4338
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
4339
  }
4340
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
unknown's avatar
unknown committed
4341
  unlink_open_table(thd, table, TRUE);
4342 4343

  /* When lock on LOCK_open is freed other threads can continue */
4344
  broadcast_refresh();
unknown's avatar
unknown committed
4345
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
4346 4347
}

4348
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
4349
			     const char* operator_name, const char* errmsg)
4350

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

unknown's avatar
unknown committed
4364
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
4365
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
4366
{
unknown's avatar
unknown committed
4367 4368
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
4369 4370 4371 4372
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
unknown's avatar
unknown committed
4373 4374 4375 4376
  DBUG_ENTER("prepare_for_repair");

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

  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
4379
  {
unknown's avatar
unknown committed
4380 4381 4382 4383
    char key[MAX_DBKEY_LENGTH];
    uint key_length;

    key_length= create_table_def_key(thd, key, table_list, 0);
Marc Alff's avatar
Marc Alff committed
4384
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4385 4386 4387
    if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
                                  &error))))
    {
Marc Alff's avatar
Marc Alff committed
4388
      mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4389
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
4390 4391
    }

unknown's avatar
unknown committed
4392
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
unknown's avatar
unknown committed
4393 4394
    {
      release_table_share(share, RELEASE_NORMAL);
Marc Alff's avatar
Marc Alff committed
4395
      mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4396 4397
      DBUG_RETURN(0);                           // Out of memory
    }
unknown's avatar
unknown committed
4398
    table= &tmp_table;
Marc Alff's avatar
Marc Alff committed
4399
    mysql_mutex_unlock(&LOCK_open);
4400
  }
4401 4402 4403 4404

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

4405 4406 4407 4408 4409 4410 4411 4412 4413
  /*
    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
4414

unknown's avatar
unknown committed
4415 4416 4417 4418 4419 4420 4421 4422 4423
  /*
    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
4424

4425 4426 4427
  if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  {
    error= send_check_errmsg(thd, table_list, "repair",
Timothy Smith's avatar
Timothy Smith committed
4428
                             "Failed repairing incompatible .frm file");
4429 4430
    goto end;
  }
4431

unknown's avatar
unknown committed
4432 4433
  /*
    Check if this is a table type that stores index and data separately,
4434 4435 4436
    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
4437
  */
unknown's avatar
unknown committed
4438
  ext= table->file->bas_ext();
unknown's avatar
unknown committed
4439 4440
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
4441

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

4447 4448
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
4449

4450 4451 4452
  /* If we could open the table, close it */
  if (table_list->table)
  {
Marc Alff's avatar
Marc Alff committed
4453
    mysql_mutex_lock(&LOCK_open);
4454
    close_cached_table(thd, table);
Marc Alff's avatar
Marc Alff committed
4455
    mysql_mutex_unlock(&LOCK_open);
4456
  }
unknown's avatar
unknown committed
4457
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
4458
  {
unknown's avatar
unknown committed
4459 4460
    error= -1;
    goto end;
unknown's avatar
unknown committed
4461
  }
Marc Alff's avatar
Marc Alff committed
4462
  if (mysql_file_rename(key_file_misc, from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
4463
  {
Marc Alff's avatar
Marc Alff committed
4464
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4465
    unlock_table_name(thd, table_list);
Marc Alff's avatar
Marc Alff committed
4466
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4467 4468 4469 4470 4471 4472
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
Marc Alff's avatar
Marc Alff committed
4473
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4474
    unlock_table_name(thd, table_list);
Marc Alff's avatar
Marc Alff committed
4475
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4476 4477 4478 4479
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
Marc Alff's avatar
Marc Alff committed
4480
  if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
unknown's avatar
unknown committed
4481
  {
Marc Alff's avatar
Marc Alff committed
4482
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4483
    unlock_table_name(thd, table_list);
Marc Alff's avatar
Marc Alff committed
4484
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4485 4486 4487
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
unknown's avatar
unknown committed
4488 4489
  }

4490 4491 4492 4493
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
Marc Alff's avatar
Marc Alff committed
4494
  mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4495
  if (reopen_name_locked_table(thd, table_list, TRUE))
unknown's avatar
unknown committed
4496
  {
unknown's avatar
unknown committed
4497
    unlock_table_name(thd, table_list);
Marc Alff's avatar
Marc Alff committed
4498
    mysql_mutex_unlock(&LOCK_open);
4499 4500 4501
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
4502
  }
Marc Alff's avatar
Marc Alff committed
4503
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4504 4505 4506

end:
  if (table == &tmp_table)
unknown's avatar
unknown committed
4507
  {
Marc Alff's avatar
Marc Alff committed
4508
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
4509
    closefrm(table, 1);				// Free allocated memory
Marc Alff's avatar
Marc Alff committed
4510
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
4511
  }
unknown's avatar
unknown committed
4512
  DBUG_RETURN(error);
unknown's avatar
unknown committed
4513
}
4514

4515

4516

unknown's avatar
unknown committed
4517 4518
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
4519 4520 4521
    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
4522
*/
unknown's avatar
unknown committed
4523 4524 4525 4526 4527
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,
4528
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
4529 4530 4531
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
4532 4533 4534
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
4535
{
4536
  TABLE_LIST *table;
4537
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
4538
  List<Item> field_list;
4539 4540
  Item *item;
  Protocol *protocol= thd->protocol;
4541
  LEX *lex= thd->lex;
4542
  int result_code;
4543
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
4544

4545 4546
  if (end_active_trans(thd))
    DBUG_RETURN(1);
4547
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
unknown's avatar
unknown committed
4548 4549 4550 4551 4552 4553 4554
  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;
4555
  if (protocol->send_result_set_metadata(&field_list,
4556
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4557
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4558

4559
  mysql_ha_rm_tables(thd, tables, FALSE);
4560

unknown's avatar
VIEW  
unknown committed
4561
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
4562 4563
  {
    char table_name[NAME_LEN*2+2];
4564
    char* db = table->db;
4565
    bool fatal_error=0;
unknown's avatar
unknown committed
4566

4567 4568
    DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
    DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
unknown's avatar
unknown committed
4569
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
4570
    thd->open_options|= extra_open_options;
4571 4572
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4573
    {
4574 4575 4576 4577 4578
      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;
4579
      select->table_list.first= (uchar*)table;
4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591
      /*
        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;
4592

4593 4594 4595 4596 4597
      open_and_lock_tables(thd, table);
      thd->no_warnings_for_error= 0;
      table->next_global= save_next_global;
      table->next_local= save_next_local;
      thd->open_options&= ~extra_open_options;
4598
#ifdef WITH_PARTITION_STORAGE_ENGINE
4599
      if (table->table)
4600 4601 4602 4603
      {
        /*
          Set up which partitions that should be processed
          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
4604
          CACHE INDEX/LOAD INDEX for specified partitions
4605 4606 4607
        */
        Alter_info *alter_info= &lex->alter_info;

4608
        if (alter_info->flags & ALTER_ADMIN_PARTITION)
4609
        {
4610 4611 4612 4613 4614
          if (!table->table->part_info)
          {
            my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
            DBUG_RETURN(TRUE);
          }
4615 4616 4617
          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,
4618
                                          PART_ADMIN);
4619
          if (num_parts_found != num_parts_opt &&
4620 4621 4622
              (!(alter_info->flags & ALTER_ALL_PARTITION)))
          {
            char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4623
            size_t length;
4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640
            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
4641
    }
4642 4643
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

unknown's avatar
unknown committed
4644
    if (prepare_func)
4645
    {
4646
      DBUG_PRINT("admin", ("calling prepare_func"));
unknown's avatar
unknown committed
4647
      switch ((*prepare_func)(thd, table, check_opt)) {
4648
      case  1:           // error, message written to net
4649
        ha_autocommit_or_rollback(thd, 1);
4650
        end_trans(thd, ROLLBACK);
4651
        close_thread_tables(thd);
4652
        DBUG_PRINT("admin", ("simple error, admin next table"));
4653 4654
        continue;
      case -1:           // error, message could be written to net
4655 4656
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4657
        goto err;
4658
        /* purecov: end */
4659
      default:           // should be 0 otherwise
4660
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4661
        ;
unknown's avatar
unknown committed
4662
      }
4663
    }
4664

4665
    /*
unknown's avatar
unknown committed
4666 4667 4668 4669 4670 4671
      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)
4672
    */
unknown's avatar
unknown committed
4673 4674
    if (!table->table)
    {
4675
      DBUG_PRINT("admin", ("open table failed"));
Marc Alff's avatar
Marc Alff committed
4676 4677
      if (thd->warning_info->is_empty())
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4678
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4679 4680 4681
      /* 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
4682
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4683
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
Marc Alff's avatar
Marc Alff committed
4684 4685 4686
      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
4687 4688 4689 4690 4691
        /* 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;
4692
      goto send_result;
unknown's avatar
unknown committed
4693
    }
4694 4695 4696

    if (table->view)
    {
4697
      DBUG_PRINT("admin", ("calling view_operator_func"));
4698 4699 4700 4701
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4702
    if (table->schema_table)
unknown's avatar
unknown committed
4703
    {
4704 4705
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
unknown's avatar
unknown committed
4706 4707
    }

4708
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
4709
    {
4710
      /* purecov: begin inspected */
unknown's avatar
unknown committed
4711
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4712
      size_t length;
4713
      DBUG_PRINT("admin", ("sending error message"));
4714
      protocol->prepare_for_resend();
4715 4716
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4717
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4718 4719 4720
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4721
      ha_autocommit_or_rollback(thd, 0);
4722
      end_trans(thd, COMMIT);
4723
      close_thread_tables(thd);
4724
      lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
4725
      table->table=0;				// For query cache
4726
      if (protocol->write())
unknown's avatar
unknown committed
4727
	goto err;
Marc Alff's avatar
Marc Alff committed
4728
      thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
4729
      continue;
4730
      /* purecov: end */
unknown's avatar
unknown committed
4731 4732
    }

4733
    /* Close all instances of the table to allow repair to rename files */
4734
    if (lock_type == TL_WRITE && table->table->s->version)
4735
    {
4736
      DBUG_PRINT("admin", ("removing table from cache"));
Marc Alff's avatar
Marc Alff committed
4737
      mysql_mutex_lock(&LOCK_open);
4738 4739
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
unknown's avatar
unknown committed
4740
      mysql_lock_abort(thd,table->table, TRUE);
unknown's avatar
unknown committed
4741 4742
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
unknown's avatar
unknown committed
4743 4744
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4745
      thd->exit_cond(old_message);
4746
      DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
4747 4748
      if (thd->killed)
	goto err;
4749 4750 4751
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4752 4753
    }

unknown's avatar
unknown committed
4754
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4755
    {
4756 4757
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4758 4759 4760
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4761 4762 4763
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4764 4765
      if (protocol->write())
        goto err;
4766
      /* purecov: end */
4767 4768
    }

4769 4770
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
unknown's avatar
unknown committed
4771 4772 4773 4774 4775
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4776
        DBUG_PRINT("admin", ("recreating table"));
4777
        ha_autocommit_or_rollback(thd, 1);
unknown's avatar
unknown committed
4778 4779
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4780
        result_code= mysql_recreate_table(thd, table);
unknown's avatar
unknown committed
4781
        reenable_binlog(thd);
4782 4783 4784 4785 4786 4787
        /*
          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
4788 4789
        if (thd->stmt_da->is_ok())
          thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
4790 4791 4792 4793
        goto send_result;
      }
    }

4794
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4795
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4796
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4797 4798 4799

send_result:

4800
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
4801
    thd->clear_error();  // these errors shouldn't get client
4802
    {
Marc Alff's avatar
Marc Alff committed
4803
      List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
4804 4805 4806 4807 4808 4809
      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
4810 4811
        protocol->store(warning_level_names[err->get_level()].str,
                        warning_level_names[err->get_level()].length,
4812
                        system_charset_info);
Marc Alff's avatar
Marc Alff committed
4813
        protocol->store(err->get_message_text(), system_charset_info);
4814 4815 4816
        if (protocol->write())
          goto err;
      }
Marc Alff's avatar
Marc Alff committed
4817
      thd->warning_info->clear_warning_info(thd->query_id);
4818
    }
4819
    protocol->prepare_for_resend();
4820 4821
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
4822

4823 4824 4825
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4826 4827
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4828
      {
4829
       char buf[MYSQL_ERRMSG_SIZE];
4830
       size_t length=my_snprintf(buf, sizeof(buf),
4831
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4832
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4833
	protocol->store(buf, length, system_charset_info);
4834
      }
unknown's avatar
unknown committed
4835 4836
      break;

unknown's avatar
unknown committed
4837 4838
    case HA_ADMIN_NOT_BASE_TABLE:
      {
4839
        char buf[MYSQL_ERRMSG_SIZE];
4840
        size_t length= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
4841
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4842
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4843
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
4844 4845 4846
      }
      break;

4847
    case HA_ADMIN_OK:
4848 4849
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
4850 4851
      break;

4852
    case HA_ADMIN_FAILED:
4853 4854 4855
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
4856 4857
      break;

unknown's avatar
unknown committed
4858
    case HA_ADMIN_REJECT:
4859 4860 4861
      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
4862
      open_for_modify= FALSE;
unknown's avatar
unknown committed
4863 4864
      break;

4865
    case HA_ADMIN_ALREADY_DONE:
4866 4867 4868
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4869 4870
      break;

4871
    case HA_ADMIN_CORRUPT:
4872 4873
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4874
      fatal_error=1;
unknown's avatar
unknown committed
4875 4876
      break;

unknown's avatar
unknown committed
4877
    case HA_ADMIN_INVALID:
4878 4879 4880
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
4881 4882
      break;

4883 4884 4885 4886 4887 4888
    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.
4889
        We have to end the row, so analyze could return more rows.
4890
      */
4891 4892 4893 4894 4895 4896
      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;
4897
      ha_autocommit_or_rollback(thd, 0);
4898
      close_thread_tables(thd);
4899
      DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
unknown's avatar
VIEW  
unknown committed
4900 4901 4902
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4903
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4904
      result_code= mysql_recreate_table(thd, table);
4905
      reenable_binlog(thd);
4906 4907 4908 4909 4910 4911
      /*
        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
4912 4913
      if (thd->stmt_da->is_ok())
        thd->stmt_da->reset_diagnostics_area();
4914
      ha_autocommit_or_rollback(thd, 0);
unknown's avatar
unknown committed
4915
      close_thread_tables(thd);
4916 4917
      if (!result_code) // recreation went ok
      {
4918
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
4919
            ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
4920 4921
          result_code= 0; // analyze went ok
      }
4922 4923 4924 4925
      /* 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);
4926 4927
      if (result_code) // either mysql_recreate_table or analyze failed
      {
4928 4929
        DBUG_ASSERT(thd->is_error());
        if (thd->is_error())
4930
        {
Marc Alff's avatar
Marc Alff committed
4931
          const char *err_msg= thd->stmt_da->message();
4932 4933
          if (!thd->vio_ok())
          {
Staale Smedseng's avatar
Staale Smedseng committed
4934
            sql_print_error("%s", err_msg);
4935 4936 4937 4938
          }
          else
          {
            /* Hijack the row already in-progress. */
4939
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4940
            protocol->store(err_msg, system_charset_info);
4941 4942
            if (protocol->write())
              goto err;
4943 4944 4945 4946 4947
            /* 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);
          }
4948
          thd->clear_error();
4949 4950
        }
      }
4951
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
4952 4953
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4954 4955
      goto send_result_message;
    }
4956 4957
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4958
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
4959 4960
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4961 4962
      break;
    }
4963

unknown's avatar
unknown committed
4964 4965 4966
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
4967
      char buf[MYSQL_ERRMSG_SIZE];
4968
      size_t length;
unknown's avatar
unknown committed
4969 4970

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4971 4972
      length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
                         table->table_name);
unknown's avatar
unknown committed
4973 4974 4975 4976 4977
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

4978
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4979
      {
4980
        char buf[MYSQL_ERRMSG_SIZE];
4981
        size_t length=my_snprintf(buf, sizeof(buf),
4982 4983
                                "Unknown - internal error %d during operation",
                                result_code);
4984
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4985 4986 4987 4988
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
4989
    }
unknown's avatar
unknown committed
4990
    if (table->table)
4991
    {
unknown's avatar
unknown committed
4992 4993
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4994
      else if (open_for_modify)
unknown's avatar
unknown committed
4995
      {
unknown's avatar
unknown committed
4996
        if (table->table->s->tmp_table)
unknown's avatar
unknown committed
4997 4998 4999
          table->table->file->info(HA_STATUS_CONST);
        else
        {
Marc Alff's avatar
Marc Alff committed
5000
          mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5001 5002
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
Marc Alff's avatar
Marc Alff committed
5003
          mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5004 5005
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
5006 5007
        query_cache_invalidate3(thd, table->table, 0);
      }
5008
    }
5009
    ha_autocommit_or_rollback(thd, 0);
5010
    end_trans(thd, COMMIT);
unknown's avatar
unknown committed
5011
    close_thread_tables(thd);
unknown's avatar
unknown committed
5012
    table->table=0;				// For query cache
5013
    if (protocol->write())
unknown's avatar
unknown committed
5014 5015 5016
      goto err;
  }

5017
  my_eof(thd);
unknown's avatar
unknown committed
5018
  DBUG_RETURN(FALSE);
5019

5020
err:
5021
  ha_autocommit_or_rollback(thd, 1);
5022
  end_trans(thd, ROLLBACK);
5023
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
5024 5025
  if (table)
    table->table=0;
unknown's avatar
unknown committed
5026
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5027 5028
}

unknown's avatar
unknown committed
5029

unknown's avatar
unknown committed
5030
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5031 5032 5033
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5034 5035 5036
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
5037
				&prepare_for_repair,
unknown's avatar
unknown committed
5038
				&handler::ha_repair, 0));
5039 5040
}

5041

unknown's avatar
unknown committed
5042
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5043 5044 5045
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5046
				"optimize", TL_WRITE, 1,0,0,0,
5047
				&handler::ha_optimize, 0));
5048 5049 5050
}


unknown's avatar
unknown committed
5051 5052 5053 5054 5055
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
5056 5057
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5058 5059

  RETURN VALUES
unknown's avatar
unknown committed
5060 5061
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
5062 5063
*/

unknown's avatar
unknown committed
5064
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
5065
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
5066
{
5067
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
5068
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
5069
  DBUG_ENTER("mysql_assign_to_keycache");
5070 5071

  check_opt.init();
Marc Alff's avatar
Marc Alff committed
5072
  mysql_mutex_lock(&LOCK_global_system_variables);
5073 5074
  if (!(key_cache= get_key_cache(key_cache_name)))
  {
Marc Alff's avatar
Marc Alff committed
5075
    mysql_mutex_unlock(&LOCK_global_system_variables);
5076
    my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
unknown's avatar
unknown committed
5077
    DBUG_RETURN(TRUE);
5078
  }
Marc Alff's avatar
Marc Alff committed
5079
  mysql_mutex_unlock(&LOCK_global_system_variables);
5080 5081
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
5082
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
5083
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
5084 5085
}

unknown's avatar
unknown committed
5086

unknown's avatar
unknown committed
5087 5088 5089 5090 5091
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
5092 5093
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
5094 5095

  RETURN VALUES
unknown's avatar
unknown committed
5096 5097
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
5098 5099
*/

unknown's avatar
unknown committed
5100
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
5101 5102
{
  DBUG_ENTER("mysql_preload_keys");
5103 5104 5105 5106 5107
  /*
    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
5108
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
5109
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
5110
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
5111 5112 5113
}


5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162

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

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

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


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

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


unknown's avatar
unknown committed
5163 5164 5165 5166 5167
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
5168
    thd		Thread object
unknown's avatar
unknown committed
5169 5170
    table       Table list element for target table
    src_table   Table list element for source table
unknown's avatar
unknown committed
5171 5172 5173
    create_info Create info

  RETURN VALUES
unknown's avatar
unknown committed
5174 5175
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
5176 5177
*/

unknown's avatar
unknown committed
5178
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
5179
                             HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
5180
{
unknown's avatar
unknown committed
5181
  TABLE *name_lock= 0;
5182
  char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
5183
  uint dst_path_length;
unknown's avatar
unknown committed
5184
  char *db= table->db;
5185
  char *table_name= table->table_name;
unknown's avatar
unknown committed
5186
  int  err;
unknown's avatar
unknown committed
5187
  bool res= TRUE;
unknown's avatar
unknown committed
5188
  uint not_used;
5189 5190 5191
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
5192
  char ts_name[FN_LEN + 1];
5193
  myf flags= MY_DONT_OVERWRITE_FILE;
unknown's avatar
unknown committed
5194
  DBUG_ENTER("mysql_create_like_table");
5195

5196

unknown's avatar
unknown committed
5197 5198 5199 5200 5201 5202 5203 5204
  /*
    By opening source table we guarantee that it exists and no concurrent
    DDL operation will mess with it. Later we also take an exclusive
    name-lock on target table name, which makes copying of .frm file,
    call to ha_create_table() and binlogging atomic against concurrent DML
    and DDL operations on target table. Thus by holding both these "locks"
    we ensure that our statement is properly isolated from all concurrent
    operations which matter.
5205
  */
unknown's avatar
unknown committed
5206
  if (open_tables(thd, &src_table, &not_used, 0))
5207 5208
    DBUG_RETURN(TRUE);

5209 5210 5211 5212 5213 5214
  /*
    For bug#25875, Newly created table through CREATE TABLE .. LIKE
                   has no ndb_dd attributes;
    Add something to get possible tablespace info from src table,
    it can get valid tablespace name only for disk-base ndb table
  */
unknown's avatar
unknown committed
5215
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
5216 5217 5218 5219 5220
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

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

unknown's avatar
unknown committed
5223
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
unknown's avatar
unknown committed
5224

unknown's avatar
unknown committed
5225 5226 5227
  /*
    Check that destination tables does not exist. Note that its name
    was already checked when it was added to the table list.
unknown's avatar
unknown committed
5228 5229 5230 5231 5232
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
5233
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
unknown's avatar
unknown committed
5234 5235 5236 5237
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
unknown's avatar
unknown committed
5238 5239 5240 5241
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
      goto err;
    if (!name_lock)
      goto table_exists;
5242
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
5243
                                          db, table_name, reg_ext, 0);
unknown's avatar
unknown committed
5244 5245 5246 5247
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

unknown's avatar
unknown committed
5248 5249
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

5250 5251
  if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    flags|= MY_SYNC;
5252

5253
  /*
unknown's avatar
unknown committed
5254
    Create a new table by copying from source table
5255
    and sync the new table if the flag MY_SYNC is set
unknown's avatar
unknown committed
5256 5257 5258 5259 5260 5261 5262 5263 5264 5265

    Altough exclusive name-lock on target table protects us from concurrent
    DML and DDL operations on it we still want to wrap .FRM creation and call
    to ha_create_table() in critical section protected by LOCK_open in order
    to provide minimal atomicity against operations which disregard name-locks,
    like I_S implementation, for example. This is a temporary and should not
    be copied. Instead we should fix our code to always honor name-locks.

    Also some engines (e.g. NDB cluster) require that LOCK_open should be held
    during the call to ha_create_table(). See bug #28614 for more info.
5266
  */
Marc Alff's avatar
Marc Alff committed
5267
  mysql_mutex_lock(&LOCK_open);
5268 5269 5270 5271
  if (src_table->schema_table)
  {
    if (mysql_create_like_schema_frm(thd, src_table, dst_path, create_info))
    {
Marc Alff's avatar
Marc Alff committed
5272
      mysql_mutex_unlock(&LOCK_open);
5273 5274 5275
      goto err;
    }
  }
5276
  else if (my_copy(src_path, dst_path, flags))
5277 5278 5279 5280 5281
  {
    if (my_errno == ENOENT)
      my_error(ER_BAD_DB_ERROR,MYF(0),db);
    else
      my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
Marc Alff's avatar
Marc Alff committed
5282
    mysql_mutex_unlock(&LOCK_open);
5283
    goto err;
5284
  }
unknown's avatar
unknown committed
5285 5286

  /*
5287 5288
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
unknown's avatar
unknown committed
5289 5290
    and temporary tables).
  */
unknown's avatar
unknown committed
5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303
#ifdef WITH_PARTITION_STORAGE_ENGINE
  /*
    For partitioned tables we need to copy the .par file as well since
    it is used in open_table_def to even be able to create a new handler.
    There is no way to find out here if the original table is a
    partitioned table so we copy the file and ignore any errors.
  */
  fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
  strmov(dst_path, tmp_path);
  fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
  strmov(src_path, tmp_path);
  my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
#endif
unknown's avatar
unknown committed
5304 5305 5306

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

5307
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
5308 5309
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
unknown's avatar
unknown committed
5310
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
Marc Alff's avatar
Marc Alff committed
5311
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5312

unknown's avatar
unknown committed
5313 5314 5315 5316
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
5317 5318
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
5319
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
5320
    }
5321
    thd->thread_specific_used= TRUE;
unknown's avatar
unknown committed
5322 5323 5324
  }
  else if (err)
  {
5325
    (void) quick_rm_table(create_info->db_type, db,
5326
			  table_name, 0); /* purecov: inspected */
5327
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
5328
  }
5329

5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347
goto binlog;

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

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

5350 5351 5352
  /*
    We have to write the query before we unlock the tables.
  */
5353
  if (thd->is_current_stmt_binlog_format_row())
5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371
  {
    /*
       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
5372
      if (src_table->table->s->tmp_table)               // Case 2
5373 5374 5375 5376
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
5377 5378

        /*
unknown's avatar
unknown committed
5379 5380 5381 5382
          Here we open the destination table, on which we already have
          name-lock. This is needed for store_create_info() to work.
          The table will be closed by unlink_open_table() at the end
          of this function.
5383
        */
unknown's avatar
unknown committed
5384
        table->table= name_lock;
Marc Alff's avatar
Marc Alff committed
5385
        mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5386 5387
        if (reopen_name_locked_table(thd, table, FALSE))
        {
Marc Alff's avatar
Marc Alff committed
5388
          mysql_mutex_unlock(&LOCK_open);
5389
          goto err;
unknown's avatar
unknown committed
5390
        }
Marc Alff's avatar
Marc Alff committed
5391
        mysql_mutex_unlock(&LOCK_open);
5392

5393 5394 5395 5396 5397 5398 5399
       /*
         The condition avoids a crash as described in BUG#48506. Other
         binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE
         when the existing object is a view will be solved by BUG 47442.
       */
        if (!table->view)
        {
5400
          int result __attribute__((unused))=
5401 5402
            store_create_info(thd, table, &query,
                              create_info, FALSE /* show_database */);
5403

5404
          DBUG_ASSERT(result == 0); // store_create_info() always return 0
5405 5406
          if (write_bin_log(thd, TRUE, query.ptr(), query.length()))
            goto err;
5407
        }
5408 5409
      }
      else                                      // Case 1
5410 5411
        if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
          goto err;
5412 5413 5414 5415 5416
    }
    /*
      Case 3 and 4 does nothing under RBR
    */
  }
5417 5418
  else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
    goto err;
5419

unknown's avatar
unknown committed
5420
  res= FALSE;
5421 5422

err:
unknown's avatar
unknown committed
5423
  if (name_lock)
5424
  {
Marc Alff's avatar
Marc Alff committed
5425
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5426
    unlink_open_table(thd, name_lock, FALSE);
Marc Alff's avatar
Marc Alff committed
5427
    mysql_mutex_unlock(&LOCK_open);
5428
  }
5429
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5430 5431 5432
}


unknown's avatar
unknown committed
5433
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
5434
{
unknown's avatar
unknown committed
5435 5436
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5437 5438
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
5439
				"analyze", lock_type, 1, 0, 0, 0,
5440
				&handler::ha_analyze, 0));
5441 5442 5443
}


unknown's avatar
unknown committed
5444
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
5445
{
unknown's avatar
unknown committed
5446 5447
  thr_lock_type lock_type = TL_READ_NO_INSERT;

5448 5449
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
5450
				"check", lock_type,
5451
				0, 0, HA_OPEN_FOR_REPAIR, 0,
unknown's avatar
unknown committed
5452
				&handler::ha_check, &view_checksum));
5453 5454
}

unknown's avatar
unknown committed
5455

unknown's avatar
unknown committed
5456
/* table_list should contain just one table */
unknown's avatar
unknown committed
5457 5458 5459 5460
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
5461 5462 5463 5464 5465 5466
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
5467 5468 5469 5470
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
5471

5472
  thd_proc_info(thd, "discard_or_import_tablespace");
unknown's avatar
unknown committed
5473

unknown's avatar
unknown committed
5474
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
5475

unknown's avatar
unknown committed
5476 5477 5478 5479 5480
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
5481
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
unknown's avatar
unknown committed
5482 5483 5484 5485
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
5486

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

5489
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
5490 5491 5492 5493

  if (error)
    goto err;

unknown's avatar
unknown committed
5494 5495 5496 5497
  /*
    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
5498 5499 5500
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
5501 5502
  error = ha_autocommit_or_rollback(thd, 0);
  if (end_active_trans(thd))
unknown's avatar
unknown committed
5503 5504 5505
    error=1;
  if (error)
    goto err;
5506
  error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
5507

unknown's avatar
unknown committed
5508
err:
5509
  ha_autocommit_or_rollback(thd, error);
unknown's avatar
unknown committed
5510
  thd->tablespace_op=FALSE;
5511
  
unknown's avatar
unknown committed
5512 5513
  if (error == 0)
  {
5514
    my_ok(thd);
unknown's avatar
unknown committed
5515
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5516
  }
unknown's avatar
unknown committed
5517

5518 5519
  table->file->print_error(error, MYF(0));
    
unknown's avatar
unknown committed
5520
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
5521
}
unknown's avatar
unknown committed
5522

5523

unknown's avatar
unknown committed
5524 5525
/*
  SYNOPSIS
5526 5527
    compare_tables()
      table                     The original table.
5528 5529
      alter_info                Alter options, fields and keys for the new
                                table.
5530 5531
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
5532 5533 5534 5535 5536 5537 5538 5539
      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
5540 5541 5542 5543
      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.
5544
      candidate_key_count OUT   The number of candidate keys in original table.
unknown's avatar
unknown committed
5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555

  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.

5556 5557 5558 5559 5560
    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
5561
  RETURN VALUES
5562 5563
    TRUE   error
    FALSE  success
unknown's avatar
unknown committed
5564 5565
*/

5566 5567 5568 5569 5570 5571
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
unknown's avatar
unknown committed
5572
               enum_alter_table_change_level *need_copy_table,
5573 5574
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
5575 5576
               uint **index_add_buffer, uint *index_add_count,
               uint *candidate_key_count)
unknown's avatar
unknown committed
5577 5578 5579
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
5580
  uint key_count;
5581 5582
  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
  Create_field *new_field, *tmp_new_field;
5583 5584
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
5585
  THD *thd= table->in_use;
5586 5587 5588 5589 5590
  /*
    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;
5591 5592 5593
  bool not_nullable= true;
  DBUG_ENTER("compare_tables");

5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612
  /*
    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,
5613 5614 5615 5616 5617
                                 &tmp_alter_info,
                                 (table->s->tmp_table != NO_TMP_TABLE),
                                 &db_options,
                                 table->file, key_info_buffer,
                                 &key_count, 0))
5618 5619 5620
    DBUG_RETURN(1);
  /* Allocate result buffers. */
  if (! (*index_drop_buffer=
5621
         (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
5622
      ! (*index_add_buffer=
5623
         (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
5624
    DBUG_RETURN(1);
5625
  
unknown's avatar
unknown committed
5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642
  /*
    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
5643 5644 5645 5646 5647 5648 5649 5650

    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
5651
  */
5652
  if (table->s->fields != alter_info->create_list.elements ||
unknown's avatar
unknown committed
5653
      table->s->db_type() != create_info->db_type ||
unknown's avatar
unknown committed
5654 5655 5656 5657
      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 ||
5658
      (table->s->row_type != create_info->row_type) ||
5659 5660
      create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
      create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
5661
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5662
      order_num ||
unknown's avatar
unknown committed
5663
      !table->s->mysql_version ||
5664
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5665 5666 5667 5668
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5669

5670 5671 5672 5673 5674 5675 5676
  /*
    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
5677 5678 5679 5680
  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
5681 5682 5683 5684 5685
  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
5686 5687 5688 5689
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5690

unknown's avatar
unknown committed
5691
    /* Check that NULL behavior is same for old and new fields */
5692
    if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
unknown's avatar
unknown committed
5693
	(uint) (field->flags & NOT_NULL_FLAG))
5694 5695 5696 5697
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
5698 5699 5700

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

5706
    /* Check if field was renamed */
5707
    field->flags&= ~FIELD_IS_RENAMED;
5708 5709
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
5710
		      tmp_new_field->field_name))
5711
      field->flags|= FIELD_IS_RENAMED;      
5712

unknown's avatar
unknown committed
5713
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
5714
    if (!(tmp= field->is_equal(tmp_new_field)))
5715 5716 5717 5718
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5719
    // Clear indexed marker
5720
    field->flags&= ~FIELD_IN_ADD_INDEX;
unknown's avatar
unknown committed
5721 5722 5723 5724 5725 5726 5727
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5728 5729 5730
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5731
  KEY *new_key_end= *key_info_buffer + key_count;
unknown's avatar
unknown committed
5732

5733 5734 5735 5736 5737 5738 5739
  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;
5740
  *candidate_key_count= 0;
5741
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
unknown's avatar
unknown committed
5742
  {
5743 5744 5745 5746
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762
   /*
      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)++;

5763
    /* Search a new key with the same name. */
5764
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5765 5766 5767 5768 5769 5770 5771
    {
      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. */
5772
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5773 5774 5775 5776 5777 5778 5779 5780 5781 5782
      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
5783 5784 5785 5786 5787

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5788 5789 5790
    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
5791 5792 5793
    {
      /*
	Key definition has changed if we are using a different field or
5794 5795
	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
5796
      */
5797 5798 5799
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
unknown's avatar
unknown committed
5800
    }
5801 5802 5803 5804
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5805 5806
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*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
    }
5815
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
unknown's avatar
unknown committed
5816
  }
5817
  /*end of for (; table_key < table_key_end;) */
unknown's avatar
unknown committed
5818

5819 5820 5821
  /*
    Step through all keys of the new table and find matching old keys.
  */
5822
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5823 5824 5825 5826 5827 5828 5829 5830 5831 5832
  {
    /* 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. */
5833
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5834 5835 5836 5837 5838 5839
      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];
5840
        field->flags|= FIELD_IN_ADD_INDEX;
5841
      }
unknown's avatar
unknown committed
5842
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5843 5844
    }
  }
5845 5846 5847

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5848 5849 5850 5851
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5852

5853
  if (*index_drop_count || *index_add_count)
5854 5855 5856 5857
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5858

5859 5860
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5861 5862 5863
}


unknown's avatar
unknown committed
5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889
/*
  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:
5890
    error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5891 5892 5893 5894 5895 5896
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
5897
    error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
unknown's avatar
unknown committed
5898 5899 5900 5901 5902
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5903 5904
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
unknown's avatar
unknown committed
5905 5906 5907 5908 5909 5910 5911 5912
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941
/**
  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;
  }
}


5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980
/**
  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
5981
*/
5982

5983 5984 5985 5986
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
unknown's avatar
unknown committed
5987
{
5988
  /* New column definitions are added here */
unknown's avatar
unknown committed
5989
  List<Create_field> new_create_list;
5990 5991 5992
  /* 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
5993
  List_iterator<Create_field> def_it(alter_info->create_list);
5994 5995
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
unknown's avatar
unknown committed
5996 5997 5998
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5999 6000 6001 6002 6003
  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;
6004

6005
  DBUG_ENTER("mysql_prepare_alter_table");
6006

6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017
  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)
6018
  {
6019 6020 6021
    /* 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;
6022
  }
6023 6024
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
unknown's avatar
unknown committed
6025

6026
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
6027
  {
6028
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
6029
    /*
6030 6031
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
6032
    */
6033 6034 6035 6036 6037
    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
6038
  Create_field *def;
6039

6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051
  /*
    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++))
6052
    {
6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064
      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;
      }
6065
    }
6066
    if (drop)
6067
    {
6068 6069
      drop_it.remove();
      continue;
6070
    }
6071 6072 6073
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
6074
    {
6075 6076 6077
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
6078
    }
6079 6080 6081 6082
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
unknown's avatar
unknown committed
6083
      {
6084 6085
	new_create_list.push_back(def);
	def_it.remove();
unknown's avatar
unknown committed
6086 6087
      }
    }
6088
    else
6089 6090
    {
      /*
6091 6092
        This field was not dropped and not changed, add it to the list
        for the new table.
6093
      */
unknown's avatar
unknown committed
6094
      def= new Create_field(field, field);
6095 6096 6097 6098
      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
6099
      {
6100 6101
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
6102
      }
6103
      if (alter)
unknown's avatar
unknown committed
6104
      {
6105
	if (def->sql_type == MYSQL_TYPE_BLOB)
unknown's avatar
unknown committed
6106
	{
6107
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
6108
          goto err;
unknown's avatar
unknown committed
6109
	}
6110 6111 6112 6113 6114
	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
6115 6116 6117
      }
    }
  }
6118 6119
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
6120
  {
6121
    if (def->change && ! def->field)
6122
    {
6123
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
6124
      goto err;
6125
    }
unknown's avatar
unknown committed
6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142
    /*
      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;
    }
6143 6144 6145 6146
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
6147
    else
6148
    {
unknown's avatar
unknown committed
6149
      Create_field *find;
6150 6151 6152 6153 6154 6155 6156 6157
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
6158
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
6159 6160 6161
        goto err;
      }
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
6162
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
6163
    }
6164
  }
6165
  if (alter_info->alter_list.elements)
6166
  {
6167
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
6168
             alter_info->alter_list.head()->name, table->s->table_name.str);
unknown's avatar
unknown committed
6169
    goto err;
6170
  }
6171
  if (!new_create_list.elements)
unknown's avatar
unknown committed
6172
  {
unknown's avatar
unknown committed
6173 6174
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
6175
    goto err;
unknown's avatar
unknown committed
6176 6177 6178
  }

  /*
6179 6180
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
6181 6182
  */

6183
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
6184
  {
6185
    char *key_name= key_info->name;
unknown's avatar
unknown committed
6186 6187 6188 6189 6190
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
6191
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206
	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
6207
      Create_field *cfield;
unknown's avatar
unknown committed
6208 6209 6210 6211 6212
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
6213 6214
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
6215 6216
	    break;
	}
6217
	else if (!my_strcasecmp(system_charset_info,
6218
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
6219
	  break;
unknown's avatar
unknown committed
6220 6221 6222 6223 6224
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
6225 6226 6227 6228 6229 6230 6231
      {
        /*
          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.

6232 6233 6234
          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.

6235 6236
          BLOBs may have cfield->length == 0, which is why we test it before
          checking whether cfield->length < key_part_length (in chars).
6237 6238 6239 6240 6241 6242 6243 6244
          
          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)
6245 6246
         */
        if (!Field::type_can_have_key_part(cfield->field->type()) ||
unknown's avatar
unknown committed
6247
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
6248 6249
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
unknown's avatar
unknown committed
6250 6251
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
6252 6253 6254 6255 6256
            (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)))
6257
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
6258
      }
6259
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
6260
      key_parts.push_back(new Key_part_spec(cfield->field_name,
6261
                                            strlen(cfield->field_name),
unknown's avatar
unknown committed
6262 6263 6264
					    key_part_length));
    }
    if (key_parts.elements)
6265 6266
    {
      KEY_CREATE_INFO key_create_info;
6267 6268
      Key *key;
      enum Key::Keytype key_type;
6269 6270 6271 6272 6273 6274
      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)
6275
        key_create_info.parser_name= *plugin_name(key_info->parser);
6276

6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290
      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;

6291
      key= new Key(key_type, key_name, strlen(key_name),
6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303
                   &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);
6304 6305
      if (key->name.str &&
	  !my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
6306
      {
6307
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
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 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406
        goto err;
      }
    }
  }

  if (alter_info->drop_list.elements)
  {
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
    goto err;
  }
  if (alter_info->alter_list.elements)
  {
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
    goto err;
  }

  if (!create_info->comment.str)
  {
    create_info->comment.str= table->s->comment.str;
    create_info->comment.length= table->s->comment.length;
  }

  table->file->update_create_info(create_info);
  if ((create_info->table_options &
       (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
      (used_fields & HA_CREATE_USED_PACK_KEYS))
    db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
  if (create_info->table_options &
      (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
    db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
  if (create_info->table_options &
      (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
    db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
			  HA_OPTION_NO_DELAY_KEY_WRITE);
  create_info->table_options|= db_create_options;

  if (table->s->tmp_table)
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

  rc= FALSE;
  alter_info->create_list.swap(new_create_list);
  alter_info->key_list.swap(new_key_list);
err:
  DBUG_RETURN(rc);
}


/*
  Alter table

  SYNOPSIS
    mysql_alter_table()
      thd              Thread handle
      new_db           If there is a RENAME clause
      new_name         If there is a RENAME clause
      create_info      Information from the parsing phase about new
                       table properties.
      table_list       The table to change.
      alter_info       Lists of fields, keys to be changed, added
                       or dropped.
      order_num        How many ORDER BY fields has been specified.
      order            List of fields to ORDER BY.
      ignore           Whether we have ALTER IGNORE TABLE

  DESCRIPTION
    This is a veery long function and is everything but the kitchen sink :)
    It is used to alter a table and not only by ALTER TABLE but also
    CREATE|DROP INDEX are mapped on this function.

    When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
    or both, then this function short cuts its operation by renaming
    the table and/or enabling/disabling the keys. In this case, the FRM is
    not changed, directly by mysql_alter_table. However, if there is a
    RENAME + change of a field, or an index, the short cut is not used.
    See how `create_list` is used to generate the new FRM regarding the
    structure of the fields. The same is done for the indices of the table.

    Important is the fact, that this function tries to do as little work as
    possible, by finding out whether a intermediate table is needed to copy
    data into and when finishing the altering to use it as the original table.
    For this reason the function compare_tables() is called, which decides
    based on all kind of data how similar are the new and the original
    tables.

  RETURN VALUES
    FALSE  OK
    TRUE   Error
*/

bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
                       HA_CREATE_INFO *create_info,
                       TABLE_LIST *table_list,
                       Alter_info *alter_info,
                       uint order_num, ORDER *order, bool ignore)
{
  TABLE *table, *new_table= 0, *name_lock= 0;
  int error= 0;
6407
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
6408 6409
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
6410
  char path[FN_REFLEN + 1];
6411
  char reg_path[FN_REFLEN+1];
unknown's avatar
unknown committed
6412
  ha_rows copied,deleted;
6413
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
6414
  legacy_db_type table_type;
6415
  frm_type_enum frm_type;
unknown's avatar
unknown committed
6416
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
6417
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
6418
  uint fast_alter_partition= 0;
6419 6420
  bool partition_changed= FALSE;
#endif
6421 6422
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
Staale Smedseng's avatar
Staale Smedseng committed
6423 6424 6425 6426 6427
  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
6428
  bool committed= 0;
6429
  bool no_pk;
unknown's avatar
unknown committed
6430 6431
  DBUG_ENTER("mysql_alter_table");

6432 6433 6434 6435 6436 6437
  /*
    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.
  */
6438
  if (table_list && table_list->db && table_list->table_name)
6439
  {
6440
    int table_kind= 0;
6441

6442 6443 6444
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
6445

6446
    if (table_kind)
6447
    {
6448 6449 6450 6451 6452 6453
      /* 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);
      }
6454

6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466
      /* 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)
      {
6467
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
6468 6469 6470
        DBUG_RETURN(TRUE);
      }
#endif
6471 6472 6473
    }
  }

6474 6475 6476 6477 6478
  /*
    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.
  */
6479
  thd_proc_info(thd, "init");
6480
  table_name=table_list->table_name;
unknown's avatar
unknown committed
6481
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
unknown's avatar
unknown committed
6482
  db=table_list->db;
unknown's avatar
unknown committed
6483
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
6484
    new_db= db;
6485 6486
  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);
6487

6488
  mysql_ha_rm_tables(thd, table_list, FALSE);
unknown's avatar
unknown committed
6489

unknown's avatar
unknown committed
6490
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
6491
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
6492
    /* Conditionally writes to binlog. */
unknown's avatar
unknown committed
6493
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
6494
						   alter_info->tablespace_op));
6495 6496 6497
  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);
6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510
  /*
    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.
  */
6511 6512
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
6513
  /* Sic: there is a race here */
6514 6515
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529
    /*
      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);
    }

6530 6531 6532 6533 6534 6535 6536 6537 6538
    /*
      Avoid problems with a rename on a table that we have locked or
      if the user is trying to to do this in a transcation context
    */

    if (thd->locked_tables || thd->active_transaction())
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
6539
      DBUG_RETURN(TRUE);
6540 6541 6542
    }

    if (wait_if_global_read_lock(thd,0,1))
6543
      DBUG_RETURN(TRUE);
Marc Alff's avatar
Marc Alff committed
6544
    mysql_mutex_lock(&LOCK_open);
6545
    if (lock_table_names(thd, table_list))
6546
    {
unknown's avatar
unknown committed
6547
      error= 1;
6548
      goto view_err;
6549
    }
6550 6551 6552 6553 6554 6555
    
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
6556
        Query_log_event qinfo(thd, thd->query(), thd->query_length(),
6557
                              FALSE, TRUE, FALSE, 0);
6558
        if ((error= mysql_bin_log.write(&qinfo)))
6559
          goto view_err_unlock;
6560
      }
6561
      my_ok(thd);
6562 6563
    }

6564
view_err_unlock:
6565 6566 6567
    unlock_table_names(thd, table_list, (TABLE_LIST*) 0);

view_err:
Marc Alff's avatar
Marc Alff committed
6568
    mysql_mutex_unlock(&LOCK_open);
6569 6570 6571
    start_waiting_global_read_lock(thd);
    DBUG_RETURN(error);
  }
6572 6573

  if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
unknown's avatar
unknown committed
6574
    DBUG_RETURN(TRUE);
6575
  table->use_all_columns();
unknown's avatar
unknown committed
6576

6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590
  /*
    Prohibit changing of the UNION list of a non-temporary MERGE table
    under LOCK tables. It would be quite difficult to reuse a shrinked
    set of tables from the old table or to open a new TABLE object for
    an extended list and verify that they belong to locked tables.
  */
  if (thd->locked_tables &&
      (create_info->used_fields & HA_CREATE_USED_UNION) &&
      (table->s->tmp_table == NO_TMP_TABLE))
  {
    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
    DBUG_RETURN(TRUE);
  }

unknown's avatar
unknown committed
6591 6592 6593
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
6594
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
unknown's avatar
unknown committed
6595
    strmov(new_name_buff,new_name);
6596
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
6597
    if (lower_case_table_names)
unknown's avatar
unknown committed
6598 6599 6600
    {
      if (lower_case_table_names != 2)
      {
6601
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
6602 6603
	new_alias= new_name;			// Create lower case table name
      }
6604
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
6605
    }
6606
    if (new_db == db &&
unknown's avatar
unknown committed
6607
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
6608 6609
    {
      /*
6610 6611
	Source and destination table names are equal: make later check
	easier.
6612
      */
unknown's avatar
unknown committed
6613
      new_alias= new_name= table_name;
6614
    }
unknown's avatar
unknown committed
6615 6616
    else
    {
unknown's avatar
unknown committed
6617
      if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
6618 6619 6620
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
6621
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
6622
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6623 6624 6625 6626
	}
      }
      else
      {
unknown's avatar
unknown committed
6627 6628 6629 6630 6631 6632 6633 6634
        if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
          DBUG_RETURN(TRUE);
        if (!name_lock)
        {
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
	  DBUG_RETURN(TRUE);
        }

6635
        build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
6636 6637
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
unknown's avatar
unknown committed
6638 6639
	{
	  /* Table will be closed in do_command() */
6640
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
6641
          goto err;
unknown's avatar
unknown committed
6642 6643 6644 6645 6646
	}
      }
    }
  }
  else
6647 6648 6649 6650
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
6651

unknown's avatar
unknown committed
6652
  old_db_type= table->s->db_type();
unknown's avatar
unknown committed
6653
  if (!create_info->db_type)
6654
  {
6655
#ifdef WITH_PARTITION_STORAGE_ENGINE
6656 6657
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
6658 6659 6660 6661
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
6662 6663 6664
        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.
6665
      */
6666
      create_info->db_type= table->part_info->default_engine_type;
6667
    }
6668
    else
6669
#endif
6670
      create_info->db_type= old_db_type;
6671
  }
unknown's avatar
unknown committed
6672

6673
  if (check_engine(thd, new_name, create_info))
unknown's avatar
unknown committed
6674
    goto err;
6675
  new_db_type= create_info->db_type;
6676

6677 6678
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
6679
      !table->file->can_switch_engines())
unknown's avatar
unknown committed
6680
  {
6681
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
unknown's avatar
unknown committed
6682 6683 6684
    goto err;
  }

6685 6686 6687 6688 6689
  /*
   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.
  */
6690
  if (create_info->row_type == ROW_TYPE_NOT_USED)
6691
  {
6692
    /* ALTER TABLE without explicit row type */
6693
    create_info->row_type= table->s->row_type;
6694 6695 6696 6697
  }
  else
  {
    /* ALTER TABLE with specific row type */
6698 6699
    create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
  }
unknown's avatar
unknown committed
6700

6701 6702 6703
  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
6704
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
6705
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
6706 6707 6708
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
6709
    goto err;
6710 6711
  }
  
6712
  thd_proc_info(thd, "setup");
6713
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
6714
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
6715
  {
6716 6717 6718 6719
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
6720 6721 6722 6723 6724 6725 6726 6727 6728
      /*
        wait_while_table_is_used() ensures that table being altered is
        opened only by this thread and that TABLE::TABLE_SHARE::version
        of TABLE object corresponding to this table is 0.
        The latter guarantees that no DML statement will open this table
        until ALTER TABLE finishes (i.e. until close_thread_tables())
        while the fact that the table is still open gives us protection
        from concurrent DDL statements.
      */
Marc Alff's avatar
Marc Alff committed
6729
      mysql_mutex_lock(&LOCK_open);
6730
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
Marc Alff's avatar
Marc Alff committed
6731
      mysql_mutex_unlock(&LOCK_open);
6732
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
6733
      error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6734 6735 6736
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
Marc Alff's avatar
Marc Alff committed
6737
      mysql_mutex_lock(&LOCK_open);
6738
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
Marc Alff's avatar
Marc Alff committed
6739
      mysql_mutex_unlock(&LOCK_open);
6740
      error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
6741 6742
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
6743 6744 6745 6746
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
6747 6748 6749
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
unknown's avatar
unknown committed
6750
      error= 0;
6751 6752 6753 6754 6755
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
    }

Marc Alff's avatar
Marc Alff committed
6756
    mysql_mutex_lock(&LOCK_open);
6757 6758 6759 6760 6761 6762 6763 6764 6765
    /*
      Unlike to the above case close_cached_table() below will remove ALL
      instances of TABLE from table cache (it will also remove table lock
      held by this thread). So to make actual table renaming and writing
      to binlog atomic we have to put them into the same critical section
      protected by LOCK_open mutex. This also removes gap for races between
      access() and mysql_rename_table() calls.
    */

6766
    if (!error && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
6767
    {
6768
      thd_proc_info(thd, "rename");
unknown's avatar
unknown committed
6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781
      /*
        Then do a 'simple' rename of the table. First we need to close all
        instances of 'source' table.
      */
      close_cached_table(thd, table);
      /*
        Then, we want check once again that target table does not exist.
        Actually the order of these two steps does not matter since
        earlier we took name-lock on the target table, so we do them
        in this particular order only to be consistent with 5.0, in which
        we don't take this name-lock and where this order really matters.
        TODO: Investigate if we need this access() check at all.
      */
6782 6783
      if (!access(new_name_buff,F_OK))
      {
6784
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
6785
	error= -1;
6786 6787 6788
      }
      else
      {
6789 6790 6791 6792 6793 6794
	*fn_ext(new_name)=0;
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
	  error= -1;
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
Konstantin Osipov's avatar
Konstantin Osipov committed
6795 6796
          (void) mysql_rename_table(old_db_type, new_db, new_alias, db,
                                  table_name, 0);
6797 6798
          error= -1;
        }
unknown's avatar
unknown committed
6799 6800
      }
    }
6801

6802 6803 6804 6805 6806 6807
    if (error == HA_ERR_WRONG_COMMAND)
    {
      error= 0;
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
6808
    }
6809

6810 6811
    if (!error)
    {
6812 6813 6814
      error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
      if (!error)
        my_ok(thd);
6815
    }
6816
    else if (error > 0)
6817
    {
6818 6819
      table->file->print_error(error, MYF(0));
      error= -1;
6820
    }
6821 6822
    if (name_lock)
      unlink_open_table(thd, name_lock, FALSE);
Marc Alff's avatar
Marc Alff committed
6823
    mysql_mutex_unlock(&LOCK_open);
6824 6825 6826
    table_list->table= NULL;                    // For query cache
    query_cache_invalidate3(thd, table_list, 0);
    DBUG_RETURN(error);
unknown's avatar
unknown committed
6827 6828
  }

6829
  /* We have to do full alter table. */
unknown's avatar
unknown committed
6830

6831 6832 6833
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6834
    goto err;
6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847
#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
6848

6849 6850
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
unknown's avatar
unknown committed
6851 6852
  
  need_copy_table= alter_info->change_level;
unknown's avatar
unknown committed
6853

unknown's avatar
unknown committed
6854 6855
  set_table_default_charset(thd, create_info, db);

6856
  if (thd->variables.old_alter_table
unknown's avatar
unknown committed
6857
      || (table->s->db_type() != create_info->db_type)
6858
#ifdef WITH_PARTITION_STORAGE_ENGINE
6859
      || partition_changed
6860
#endif
6861
     )
6862
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
unknown's avatar
unknown committed
6863
  else
6864
  {
unknown's avatar
unknown committed
6865
    enum_alter_table_change_level need_copy_table_res;
6866
    /* Check how much the tables differ. */
6867 6868
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6869
                       &need_copy_table_res,
6870 6871
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
6872 6873
                       &index_add_buffer, &index_add_count,
                       &candidate_key_count))
6874
      goto err;
6875 6876 6877
   
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6878 6879 6880 6881 6882 6883 6884 6885 6886 6887
  }

  /*
    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
6888
    ulong alter_flags= 0;
6889 6890 6891 6892 6893 6894
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

6895
    alter_flags= table->file->alter_table_flags(alter_info->flags);
unknown's avatar
unknown committed
6896
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6897 6898 6899 6900 6901 6902 6903 6904 6905
    /* 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)
      {
6906 6907 6908 6909 6910
        /* 
           Unique key. Check for "PRIMARY". 
           or if dropping last unique key
        */
        if ((uint) (key - table->key_info) == table->s->primary_key)
6911
        {
6912
          DBUG_PRINT("info", ("Dropping primary key"));
6913 6914 6915 6916
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
6917
          candidate_key_count--;
6918 6919 6920
        }
        else
        {
6921 6922 6923
          KEY_PART_INFO *part_end= key->key_part + key->key_parts;
          bool is_candidate_key= true;

6924 6925 6926
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939

          /*
            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--;
6940 6941 6942 6943 6944 6945 6946 6947 6948
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_DROP_INDEX;
        needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
      }
    }
6949 6950
    no_pk= ((table->s->primary_key == MAX_KEY) ||
            (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
6951 6952 6953 6954 6955 6956 6957 6958 6959
    /* 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)
      {
6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984
        /* 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))
6985
        {
6986
          DBUG_PRINT("info", ("Adding primary key"));
6987 6988 6989 6990
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
          pk_changed++;
6991
          no_pk= false;
6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007
        }
        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;
      }
    }

7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021
    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));
7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032
    /*
      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. */
7033
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
7034 7035 7036 7037 7038
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
7039
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
7040 7041 7042 7043 7044
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
unknown's avatar
unknown committed
7045

7046 7047
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
7048
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
7049 7050
    so that ALTER TABLE won't break when somebody will add new flag
  */
7051
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7052
    create_info->frm_only= 1;
7053

7054
#ifdef WITH_PARTITION_STORAGE_ENGINE
unknown's avatar
unknown committed
7055
  if (fast_alter_partition)
unknown's avatar
unknown committed
7056
  {
unknown's avatar
unknown committed
7057
    DBUG_ASSERT(!name_lock);
unknown's avatar
unknown committed
7058 7059 7060 7061
    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
7062
  }
7063
#endif
unknown's avatar
unknown committed
7064

7065 7066 7067 7068 7069 7070
  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);

7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085
  /*
    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.
7086 7087
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113
      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);
    }
  }
7114 7115
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
7116

7117 7118 7119 7120 7121 7122
  /*
    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
7123
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
7124 7125 7126
                                    create_info,
                                    alter_info,
                                    1, 0);
7127 7128
  reenable_binlog(thd);
  if (error)
unknown's avatar
unknown committed
7129
    goto err;
7130 7131

  /* Open the table if we need to copy the data. */
7132
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
7133
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7134
  {
7135
    if (table->s->tmp_table)
7136 7137 7138 7139
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
7140
      tbl.table_name= tbl.alias= tmp_name;
unknown's avatar
unknown committed
7141
      /* Table is in thd->temporary_tables */
7142 7143
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
7144 7145 7146
    }
    else
    {
7147
      char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
7148
      /* table is a normal table: Create temporary table in same directory */
7149
      build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
7150
                           FN_IS_TMP);
7151
      /* Open our intermediate table */
7152 7153 7154
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
7155
      goto err1;
7156 7157 7158 7159
    /*
      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
7160 7161
  }

7162
  /* Copy the data if necessary. */
7163
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
7164
  thd->cuted_fields=0L;
unknown's avatar
unknown committed
7165
  copied=deleted=0;
7166 7167 7168 7169
  /*
    We do not copy data for MERGE tables. Only the children have data.
    MERGE tables have HA_NO_COPY_ON_ALTER set.
  */
7170
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
7171
  {
7172
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
unknown's avatar
unknown committed
7173
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7174
    new_table->next_number_field=new_table->found_next_number_field;
7175
    thd_proc_info(thd, "copy to tmp table");
7176 7177 7178
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
7179
                                    alter_info->keys_onoff,
unknown's avatar
unknown committed
7180
                                    alter_info->error_if_not_empty);
7181
  }
7182
  else
7183
  {
Marc Alff's avatar
Marc Alff committed
7184
    mysql_mutex_lock(&LOCK_open);
7185
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
Marc Alff's avatar
Marc Alff committed
7186
    mysql_mutex_unlock(&LOCK_open);
7187
    thd_proc_info(thd, "manage keys");
7188 7189
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
7190 7191
    error= ha_autocommit_or_rollback(thd, 0);
    if (end_active_trans(thd))
7192
      error= 1;
7193
  }
7194
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
7195

7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208
  /* 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"));

7209
    table->file->ha_prepare_for_alter();
7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236
    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;
7237
        goto err1;
7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259
      }
    }
    /*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));
7260
        goto err1;
7261 7262 7263 7264 7265 7266
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
7267
        goto err1;
7268 7269 7270 7271
      }
    }
    /*end of if (index_drop_count)*/

7272 7273 7274 7275
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
7276

7277 7278
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
7279
    if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
7280
      goto err1;
7281
    committed= 1;
7282 7283 7284
  }
  /*end of if (! new_table) for add/drop index*/

unknown's avatar
unknown committed
7285
  if (table->s->tmp_table != NO_TMP_TABLE)
unknown's avatar
unknown committed
7286 7287 7288
  {
    /* We changed a temporary table */
    if (error)
7289
      goto err1;
7290 7291 7292 7293 7294 7295
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
unknown's avatar
unknown committed
7296
    /* Remove link to old table and rename the new one */
unknown's avatar
unknown committed
7297
    close_temporary_table(thd, table, 1, 1);
7298 7299
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
7300
      goto err1;
7301 7302

    if (!thd->is_current_stmt_binlog_format_row() &&
7303 7304
        write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7305 7306 7307
    goto end_temporary;
  }

7308 7309
  if (new_table)
  {
7310 7311 7312 7313
    /*
      Close the intermediate table that will be the new table.
      Note that MERGE tables do not have their children attached here.
    */
unknown's avatar
unknown committed
7314
    intern_close_table(new_table);
7315
    my_free(new_table,MYF(0));
7316
  }
Marc Alff's avatar
Marc Alff committed
7317
  mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
7318 7319
  if (error)
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
7320
    (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
Marc Alff's avatar
Marc Alff committed
7321
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
7322 7323
    goto err;
  }
7324

unknown's avatar
unknown committed
7325
  /*
7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338
    Data is copied. Now we:
    1) Wait until all other threads close old version of table.
    2) Close instances of table open by this thread and replace them
       with exclusive name-locks.
    3) Rename the old table to a temp name, rename the new one to the
       old name.
    4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
       we reopen new version of table.
    5) Write statement to the binary log.
    6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
       remove name-locks from list of open tables and table cache.
    7) If we are not not under LOCK TABLES we rely on close_thread_tables()
       call to remove name-locks from table cache and list of open table.
unknown's avatar
unknown committed
7339 7340
  */

7341
  thd_proc_info(thd, "rename result table");
7342 7343
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
7344 7345
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
unknown's avatar
unknown committed
7346

7347
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
7348
  close_data_files_and_morph_locks(thd, db, table_name);
unknown's avatar
unknown committed
7349

unknown's avatar
unknown committed
7350
  error=0;
7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365
  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.
  */
7366 7367 7368 7369 7370 7371
  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;
  }
7372 7373
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
unknown's avatar
unknown committed
7374 7375
  {
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7376
    (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
unknown's avatar
unknown committed
7377
  }
7378
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
7379
                              new_alias, FN_FROM_IS_TMP) ||
7380
           ((new_name != table_name || new_db != db) && // we also do rename
7381
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
7382 7383
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
7384
           Table_triggers_list::change_table_name(thd, db, table_name,
7385
                                                  new_db, new_alias)))
7386 7387
  {
    /* Try to get everything back. */
unknown's avatar
unknown committed
7388
    error=1;
Konstantin Osipov's avatar
Konstantin Osipov committed
7389 7390 7391 7392
    (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
7393
  }
7394

unknown's avatar
unknown committed
7395 7396
  if (error)
  {
7397 7398
    /* This shouldn't happen. But let us play it safe. */
    goto err_with_placeholders;
unknown's avatar
unknown committed
7399
  }
7400

7401
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
unknown's avatar
unknown committed
7402
  {
unknown's avatar
unknown committed
7403
    /*
7404 7405
      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.
7406
      NO need to tamper with MERGE tables. The real open is done later.
unknown's avatar
unknown committed
7407
    */
7408 7409 7410 7411 7412 7413 7414 7415
    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);
7416 7417
      table_list->table= name_lock;
      if (reopen_name_locked_table(thd, table_list, FALSE))
7418 7419
        goto err_with_placeholders;
      t_table= table_list->table;
unknown's avatar
unknown committed
7420
    }
7421
    else
7422
    {
7423 7424 7425
      if (reopen_table(table))
        goto err_with_placeholders;
      t_table= table;
7426 7427
    }
    /* Tell the handler that a new frm file is in place. */
7428 7429
    if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                               create_info))
7430 7431
      goto err_with_placeholders;
    if (thd->locked_tables && new_name == table_name && new_db == db)
7432
    {
7433 7434 7435 7436 7437 7438 7439 7440
      /*
        We are going to reopen table down on the road, so we have to restore
        state of the TABLE object which we used for obtaining of handler
        object to make it suitable for reopening.
      */
      DBUG_ASSERT(t_table == table);
      table->open_placeholder= 1;
      close_handle_and_leave_table_as_lock(table);
7441 7442
    }
  }
7443

Konstantin Osipov's avatar
Konstantin Osipov committed
7444
  (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);
7445 7446

  if (thd->locked_tables && new_name == table_name && new_db == db)
unknown's avatar
unknown committed
7447
  {
7448
    thd->in_lock_tables= 1;
7449
    error= reopen_tables(thd, 1, 1);
7450
    thd->in_lock_tables= 0;
unknown's avatar
unknown committed
7451
    if (error)
7452
      goto err_with_placeholders;
unknown's avatar
unknown committed
7453
  }
Marc Alff's avatar
Marc Alff committed
7454
  mysql_mutex_unlock(&LOCK_open);
7455

7456
  thd_proc_info(thd, "end");
7457

7458 7459
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

7460
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
7461
                      thd->query(), thd->query_length(),
7462 7463
                      db, table_name);

unknown's avatar
unknown committed
7464
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
7465
                thd->is_current_stmt_binlog_format_row() &&
7466
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
7467 7468
  if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
    DBUG_RETURN(TRUE);
7469

7470
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
7471
  {
unknown's avatar
unknown committed
7472 7473 7474
    /*
      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
7475
      shutdown. But we do not need to attach MERGE children.
unknown's avatar
unknown committed
7476
    */
7477
    char path[FN_REFLEN];
7478
    TABLE *t_table;
7479
    build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0);
7480 7481
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
unknown's avatar
unknown committed
7482
    {
7483
      intern_close_table(t_table);
7484
      my_free(t_table, MYF(0));
unknown's avatar
unknown committed
7485
    }
7486
    else
7487
      sql_print_warning("Could not open table %s.%s after rename\n",
unknown's avatar
unknown committed
7488
                        new_db,table_name);
7489
    ha_flush_logs(old_db_type);
7490
  }
unknown's avatar
unknown committed
7491
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
7492
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
7493

7494
  if (thd->locked_tables && (new_name != table_name || new_db != db))
unknown's avatar
unknown committed
7495
  {
7496 7497 7498 7499 7500 7501
    /*
      If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
      to remove placeholders for the old table and for the target table
      from the list of open tables and table cache. If we are not under
      LOCK TABLES we can rely on close_thread_tables() doing this job.
    */
Marc Alff's avatar
Marc Alff committed
7502
    mysql_mutex_lock(&LOCK_open);
7503
    unlink_open_table(thd, table, FALSE);
unknown's avatar
unknown committed
7504
    unlink_open_table(thd, name_lock, FALSE);
Marc Alff's avatar
Marc Alff committed
7505
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
7506 7507
  }

unknown's avatar
unknown committed
7508
end_temporary:
7509 7510
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
7511
	      (ulong) thd->warning_info->statement_warn_count());
7512
  my_ok(thd, copied + deleted, 0L, tmp_name);
unknown's avatar
unknown committed
7513
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
7514
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7515

7516
err1:
7517 7518 7519 7520 7521 7522
  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
7523 7524
    (void) quick_rm_table(new_db_type, new_db, tmp_name,
                          create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
7525

7526
err:
7527 7528 7529 7530 7531 7532
  /*
    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
7533 7534
  if (alter_info->error_if_not_empty &&
      thd->warning_info->current_row_for_warning())
7535
  {
7536 7537
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
unknown's avatar
unknown committed
7538
    switch (alter_info->datetime_field->sql_type)
7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554
    {
      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
7555
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
unknown's avatar
unknown committed
7556
                                 f_val, strlength(f_val), t_type,
unknown's avatar
unknown committed
7557
                                 alter_info->datetime_field->field_name);
7558 7559
    thd->abort_on_warning= save_abort_on_warning;
  }
unknown's avatar
unknown committed
7560 7561
  if (name_lock)
  {
Marc Alff's avatar
Marc Alff committed
7562
    mysql_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
7563
    unlink_open_table(thd, name_lock, FALSE);
Marc Alff's avatar
Marc Alff committed
7564
    mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
7565
  }
unknown's avatar
unknown committed
7566
  DBUG_RETURN(TRUE);
7567 7568 7569 7570 7571 7572 7573 7574 7575 7576

err_with_placeholders:
  /*
    An error happened while we were holding exclusive name-lock on table
    being altered. To be safe under LOCK TABLES we should remove placeholders
    from list of open tables list and table cache.
  */
  unlink_open_table(thd, table, FALSE);
  if (name_lock)
    unlink_open_table(thd, name_lock, FALSE);
Marc Alff's avatar
Marc Alff committed
7577
  mysql_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
7578
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7579
}
unknown's avatar
unknown committed
7580
/* mysql_alter_table */
unknown's avatar
unknown committed
7581 7582

static int
unknown's avatar
unknown committed
7583
copy_data_between_tables(TABLE *from,TABLE *to,
unknown's avatar
unknown committed
7584
			 List<Create_field> &create,
7585
                         bool ignore,
7586
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
7587
			 ha_rows *copied,
unknown's avatar
unknown committed
7588
			 ha_rows *deleted,
7589 7590
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
unknown's avatar
unknown committed
7591 7592 7593 7594 7595
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
7596
  uint length= 0;
unknown's avatar
unknown committed
7597 7598 7599 7600 7601
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
7602
  ha_rows examined_rows;
7603
  bool auto_increment_field_copied= 0;
7604
  ulong save_sql_mode;
7605
  ulonglong prev_insert_id;
unknown's avatar
unknown committed
7606 7607
  DBUG_ENTER("copy_data_between_tables");

7608 7609 7610 7611 7612 7613
  /*
    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
  */
7614
  error= ha_enable_transaction(thd, FALSE);
7615 7616
  if (error)
    DBUG_RETURN(-1);
7617
  
7618
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
7619 7620
    DBUG_RETURN(-1);				/* purecov: inspected */

7621
  if (to->file->ha_external_lock(thd, F_WRLCK))
7622
    DBUG_RETURN(-1);
7623

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

7627 7628 7629 7630 7631
  /* 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));

7632
  from->file->info(HA_STATUS_VARIABLE);
7633
  to->file->ha_start_bulk_insert(from->file->stats.records);
unknown's avatar
unknown committed
7634

7635 7636
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
7637 7638
  List_iterator<Create_field> it(create);
  Create_field *def;
unknown's avatar
unknown committed
7639 7640 7641 7642 7643
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
7644 7645
    {
      if (*ptr == to->next_number_field)
7646
      {
7647
        auto_increment_field_copied= TRUE;
7648 7649 7650 7651 7652 7653 7654 7655 7656
        /*
          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
7657
      (copy_end++)->set(*ptr,def->field,0);
7658 7659
    }

unknown's avatar
unknown committed
7660 7661
  }

7662 7663
  found_count=delete_count=0;

unknown's avatar
unknown committed
7664 7665
  if (order)
  {
7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683
    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
7684

7685 7686 7687 7688 7689 7690 7691 7692 7693 7694
      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
7695 7696
  };

7697 7698
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
7699
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
7700
  if (ignore)
unknown's avatar
unknown committed
7701
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
Marc Alff's avatar
Marc Alff committed
7702
  thd->warning_info->reset_current_row_for_warning();
7703
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
7704 7705 7706 7707
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
7708
      thd->send_kill_message();
unknown's avatar
unknown committed
7709 7710 7711
      error= 1;
      break;
    }
7712 7713 7714 7715 7716 7717
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
7718 7719
    if (to->next_number_field)
    {
7720
      if (auto_increment_field_copied)
7721
        to->auto_increment_field_not_null= TRUE;
7722 7723 7724
      else
        to->next_number_field->reset();
    }
7725
    
unknown's avatar
unknown committed
7726
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
7727
    {
unknown's avatar
unknown committed
7728
      copy_ptr->do_copy(copy_ptr);
7729
    }
7730
    prev_insert_id= to->file->next_insert_id;
7731
    error=to->file->ha_write_row(to->record[0]);
7732 7733
    to->auto_increment_field_not_null= FALSE;
    if (error)
unknown's avatar
unknown committed
7734
    {
7735
      if (!ignore ||
7736
          to->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
7737
      {
7738
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
7739 7740 7741 7742
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
unknown's avatar
unknown committed
7743
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
7744
             if (key_nr == 0 &&
unknown's avatar
unknown committed
7745 7746
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
7747
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
unknown's avatar
unknown committed
7748
             to->file->print_keydup_error(key_nr, err_msg);
7749 7750 7751 7752
             break;
           }
         }

unknown's avatar
unknown committed
7753 7754 7755
	to->file->print_error(error,MYF(0));
	break;
      }
7756
      to->file->restore_auto_increment(prev_insert_id);
unknown's avatar
unknown committed
7757 7758 7759
      delete_count++;
    }
    else
7760
      found_count++;
Marc Alff's avatar
Marc Alff committed
7761
    thd->warning_info->inc_current_row_for_warning();
unknown's avatar
unknown committed
7762 7763
  }
  end_read_record(&info);
7764
  free_io_cache(from);
7765
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
7766

7767
  if (to->file->ha_end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
7768
  {
unknown's avatar
unknown committed
7769
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
7770 7771
    error=1;
  }
unknown's avatar
unknown committed
7772
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7773

7774 7775 7776 7777 7778 7779
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7780 7781 7782 7783
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
7784
  if (ha_autocommit_or_rollback(thd, 0))
7785
    error=1;
7786
  if (end_active_trans(thd))
7787
    error=1;
7788

unknown's avatar
unknown committed
7789
 err:
7790
  thd->variables.sql_mode= save_sql_mode;
7791
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
7792
  free_io_cache(from);
unknown's avatar
unknown committed
7793 7794
  *copied= found_count;
  *deleted=delete_count;
7795
  to->file->ha_release_auto_increment();
7796
  if (to->file->ha_external_lock(thd,F_UNLCK))
7797
    error=1;
unknown's avatar
unknown committed
7798 7799
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7800

unknown's avatar
unknown committed
7801

7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7813
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7814 7815
{
  HA_CREATE_INFO create_info;
7816 7817 7818
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7819 7820 7821 7822 7823 7824
  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;
7825 7826

  bzero((char*) &create_info, sizeof(create_info));
unknown's avatar
unknown committed
7827
  create_info.row_type=ROW_TYPE_NOT_USED;
7828
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
7829
  /* Force alter table to recreate table */
7830
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7831
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7832 7833
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7834 7835 7836
}


7837 7838
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7839 7840 7841 7842 7843
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
7844
  DBUG_ENTER("mysql_checksum_table");
7845 7846 7847

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7848 7849
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7850
  item->maybe_null= 1;
7851
  if (protocol->send_result_set_metadata(&field_list,
7852
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
7853
    DBUG_RETURN(TRUE);
7854

7855
  /* Open one table after the other to keep lock time as short as possible. */
unknown's avatar
VIEW  
unknown committed
7856
  for (table= tables; table; table= table->next_local)
7857 7858
  {
    char table_name[NAME_LEN*2+2];
7859
    TABLE *t;
7860

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

7863
    t= table->table= open_n_lock_single_table(thd, table, TL_READ);
unknown's avatar
unknown committed
7864
    thd->clear_error();			// these errors shouldn't get client
7865 7866 7867 7868

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

7869
    if (!t)
7870
    {
unknown's avatar
unknown committed
7871
      /* Table didn't exist */
7872
      protocol->store_null();
unknown's avatar
unknown committed
7873
      thd->clear_error();
7874 7875 7876
    }
    else
    {
7877
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7878 7879
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7880
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
7881
	       (check_opt->flags & T_QUICK))
7882
	protocol->store_null();
7883 7884
      else
      {
7885 7886
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
7887
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7888

7889
        t->use_all_columns();
7890

unknown's avatar
unknown committed
7891
	if (t->file->ha_rnd_init(1))
7892 7893 7894
	  protocol->store_null();
	else
	{
7895
	  for (;;)
7896
	  {
7897 7898 7899 7900 7901 7902 7903 7904 7905 7906
            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;
            }
7907
	    ha_checksum row_crc= 0;
7908 7909 7910 7911 7912 7913 7914
            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
7915 7916 7917 7918
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
7919 7920 7921
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
7922 7923
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7924

7925
	    for (uint i= 0; i < t->s->fields; i++ )
7926 7927
	    {
	      Field *f= t->field[i];
7928 7929 7930 7931 7932 7933 7934 7935
        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))
7936 7937 7938
	      {
		String tmp;
		f->val_str(&tmp);
7939
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7940 7941
	      }
	      else
7942
		row_crc= my_checksum(row_crc, f->ptr,
unknown's avatar
unknown committed
7943
				     f->pack_length());
7944
	    }
7945

7946 7947 7948
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
7949
          t->file->ha_rnd_end();
7950
	}
7951
      }
unknown's avatar
unknown committed
7952
      thd->clear_error();
7953 7954 7955 7956 7957 7958 7959
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

7960
  my_eof(thd);
unknown's avatar
unknown committed
7961
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7962

7963 7964 7965 7966
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
7967
  DBUG_RETURN(TRUE);
7968
}
7969 7970

static bool check_engine(THD *thd, const char *table_name,
7971
                         HA_CREATE_INFO *create_info)
7972
{
7973
  handlerton **new_engine= &create_info->db_type;
unknown's avatar
unknown committed
7974
  handlerton *req_engine= *new_engine;
unknown's avatar
unknown committed
7975
  bool no_substitution=
7976
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
7977 7978
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
7979 7980
    return TRUE;

unknown's avatar
unknown committed
7981
  if (req_engine && req_engine != *new_engine)
7982
  {
unknown's avatar
unknown committed
7983
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
7984 7985
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
unknown's avatar
unknown committed
7986
                       ha_resolve_storage_engine_name(*new_engine),
7987 7988
                       table_name);
  }
7989 7990 7991 7992 7993
  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
7994
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
unknown's avatar
unknown committed
7995
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
7996 7997 7998
      *new_engine= 0;
      return TRUE;
    }
7999
    *new_engine= myisam_hton;
8000
  }
8001 8002
  return FALSE;
}