sql_table.cc 225 KB
Newer Older
1
/* Copyright (C) 2000-2004 MySQL AB
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2 3 4

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

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

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

/* drop and alter of tables */

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

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

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

serg@serg.mylan's avatar
serg@serg.mylan committed
32
const char *primary_key_name="PRIMARY";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33 34 35 36

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

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

57 58 59 60 61 62 63 64 65 66 67 68 69 70
#ifndef DBUG_OFF

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


71 72 73 74 75 76 77 78 79 80 81 82 83
/*
  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.
*/

84 85
uint filename_to_tablename(const char *from, char *to, uint to_length)
{
86 87 88 89 90 91
  uint errors;
  uint res;
  DBUG_ENTER("filename_to_tablename");
  DBUG_PRINT("enter", ("from '%s'", from));

  if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length))
92
  {
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    /* 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.
      */
    }
110
  }
111 112 113

  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(res);
114 115 116
}


117 118 119 120 121 122 123 124 125 126 127 128 129
/*
  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.
*/

130 131
uint tablename_to_filename(const char *from, char *to, uint to_length)
{
132
  uint errors, length;
133 134 135
  DBUG_ENTER("tablename_to_filename");
  DBUG_PRINT("enter", ("from '%s'", from));

bar@mysql.com's avatar
bar@mysql.com committed
136 137
  if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
                                 MYSQL50_TABLE_NAME_PREFIX_LENGTH))
138 139 140
    DBUG_RETURN((uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
                                to_length-1) -
                        (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH)));
141 142 143 144 145 146 147 148
  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;
  }
149 150
  DBUG_PRINT("exit", ("to '%s'", to));
  DBUG_RETURN(length);
151 152 153
}


154
/*
155
  Creates path to a file: mysql_data_dir/db/table.ext
156 157

  SYNOPSIS
158
   build_table_filename()
159
     buff                       Where to write result in my_charset_filename.
160
                                This may be the same as table_name.
161 162 163 164 165 166
     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.
167 168 169 170 171 172

  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".
173 174
    Unless flags indicate a temporary table name.
    'db' is always converted.
175
    'ext' is not converted.
176

177 178 179 180 181
    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.
182

183 184
  RETURN
    path length
185 186 187
*/

uint build_table_filename(char *buff, size_t bufflen, const char *db,
188
                          const char *table_name, const char *ext, uint flags)
189 190 191
{
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
192
  DBUG_ENTER("build_table_filename");
193 194
  DBUG_PRINT("enter", ("db: '%s'  table_name: '%s'  ext: '%s'  flags: %x",
                       db, table_name, ext, flags));
195 196 197 198 199 200

  if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP
    strnmov(tbbuff, table_name, sizeof(tbbuff));
  else
    VOID(tablename_to_filename(table_name, tbbuff, sizeof(tbbuff)));

201
  VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
202 203 204 205 206 207 208 209

  char *end = buff + bufflen;
  /* Don't add FN_ROOTDIR if mysql_data_home already includes it */
  char *pos = strnmov(buff, mysql_data_home, bufflen);
  int rootdir_len= strlen(FN_ROOTDIR);
  if (pos - rootdir_len >= buff &&
      memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0)
    pos= strnmov(pos, FN_ROOTDIR, end - pos);
210 211 212 213 214 215
  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);
216

217
  DBUG_PRINT("exit", ("buff: '%s'", buff));
218
  DBUG_RETURN(pos - buff);
219 220 221
}


222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
/*
  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
*/

240
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
241
{
242 243
  DBUG_ENTER("build_tmptable_filename");

244 245 246 247
  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);
248

249 250 251 252 253 254 255
  if (lower_case_table_names)
  {
    /* Convert all except tmpdir to lower case */
    my_casedn_str(files_charset_info, p);
  }

  uint length= unpack_filename(buff, buff);
256 257
  DBUG_PRINT("exit", ("buff: '%s'", buff));
  DBUG_RETURN(length);
258 259
}

260 261 262
/*
--------------------------------------------------------------------------

263
   MODULE: DDL log
264 265 266 267 268 269 270 271
   -----------------

   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
272
   ddl log while we are executing. These entries are dropped when the
273 274 275 276
   operation is completed.

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

277
   There is only one ddl log in the system and it is protected by a mutex
278 279 280
   and there is a global struct that contains information about its current
   state.

281 282
   History:
   First version written in 2006 by Mikael Ronstrom
283 284 285 286
--------------------------------------------------------------------------
*/


287
typedef struct st_global_ddl_log
288
{
289 290 291 292 293 294
  /*
    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.
  */
295
  char file_entry_buf[4*IO_SIZE];
296 297
  char file_name_str[FN_REFLEN];
  char *file_name;
298 299 300
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
301 302
  File file_id;
  uint name_len;
303
  uint io_size;
304
  bool inited;
305
  bool recovery_phase;
306
} GLOBAL_DDL_LOG;
307

308
GLOBAL_DDL_LOG global_ddl_log;
309

310
pthread_mutex_t LOCK_gdl;
311

312 313 314 315 316
#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
317

318 319
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
320
#define DDL_LOG_IO_SIZE_POS 8
321

322
/*
323
  Read one entry from ddl log file
324
  SYNOPSIS
325
    read_ddl_log_file_entry()
326
    entry_no                     Entry number to read
327
  RETURN VALUES
328 329
    TRUE                         Error
    FALSE                        Success
330 331
*/

332
static bool read_ddl_log_file_entry(uint entry_no)
333 334
{
  bool error= FALSE;
335
  File file_id= global_ddl_log.file_id;
336
  uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
337 338
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
339

340
  if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
341
               MYF(MY_WME)) != io_size)
342 343 344 345 346 347
    error= TRUE;
  DBUG_RETURN(error);
}


/*
348
  Write one entry from ddl log file
349
  SYNOPSIS
350
    write_ddl_log_file_entry()
351 352 353 354 355 356
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

357
static bool write_ddl_log_file_entry(uint entry_no)
358 359
{
  bool error= FALSE;
360 361 362
  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");
363

364
  if (my_pwrite(file_id, (uchar*)file_entry_buf,
365
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
366 367 368 369 370 371
    error= TRUE;
  DBUG_RETURN(error);
}


/*
372
  Write ddl log header
373
  SYNOPSIS
374
    write_ddl_log_header()
375 376 377 378 379
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

380
static bool write_ddl_log_header()
381 382
{
  uint16 const_var;
383
  bool error= FALSE;
384
  DBUG_ENTER("write_ddl_log_header");
385

386 387
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
388
  const_var= FN_LEN;
389
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
390
            (ulong) const_var);
391
  const_var= IO_SIZE;
392
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
393
            (ulong) const_var);
394
  if (write_ddl_log_file_entry(0UL))
395 396 397 398 399
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
400
  DBUG_RETURN(error);
401 402 403
}


404
/*
405
  Create ddl log file name
406
  SYNOPSIS
407
    create_ddl_log_file_name()
408 409 410 411 412
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

413
static inline void create_ddl_log_file_name(char *file_name)
414
{
415
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
416 417 418
}


419
/*
420
  Read header of ddl log file
421
  SYNOPSIS
422
    read_ddl_log_header()
423
  RETURN VALUES
424 425
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
426
  DESCRIPTION
427 428 429
    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.
430 431
*/

432
static uint read_ddl_log_header()
433
{
434
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
435
  char file_name[FN_REFLEN];
436
  uint entry_no;
437
  bool successful_open= FALSE;
438
  DBUG_ENTER("read_ddl_log_header");
439

440
  create_ddl_log_file_name(file_name);
441
  if ((global_ddl_log.file_id= my_open(file_name,
442
                                        O_RDWR | O_BINARY, MYF(0))) >= 0)
443
  {
444
    if (read_ddl_log_file_entry(0UL))
445
    {
446 447
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
448
    }
449 450
    else
      successful_open= TRUE;
451
  }
452 453
  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]);
454 455
  if (successful_open)
  {
456
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
457 458 459
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
460
  else
461 462 463
  {
    entry_no= 0;
  }
464 465 466 467
  global_ddl_log.first_free= NULL;
  global_ddl_log.first_used= NULL;
  global_ddl_log.num_entries= 0;
  VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
468
  DBUG_RETURN(entry_no);
469 470 471 472
}


/*
473
  Read a ddl log entry
474
  SYNOPSIS
475
    read_ddl_log_entry()
476 477 478 479 480 481
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
482
    Read a specified entry in the ddl log
483 484
*/

485
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
486
{
487
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
488
  uint inx;
489
  uchar single_char;
490
  DBUG_ENTER("read_ddl_log_entry");
491

492
  if (read_ddl_log_file_entry(read_entry))
493 494 495
  {
    DBUG_RETURN(TRUE);
  }
496
  ddl_log_entry->entry_pos= read_entry;
497 498 499 500
  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;
501 502 503 504 505 506 507
  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];
508 509 510 511 512
  DBUG_RETURN(FALSE);
}


/*
513
  Initialise ddl log
514
  SYNOPSIS
515
    init_ddl_log()
516

517
  DESCRIPTION
518
    Write the header of the ddl log file and length of names. Also set
519
    number of entries to zero.
520 521 522 523

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
524 525
*/

526
static bool init_ddl_log()
527
{
528
  char file_name[FN_REFLEN];
529
  DBUG_ENTER("init_ddl_log");
530

531
  if (global_ddl_log.inited)
532 533
    goto end;

534
  global_ddl_log.io_size= IO_SIZE;
535
  create_ddl_log_file_name(file_name);
536 537 538 539
  if ((global_ddl_log.file_id= my_create(file_name,
                                         CREATE_MODE,
                                         O_RDWR | O_TRUNC | O_BINARY,
                                         MYF(MY_WME))) < 0)
540
  {
541
    /* Couldn't create ddl log file, this is serious error */
542 543
    sql_print_error("Failed to open ddl log file");
    DBUG_RETURN(TRUE);
544
  }
545
  global_ddl_log.inited= TRUE;
546
  if (write_ddl_log_header())
547
  {
548
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
549
    global_ddl_log.inited= FALSE;
550
    DBUG_RETURN(TRUE);
551
  }
552 553

end:
554
  DBUG_RETURN(FALSE);
555 556 557 558
}


/*
559
  Execute one action in a ddl log entry
560
  SYNOPSIS
561 562
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
563
  RETURN VALUES
564 565
    TRUE                       Error
    FALSE                      Success
566 567
*/

568
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
569
{
570 571
  bool frm_action= FALSE;
  LEX_STRING handler_name;
572
  handler *file= NULL;
573
  MEM_ROOT mem_root;
574
  int error= TRUE;
575
  char to_path[FN_REFLEN];
576
  char from_path[FN_REFLEN];
577
#ifdef WITH_PARTITION_STORAGE_ENGINE
578
  char *par_ext= (char*)".par";
579
#endif
580
  handlerton *hton;
581
  DBUG_ENTER("execute_ddl_log_action");
582

583
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
584 585 586
  {
    DBUG_RETURN(FALSE);
  }
587 588
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
589
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
590
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
591 592 593
    frm_action= TRUE;
  else
  {
antony@ppcg5.local's avatar
antony@ppcg5.local committed
594 595
    plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
    if (!plugin)
596 597 598 599
    {
      my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
      goto error;
    }
antony@ppcg5.local's avatar
antony@ppcg5.local committed
600 601
    hton= plugin_data(plugin, handlerton*);
    file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
602
    if (!file)
603 604
    {
      mem_alloc_error(sizeof(handler));
605
      goto error;
606
    }
607
  }
608
  switch (ddl_log_entry->action_type)
609
  {
610
    case DDL_LOG_REPLACE_ACTION:
611
    case DDL_LOG_DELETE_ACTION:
612
    {
613
      if (ddl_log_entry->phase == 0)
614 615 616
      {
        if (frm_action)
        {
617 618 619
          strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
          if ((error= my_delete(to_path, MYF(MY_WME))))
          {
620
            if (my_errno != ENOENT)
621 622
              break;
          }
623
#ifdef WITH_PARTITION_STORAGE_ENGINE
624 625
          strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
          VOID(my_delete(to_path, MYF(MY_WME)));
626
#endif
627 628 629
        }
        else
        {
630 631 632 633 634
          if ((error= file->delete_table(ddl_log_entry->name)))
          {
            if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
              break;
          }
635
        }
636
        if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
637 638 639
          break;
        VOID(sync_ddl_log());
        error= FALSE;
640 641
        if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
          break;
642
      }
643 644 645 646 647 648
      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.
      */
649
    }
650
    case DDL_LOG_RENAME_ACTION:
651
    {
652 653 654
      error= TRUE;
      if (frm_action)
      {
655
        strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
656
        strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
657
        if (my_rename(from_path, to_path, MYF(MY_WME)))
658
          break;
659
#ifdef WITH_PARTITION_STORAGE_ENGINE
660
        strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
661
        strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
662
        VOID(my_rename(from_path, to_path, MYF(MY_WME)));
663
#endif
664 665 666
      }
      else
      {
667 668
        if (file->rename_table(ddl_log_entry->from_name,
                               ddl_log_entry->name))
669
          break;
670 671
      }
      if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
672 673 674
        break;
      VOID(sync_ddl_log());
      error= FALSE;
675
      break;
676
    }
677 678 679 680 681 682 683 684
    default:
      DBUG_ASSERT(0);
      break;
  }
  delete file;
error:
  free_root(&mem_root, MYF(0)); 
  DBUG_RETURN(error);
685 686 687
}


688
/*
689
  Get a free entry in the ddl log
690
  SYNOPSIS
691 692
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
693
  RETURN VALUES
694 695
    TRUE                       Error
    FALSE                      Success
696 697
*/

698 699
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
700
{
701 702 703
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
704

705
  if (global_ddl_log.first_free == NULL)
706
  {
707 708
    if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
                              sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
709
    {
710
      sql_print_error("Failed to allocate memory for ddl log free list");
711 712
      DBUG_RETURN(TRUE);
    }
713
    global_ddl_log.num_entries++;
714
    used_entry->entry_pos= global_ddl_log.num_entries;
715
    *write_header= TRUE;
716 717 718
  }
  else
  {
719 720
    used_entry= global_ddl_log.first_free;
    global_ddl_log.first_free= used_entry->next_log_entry;
721
    *write_header= FALSE;
722 723 724 725 726 727
  }
  /*
    Move from free list to used list
  */
  used_entry->next_log_entry= first_used;
  used_entry->prev_log_entry= NULL;
728
  global_ddl_log.first_used= used_entry;
729 730 731 732
  if (first_used)
    first_used->prev_log_entry= used_entry;

  *active_entry= used_entry;
733
  DBUG_RETURN(FALSE);
734 735 736 737
}


/*
738
  External interface methods for the DDL log Module
739 740 741 742
  ---------------------------------------------------
*/

/*
743
  SYNOPSIS
744 745 746
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
747

748
  RETURN VALUES
749 750 751 752
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
753
    A careful write of the ddl log is performed to ensure that we can
754
    handle crashes occurring during CREATE and ALTER TABLE processing.
755 756
*/

757 758
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
759
{
760
  bool error, write_header;
761 762 763 764 765 766
  DBUG_ENTER("write_ddl_log_entry");

  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
767 768
  global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
                                    (char)DDL_LOG_ENTRY_CODE;
769
  global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
770
                                    (char)ddl_log_entry->action_type;
771 772 773 774
  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);
775 776
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
          ddl_log_entry->name, FN_LEN - 1);
777 778
  if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
      ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
779
  {
780
    DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
781 782
    strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
          ddl_log_entry->from_name, FN_LEN - 1);
783
  }
784
  else
785 786
    global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
  DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
787 788
  strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
          ddl_log_entry->handler_name, FN_LEN - 1);
789
  if (get_free_ddl_log_entry(active_entry, &write_header))
790 791 792 793
  {
    DBUG_RETURN(TRUE);
  }
  error= FALSE;
794
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
795
  {
796
    error= TRUE;
797 798 799
    sql_print_error("Failed to write entry_no = %u",
                    (*active_entry)->entry_pos);
  }
800 801
  if (write_header && !error)
  {
802 803
    VOID(sync_ddl_log());
    if (write_ddl_log_header())
804 805
      error= TRUE;
  }
806
  if (error)
807
    release_ddl_log_memory_entry(*active_entry);
808
  DBUG_RETURN(error);
809 810 811 812
}


/*
813
  Write final entry in the ddl log
814
  SYNOPSIS
815
    write_execute_ddl_log_entry()
816 817 818 819
    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.
820 821 822
    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
823 824 825
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
826
  RETURN VALUES
827 828 829 830
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
831
    This is the last write in the ddl log. The previous log entries have
832
    already been written but not yet synched to disk.
833 834 835 836
    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 
837
*/ 
838

839 840 841
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
842
{
843
  bool write_header= FALSE;
844 845
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
846

847 848 849 850
  if (init_ddl_log())
  {
    DBUG_RETURN(TRUE);
  }
851 852
  if (!complete)
  {
853 854 855 856 857 858
    /*
      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.
    */
859
    VOID(sync_ddl_log());
860
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
861 862
  }
  else
863
    file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
864 865 866 867 868 869
  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;
870
  if (!(*active_entry))
871
  {
872
    if (get_free_ddl_log_entry(active_entry, &write_header))
873 874 875
    {
      DBUG_RETURN(TRUE);
    }
876
  }
877
  if (write_ddl_log_file_entry((*active_entry)->entry_pos))
878
  {
879
    sql_print_error("Error writing execute entry in ddl log");
880
    release_ddl_log_memory_entry(*active_entry);
881 882
    DBUG_RETURN(TRUE);
  }
883
  VOID(sync_ddl_log());
884 885
  if (write_header)
  {
886
    if (write_ddl_log_header())
887
    {
888
      release_ddl_log_memory_entry(*active_entry);
889 890 891
      DBUG_RETURN(TRUE);
    }
  }
892 893 894 895
  DBUG_RETURN(FALSE);
}


896
/*
897
  For complex rename operations we need to deactivate individual entries.
898
  SYNOPSIS
899
    deactivate_ddl_log_entry()
900 901 902 903 904 905 906 907
    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
908
    do in a safe manner unless the ddl log is informed of the phases in
909 910 911 912 913 914 915 916 917 918 919
    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.
*/

920
bool deactivate_ddl_log_entry(uint entry_no)
921
{
922 923
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
924

925
  if (!read_ddl_log_file_entry(entry_no))
926
  {
927
    if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
928
    {
929 930 931 932 933 934
      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)
935
      {
936 937
        DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
        file_entry_buf[DDL_LOG_PHASE_POS]= 1;
938 939 940 941 942
      }
      else
      {
        DBUG_ASSERT(0);
      }
943 944 945 946 947 948
      if (write_ddl_log_file_entry(entry_no))
      {
        sql_print_error("Error in deactivating log entry. Position = %u",
                        entry_no);
        DBUG_RETURN(TRUE);
      }
949 950
    }
  }
951 952 953 954 955 956
  else
  {
    sql_print_error("Failed in reading entry before deactivating it");
    DBUG_RETURN(TRUE);
  }
  DBUG_RETURN(FALSE);
957 958 959 960
}


/*
961
  Sync ddl log file
962
  SYNOPSIS
963
    sync_ddl_log()
964 965 966 967 968
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

969
bool sync_ddl_log()
970 971
{
  bool error= FALSE;
972
  DBUG_ENTER("sync_ddl_log");
973

974 975
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
976 977 978 979
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
980 981
  {
    /* Write to error log */
982
    sql_print_error("Failed to sync ddl log");
983 984 985 986 987 988
    error= TRUE;
  }
  DBUG_RETURN(error);
}


989 990 991
/*
  Release a log memory entry
  SYNOPSIS
992
    release_ddl_log_memory_entry()
993 994 995 996 997
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

998
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
999
{
1000 1001 1002 1003
  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");
1004

1005
  global_ddl_log.first_free= log_entry;
1006 1007 1008 1009 1010
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1011
    global_ddl_log.first_used= next_log_entry;
1012 1013
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1014
  DBUG_VOID_RETURN;
1015 1016 1017
}


1018
/*
1019
  Execute one entry in the ddl log. Executing an entry means executing
1020 1021
  a linked list of actions.
  SYNOPSIS
1022
    execute_ddl_log_entry()
1023 1024 1025 1026 1027 1028
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1029
bool execute_ddl_log_entry(THD *thd, uint first_entry)
1030
{
1031
  DDL_LOG_ENTRY ddl_log_entry;
1032
  uint read_entry= first_entry;
1033
  DBUG_ENTER("execute_ddl_log_entry");
1034

1035
  pthread_mutex_lock(&LOCK_gdl);
1036 1037
  do
  {
1038
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1039 1040
    {
      /* Write to error log and continue with next log entry */
1041 1042
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1043 1044
      break;
    }
1045 1046
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1047

1048
    if (execute_ddl_log_action(thd, &ddl_log_entry))
1049
    {
1050
      /* Write to error log and continue with next log entry */
1051 1052
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1053
      break;
1054
    }
1055
    read_entry= ddl_log_entry.next_entry;
1056
  } while (read_entry);
1057
  pthread_mutex_unlock(&LOCK_gdl);
1058 1059 1060
  DBUG_RETURN(FALSE);
}

1061

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
/*
  Close the ddl log
  SYNOPSIS
    close_ddl_log()
  RETURN VALUES
    NONE
*/

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


1082
/*
1083
  Execute the ddl log at recovery of MySQL Server
1084
  SYNOPSIS
1085
    execute_ddl_log_recovery()
1086 1087 1088 1089
  RETURN VALUES
    NONE
*/

1090
void execute_ddl_log_recovery()
1091
{
1092
  uint num_entries, i;
1093
  THD *thd;
1094
  DDL_LOG_ENTRY ddl_log_entry;
1095
  char file_name[FN_REFLEN];
1096
  DBUG_ENTER("execute_ddl_log_recovery");
1097

1098 1099 1100 1101 1102 1103 1104
  /*
    Initialise global_ddl_log struct
  */
  bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
  global_ddl_log.inited= FALSE;
  global_ddl_log.recovery_phase= TRUE;
  global_ddl_log.io_size= IO_SIZE;
monty@mysql.com's avatar
monty@mysql.com committed
1105
  global_ddl_log.file_id= (File) -1;
1106

1107 1108 1109 1110 1111 1112 1113 1114
  /*
    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();

1115
  num_entries= read_ddl_log_header();
1116
  for (i= 1; i < num_entries + 1; i++)
1117
  {
1118
    if (read_ddl_log_entry(i, &ddl_log_entry))
1119
    {
1120 1121 1122
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1123
    }
1124
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1125
    {
1126
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1127
      {
1128 1129
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
1130 1131 1132
      }
    }
  }
1133
  close_ddl_log();
1134 1135 1136 1137 1138 1139
  create_ddl_log_file_name(file_name);
  VOID(my_delete(file_name, MYF(0)));
  global_ddl_log.recovery_phase= FALSE;
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
1140
  DBUG_VOID_RETURN;
1141 1142 1143 1144
}


/*
1145
  Release all memory allocated to the ddl log
1146
  SYNOPSIS
1147
    release_ddl_log()
1148 1149 1150 1151
  RETURN VALUES
    NONE
*/

1152
void release_ddl_log()
1153
{
1154 1155 1156
  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");
1157

1158
  pthread_mutex_lock(&LOCK_gdl);
1159 1160
  while (used_list)
  {
1161
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1162
    my_free(used_list, MYF(0));
1163
    used_list= tmp;
1164 1165 1166
  }
  while (free_list)
  {
1167
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1168
    my_free(free_list, MYF(0));
1169
    free_list= tmp;
1170
  }
1171
  close_ddl_log();
monty@mysql.com's avatar
monty@mysql.com committed
1172
  global_ddl_log.inited= 0;
1173
  pthread_mutex_unlock(&LOCK_gdl);
1174
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1175
  DBUG_VOID_RETURN;
1176 1177 1178
}


1179 1180 1181
/*
---------------------------------------------------------------------------

1182
  END MODULE DDL log
1183 1184 1185 1186 1187
  --------------------

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

1188

1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
/**
   @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);
}


1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
/*
  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
      WFRM_CREATE_HANDLER_FILES If set we need to create the handler file as
                                part of the creation of the frm file
      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];
1246 1247
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
1248 1249 1250
  char frm_name[FN_REFLEN+1];
  DBUG_ENTER("mysql_write_frm");

1251 1252 1253
  /*
    Build shadow frm file name
  */
1254
  build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
1255
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1256
  if (flags & WFRM_WRITE_SHADOW)
1257
  {
1258 1259 1260 1261 1262 1263 1264 1265
    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))
1266 1267 1268 1269 1270
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1271 1272 1273 1274 1275
      partition_info *part_info= lpt->table->part_info;
      char *part_syntax_buf;
      uint syntax_len;

      if (part_info)
1276
      {
1277
        TABLE_SHARE *share= lpt->table->s;
1278 1279
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1280
                                                         TRUE, TRUE)))
1281 1282 1283 1284
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
1285 1286 1287 1288 1289
        share->partition_info_len= part_info->part_info_len= syntax_len;
        if (share->partition_info_buffer_size < syntax_len + 1)
        {
          share->partition_info_buffer_size= syntax_len+1;
          if (!(share->partition_info=
1290
                  (char*) alloc_root(&share->mem_root, syntax_len+1)))
1291 1292 1293 1294
            DBUG_RETURN(TRUE);

        }
        memcpy((char*) share->partition_info, part_syntax_buf, syntax_len + 1);
1295 1296 1297
      }
    }
#endif
1298 1299 1300 1301
    /* 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,
1302
                          lpt->alter_info->create_list, lpt->key_count,
1303
                          lpt->key_info_buffer, lpt->table->file)) ||
1304 1305
         lpt->table->file->create_handler_files(shadow_path, NULL,
                                                CHF_CREATE_FLAG,
1306
                                                lpt->create_info))
1307 1308 1309 1310 1311
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
1312 1313 1314 1315 1316 1317 1318 1319 1320
  }
  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.
    */
1321 1322
    uchar *data;
    size_t length;
1323
    if (readfrm(shadow_path, &data, &length) ||
1324 1325
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
1326 1327
      my_free(data, MYF(MY_ALLOW_ZERO_PTR));
      my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
1328 1329 1330 1331
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1332
    error= my_delete(shadow_frm_name, MYF(MY_WME));
1333
  }
1334 1335
  if (flags & WFRM_INSTALL_SHADOW)
  {
1336 1337 1338
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1339 1340 1341 1342
    /*
      Build frm file name
    */
    build_table_filename(path, sizeof(path), lpt->db,
1343
                         lpt->table_name, "", 0);
1344 1345 1346 1347
    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.
1348 1349 1350 1351 1352 1353
      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.
1354 1355 1356
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1357
#ifdef WITH_PARTITION_STORAGE_ENGINE
1358
        lpt->table->file->create_handler_files(path, shadow_path,
1359
                                               CHF_DELETE_FLAG, NULL) ||
1360 1361
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1362
#endif
1363
#ifdef WITH_PARTITION_STORAGE_ENGINE
1364
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1365
        lpt->table->file->create_handler_files(path, shadow_path,
1366
                                               CHF_RENAME_FLAG, NULL))
1367 1368 1369
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1370 1371 1372 1373
    {
      error= 1;
    }
    VOID(pthread_mutex_unlock(&LOCK_open));
1374
#ifdef WITH_PARTITION_STORAGE_ENGINE
1375
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1376
    part_info->frm_log_entry= NULL;
1377
    VOID(sync_ddl_log());
1378
#endif
1379
  }
1380

1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
end:
  DBUG_RETURN(error);
}


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

  RETURN VALUES
    NONE

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

void write_bin_log(THD *thd, bool clear_error,
                   char const *query, ulong query_length)
{
  if (mysql_bin_log.is_open())
  {
    if (clear_error)
      thd->clear_error();
    thd->binlog_query(THD::STMT_QUERY_TYPE,
                      query, query_length, FALSE, FALSE);
  }
}

1414

1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
/*
 delete (drop) tables.

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

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

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

  RETURN
1433 1434
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
1435 1436

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

1438 1439
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1440
{
1441
  bool error= FALSE, need_start_waiters= FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1442 1443 1444 1445
  DBUG_ENTER("mysql_rm_table");

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

1446
  if (!drop_temporary)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1447
  {
1448
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1449
    {
1450
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1451
      DBUG_RETURN(TRUE);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1452
    }
1453 1454
    else
      need_start_waiters= TRUE;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1455
  }
1456 1457 1458 1459 1460 1461

  /*
    Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
    LOCK_open during wait_if_global_read_lock(), other threads could not
    close their tables. This would make a pretty deadlock.
  */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1462
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1463

1464 1465 1466
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1467
  if (error)
1468
    DBUG_RETURN(TRUE);
1469
  send_ok(thd);
1470
  DBUG_RETURN(FALSE);
1471 1472
}

1473
/*
1474 1475 1476 1477 1478 1479 1480 1481 1482
  Execute the drop of a normal or temporary table

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

1487 1488 1489 1490 1491 1492 1493 1494 1495
  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.
1496 1497 1498 1499 1500

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
1501
*/
1502 1503

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1504 1505
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1506 1507
{
  TABLE_LIST *table;
1508 1509
  char path[FN_REFLEN], *alias;
  uint path_length;
1510
  String wrong_tables;
1511
  int error= 0;
1512
  int non_temp_tables_count= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1513
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1514
  String built_query;
1515 1516
  DBUG_ENTER("mysql_rm_table_part2");

1517 1518 1519
  LINT_INIT(alias);
  LINT_INIT(path_length);

1520
  if (thd->current_stmt_binlog_row_based && !dont_log_query)
1521 1522 1523 1524 1525 1526 1527
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1528

1529
  mysql_ha_rm_tables(thd, tables, FALSE);
1530

1531 1532
  pthread_mutex_lock(&LOCK_open);

1533 1534 1535 1536 1537 1538 1539 1540 1541
  /*
    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;
1542
    table->db_type= NULL;
1543
    if ((share= get_cached_table_share(table->db, table->table_name)))
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1544
      table->db_type= share->db_type();
1545 1546

    /* Disable drop of enabled log tables */
1547
    if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) &&
1548 1549
        check_if_log_table(table->db_length, table->db,
                           table->table_name_length, table->table_name, 1))
1550
    {
1551
      my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
1552
      pthread_mutex_unlock(&LOCK_open);
1553 1554
      DBUG_RETURN(1);
    }
1555 1556
  }

1557 1558
  if (!drop_temporary && lock_table_names_exclusively(thd, tables))
  {
1559
    pthread_mutex_unlock(&LOCK_open);
1560
    DBUG_RETURN(1);
1561
  }
1562

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1566
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1567
  {
1568
    char *db=table->db;
1569 1570
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1571

1572 1573 1574
    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));
1575 1576 1577 1578 1579 1580 1581 1582 1583

    error= drop_temporary_table(thd, table);

    switch (error) {
    case  0:
      // removed temporary table
      tmp_table_deleted= 1;
      continue;
    case -1:
1584
      DBUG_ASSERT(thd->in_sub_stmt);
1585 1586 1587 1588 1589
      error= 1;
      goto err_with_placeholders;
    default:
      // temporary table not found
      error= 0;
1590
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1591

1592 1593 1594 1595 1596 1597
    /*
      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.
      */
1598
    if (thd->current_stmt_binlog_row_based && !dont_log_query)
1599
    {
1600
      non_temp_tables_count++;
1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615
      /*
        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("`,");
    }

1616
    table_type= table->db_type;
1617
    if (!drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1618
    {
1619
      TABLE *locked_table;
1620 1621 1622 1623
      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);
1624 1625 1626 1627 1628 1629 1630
      /*
        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;

1631
      if (thd->killed)
1632
      {
1633 1634
        error= -1;
        goto err_with_placeholders;
1635
      }
1636
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
1637
      /* remove .frm file and engine files */
1638
      path_length= build_table_filename(path, sizeof(path),
1639
                                        db, alias, reg_ext, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1640
    }
1641 1642
    if (drop_temporary ||
        (table_type == NULL &&        
1643 1644 1645
         (access(path, F_OK) &&
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
1646
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1647
    {
1648
      // Table was not found on disk and table can't be created from engine
1649
      if (if_exists)
1650 1651
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
1652
			    table->table_name);
1653
      else
1654
        error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1655 1656 1657
    }
    else
    {
1658
      char *end;
1659 1660 1661 1662 1663
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
1664 1665
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
1666
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
1667
                             !dont_log_query);
1668
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
1669
	  (if_exists || table_type == NULL))
1670
	error= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1671
      if (error == HA_ERR_ROW_IS_REFERENCED)
monty@mysql.com's avatar
monty@mysql.com committed
1672 1673
      {
	/* the table is referenced by a foreign key constraint */
1674
	foreign_key_error=1;
monty@mysql.com's avatar
monty@mysql.com committed
1675
      }
1676
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
1677
      {
1678
        int new_error;
1679 1680
	/* Delete the table definition file */
	strmov(end,reg_ext);
1681
	if (!(new_error=my_delete(path,MYF(MY_WME))))
1682
        {
1683
	  some_tables_deleted=1;
1684 1685
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
1686
        }
1687
        error|= new_error;
1688
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1689 1690 1691 1692 1693
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
1694
      wrong_tables.append(String(table->table_name,system_charset_info));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1695
    }
1696 1697
    DBUG_PRINT("table", ("table: 0x%lx  s: 0x%lx", (long) table->table,
                         table->table ? (long) table->table->s : (long) -1));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1698
  }
1699 1700 1701 1702 1703
  /*
    It's safe to unlock LOCK_open: we have an exclusive lock
    on the table name.
  */
  pthread_mutex_unlock(&LOCK_open);
1704
  thd->thread_specific_used|= tmp_table_deleted;
1705 1706 1707 1708
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
1709
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
1710
                      wrong_tables.c_ptr());
1711
    else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1712
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
1713 1714 1715 1716
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1717
  {
1718
    query_cache_invalidate3(thd, tables, 0);
1719
    if (!dont_log_query)
1720
    {
1721
      if (!thd->current_stmt_binlog_row_based ||
1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732
          non_temp_tables_count > 0 && !tmp_table_deleted)
      {
        /*
          In this case, we are either using statement-based
          replication or using row-based replication but have only
          deleted one or more non-temporary tables (and no temporary
          tables).  In this case, we can write the original query into
          the binary log.
         */
        write_bin_log(thd, !error, thd->query, thd->query_length);
      }
1733
      else if (thd->current_stmt_binlog_row_based &&
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
               non_temp_tables_count > 0 &&
               tmp_table_deleted)
      {
        /*
          In this case we have deleted both temporary and
          non-temporary tables, so:
          - since we have deleted a non-temporary table we have to
            binlog the statement, but
          - since we have deleted a temporary table we cannot binlog
            the statement (since the table has not been created on the
            slave, this might cause the slave to stop).

          Instead, we write a built statement, only containing the
          non-temporary tables, to the binary log
        */
        built_query.chop();                  // Chop of the last comma
        built_query.append(" /* generated by server */");
        write_bin_log(thd, !error, built_query.ptr(), built_query.length());
      }
      /*
        The remaining cases are:
        - no tables where deleted and
        - only temporary tables where deleted and row-based
          replication is used.
        In both these cases, nothing should be written to the binary
        log.
      */
1761
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1762
  }
1763 1764
  pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
1765
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
1766
  pthread_mutex_unlock(&LOCK_open);
1767
  thd->no_warnings_for_error= 0;
1768
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1769 1770 1771
}


1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786
/*
  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
*/

1787
bool quick_rm_table(handlerton *base,const char *db,
1788
                    const char *table_name, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1789 1790
{
  char path[FN_REFLEN];
1791 1792 1793
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

1794
  uint path_length= build_table_filename(path, sizeof(path),
1795
                                         db, table_name, reg_ext, flags);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1796
  if (my_delete(path,MYF(0)))
1797
    error= 1; /* purecov: inspected */
1798
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
1799 1800
  DBUG_RETURN(ha_delete_table(current_thd, base, path, db, table_name, 0) ||
              error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1801 1802
}

1803 1804 1805
/*
  Sort keys in the following order:
  - PRIMARY KEY
1806 1807
  - UNIQUE keys where all column are NOT NULL
  - UNIQUE keys that don't contain partial segments
1808 1809 1810 1811 1812 1813 1814 1815 1816 1817
  - 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)
{
1818 1819 1820
  ulong a_flags= a->flags, b_flags= b->flags;
  
  if (a_flags & HA_NOSAME)
1821
  {
1822
    if (!(b_flags & HA_NOSAME))
1823
      return -1;
1824
    if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
1825 1826
    {
      /* Sort NOT NULL keys before other keys */
1827
      return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
1828 1829 1830 1831 1832
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
1833 1834 1835
    /* 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;
1836
  }
1837
  else if (b_flags & HA_NOSAME)
1838 1839
    return 1;					// Prefer b

1840
  if ((a_flags ^ b_flags) & HA_FULLTEXT)
1841
  {
1842
    return (a_flags & HA_FULLTEXT) ? 1 : -1;
1843
  }
1844
  /*
1845
    Prefer original key order.	usable_key_parts contains here
1846 1847 1848 1849 1850
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
1851 1852
}

1853 1854
/*
  Check TYPELIB (set or enum) for duplicates
1855

1856 1857 1858
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
1859 1860
    name	  name of the checked column
    typelib	  list of values for the column
1861
    dup_val_count  returns count of duplicate elements
1862 1863

  DESCRIPTION
1864
    This function prints an warning for each value in list
1865 1866 1867
    which has some duplicates on its right

  RETURN VALUES
1868 1869
    0             ok
    1             Error
1870 1871
*/

1872
bool check_duplicates_in_interval(const char *set_or_name,
1873
                                  const char *name, TYPELIB *typelib,
1874
                                  CHARSET_INFO *cs, unsigned int *dup_val_count)
1875
{
1876
  TYPELIB tmp= *typelib;
1877
  const char **cur_value= typelib->type_names;
1878
  unsigned int *cur_length= typelib->type_lengths;
1879
  *dup_val_count= 0;  
1880 1881
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
1882
  {
1883 1884 1885 1886
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
1887
    {
1888 1889 1890 1891 1892 1893 1894
      if ((current_thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
                 name,*cur_value,set_or_name);
        return 1;
      }
monty@mysql.com's avatar
monty@mysql.com committed
1895
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
1896 1897 1898
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
1899
      (*dup_val_count)++;
1900 1901
    }
  }
1902
  return 0;
1903
}
1904

1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939

/*
  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++)
  {
    uint length= cs->cset->numchars(cs, *pos, *pos + *len);
    *tot_length+= length;
    set_if_bigger(*max_length, (uint32)length);
  }
}


1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950
/*
  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
1951
    This function prepares a Create_field instance.
1952 1953 1954 1955 1956 1957 1958
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

1959
int prepare_create_field(Create_field *sql_field, 
monty@mysql.com's avatar
monty@mysql.com committed
1960 1961
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
1962
			 longlong table_flags)
1963
{
1964
  unsigned int dup_val_count;
1965
  DBUG_ENTER("prepare_field");
monty@mysql.com's avatar
monty@mysql.com committed
1966 1967

  /*
1968
    This code came from mysql_prepare_create_table.
monty@mysql.com's avatar
monty@mysql.com committed
1969 1970 1971 1972 1973
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
1974 1975 1976 1977
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
monty@mysql.com's avatar
monty@mysql.com committed
1978 1979 1980 1981 1982 1983 1984
    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;
1985
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
1986
    break;
1987
  case MYSQL_TYPE_GEOMETRY:
1988
#ifdef HAVE_SPATIAL
monty@mysql.com's avatar
monty@mysql.com committed
1989 1990 1991 1992
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
1993
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
1994 1995 1996 1997 1998 1999 2000 2001
    }
    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;
2002
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
2003 2004 2005 2006 2007
    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);
2008
#endif /*HAVE_SPATIAL*/
monty@mysql.com's avatar
monty@mysql.com committed
2009
  case MYSQL_TYPE_VARCHAR:
2010
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
monty@mysql.com's avatar
monty@mysql.com committed
2011 2012 2013 2014 2015 2016 2017 2018
    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)
2019
      {
monty@mysql.com's avatar
monty@mysql.com committed
2020 2021
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
2022 2023
        DBUG_RETURN(1);
      }
monty@mysql.com's avatar
monty@mysql.com committed
2024 2025 2026
    }
#endif
    /* fall through */
2027
  case MYSQL_TYPE_STRING:
monty@mysql.com's avatar
monty@mysql.com committed
2028 2029 2030 2031
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
2032
  case MYSQL_TYPE_ENUM:
monty@mysql.com's avatar
monty@mysql.com committed
2033 2034 2035 2036 2037
    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;
2038 2039 2040 2041
    if (check_duplicates_in_interval("ENUM",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
2042
    break;
2043
  case MYSQL_TYPE_SET:
monty@mysql.com's avatar
monty@mysql.com committed
2044 2045 2046 2047 2048
    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;
2049 2050 2051 2052
    if (check_duplicates_in_interval("SET",sql_field->field_name,
                                     sql_field->interval,
                                     sql_field->charset, &dup_val_count))
      DBUG_RETURN(1);
2053 2054 2055 2056 2057 2058
    /* Check that count of unique members is not more then 64 */
    if (sql_field->interval->count -  dup_val_count > sizeof(longlong)*8)
    {
       my_error(ER_TOO_BIG_SET, MYF(0), sql_field->field_name);
       DBUG_RETURN(1);
    }
monty@mysql.com's avatar
monty@mysql.com committed
2059
    break;
2060 2061 2062 2063 2064
  case MYSQL_TYPE_DATE:			// Rest of string types
  case MYSQL_TYPE_NEWDATE:
  case MYSQL_TYPE_TIME:
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_NULL:
monty@mysql.com's avatar
monty@mysql.com committed
2065 2066
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
2067
  case MYSQL_TYPE_BIT:
ramil@mysql.com's avatar
ramil@mysql.com committed
2068
    /* 
2069 2070
      We have sql_field->pack_flag already set here, see
      mysql_prepare_create_table().
ramil@mysql.com's avatar
ramil@mysql.com committed
2071
    */
monty@mysql.com's avatar
monty@mysql.com committed
2072
    break;
2073
  case MYSQL_TYPE_NEWDECIMAL:
monty@mysql.com's avatar
monty@mysql.com committed
2074 2075 2076 2077 2078 2079 2080
    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;
2081
  case MYSQL_TYPE_TIMESTAMP:
monty@mysql.com's avatar
monty@mysql.com committed
2082 2083 2084
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2085
      if (!*timestamps)
2086
      {
monty@mysql.com's avatar
monty@mysql.com committed
2087
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2088
        (*timestamps_with_niladic)++;
2089
      }
monty@mysql.com's avatar
monty@mysql.com committed
2090 2091 2092 2093
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2094
      (*timestamps_with_niladic)++;
monty@mysql.com's avatar
monty@mysql.com committed
2095

2096
    (*timestamps)++;
monty@mysql.com's avatar
monty@mysql.com committed
2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111
    /* 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;
2112 2113 2114
  DBUG_RETURN(0);
}

2115
/*
2116
  Preparation for table creation
2117 2118

  SYNOPSIS
2119
    mysql_prepare_create_table()
2120 2121
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
2122
      alter_info                List of columns and indexes to create
2123 2124 2125 2126 2127 2128
      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.
2129

2130
  DESCRIPTION
2131
    Prepares the table and key structures for table creation.
2132

2133
  NOTES
2134
    sets create_info->varchar if the table has a varchar
2135

2136
  RETURN VALUES
2137 2138
    FALSE    OK
    TRUE     error
2139
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2140

2141 2142 2143 2144 2145 2146 2147
static bool
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
                           Alter_info *alter_info,
                           bool tmp_table,
                           uint *db_options,
                           handler *file, KEY **key_info_buffer,
                           uint *key_count, int select_field_count)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2148
{
2149
  const char	*key_name;
2150
  Create_field	*sql_field,*dup_field;
monty@mysql.com's avatar
monty@mysql.com committed
2151
  uint		field,null_fields,blob_columns,max_key_length;
monty@mysql.com's avatar
monty@mysql.com committed
2152
  ulong		record_offset= 0;
2153
  KEY		*key_info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2154
  KEY_PART_INFO *key_part_info;
2155 2156 2157
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
2158 2159
  List_iterator<Create_field> it(alter_info->create_list);
  List_iterator<Create_field> it2(alter_info->create_list);
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
2160
  uint total_uneven_bit_length= 0;
2161
  DBUG_ENTER("mysql_prepare_create_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2162

2163
  select_field_pos= alter_info->create_list.elements - select_field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2164
  null_fields=blob_columns=0;
2165
  create_info->varchar= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2166
  max_key_length= file->max_key_length();
2167

2168
  for (field_no=0; (sql_field=it++) ; field_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2169
  {
2170 2171
    CHARSET_INFO *save_cs;

2172 2173 2174 2175 2176 2177
    /*
      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;
2178
    if (!sql_field->charset)
2179 2180 2181
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2182 2183 2184
      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.
2185
    */
2186
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2187
      sql_field->charset= create_info->table_charset;
2188

2189
    save_cs= sql_field->charset;
2190 2191 2192 2193 2194
    if ((sql_field->flags & BINCMP_FLAG) &&
	!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
						    MY_CS_BINSORT,MYF(0))))
    {
      char tmp[64];
2195 2196
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2197
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
2198
      DBUG_RETURN(TRUE);
2199
    }
2200

2201
    /*
2202
      Convert the default value from client character
2203 2204 2205
      set into the column character set if necessary.
    */
    if (sql_field->def && 
2206
        save_cs != sql_field->def->collation.collation &&
2207 2208 2209 2210
        (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))
2211
    {
2212
      /*
2213
        Starting from 5.1 we work here with a copy of Create_field
2214 2215 2216 2217 2218 2219 2220 2221
        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.
      */
2222
      sql_field->def= sql_field->def->safe_charset_converter(save_cs);
2223 2224 2225 2226 2227

      if (sql_field->def == NULL)
      {
        /* Could not convert */
        my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2228
        DBUG_RETURN(TRUE);
2229 2230 2231
      }
    }

2232 2233
    if (sql_field->sql_type == MYSQL_TYPE_SET ||
        sql_field->sql_type == MYSQL_TYPE_ENUM)
2234 2235 2236
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2237
      TYPELIB *interval= sql_field->interval;
2238 2239 2240 2241 2242 2243

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2244
      if (!interval)
2245
      {
2246
        /*
2247 2248 2249
          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.
2250
        */
2251
        interval= sql_field->interval= typelib(thd->mem_root,
2252
                                               sql_field->interval_list);
2253
        List_iterator<String> int_it(sql_field->interval_list);
2254
        String conv, *tmp;
2255 2256 2257 2258 2259
        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);
2260
        for (uint i= 0; (tmp= int_it++); i++)
2261
        {
2262
          uint lengthsp;
2263 2264 2265 2266 2267
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2268
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
2269
                                                  conv.length());
2270 2271
            interval->type_lengths[i]= conv.length();
          }
2272

2273
          // Strip trailing spaces.
2274 2275
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2276 2277
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2278
          if (sql_field->sql_type == MYSQL_TYPE_SET)
2279 2280 2281 2282 2283
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2284
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2285
              DBUG_RETURN(TRUE);
2286 2287
            }
          }
2288
        }
2289
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2290 2291
      }

2292
      if (sql_field->sql_type == MYSQL_TYPE_SET)
2293
      {
2294
        uint32 field_length;
2295
        if (sql_field->def != NULL)
2296 2297 2298 2299 2300
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2301 2302 2303 2304 2305
          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);
2306
              DBUG_RETURN(TRUE);
2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318
            }

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

2319 2320 2321
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2322
            DBUG_RETURN(TRUE);
2323 2324
          }
        }
2325 2326
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2327
      }
2328
      else  /* MYSQL_TYPE_ENUM */
2329
      {
2330
        uint32 field_length;
2331
        DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
2332
        if (sql_field->def != NULL)
2333 2334
        {
          String str, *def= sql_field->def->val_str(&str);
2335
          if (def == NULL) /* SQL "NULL" maps to NULL */
2336
          {
2337 2338 2339
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
2340
              DBUG_RETURN(TRUE);
2341 2342 2343 2344 2345 2346 2347 2348 2349 2350
            }

            /* 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);
2351
              DBUG_RETURN(TRUE);
2352
            }
2353 2354
          }
        }
2355 2356
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2357 2358 2359 2360
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2361
    if (sql_field->sql_type == MYSQL_TYPE_BIT)
2362
    { 
ramil@mysql.com's avatar
ramil@mysql.com committed
2363
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2364
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2365 2366 2367 2368 2369
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2370
    sql_field->create_length_to_internal_length();
2371
    if (prepare_blob_field(thd, sql_field))
2372
      DBUG_RETURN(TRUE);
2373

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

2377 2378
    if (check_column_name(sql_field->field_name))
    {
2379
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2380
      DBUG_RETURN(TRUE);
2381
    }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2382

2383 2384
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2385
    {
2386
      if (my_strcasecmp(system_charset_info,
2387 2388
			sql_field->field_name,
			dup_field->field_name) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2389
      {
2390 2391 2392 2393
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2394 2395
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2396
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2397
	  DBUG_RETURN(TRUE);
2398 2399 2400
	}
	else
	{
2401
	  /* Field redefined */
2402
	  sql_field->def=		dup_field->def;
2403
	  sql_field->sql_type=		dup_field->sql_type;
2404 2405 2406
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2407
	  sql_field->length=		dup_field->char_length;
2408
          sql_field->pack_length=	dup_field->pack_length;
2409
          sql_field->key_length=	dup_field->key_length;
2410
	  sql_field->decimals=		dup_field->decimals;
2411
	  sql_field->create_length_to_internal_length();
2412
	  sql_field->unireg_check=	dup_field->unireg_check;
2413 2414 2415 2416 2417 2418 2419 2420
          /* 
            We're making one field from two, the result field will have
            dup_field->flags as flags. If we've incremented null_fields
            because of sql_field->flags, decrement it back.
          */
          if (!(sql_field->flags & NOT_NULL_FLAG))
            null_fields--;
	  sql_field->flags=		dup_field->flags;
andrey@lmy004's avatar
andrey@lmy004 committed
2421
          sql_field->interval=          dup_field->interval;
2422 2423 2424
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2425
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2426 2427
      }
    }
2428 2429 2430 2431
    /* Don't pack rows in old tables if the user has requested this */
    if ((sql_field->flags & BLOB_FLAG) ||
	sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED)
2432
      (*db_options)|= HA_OPTION_PACK_RECORD;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2433 2434
    it2.rewind();
  }
2435 2436 2437

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2438
  null_fields+= total_uneven_bit_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2439 2440 2441 2442

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

monty@mysql.com's avatar
monty@mysql.com committed
2445 2446
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2447
			     file->ha_table_flags()))
2448
      DBUG_RETURN(TRUE);
2449
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2450
      create_info->varchar= TRUE;
2451
    sql_field->offset= record_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2452 2453
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2454
    record_offset+= sql_field->pack_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2455
  }
2456 2457
  if (timestamps_with_niladic > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2458 2459
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2460
    DBUG_RETURN(TRUE);
2461
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2462 2463
  if (auto_increment > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2464
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2465
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2466 2467
  }
  if (auto_increment &&
2468
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2469
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2470 2471
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
2472
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2473 2474
  }

2475
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2476
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2477 2478
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
2479
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2480 2481 2482
  }

  /* Create keys */
2483

2484 2485
  List_iterator<Key> key_iterator(alter_info->key_list);
  List_iterator<Key> key_iterator2(alter_info->key_list);
2486
  uint key_parts=0, fk_key_count=0;
2487
  bool primary_key=0,unique_key=0;
2488
  Key *key, *key2;
2489
  uint tmp, key_number;
2490 2491
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2492

2493
  /* Calculate number of key segements */
2494
  *key_count= 0;
2495

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2496 2497
  while ((key=key_iterator++))
  {
2498 2499
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2500
    LEX_STRING key_name_str;
2501 2502 2503
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
2504
      Foreign_key *fk_key= (Foreign_key*) key;
2505 2506 2507
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2508 2509 2510
        my_error(ER_WRONG_FK_DEF, MYF(0),
                 (fk_key->name ?  fk_key->name : "foreign key without name"),
                 ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
2511
	DBUG_RETURN(TRUE);
2512 2513 2514
      }
      continue;
    }
2515
    (*key_count)++;
2516
    tmp=file->max_key_parts();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2517 2518 2519
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
2520
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2521
    }
2522 2523 2524 2525
    key_name_str.str= (char*) key->name;
    key_name_str.length= key->name ? strlen(key->name) : 0;
    if (check_string_char_length(&key_name_str, "", NAME_CHAR_LEN,
                                 system_charset_info, 1))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2526
    {
2527
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
2528
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2529
    }
2530
    key_iterator2.rewind ();
2531
    if (key->type != Key::FOREIGN_KEY)
2532
    {
2533
      while ((key2 = key_iterator2++) != key)
2534
      {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2535
	/*
2536 2537 2538
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2539
        */
2540 2541 2542
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2543
        {
2544
          /* TODO: issue warning message */
2545 2546 2547 2548 2549 2550 2551
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2552 2553 2554
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2555 2556 2557
          }
          break;
        }
2558 2559 2560 2561 2562 2563
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2564
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2565 2566 2567
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
2568
      DBUG_RETURN(TRUE);
2569
    }
2570
  }
2571
  tmp=file->max_keys();
2572
  if (*key_count > tmp)
2573 2574
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
2575
    DBUG_RETURN(TRUE);
2576
  }
2577

2578
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
2579
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
2580
  if (!*key_info_buffer || ! key_part_info)
2581
    DBUG_RETURN(TRUE);				// Out of memory
2582

2583
  key_iterator.rewind();
2584
  key_number=0;
2585
  for (; (key=key_iterator++) ; key_number++)
2586 2587
  {
    uint key_length=0;
2588
    Key_part_spec *column;
2589

2590 2591 2592 2593 2594 2595 2596 2597 2598 2599
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

2600
    switch (key->type) {
2601
    case Key::MULTIPLE:
2602
	key_info->flags= 0;
2603
	break;
2604
    case Key::FULLTEXT:
2605
	key_info->flags= HA_FULLTEXT;
2606
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
2607
          key_info->flags|= HA_USES_PARSER;
2608 2609
        else
          key_info->parser_name= 0;
2610
	break;
2611
    case Key::SPATIAL:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2612
#ifdef HAVE_SPATIAL
2613
	key_info->flags= HA_SPATIAL;
2614
	break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2615
#else
2616 2617
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
2618
	DBUG_RETURN(TRUE);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2619
#endif
2620 2621 2622 2623
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
2624 2625
      key_info->flags = HA_NOSAME;
      break;
2626
    }
2627 2628
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
2629

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2630 2631
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
2632
    key_info->usable_key_parts= key_number;
2633
    key_info->algorithm= key->key_create_info.algorithm;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2634

2635 2636
    if (key->type == Key::FULLTEXT)
    {
2637
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
2638
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2639 2640
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
2641
	DBUG_RETURN(TRUE);
2642 2643
      }
    }
2644 2645 2646
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
2647
       actually should be replaced by special GEOM type
2648 2649 2650
       in near future when new frm file is ready
       checking for proper key parts number:
    */
2651

2652
    /* TODO: Add proper checks if handler supports key_type and algorithm */
2653
    if (key_info->flags & HA_SPATIAL)
2654
    {
2655
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
2656 2657 2658
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
2659
        DBUG_RETURN(TRUE);
2660
      }
2661 2662
      if (key_info->key_parts != 1)
      {
2663
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
2664
	DBUG_RETURN(TRUE);
2665
      }
2666
    }
2667
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
2668
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2669
#ifdef HAVE_RTREE_KEYS
2670 2671
      if ((key_info->key_parts & 1) == 1)
      {
2672
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
2673
	DBUG_RETURN(TRUE);
2674
      }
2675
      /* TODO: To be deleted */
2676
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
2677
      DBUG_RETURN(TRUE);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2678
#else
2679 2680
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
2681
      DBUG_RETURN(TRUE);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2682
#endif
2683
    }
2684

2685 2686 2687 2688 2689
    /* 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
    */
2690 2691
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2692 2693 2694 2695 2696
                           create_info->key_block_size);

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

2697
    List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
2698
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2699 2700
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2701
      uint length;
2702
      Key_part_spec *dup_column;
2703

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2704 2705 2706
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
2707
	     my_strcasecmp(system_charset_info,
2708 2709
			   column->field_name,
			   sql_field->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2710 2711 2712
	field++;
      if (!sql_field)
      {
2713
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
2714
	DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2715
      }
2716
      while ((dup_column= cols2++) != column)
2717 2718 2719 2720 2721 2722 2723
      {
        if (!my_strcasecmp(system_charset_info,
	     	           column->field_name, dup_column->field_name))
	{
	  my_printf_error(ER_DUP_FIELDNAME,
			  ER(ER_DUP_FIELDNAME),MYF(0),
			  column->field_name);
2724
	  DBUG_RETURN(TRUE);
2725 2726 2727
	}
      }
      cols2.rewind();
2728
      if (key->type == Key::FULLTEXT)
2729
      {
2730 2731
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
2732 2733
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
2734
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
2735 2736
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
2737
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748
	    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));
2749
      }
2750
      else
2751
      {
2752 2753
	column->length*= sql_field->charset->mbmaxlen;

2754 2755 2756
        if (key->type == Key::SPATIAL && column->length)
        {
          my_error(ER_WRONG_SUB_KEY, MYF(0));
2757
	  DBUG_RETURN(TRUE);
2758 2759
	}

2760 2761
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
2762
	{
2763
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
2764
	  {
2765
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
2766
	    DBUG_RETURN(TRUE);
2767
	  }
2768 2769
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
2770
            column->length= 25;
2771 2772
	  if (!column->length)
	  {
2773
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
2774
	    DBUG_RETURN(TRUE);
2775 2776
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
2777
#ifdef HAVE_SPATIAL
2778
	if (key->type == Key::SPATIAL)
2779
	{
2780
	  if (!column->length)
2781 2782
	  {
	    /*
2783 2784
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
2785
	    */
2786
	    column->length= 4*sizeof(double);
2787 2788
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
2789
#endif
2790 2791 2792 2793 2794 2795 2796
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
monty@mysql.com's avatar
monty@mysql.com committed
2797
            null_fields--;
2798 2799
	  }
	  else
2800 2801 2802 2803 2804
          {
            key_info->flags|= HA_NULL_PART_KEY;
            if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
            {
              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
2805
              DBUG_RETURN(TRUE);
2806 2807 2808 2809 2810
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
2811
              DBUG_RETURN(TRUE);
2812 2813
            }
          }
2814 2815 2816
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
2817
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
2818 2819
	    auto_increment--;			// Field is used
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2820
      }
2821

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2822 2823 2824
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
2825 2826
      length= sql_field->key_length;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2827 2828 2829 2830
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
monty@mysql.com's avatar
monty@mysql.com committed
2831
	  if ((length=column->length) > max_key_length ||
2832
	      length > file->max_key_part_length())
2833
	  {
monty@mysql.com's avatar
monty@mysql.com committed
2834
	    length=min(max_key_length, file->max_key_part_length());
2835 2836 2837 2838 2839 2840 2841 2842
	    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);
2843 2844
              /* Align key length to multibyte char boundary */
              length-= length % sql_field->charset->mbmaxlen;
2845 2846 2847 2848
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
2849
	      DBUG_RETURN(TRUE);
2850 2851
	    }
	  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2852
	}
2853
	else if (!f_is_geom(sql_field->pack_flag) &&
2854
		  (column->length > length ||
gkodinov/kgeorge@magare.gmz's avatar
gkodinov/kgeorge@magare.gmz committed
2855
                   !Field::type_can_have_key_part (sql_field->sql_type) ||
2856
		   ((f_is_packed(sql_field->pack_flag) ||
2857
		     ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
2858 2859 2860
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2861
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
2862
	  DBUG_RETURN(TRUE);
2863
	}
2864
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
2865
	  length=column->length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2866 2867 2868
      }
      else if (length == 0)
      {
2869
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
2870
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2871
      }
2872
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
2873
      {
2874
        length= file->max_key_part_length();
2875 2876 2877 2878 2879 2880 2881 2882
	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);
2883 2884
          /* Align key length to multibyte char boundary */
          length-= length % sql_field->charset->mbmaxlen;
2885 2886 2887 2888
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
2889
	  DBUG_RETURN(TRUE);
2890
	}
2891 2892
      }
      key_part_info->length=(uint16) length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2893
      /* Use packed keys for long strings on the first column */
2894
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2895
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
2896 2897
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2898 2899
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
2900 2901 2902
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB) ||
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2903 2904 2905
	else
	  key_info->flags|= HA_PACK_KEY;
      }
2906 2907 2908 2909
      /* Check if the key segment is partial, set the key flag accordingly */
      if (length != sql_field->key_length)
        key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2910 2911 2912 2913 2914 2915 2916
      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)
2917 2918 2919
	{
	  if (primary_key)
	  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2920 2921
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
2922
	    DBUG_RETURN(TRUE);
2923 2924 2925 2926
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
2927
	else if (!(key_name = key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2928
	  key_name=make_unique_key_name(sql_field->field_name,
2929 2930
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2931
	{
2932
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
2933
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2934 2935 2936 2937
	}
	key_info->name=(char*) key_name;
      }
    }
2938 2939
    if (!key_info->name || check_column_name(key_info->name))
    {
2940
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
2941
      DBUG_RETURN(TRUE);
2942
    }
2943 2944
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2945
    key_info->key_length=(uint16) key_length;
2946
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2947
    {
2948
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
2949
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2950
    }
2951
    key_info++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2952
  }
2953
  if (!unique_key && !primary_key &&
2954
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
2955
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2956
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
2957
    DBUG_RETURN(TRUE);
2958
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2959 2960
  if (auto_increment > 0)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2961
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
2962
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2963
  }
2964
  /* Sort keys in optimized order */
2965 2966
  my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
	   (qsort_cmp) sort_keys);
monty@mysql.com's avatar
monty@mysql.com committed
2967
  create_info->null_bits= null_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2968

2969
  DBUG_RETURN(FALSE);
2970 2971
}

2972

2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988
/*
  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)
{
2989 2990 2991 2992 2993
  /*
    If the table character set was not given explicitly,
    let's fetch the database default character set and
    apply it to the table.
  */
2994 2995 2996
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
2997 2998 2999

    load_db_opt_by_name(thd, db, &db_info);

3000 3001 3002 3003 3004
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017
/*
  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
*/

3018
static bool prepare_blob_field(THD *thd, Create_field *sql_field)
3019 3020 3021 3022 3023 3024 3025 3026 3027
{
  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];

3028 3029
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
3030 3031 3032 3033 3034
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
3035
    sql_field->sql_type= MYSQL_TYPE_BLOB;
3036 3037
    sql_field->flags|= BLOB_FLAG;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
3038
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
3039 3040 3041 3042 3043 3044 3045
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
    
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
3046
    if (sql_field->sql_type == MYSQL_TYPE_BLOB)
3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057
    {
      /* 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);
}


3058
/*
3059
  Preparation of Create_field for SP function return values.
3060 3061
  Based on code used in the inner loop of mysql_prepare_create_table()
  above.
3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

3073
void sp_prepare_create_field(THD *thd, Create_field *sql_field)
3074
{
3075 3076
  if (sql_field->sql_type == MYSQL_TYPE_SET ||
      sql_field->sql_type == MYSQL_TYPE_ENUM)
3077 3078
  {
    uint32 field_length, dummy;
3079
    if (sql_field->sql_type == MYSQL_TYPE_SET)
3080 3081 3082 3083 3084 3085 3086
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
3087
    else /* MYSQL_TYPE_ENUM */
3088 3089 3090 3091 3092 3093 3094 3095 3096
    {
      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);
  }

3097
  if (sql_field->sql_type == MYSQL_TYPE_BIT)
3098 3099 3100 3101 3102
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
3103 3104 3105 3106
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3107 3108


3109 3110 3111 3112
/*
  Create a table

  SYNOPSIS
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3113
    mysql_create_table_no_lock()
3114 3115 3116
    thd			Thread object
    db			Database
    table_name		Table name
3117
    create_info	        Create information (like MAX_ROWS)
3118 3119
    fields		List of fields to create
    keys		List of keys to create
3120
    internal_tmp_table  Set to 1 if this is an internal temporary table
3121
			(From ALTER TABLE)
3122
    select_field_count
3123 3124

  DESCRIPTION
3125
    If one creates a temporary table, this is automatically opened
3126

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3127 3128 3129 3130 3131
    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.

3132 3133 3134 3135 3136 3137
    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
3138 3139
    FALSE OK
    TRUE  error
3140 3141
*/

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3142
bool mysql_create_table_no_lock(THD *thd,
3143
                                const char *db, const char *table_name,
3144 3145 3146 3147
                                HA_CREATE_INFO *create_info,
                                Alter_info *alter_info,
                                bool internal_tmp_table,
                                uint select_field_count)
3148
{
3149
  char		path[FN_REFLEN];
3150
  uint          path_length;
3151 3152 3153 3154
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
3155
  bool		error= TRUE;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3156
  DBUG_ENTER("mysql_create_table_no_lock");
3157 3158
  DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                       db, table_name, internal_tmp_table));
3159

3160

3161
  /* Check for duplicate fields and check type of table to create */
3162
  if (!alter_info->create_list.elements)
3163
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3164 3165
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
3166
    DBUG_RETURN(TRUE);
3167
  }
3168
  if (check_engine(thd, table_name, create_info))
3169
    DBUG_RETURN(TRUE);
3170
  db_options= create_info->table_options;
3171 3172 3173
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3174 3175
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3176
  {
3177
    mem_alloc_error(sizeof(handler));
3178 3179
    DBUG_RETURN(TRUE);
  }
3180
#ifdef WITH_PARTITION_STORAGE_ENGINE
3181 3182
  partition_info *part_info= thd->work_part_info;

3183 3184 3185 3186 3187 3188 3189 3190
  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.
    */
3191
    thd->work_part_info= part_info= new partition_info();
3192 3193 3194 3195 3196 3197
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
3198
    part_info->default_engine_type= create_info->db_type;
3199
    part_info->is_auto_partitioned= TRUE;
3200
  }
3201 3202 3203
  if (part_info)
  {
    /*
3204 3205 3206 3207 3208 3209 3210 3211 3212
      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.
3213
    */
3214
    List_iterator<Key> key_iterator(alter_info->key_list);
3215
    Key *key;
3216
    handlerton *part_engine_type= create_info->db_type;
3217 3218
    char *part_syntax_buf;
    uint syntax_len;
3219
    handlerton *engine_type;
3220 3221 3222 3223 3224
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
3225 3226
    while ((key= key_iterator++))
    {
3227 3228
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
3229 3230 3231 3232 3233
      {
        my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
        goto err;
      }
    }
3234
    if ((part_engine_type == partition_hton) &&
3235
        part_info->default_engine_type)
3236 3237 3238 3239 3240 3241
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
3242
      ;
3243
    }
3244 3245
    else
    {
3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257
      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);
        }
      }
3258
    }
3259 3260
    DBUG_PRINT("info", ("db_type = %d",
                         ha_legacy_type(part_info->default_engine_type)));
3261
    if (part_info->check_partition_info(thd, &engine_type, file,
3262
                                        create_info, TRUE))
3263
      goto err;
3264
    part_info->default_engine_type= engine_type;
3265

3266 3267 3268 3269 3270 3271
    /*
      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,
3272
                                                     TRUE, TRUE)))
3273
      goto err;
3274 3275
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
3276 3277
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3278
        create_info->db_type == partition_hton)
3279 3280 3281 3282 3283
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
3284 3285
      DBUG_PRINT("info", ("db_type: %d",
                          ha_legacy_type(create_info->db_type)));
3286
      delete file;
3287
      create_info->db_type= partition_hton;
3288 3289 3290 3291
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3292 3293 3294 3295 3296 3297 3298 3299
      /*
        If we have default number of partitions or subpartitions we
        might require to set-up the part_info object such that it
        creates a proper .par file. The current part_info object is
        only used to create the frm-file and .par-file.
      */
      if (part_info->use_default_no_partitions &&
          part_info->no_parts &&
3300 3301
          (int)part_info->no_parts !=
          file->get_default_no_partitions(create_info))
3302
      {
3303
        uint i;
3304
        List_iterator<partition_element> part_it(part_info->partitions);
3305 3306 3307 3308
        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;
3309 3310 3311 3312
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3313
               (int)part_info->no_subparts !=
3314
                 file->get_default_no_partitions(create_info))
3315
      {
3316
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3317
        part_info->no_subparts= file->get_default_no_partitions(create_info);
3318 3319 3320 3321
      }
    }
    else if (create_info->db_type != engine_type)
    {
3322 3323 3324 3325 3326 3327
      /*
        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.
      */
3328
      delete file;
3329 3330
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3331 3332 3333 3334
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3335 3336 3337
    }
  }
#endif
3338

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

3341 3342 3343 3344 3345
  if (mysql_prepare_create_table(thd, create_info, alter_info,
                                 internal_tmp_table,
                                 &db_options, file,
                                 &key_info_buffer, &key_count,
                                 select_field_count))
3346
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3347 3348 3349 3350

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3351
    path_length= build_tmptable_filename(thd, path, sizeof(path));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3352 3353
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
3354 3355
  else  
  {
3356 3357
 #ifdef FN_DEVCHAR
    /* check if the table name contains FN_DEVCHAR when defined */
3358
    if (strchr(alias, FN_DEVCHAR))
3359
    {
3360 3361 3362
      my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
      DBUG_RETURN(TRUE);
    }
3363
#endif
3364 3365
    path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
                                      internal_tmp_table ? FN_IS_TMP : 0);
3366
  }
3367

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3368
  /* Check if table already exists */
3369 3370
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      find_temporary_table(thd, db, table_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3371
  {
3372
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3373 3374
    {
      create_info->table_existed= 1;		// Mark that table existed
3375 3376 3377
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
3378 3379
      error= 0;
      goto err;
3380
    }
monty@mysql.com's avatar
monty@mysql.com committed
3381
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3382
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3383
  }
3384

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3385
  VOID(pthread_mutex_lock(&LOCK_open));
3386
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3387 3388 3389 3390
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3391 3392
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3393
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3394
    }
3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407
    /*
      We don't assert here, but check the result, because the table could be
      in the table definition cache and in the same time the .frm could be
      missing from the disk, in case of manual intervention which deletes
      the .frm file. The user has to use FLUSH TABLES; to clear the cache.
      Then she could create the table. This case is pretty obscure and
      therefore we don't introduce a new error message only for it.
    */
    if (get_cached_table_share(db, alias))
    {
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
      goto unlock_and_end;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3408 3409
  }

3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422
  /*
    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;
3423 3424 3425
    int retcode = ha_table_exists_in_engine(thd, db, table_name);
    DBUG_PRINT("info", ("exists_in_engine: %u",retcode));
    switch (retcode)
3426
    {
3427 3428 3429 3430 3431
      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"));
3432

3433 3434 3435 3436 3437 3438 3439 3440 3441
        if (create_if_not_exists)
          goto warn;
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        goto unlock_and_end;
        break;
      default:
        DBUG_PRINT("info", ("error: %u from storage engine", retcode));
        my_error(retcode, MYF(0),table_name);
        goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3442 3443 3444 3445
    }
  }

  thd->proc_info="creating table";
3446
  create_info->table_existed= 0;		// Mark that table is created
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3447

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3448
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
3449
    create_info->data_file_name= create_info->index_file_name= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3450
  create_info->table_options=db_options;
3451

3452
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3453 3454
  if (rea_create_table(thd, path, db, table_name,
                       create_info, alter_info->create_list,
3455
                       key_count, key_info_buffer, file))
3456
    goto unlock_and_end;
3457

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3458 3459 3460 3461 3462 3463
  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);
3464
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3465
    }
3466
    thd->thread_specific_used= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3467
  }
monty@mysql.com's avatar
monty@mysql.com committed
3468

3469 3470 3471 3472 3473
  /*
    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.
3474
    Otherwise, the statement shall be binlogged.
3475 3476
   */
  if (!internal_tmp_table &&
3477 3478
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
3479
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3480
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
3481
  error= FALSE;
3482
unlock_and_end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3483
  VOID(pthread_mutex_unlock(&LOCK_open));
3484 3485

err:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3486
  thd->proc_info="After create";
3487
  delete file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3488
  DBUG_RETURN(error);
3489 3490

warn:
3491
  error= FALSE;
3492 3493 3494 3495
  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
3496
  goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3497 3498
}

3499 3500

/*
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3501
  Database and name-locking aware wrapper for mysql_create_table_no_lock(),
3502 3503 3504 3505
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
3506 3507 3508
                        Alter_info *alter_info,
                        bool internal_tmp_table,
                        uint select_field_count)
3509
{
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3510
  TABLE *name_lock= 0;
3511 3512 3513 3514 3515 3516
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
3517
         hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530
  {
    wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
    pthread_mutex_lock(&LOCK_lock_db);
  }

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

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555
  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
    {
      result= TRUE;
      goto unlock;
    }
    if (!name_lock)
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                            ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                            table_name);
        create_info->table_existed= 1;
        result= FALSE;
      }
      else
      {
        my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
        result= TRUE;
      }
      goto unlock;
    }
  }
3556

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3557
  result= mysql_create_table_no_lock(thd, db, table_name, create_info,
3558 3559 3560
                                     alter_info,
                                     internal_tmp_table,
                                     select_field_count);
3561

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3562 3563 3564 3565 3566 3567 3568
unlock:
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
3569 3570 3571 3572 3573 3574 3575 3576
  pthread_mutex_lock(&LOCK_lock_db);
  if (!--creating_table && creating_database)
    pthread_cond_signal(&COND_refresh);
  pthread_mutex_unlock(&LOCK_lock_db);
  DBUG_RETURN(result);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3577 3578 3579 3580 3581 3582 3583 3584
/*
** 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++)
3585
    if (!my_strcasecmp(system_charset_info,name,key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3586 3587 3588 3589 3590 3591 3592 3593 3594 3595
      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;

3596 3597
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3598
    return (char*) field_name;			// Use fieldname
3599 3600 3601 3602 3603 3604
  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
  */
3605
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3606
  {
3607 3608
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3609 3610 3611
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3612
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3613 3614
}

3615

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3616 3617 3618 3619
/****************************************************************************
** Alter a table definition
****************************************************************************/

3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633

/*
  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.
3634 3635
                                NO_FRM_RENAME  Don't rename the FRM file
                                but only the table in the storage engine.
3636 3637

  RETURN
3638 3639
    FALSE   OK
    TRUE    Error
3640 3641
*/

3642
bool
3643 3644 3645
mysql_rename_table(handlerton *base, const char *old_db,
                   const char *old_name, const char *new_db,
                   const char *new_name, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3646
{
3647
  THD *thd= current_thd;
3648 3649 3650
  char from[FN_REFLEN], to[FN_REFLEN], lc_from[FN_REFLEN], lc_to[FN_REFLEN];
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
3651
  handler *file;
3652
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3653
  DBUG_ENTER("mysql_rename_table");
3654 3655
  DBUG_PRINT("enter", ("old: '%s'.'%s'  new: '%s'.'%s'",
                       old_db, old_name, new_db, new_name));
3656

3657
  file= (base == NULL ? 0 :
3658 3659
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

3660 3661 3662 3663
  build_table_filename(from, sizeof(from), old_db, old_name, "",
                       flags & FN_FROM_IS_TMP);
  build_table_filename(to, sizeof(to), new_db, new_name, "",
                       flags & FN_TO_IS_TMP);
3664 3665 3666 3667 3668 3669

  /*
    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.
   */
3670
  if (lower_case_table_names == 2 && file &&
3671
      !(file->ha_table_flags() & HA_FILE_BASED))
3672
  {
3673 3674
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
3675 3676
    build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "",
                         flags & FN_FROM_IS_TMP);
3677
    from_base= lc_from;
3678

3679 3680
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
3681 3682
    build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "",
                         flags & FN_TO_IS_TMP);
3683
    to_base= lc_to;
3684 3685
  }

3686
  if (!file || !(error=file->rename_table(from_base, to_base)))
3687
  {
3688
    if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
3689
    {
3690
      error=my_errno;
3691
      /* Restore old file name */
3692
      if (file)
3693
        file->rename_table(to_base, from_base);
3694 3695
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3696
  delete file;
3697 3698 3699
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
3700 3701
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3702 3703
}

3704

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3705
/*
3706 3707 3708 3709 3710 3711
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
3712
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
3713
			HA_EXTRA_FORCE_REOPEN if table is not be used
3714 3715 3716 3717 3718 3719 3720
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

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

3723 3724
void wait_while_table_is_used(THD *thd, TABLE *table,
                              enum ha_extra_function function)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3725
{
3726
  DBUG_ENTER("wait_while_table_is_used");
3727
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %lu",
3728 3729
                       table->s->table_name.str, (ulong) table->s,
                       table->db_stat, table->s->version));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3730

3731 3732
  safe_mutex_assert_owner(&LOCK_open);

3733
  VOID(table->file->extra(function));
3734
  /* Mark all tables that are in use as 'old' */
3735
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
3736 3737

  /* Wait until all there are no other threads that has this table open */
3738 3739 3740
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
3741 3742
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3743

3744 3745
/*
  Close a cached table
3746

3747
  SYNOPSIS
3748
    close_cached_table()
3749 3750 3751 3752 3753 3754
    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.
3755

3756 3757 3758 3759
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
3760

3761
void close_cached_table(THD *thd, TABLE *table)
3762 3763
{
  DBUG_ENTER("close_cached_table");
3764

3765
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
3766 3767
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
3768
  {
3769 3770
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3771
  }
3772
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3773
  unlink_open_table(thd, table, TRUE);
3774 3775

  /* When lock on LOCK_open is freed other threads can continue */
3776
  broadcast_refresh();
monty@mysql.com's avatar
monty@mysql.com committed
3777
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3778 3779
}

3780
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
3781
			     const char* operator_name, const char* errmsg)
3782

3783
{
3784 3785
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3786 3787
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
3788
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3789
  protocol->store(errmsg, system_charset_info);
3790
  thd->clear_error();
3791
  if (protocol->write())
3792 3793 3794 3795
    return -1;
  return 1;
}

3796

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3797
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
3798
			       HA_CHECK_OPT *check_opt)
3799
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3800
  DBUG_ENTER("prepare_for_restore");
3801

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3802 3803 3804 3805 3806 3807
  if (table->table) // do not overwrite existing tables on restore
  {
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				  "table exists, will not overwrite on restore"
				  ));
  }
3808
  else
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3809
  {
3810
    char* backup_dir= thd->lex->backup_dir;
3811
    char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
3812 3813
    char* table_name= table->table_name;
    char* db= table->db;
3814

3815 3816 3817
    VOID(tablename_to_filename(table->table_name, uname, sizeof(uname)));

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

3820 3821
    build_table_filename(dst_path, sizeof(dst_path),
                         db, table_name, reg_ext, 0);
3822

3823
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3824
      DBUG_RETURN(-1);
3825

3826
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3827
    {
3828
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3829
      unlock_table_name(thd, table);
3830
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3831 3832 3833
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
3834
    if (mysql_truncate(thd, table, 1))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3835
    {
3836
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3837
      unlock_table_name(thd, table);
3838
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3839 3840
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
3841
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3842
  }
3843

3844 3845 3846 3847
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
3848
  pthread_mutex_lock(&LOCK_open);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3849
  if (reopen_name_locked_table(thd, table, TRUE))
3850
  {
3851
    unlock_table_name(thd, table);
3852
    pthread_mutex_unlock(&LOCK_open);
3853 3854
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
3855
  }
3856 3857
  /* A MERGE table must not come here. */
  DBUG_ASSERT(!table->table || !table->table->child_l);
3858
  pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3859
  DBUG_RETURN(0);
3860
}
3861

3862

3863
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
3864
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3865
{
3866 3867
  int error= 0;
  TABLE tmp_table, *table;
3868 3869 3870 3871
  TABLE_SHARE *share;
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext;
  MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3872 3873 3874 3875
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
3876 3877

  if (!(table= table_list->table))		/* if open_ltable failed */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3878
  {
3879 3880 3881 3882 3883 3884 3885 3886 3887
    char key[MAX_DBKEY_LENGTH];
    uint key_length;

    key_length= create_table_def_key(thd, key, table_list, 0);
    pthread_mutex_lock(&LOCK_open);
    if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
                                  &error))))
    {
      pthread_mutex_unlock(&LOCK_open);
3888
      DBUG_RETURN(0);				// Can't open frm file
3889 3890
    }

3891
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
3892 3893 3894 3895 3896
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
3897
    table= &tmp_table;
3898
    pthread_mutex_unlock(&LOCK_open);
3899
  }
3900 3901 3902 3903

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

3904 3905 3906 3907 3908 3909 3910 3911 3912
  /*
    REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
  */
  if (table->s->tmp_table)
  {
    error= send_check_errmsg(thd, table_list, "repair",
			     "Cannot repair temporary table from .frm file");
    goto end;
  }
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3913

3914 3915 3916 3917 3918 3919 3920 3921 3922
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3923

3924 3925
  /*
    Check if this is a table type that stores index and data separately,
3926 3927 3928
    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. 
3929
  */
3930
  ext= table->file->bas_ext();
3931 3932
  if (!ext[0] || !ext[1])
    goto end;					// No data file
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3933

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

3939 3940
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
3941

3942 3943 3944 3945 3946 3947 3948
  /* If we could open the table, close it */
  if (table_list->table)
  {
    pthread_mutex_lock(&LOCK_open);
    close_cached_table(thd, table);
    pthread_mutex_unlock(&LOCK_open);
  }
3949
  if (lock_and_wait_for_table_name(thd,table_list))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3950
  {
3951 3952
    error= -1;
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3953
  }
3954
  if (my_rename(from, tmp, MYF(MY_WME)))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3955
  {
3956
    pthread_mutex_lock(&LOCK_open);
3957
    unlock_table_name(thd, table_list);
3958
    pthread_mutex_unlock(&LOCK_open);
3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3980 3981
  }

3982 3983 3984 3985
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
3986
  pthread_mutex_lock(&LOCK_open);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3987
  if (reopen_name_locked_table(thd, table_list, TRUE))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3988
  {
3989
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3990
    pthread_mutex_unlock(&LOCK_open);
3991 3992 3993
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3994
  }
3995
  pthread_mutex_unlock(&LOCK_open);
3996 3997 3998

end:
  if (table == &tmp_table)
3999 4000 4001 4002 4003
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
4004
  DBUG_RETURN(error);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4005
}
4006

4007

4008

4009 4010
/*
  RETURN VALUES
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
4011 4012 4013
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
4014
*/
4015 4016 4017 4018 4019
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,
4020
                              bool no_warnings_for_error,
4021 4022 4023
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4024 4025 4026
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4027
{
4028
  TABLE_LIST *table;
4029
  SELECT_LEX *select= &thd->lex->select_lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4030
  List<Item> field_list;
4031 4032
  Item *item;
  Protocol *protocol= thd->protocol;
4033
  LEX *lex= thd->lex;
4034
  int result_code;
4035
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4036

4037 4038
  if (end_active_trans(thd))
    DBUG_RETURN(1);
4039
  field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4040 4041 4042 4043 4044 4045 4046
  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;
4047 4048
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4049
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4050

4051
  mysql_ha_rm_tables(thd, tables, FALSE);
4052

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4053
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4054 4055
  {
    char table_name[NAME_LEN*2+2];
4056
    char* db = table->db;
4057
    bool fatal_error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4058

4059 4060
    DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
    DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
serg@serg.mylan's avatar
serg@serg.mylan committed
4061
    strxmov(table_name, db, ".", table->table_name, NullS);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4062
    thd->open_options|= extra_open_options;
4063 4064
    table->lock_type= lock_type;
    /* open only one table from local list of command */
4065
    {
4066 4067 4068 4069 4070
      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;
4071
      select->table_list.first= (uchar*)table;
4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083
      /*
        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;
4084

4085 4086 4087 4088 4089 4090
      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;
    }
4091 4092
    DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));

4093
    if (prepare_func)
4094
    {
4095
      DBUG_PRINT("admin", ("calling prepare_func"));
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
4096
      switch ((*prepare_func)(thd, table, check_opt)) {
4097
      case  1:           // error, message written to net
4098
        ha_autocommit_or_rollback(thd, 1);
4099
        close_thread_tables(thd);
4100
        DBUG_PRINT("admin", ("simple error, admin next table"));
4101 4102
        continue;
      case -1:           // error, message could be written to net
4103 4104
        /* purecov: begin inspected */
        DBUG_PRINT("admin", ("severe error, stop"));
4105
        goto err;
4106
        /* purecov: end */
4107
      default:           // should be 0 otherwise
4108
        DBUG_PRINT("admin", ("prepare_func succeeded"));
4109
        ;
4110
      }
4111
    }
4112

4113
    /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4114 4115 4116 4117 4118 4119
      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)
4120
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4121 4122
    if (!table->table)
    {
4123
      DBUG_PRINT("admin", ("open table failed"));
4124 4125 4126
      if (!thd->warn_list.elements)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
4127 4128 4129
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
4130 4131 4132 4133
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                     ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
      result_code= HA_ADMIN_CORRUPT;
      goto send_result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4134
    }
4135 4136 4137

    if (table->view)
    {
4138
      DBUG_PRINT("admin", ("calling view_operator_func"));
4139 4140 4141 4142
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

4143
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4144
    {
4145
      /* purecov: begin inspected */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
4146
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4147
      uint length;
4148
      DBUG_PRINT("admin", ("sending error message"));
4149
      protocol->prepare_for_resend();
4150 4151
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4152
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4153 4154 4155
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4156
      ha_autocommit_or_rollback(thd, 0);
4157
      close_thread_tables(thd);
4158
      lex->reset_query_tables_list(FALSE);
4159
      table->table=0;				// For query cache
4160
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4161 4162
	goto err;
      continue;
4163
      /* purecov: end */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4164 4165
    }

4166
    /* Close all instances of the table to allow repair to rename files */
4167
    if (lock_type == TL_WRITE && table->table->s->version)
4168
    {
4169
      DBUG_PRINT("admin", ("removing table from cache"));
4170
      pthread_mutex_lock(&LOCK_open);
4171 4172
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
4173
      mysql_lock_abort(thd,table->table, TRUE);
4174 4175
      remove_table_from_cache(thd, table->table->s->db.str,
                              table->table->s->table_name.str,
monty@mysql.com's avatar
monty@mysql.com committed
4176 4177
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4178
      thd->exit_cond(old_message);
4179
      DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
4180 4181
      if (thd->killed)
	goto err;
4182 4183 4184
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4185 4186
    }

4187
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4188
    {
4189 4190
      /* purecov: begin inspected */
      DBUG_PRINT("admin", ("sending crashed warning"));
4191 4192 4193
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4194 4195 4196
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4197 4198
      if (protocol->write())
        goto err;
4199
      /* purecov: end */
4200 4201
    }

4202 4203
    if (operator_func == &handler::ha_repair &&
        !(check_opt->sql_flags & TT_USEFRM))
4204 4205 4206 4207 4208
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4209
        my_bool save_no_send_ok= thd->net.no_send_ok;
4210
        DBUG_PRINT("admin", ("recreating table"));
4211
        ha_autocommit_or_rollback(thd, 1);
4212 4213
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4214 4215 4216
        thd->net.no_send_ok= TRUE;
        result_code= mysql_recreate_table(thd, table);
        thd->net.no_send_ok= save_no_send_ok;
4217 4218 4219 4220 4221 4222
        reenable_binlog(thd);
        goto send_result;
      }

    }

4223
    DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
4224
    result_code = (table->table->file->*operator_func)(thd, check_opt);
4225
    DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
4226 4227 4228

send_result:

4229
    lex->cleanup_after_one_table_open();
4230
    thd->clear_error();  // these errors shouldn't get client
4231
    {
4232 4233 4234 4235 4236 4237 4238
      List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
      MYSQL_ERROR *err;
      while ((err= it++))
      {
        protocol->prepare_for_resend();
        protocol->store(table_name, system_charset_info);
        protocol->store((char*) operator_name, system_charset_info);
4239 4240 4241
        protocol->store(warning_level_names[err->level].str,
                        warning_level_names[err->level].length,
                        system_charset_info);
4242 4243 4244 4245 4246
        protocol->store(err->msg, system_charset_info);
        if (protocol->write())
          goto err;
      }
      mysql_reset_errors(thd, true);
4247
    }
4248
    protocol->prepare_for_resend();
4249 4250
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4251

4252 4253 4254
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4255 4256
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4257
      {
4258 4259
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
4260
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4261
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4262
	protocol->store(buf, length, system_charset_info);
4263
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4264 4265
      break;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4266 4267
    case HA_ADMIN_NOT_BASE_TABLE:
      {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4268 4269
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4270
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4271
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4272
        protocol->store(buf, length, system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4273 4274 4275
      }
      break;

4276
    case HA_ADMIN_OK:
4277 4278
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4279 4280
      break;

4281
    case HA_ADMIN_FAILED:
4282 4283 4284
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4285 4286
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4287
    case HA_ADMIN_REJECT:
4288 4289 4290
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation need committed state"),
                      system_charset_info);
monty@mysql.com's avatar
monty@mysql.com committed
4291
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4292 4293
      break;

4294
    case HA_ADMIN_ALREADY_DONE:
4295 4296 4297
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4298 4299
      break;

4300
    case HA_ADMIN_CORRUPT:
4301 4302
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4303
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4304 4305
      break;

4306
    case HA_ADMIN_INVALID:
4307 4308 4309
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
4310 4311
      break;

4312 4313
    case HA_ADMIN_TRY_ALTER:
    {
4314
      my_bool save_no_send_ok= thd->net.no_send_ok;
4315 4316 4317 4318 4319
      /*
        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.
      */
4320
      ha_autocommit_or_rollback(thd, 0);
4321
      close_thread_tables(thd);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4322 4323 4324
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4325
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4326 4327 4328
      thd->net.no_send_ok= TRUE;
      result_code= mysql_recreate_table(thd, table);
      thd->net.no_send_ok= save_no_send_ok;
4329
      reenable_binlog(thd);
4330
      ha_autocommit_or_rollback(thd, 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4331
      close_thread_tables(thd);
4332 4333
      if (!result_code) // recreation went ok
      {
4334
        if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
4335 4336 4337
            ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
          result_code= 0; // analyze went ok
      }
4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349
      if (result_code) // either mysql_recreate_table or analyze failed
      {
        const char *err_msg;
        if ((err_msg= thd->net.last_error))
        {
          if (!thd->vio_ok())
          {
            sql_print_error(err_msg);
          }
          else
          {
            /* Hijack the row already in-progress. */
4350
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4351 4352 4353 4354 4355 4356 4357 4358 4359
            protocol->store(err_msg, system_charset_info);
            (void)protocol->write();
            /* 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);
          }
        }
      }
4360
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4361 4362
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4363 4364
      goto send_result_message;
    }
4365 4366
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4367
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4368 4369
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4370 4371
      break;
    }
4372

4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
      char buf[ERRMSGSIZE];
      uint length;

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      length=my_snprintf(buf, ERRMSGSIZE, ER(ER_TABLE_NEEDS_UPGRADE), table->table_name);
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

4386
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4387 4388 4389 4390 4391
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
4392
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4393 4394 4395 4396
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4397
    }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4398
    if (table->table)
4399
    {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4400 4401
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4402
      else if (open_for_modify)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4403
      {
holyfoot@deer.(none)'s avatar
holyfoot@deer.(none) committed
4404
        if (table->table->s->tmp_table)
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4405 4406 4407 4408
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
4409 4410
          remove_table_from_cache(thd, table->table->s->db.str,
                                  table->table->s->table_name.str, RTFC_NO_FLAG);
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4411 4412 4413
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4414 4415
        query_cache_invalidate3(thd, table->table, 0);
      }
4416
    }
4417
    ha_autocommit_or_rollback(thd, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4418
    close_thread_tables(thd);
4419
    table->table=0;				// For query cache
4420
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4421 4422 4423
      goto err;
  }

4424
  send_eof(thd);
4425
  DBUG_RETURN(FALSE);
4426

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4427
 err:
4428
  ha_autocommit_or_rollback(thd, 1);
4429
  close_thread_tables(thd);			// Shouldn't be needed
4430 4431
  if (table)
    table->table=0;
4432
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4433 4434
}

4435

4436
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
4437 4438
{
  DBUG_ENTER("mysql_backup_table");
4439 4440
  WARN_DEPRECATED(thd, "5.2", "BACKUP TABLE",
                  "MySQL Administrator (mysqldump, mysql)");
4441
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4442
				"backup", TL_READ, 0, 0, 0, 0,
4443
				&handler::backup, 0));
4444
}
4445

4446

4447
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
4448 4449
{
  DBUG_ENTER("mysql_restore_table");
4450 4451
  WARN_DEPRECATED(thd, "5.2", "RESTORE TABLE",
                  "MySQL Administrator (mysqldump, mysql)");
4452
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4453
				"restore", TL_WRITE, 1, 1, 0,
4454
				&prepare_for_restore,
4455
				&handler::restore, 0));
4456
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4457

4458

4459
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4460 4461 4462
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4463 4464 4465
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4466
				&prepare_for_repair,
4467
				&handler::ha_repair, 0));
4468 4469
}

4470

4471
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4472 4473 4474
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4475
				"optimize", TL_WRITE, 1,0,0,0,
4476
				&handler::optimize, 0));
4477 4478 4479
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4480 4481 4482 4483 4484
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4485 4486
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4487 4488

  RETURN VALUES
4489 4490
   FALSE ok
   TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4491 4492
*/

4493
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4494
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4495
{
4496
  HA_CHECK_OPT check_opt;
4497
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4498
  DBUG_ENTER("mysql_assign_to_keycache");
4499 4500 4501 4502 4503 4504 4505

  check_opt.init();
  pthread_mutex_lock(&LOCK_global_system_variables);
  if (!(key_cache= get_key_cache(key_cache_name)))
  {
    pthread_mutex_unlock(&LOCK_global_system_variables);
    my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
4506
    DBUG_RETURN(TRUE);
4507 4508 4509 4510
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4511
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4512
				0, 0, &handler::assign_to_keycache, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4513 4514
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4515 4516 4517 4518 4519 4520

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

  SYNOPSIS
    reassign_keycache_tables()
4521 4522 4523
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4524

4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537
  NOTES
    This is called when one sets a key cache size to zero, in which
    case we have to move the tables associated to this key cache to
    the "default" one.

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

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

 RETURN VALUES
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4538 4539 4540
    0	  ok
*/

4541 4542
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4543 4544 4545
{
  DBUG_ENTER("reassign_keycache_tables");

4546 4547
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
4548
  src_cache->param_buff_size= 0;		// Free key cache
4549 4550
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4551
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4552 4553 4554
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4555 4556 4557 4558 4559
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
4560 4561
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4562 4563

  RETURN VALUES
4564 4565
    FALSE ok
    TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4566 4567
*/

4568
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4569 4570
{
  DBUG_ENTER("mysql_preload_keys");
4571 4572 4573 4574 4575
  /*
    We cannot allow concurrent inserts. The storage engine reads
    directly from the index file, bypassing the cache. It could read
    outdated information if parallel inserts into cache blocks happen.
  */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4576
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
4577
				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
4578
				&handler::preload_keys, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4579 4580 4581
}


venu@myvenu.com's avatar
venu@myvenu.com committed
4582 4583 4584 4585 4586
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
4587
    thd		Thread object
4588 4589
    table       Table list element for target table
    src_table   Table list element for source table
venu@myvenu.com's avatar
venu@myvenu.com committed
4590 4591 4592
    create_info Create info

  RETURN VALUES
4593 4594
    FALSE OK
    TRUE  error
venu@myvenu.com's avatar
venu@myvenu.com committed
4595 4596
*/

4597
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
4598
                             HA_CREATE_INFO *create_info)
venu@myvenu.com's avatar
venu@myvenu.com committed
4599
{
4600
  TABLE *name_lock= 0;
4601
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
4602
  uint dst_path_length;
venu@myvenu.com's avatar
venu@myvenu.com committed
4603
  char *db= table->db;
4604
  char *table_name= table->table_name;
4605
  int  err;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4606
  bool res= TRUE;
4607
  uint not_used;
4608 4609 4610
#ifdef WITH_PARTITION_STORAGE_ENGINE
  char tmp_path[FN_REFLEN];
#endif
4611
  char ts_name[FN_LEN];
venu@myvenu.com's avatar
venu@myvenu.com committed
4612
  DBUG_ENTER("mysql_create_like_table");
4613

4614

4615 4616
  /* CREATE TABLE ... LIKE is not allowed for views. */
  src_table->required_type= FRMTYPE_TABLE;
venu@myvenu.com's avatar
venu@myvenu.com committed
4617

4618 4619 4620 4621 4622 4623 4624 4625
  /*
    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.
4626
  */
4627
  if (open_tables(thd, &src_table, &not_used, 0))
4628 4629
    DBUG_RETURN(TRUE);

4630 4631 4632 4633 4634 4635
  /*
    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
  */
4636
  if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
4637 4638 4639 4640 4641
  {
    create_info->tablespace= ts_name;
    create_info->storage_media= HA_SM_DISK;
  }

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

4644 4645 4646 4647 4648
  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););

  /*
    Check that destination tables does not exist. Note that its name
    was already checked when it was added to the table list.
venu@myvenu.com's avatar
venu@myvenu.com committed
4649 4650 4651 4652 4653
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
4654
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
venu@myvenu.com's avatar
venu@myvenu.com committed
4655 4656 4657 4658
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4659 4660 4661 4662
    if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
      goto err;
    if (!name_lock)
      goto table_exists;
4663
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
4664
                                          db, table_name, reg_ext, 0);
venu@myvenu.com's avatar
venu@myvenu.com committed
4665 4666 4667 4668
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

4669 4670
  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););

4671
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
4672
    Create a new table by copying from source table
4673 4674 4675 4676 4677 4678 4679 4680 4681 4682

    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.
4683
  */
4684
  VOID(pthread_mutex_lock(&LOCK_open));
4685 4686 4687 4688 4689 4690
  if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
  {
    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);
4691
    VOID(pthread_mutex_unlock(&LOCK_open));
4692
    goto err;
4693
  }
venu@myvenu.com's avatar
venu@myvenu.com committed
4694 4695

  /*
4696 4697
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
venu@myvenu.com's avatar
venu@myvenu.com committed
4698 4699
    and temporary tables).
  */
4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712
#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
4713 4714 4715

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

4716
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
4717 4718
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
4719
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
4720
  VOID(pthread_mutex_unlock(&LOCK_open));
4721

venu@myvenu.com's avatar
venu@myvenu.com committed
4722 4723 4724 4725
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
4726 4727
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
4728
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4729 4730 4731 4732
    }
  }
  else if (err)
  {
4733
    (void) quick_rm_table(create_info->db_type, db,
4734
			  table_name, 0); /* purecov: inspected */
4735
    goto err;	    /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4736
  }
4737

4738 4739
  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););

4740 4741 4742
  /*
    We have to write the query before we unlock the tables.
  */
4743
  if (thd->current_stmt_binlog_row_based)
4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761
  {
    /*
       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))
    {
4762
      if (src_table->table->s->tmp_table)               // Case 2
4763 4764 4765 4766 4767 4768
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't

        /*
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4769 4770 4771 4772
          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.
4773
        */
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4774 4775 4776 4777 4778
        table->table= name_lock;
        VOID(pthread_mutex_lock(&LOCK_open));
        if (reopen_name_locked_table(thd, table, FALSE))
        {
          VOID(pthread_mutex_unlock(&LOCK_open));
4779
          goto err;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4780 4781
        }
        VOID(pthread_mutex_unlock(&LOCK_open));
4782

4783 4784
        IF_DBUG(int result=) store_create_info(thd, table, &query,
                                               create_info);
4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795

        DBUG_ASSERT(result == 0); // store_create_info() always return 0
        write_bin_log(thd, TRUE, query.ptr(), query.length());
      }
      else                                      // Case 1
        write_bin_log(thd, TRUE, thd->query, thd->query_length);
    }
    /*
      Case 3 and 4 does nothing under RBR
    */
  }
4796
  else
4797 4798
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

4799
  res= FALSE;
4800
  goto err;
4801

venu@myvenu.com's avatar
venu@myvenu.com committed
4802 4803 4804 4805
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
4806 4807
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
4808
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4809
		 ER_TABLE_EXISTS_ERROR,warn_buff);
4810
    res= FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
4811
  }
4812 4813 4814 4815
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4816
  if (name_lock)
4817 4818
  {
    pthread_mutex_lock(&LOCK_open);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
4819
    unlink_open_table(thd, name_lock, FALSE);
4820 4821
    pthread_mutex_unlock(&LOCK_open);
  }
4822
  DBUG_RETURN(res);
venu@myvenu.com's avatar
venu@myvenu.com committed
4823 4824 4825
}


4826
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4827
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4828 4829
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4830 4831
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4832
				"analyze", lock_type, 1, 0, 0, 0,
4833
				&handler::analyze, 0));
4834 4835 4836
}


4837
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
4838
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4839 4840
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4841 4842
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4843
				"check", lock_type,
4844
				0, 0, HA_OPEN_FOR_REPAIR, 0,
4845
				&handler::ha_check, &view_checksum));
4846 4847
}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4849
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
4850 4851 4852 4853
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4854 4855 4856 4857 4858 4859
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
4860 4861 4862 4863
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4864 4865 4866

  thd->proc_info="discard_or_import_tablespace";

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

monty@mysql.com's avatar
monty@mysql.com committed
4869 4870 4871 4872 4873
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
4874
  if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4875 4876 4877 4878
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
4879

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4880 4881 4882 4883 4884 4885 4886
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

monty@mysql.com's avatar
monty@mysql.com committed
4887 4888 4889 4890
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4891 4892 4893 4894 4895 4896 4897 4898
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
    goto err;
4899
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
4900

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4901
err:
4902
  ha_autocommit_or_rollback(thd, error);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4903
  close_thread_tables(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4904
  thd->tablespace_op=FALSE;
4905
  
monty@mysql.com's avatar
monty@mysql.com committed
4906 4907
  if (error == 0)
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4908
    send_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
4909
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4910
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4911

4912 4913
  table->file->print_error(error, MYF(0));
    
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4914
  DBUG_RETURN(-1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4915
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4916

4917

4918 4919
/*
  SYNOPSIS
4920 4921
    compare_tables()
      table                     The original table.
4922 4923
      alter_info                Alter options, fields and keys for the new
                                table.
4924 4925
      create_info               Create options for the new table.
      order_num                 Number of order list elements.
4926 4927 4928 4929 4930 4931 4932 4933
      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
4934 4935 4936 4937
      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.
4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948

  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.

4949 4950 4951 4952 4953
    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.

4954
  RETURN VALUES
4955 4956
    TRUE   error
    FALSE  success
4957 4958
*/

4959 4960 4961 4962 4963 4964
static
bool
compare_tables(TABLE *table,
               Alter_info *alter_info,
               HA_CREATE_INFO *create_info,
               uint order_num,
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
4965
               enum_alter_table_change_level *need_copy_table,
4966 4967 4968
               KEY **key_info_buffer,
               uint **index_drop_buffer, uint *index_drop_count,
               uint **index_add_buffer, uint *index_add_count)
4969 4970 4971
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
4972
  uint key_count;
4973 4974
  List_iterator_fast<Create_field> new_field_it(alter_info->create_list);
  Create_field *new_field;
4975 4976
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
4977 4978 4979 4980 4981
  /*
    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;
4982
  DBUG_ENTER("compare_tables");
4983

4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017
  {
    THD *thd= table->in_use;
    /*
      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,
                                   &tmp_alter_info,
                                   (table->s->tmp_table != NO_TMP_TABLE),
                                   &db_options,
                                   table->file, key_info_buffer,
                                   &key_count, 0))
      DBUG_RETURN(1);
    /* Allocate result buffers. */
    if (! (*index_drop_buffer=
           (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
        ! (*index_add_buffer=
           (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
      DBUG_RETURN(1);
  }
5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034
  /*
    Some very basic checks. If number of fields changes, or the
    handler, we need to run full ALTER TABLE. In the future
    new fields can be added and old dropped without copy, but
    not yet.

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

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

    At the moment we can't handle altering temporary tables without a copy.
    We also test if OPTIMIZE TABLE was given and was mapped to alter table.
    In that case we always do full copy.
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
5035 5036 5037 5038 5039 5040 5041 5042

    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.
5043
  */
5044
  if (table->s->fields != alter_info->create_list.elements ||
antony@ppcg5.local's avatar
antony@ppcg5.local committed
5045
      table->s->db_type() != create_info->db_type ||
5046 5047 5048 5049
      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 ||
5050
      create_info->used_fields & HA_CREATE_USED_ROW_FORMAT ||
5051
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
5052
      order_num ||
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
5053
      !table->s->mysql_version ||
5054
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
5055 5056 5057 5058
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069

  /*
    Go through fields and check if the original ones are compatible
    with new table.
  */
  for (f_ptr= table->field, new_field= new_field_it++;
       (field= *f_ptr); f_ptr++, new_field= new_field_it++)
  {
    /* Make sure we have at least the default charset in use. */
    if (!new_field->charset)
      new_field->charset= create_info->default_table_charset;
5070

5071 5072 5073
    /* Check that NULL behavior is same for old and new fields */
    if ((new_field->flags & NOT_NULL_FLAG) !=
	(uint) (field->flags & NOT_NULL_FLAG))
5074 5075 5076 5077
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5078 5079 5080 5081 5082 5083 5084 5085

    /* Don't pack rows in old tables if the user has requested this. */
    if (create_info->row_type == ROW_TYPE_DYNAMIC ||
	(new_field->flags & BLOB_FLAG) ||
	new_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED)
      create_info->table_options|= HA_OPTION_PACK_RECORD;

5086
    /* Check if field was renamed */
5087
    field->flags&= ~FIELD_IS_RENAMED;
5088 5089 5090 5091
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
		      new_field->field_name))
      field->flags|= FIELD_IS_RENAMED;      
5092

5093 5094
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
    if (!(tmp= field->is_equal(new_field)))
5095 5096 5097 5098
    {
      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
      DBUG_RETURN(0);
    }
5099
    // Clear indexed marker
5100
    field->flags&= ~FIELD_IN_ADD_INDEX;
5101 5102 5103 5104 5105 5106 5107
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
5108 5109 5110
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
5111
  KEY *new_key_end= *key_info_buffer + key_count;
5112

5113 5114 5115 5116 5117 5118 5119 5120
  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;
  for (table_key= table->key_info; table_key < table_key_end; table_key++)
5121
  {
5122 5123 5124 5125 5126
    KEY_PART_INFO *table_part;
    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
    KEY_PART_INFO *new_part;

    /* Search a new key with the same name. */
5127
    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5128 5129 5130 5131 5132 5133 5134
    {
      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. */
5135
      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
5136 5137 5138 5139 5140 5141 5142 5143 5144 5145
      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;
5146 5147 5148 5149 5150

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
5151 5152 5153
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
5154 5155 5156
    {
      /*
	Key definition has changed if we are using a different field or
5157 5158
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
5159
      */
5160 5161 5162
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
5163
    }
5164 5165 5166 5167
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
5168 5169
    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5170 5171 5172 5173 5174 5175
    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];
5176
      field->flags|= FIELD_IN_ADD_INDEX;
5177
    }
5178
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
5179
  }
5180
  /*end of for (; table_key < table_key_end;) */
5181

5182 5183 5184
  /*
    Step through all keys of the new table and find matching old keys.
  */
5185
  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
5186 5187 5188 5189 5190 5191 5192 5193 5194 5195
  {
    /* 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. */
5196
      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
5197 5198 5199 5200 5201 5202
      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];
5203
        field->flags|= FIELD_IN_ADD_INDEX;
5204
      }
marty@linux.site's avatar
marty@linux.site committed
5205
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
5206 5207
    }
  }
5208 5209 5210

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
5211 5212 5213 5214
  {
    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
    DBUG_RETURN(0);
  }
5215

5216
  if (*index_drop_count || *index_add_count)
5217 5218 5219 5220
  {
    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
    DBUG_RETURN(0);
  }
5221

5222 5223
  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
  DBUG_RETURN(0);
5224 5225 5226
}


andrey@example.com's avatar
andrey@example.com committed
5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265
/*
  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:
    error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
    break;
  case LEAVE_AS_IS:
    if (!indexes_were_disabled)
      break;
    /* fall-through: disabled indexes */
  case DISABLE:
    error= table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
  }

  if (error == HA_ERR_WRONG_COMMAND)
  {
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5266 5267
                        ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                        table->s->table_name.str);
andrey@example.com's avatar
andrey@example.com committed
5268 5269 5270 5271 5272 5273 5274 5275
    error= 0;
  } else if (error)
    table->file->print_error(error, MYF(0));

  DBUG_RETURN(error);
}


5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314
/**
  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
5315
*/
5316

5317 5318 5319 5320
static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
                          HA_CREATE_INFO *create_info,
                          Alter_info *alter_info)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5321
{
5322
  /* New column definitions are added here */
5323
  List<Create_field> new_create_list;
5324 5325 5326
  /* New key definitions are added here */
  List<Key> new_key_list;
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
5327
  List_iterator<Create_field> def_it(alter_info->create_list);
5328 5329
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
  List_iterator<Key> key_it(alter_info->key_list);
5330 5331 5332
  List_iterator<Create_field> find_it(new_create_list);
  List_iterator<Create_field> field_it(new_create_list);
  List<Key_part_spec> key_parts;
5333 5334 5335 5336 5337
  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;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5338

5339
  DBUG_ENTER("mysql_prepare_alter_table");
5340

5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351
  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)
5352
  {
5353 5354 5355 5356 5357 5358
    /* 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;
  }
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5359 5360
  if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
    create_info->transactional= table->s->transactional;
5361

5362
  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
5363
  {
kostja@bodhi.(none)'s avatar
kostja@bodhi.(none) committed
5364
    char *tablespace= static_cast<char *>(thd->alloc(FN_LEN));
5365
    /*
5366 5367
       Regular alter table of disk stored table (no tablespace/storage change)
       Copy tablespace name
5368
    */
5369 5370 5371 5372 5373
    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
5374
  Create_field *def;
5375

5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387
  /*
    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++))
5388
    {
5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400
      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;
      }
5401
    }
5402
    if (drop)
5403
    {
5404 5405
      drop_it.remove();
      continue;
5406
    }
5407 5408 5409
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
5410
    {
5411 5412 5413
      if (def->change &&
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
5414
    }
5415 5416 5417 5418
    if (def)
    {						// Field is changed
      def->field=field;
      if (!def->after)
5419
      {
5420 5421
	new_create_list.push_back(def);
	def_it.remove();
5422 5423
      }
    }
5424
    else
5425 5426
    {
      /*
5427 5428
        This field was not dropped and not changed, add it to the list
        for the new table.
5429
      */
5430
      def= new Create_field(field, field);
5431 5432 5433 5434
      new_create_list.push_back(def);
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5435
      {
5436 5437
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5438
      }
5439
      if (alter)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5440
      {
5441
	if (def->sql_type == MYSQL_TYPE_BLOB)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5442
	{
5443
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5444
          goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5445
	}
5446 5447 5448 5449 5450
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
	alter_it.remove();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5451 5452 5453
      }
    }
  }
5454 5455
  def_it.rewind();
  while ((def=def_it++))			// Add new columns
5456
  {
5457
    if (def->change && ! def->field)
5458
    {
5459
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
5460
      goto err;
5461
    }
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478
    /*
      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;
    }
5479 5480 5481 5482
    if (!def->after)
      new_create_list.push_back(def);
    else if (def->after == first_keyword)
      new_create_list.push_front(def);
5483
    else
5484
    {
5485
      Create_field *find;
5486 5487 5488 5489 5490 5491 5492 5493
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
      }
      if (!find)
      {
5494
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
5495 5496 5497
        goto err;
      }
      find_it.after(def);			// Put element after this
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
5498
      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
5499
    }
5500
  }
5501
  if (alter_info->alter_list.elements)
5502
  {
5503
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
5504
             alter_info->alter_list.head()->name, table->s->table_name.str);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5505
    goto err;
5506
  }
5507
  if (!new_create_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5508
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5509 5510
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5511
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5512 5513 5514
  }

  /*
5515 5516
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5517 5518
  */

5519
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5520
  {
5521
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5522 5523 5524 5525 5526
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
5527
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542
	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;
5543
      Create_field *cfield;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5544 5545 5546 5547 5548
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
5549 5550
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5551 5552
	    break;
	}
5553
	else if (!my_strcasecmp(system_charset_info,
5554
				key_part_name, cfield->field_name))
5555
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5556 5557 5558 5559 5560
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
5561 5562 5563 5564 5565 5566 5567
      {
        /*
          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.

5568 5569 5570
          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.

5571 5572 5573 5574
          BLOBs may have cfield->length == 0, which is why we test it before
          checking whether cfield->length < key_part_length (in chars).
         */
        if (!Field::type_can_have_key_part(cfield->field->type()) ||
5575
            !Field::type_can_have_key_part(cfield->sql_type) ||
5576 5577
            /* spatial keys can't have sub-key length */
            (key_info->flags & HA_SPATIAL) ||
bar@mysql.com's avatar
bar@mysql.com committed
5578 5579
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
5580 5581 5582
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5583
      }
5584
      key_part_length /= key_part->field->charset()->mbmaxlen;
5585
      key_parts.push_back(new Key_part_spec(cfield->field_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5586 5587 5588
					    key_part_length));
    }
    if (key_parts.elements)
5589 5590
    {
      KEY_CREATE_INFO key_create_info;
5591 5592
      Key *key;
      enum Key::Keytype key_type;
5593 5594 5595 5596 5597 5598 5599 5600
      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)
        key_create_info.parser_name= *key_info->parser_name;

5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735
      if (key_info->flags & HA_SPATIAL)
        key_type= Key::SPATIAL;
      else if (key_info->flags & HA_NOSAME)
      {
        if (! my_strcasecmp(system_charset_info, key_name, primary_key_name))
          key_type= Key::PRIMARY;
        else
          key_type= Key::UNIQUE;
      }
      else if (key_info->flags & HA_FULLTEXT)
        key_type= Key::FULLTEXT;
      else
        key_type= Key::MULTIPLE;

      key= new Key(key_type, key_name,
                   &key_create_info,
                   test(key_info->flags & HA_GENERATED_KEY),
                   key_parts);
      new_key_list.push_back(key);
    }
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
    {
      if (key->type != Key::FOREIGN_KEY)
        new_key_list.push_back(key);
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
        goto err;
      }
    }
  }

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

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

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

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

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


/*
  Alter table

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

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

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

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

  RETURN VALUES
    FALSE  OK
    TRUE   Error
*/

bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
                       HA_CREATE_INFO *create_info,
                       TABLE_LIST *table_list,
                       Alter_info *alter_info,
                       uint order_num, ORDER *order, bool ignore)
{
  TABLE *table, *new_table= 0, *name_lock= 0;
  int error= 0;
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
  char path[FN_REFLEN];
  char reg_path[FN_REFLEN+1];
5736
  ha_rows copied,deleted;
5737
  handlerton *old_db_type, *new_db_type, *save_old_db_type;
5738
  legacy_db_type table_type;
5739
  frm_type_enum frm_type;
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
5740
  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
5741
#ifdef WITH_PARTITION_STORAGE_ENGINE
5742
  uint fast_alter_partition= 0;
5743 5744
  bool partition_changed= FALSE;
#endif
5745 5746 5747 5748 5749 5750
  bool need_lock_for_indexes= TRUE;
  KEY  *key_info_buffer;
  uint index_drop_count;
  uint *index_drop_buffer;
  uint index_add_count;
  uint *index_add_buffer;
5751
  bool committed= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5752 5753
  DBUG_ENTER("mysql_alter_table");

5754 5755 5756 5757 5758
  LINT_INIT(index_add_count);
  LINT_INIT(index_drop_count);
  LINT_INIT(index_add_buffer);
  LINT_INIT(index_drop_buffer);

5759 5760 5761 5762 5763 5764
  /*
    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.
  */
5765
  if (table_list && table_list->db && table_list->table_name)
5766
  {
5767
    int table_kind= 0;
5768

5769 5770 5771
    table_kind= check_if_log_table(table_list->db_length, table_list->db,
                                   table_list->table_name_length,
                                   table_list->table_name, 0);
5772

5773
    if (table_kind)
5774
    {
5775 5776 5777 5778 5779 5780
      /* 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);
      }
5781

5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793
      /* 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)
      {
5794
        my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
5795 5796 5797
        DBUG_RETURN(TRUE);
      }
#endif
5798 5799 5800
    }
  }

5801 5802 5803 5804 5805
  /*
    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.
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5806
  thd->proc_info="init";
5807
  table_name=table_list->table_name;
5808
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5809
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
5810
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
5811
    new_db= db;
5812 5813
  build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext, 0);
  build_table_filename(path, sizeof(path), db, table_name, "", 0);
5814

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5817
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
5818
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
5819
    /* Conditionally writes to binlog. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5820
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
5821
						   alter_info->tablespace_op));
5822 5823 5824
  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);
5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837
  /*
    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.
  */
5838 5839
  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
  /* Rename a view */
5840
  /* Sic: there is a race here */
5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851
  if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
  {
    /*
      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));
5852
      DBUG_RETURN(TRUE);
5853 5854 5855
    }

    if (wait_if_global_read_lock(thd,0,1))
5856
      DBUG_RETURN(TRUE);
5857 5858
    VOID(pthread_mutex_lock(&LOCK_open));
    if (lock_table_names(thd, table_list))
5859
    {
5860
      error= 1;
5861
      goto view_err;
5862
    }
5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881
    
    if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
    {
      if (mysql_bin_log.is_open())
      {
        thd->clear_error();
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
        mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }

    unlock_table_names(thd, table_list, (TABLE_LIST*) 0);

view_err:
    pthread_mutex_unlock(&LOCK_open);
    start_waiting_global_read_lock(thd);
    DBUG_RETURN(error);
  }
5882 5883

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

5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900
  /*
    Prohibit changing of the UNION list of a non-temporary MERGE table
    under LOCK tables. It would be quite difficult to reuse a shrinked
    set of tables from the old table or to open a new TABLE object for
    an extended list and verify that they belong to locked tables.
  */
  if (thd->locked_tables &&
      (create_info->used_fields & HA_CREATE_USED_UNION) &&
      (table->s->tmp_table == NO_TMP_TABLE))
  {
    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
    DBUG_RETURN(TRUE);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5901 5902 5903
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
5904
    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5905
    strmov(new_name_buff,new_name);
5906
    strmov(new_alias= new_alias_buff, new_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5907
    if (lower_case_table_names)
5908 5909 5910
    {
      if (lower_case_table_names != 2)
      {
5911
	my_casedn_str(files_charset_info, new_name_buff);
5912 5913
	new_alias= new_name;			// Create lower case table name
      }
5914
      my_casedn_str(files_charset_info, new_name);
5915
    }
5916
    if (new_db == db &&
monty@mysql.com's avatar
monty@mysql.com committed
5917
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
5918 5919
    {
      /*
5920 5921
	Source and destination table names are equal: make later check
	easier.
5922
      */
5923
      new_alias= new_name= table_name;
5924
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5925 5926
    else
    {
5927
      if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5928 5929 5930
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
5931
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
5932
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5933 5934 5935 5936
	}
      }
      else
      {
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5937 5938 5939 5940 5941 5942 5943 5944
        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);
        }

5945 5946 5947
        build_table_filename(new_name_buff, sizeof(new_name_buff),
                             new_db, new_name_buff, reg_ext, 0);
        if (!access(new_name_buff, F_OK))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5948 5949
	{
	  /* Table will be closed in do_command() */
5950
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5951
          goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5952 5953 5954 5955 5956
	}
      }
    }
  }
  else
5957 5958 5959 5960
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5961

antony@ppcg5.local's avatar
antony@ppcg5.local committed
5962
  old_db_type= table->s->db_type();
5963
  if (!create_info->db_type)
5964
  {
5965
#ifdef WITH_PARTITION_STORAGE_ENGINE
5966 5967
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
5968 5969 5970 5971
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
5972 5973 5974
        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.
5975
      */
5976
      create_info->db_type= table->part_info->default_engine_type;
5977
    }
5978
    else
5979
#endif
5980
      create_info->db_type= old_db_type;
5981
  }
5982

5983
  if (check_engine(thd, new_name, create_info))
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
5984
    goto err;
5985
  new_db_type= create_info->db_type;
5986

5987 5988
  if ((new_db_type != old_db_type ||
       alter_info->flags & ALTER_PARTITION) &&
5989
      !table->file->can_switch_engines())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5990
  {
5991
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5992 5993 5994
    goto err;
  }

5995
  if (create_info->row_type == ROW_TYPE_NOT_USED)
5996
    create_info->row_type= table->s->row_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5997

5998 5999 6000
  DBUG_PRINT("info", ("old type: %s  new type: %s",
             ha_resolve_storage_engine_name(old_db_type),
             ha_resolve_storage_engine_name(new_db_type)));
monty@mysql.com's avatar
monty@mysql.com committed
6001
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
6002
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
6003 6004 6005
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6006
    goto err;
6007 6008
  }
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6009
  thd->proc_info="setup";
6010
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
6011
      !table->s->tmp_table) // no need to touch frm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6012
  {
6013 6014 6015 6016
    switch (alter_info->keys_onoff) {
    case LEAVE_AS_IS:
      break;
    case ENABLE:
6017 6018 6019 6020 6021 6022 6023 6024 6025 6026
      /*
        wait_while_table_is_used() ensures that table being altered is
        opened only by this thread and that TABLE::TABLE_SHARE::version
        of TABLE object corresponding to this table is 0.
        The latter guarantees that no DML statement will open this table
        until ALTER TABLE finishes (i.e. until close_thread_tables())
        while the fact that the table is still open gives us protection
        from concurrent DDL statements.
      */
      VOID(pthread_mutex_lock(&LOCK_open));
6027
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6028
      VOID(pthread_mutex_unlock(&LOCK_open));
6029
      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
6030 6031 6032 6033
      error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
    case DISABLE:
6034
      VOID(pthread_mutex_lock(&LOCK_open));
6035
      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6036
      VOID(pthread_mutex_unlock(&LOCK_open));
6037 6038 6039
      error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
      /* COND_refresh will be signaled in close_thread_tables() */
      break;
6040 6041 6042 6043
    default:
      DBUG_ASSERT(FALSE);
      error= 0;
      break;
6044 6045 6046
    }
    if (error == HA_ERR_WRONG_COMMAND)
    {
6047
      error= 0;
6048 6049 6050 6051 6052
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->alias);
    }

6053 6054 6055 6056 6057 6058 6059 6060 6061 6062
    VOID(pthread_mutex_lock(&LOCK_open));
    /*
      Unlike to the above case close_cached_table() below will remove ALL
      instances of TABLE from table cache (it will also remove table lock
      held by this thread). So to make actual table renaming and writing
      to binlog atomic we have to put them into the same critical section
      protected by LOCK_open mutex. This also removes gap for races between
      access() and mysql_rename_table() calls.
    */

6063
    if (!error && (new_name != table_name || new_db != db))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6064
    {
6065
      thd->proc_info="rename";
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078
      /*
        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.
      */
6079 6080
      if (!access(new_name_buff,F_OK))
      {
6081
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
6082
	error= -1;
6083 6084 6085
      }
      else
      {
6086 6087 6088 6089 6090 6091 6092 6093 6094 6095
	*fn_ext(new_name)=0;
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
	  error= -1;
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
          VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
                                  table_name, 0));
          error= -1;
        }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6096 6097
      }
    }
6098

6099 6100 6101 6102 6103 6104
    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);
6105
    }
6106

6107 6108 6109 6110
    if (!error)
    {
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
      send_ok(thd);
6111
    }
6112
    else if (error > 0)
6113
    {
6114 6115
      table->file->print_error(error, MYF(0));
      error= -1;
6116
    }
6117 6118 6119 6120 6121 6122
    if (name_lock)
      unlink_open_table(thd, name_lock, FALSE);
    VOID(pthread_mutex_unlock(&LOCK_open));
    table_list->table= NULL;                    // For query cache
    query_cache_invalidate3(thd, table_list, 0);
    DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6123 6124
  }

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

6127 6128 6129
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
6130
    goto err;
6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143
#endif
  /*
    If the old table had partitions and we are doing ALTER TABLE ...
    engine= <new_engine>, the new table must preserve the original
    partitioning. That means that the new engine is still the
    partitioning engine, not the engine specified in the parser.
    This is discovered  in prep_alter_part_table, which in such case
    updates create_info->db_type.
    Now we need to update the stack copy of create_info->db_type,
    as otherwise we won't be able to correctly move the files of the
    temporary table to the result table files.
  */
  new_db_type= create_info->db_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6144

6145 6146
  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
    goto err;
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6147 6148
  
  need_copy_table= alter_info->change_level;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6149

6150 6151
  set_table_default_charset(thd, create_info, db);

6152
  if (thd->variables.old_alter_table
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6153
      || (table->s->db_type() != create_info->db_type)
6154
#ifdef WITH_PARTITION_STORAGE_ENGINE
6155
      || partition_changed
6156
#endif
6157
     )
6158
    need_copy_table= ALTER_TABLE_DATA_CHANGED;
6159
  else
6160
  {
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6161
    enum_alter_table_change_level need_copy_table_res;
6162
    /* Check how much the tables differ. */
6163 6164
    if (compare_tables(table, alter_info,
                       create_info, order_num,
6165
                       &need_copy_table_res,
6166 6167 6168 6169
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
                       &index_add_buffer, &index_add_count))
      goto err;
6170 6171 6172
   
    if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
      need_copy_table= need_copy_table_res;
6173 6174 6175 6176 6177 6178 6179 6180 6181 6182
  }

  /*
    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;
6183
    ulong alter_flags= 0;
6184 6185 6186 6187 6188 6189
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

antony@ppcg5.local's avatar
antony@ppcg5.local committed
6190 6191
    if (table->s->db_type()->alter_table_flags)
      alter_flags= table->s->db_type()->alter_table_flags(alter_info->flags);
6192
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269
    /* 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)
      {
        /* Unique key. Check for "PRIMARY". */
        if (! my_strcasecmp(system_charset_info,
                            key->name, primary_key_name))
        {
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_DROP_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
          pk_changed++;
        }
        else
        {
          /* Non-primary unique key. */
          needed_online_flags|=  HA_ONLINE_DROP_UNIQUE_INDEX;
          needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
        }
      }
      else
      {
        /* Non-unique key. */
        needed_online_flags|=  HA_ONLINE_DROP_INDEX;
        needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
      }
    }

    /* 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)
      {
        /* Unique key. Check for "PRIMARY". */
        if (! my_strcasecmp(system_charset_info,
                            key->name, primary_key_name))
        {
          /* Primary key. */
          needed_online_flags|=  HA_ONLINE_ADD_PK_INDEX;
          needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
          pk_changed++;
        }
        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;
      }
    }

    /*
      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. */
6270
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6271 6272 6273 6274 6275
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
6276
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
6277 6278 6279 6280 6281
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
6282

6283 6284
  /*
    better have a negative test here, instead of positive, like
monty@mysql.com's avatar
monty@mysql.com committed
6285
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
6286 6287
    so that ALTER TABLE won't break when somebody will add new flag
  */
6288
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
6289
    create_info->frm_only= 1;
6290

6291
#ifdef WITH_PARTITION_STORAGE_ENGINE
6292
  if (fast_alter_partition)
6293
  {
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6294
    DBUG_ASSERT(!name_lock);
6295 6296 6297 6298
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           db, table_name,
                                           fast_alter_partition));
6299
  }
6300
#endif
6301

6302 6303 6304 6305 6306 6307
  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);

6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322
  /*
    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.
6323 6324
      At end, rename intermediate tables, and symlinks to intermediate
      table, to final table name.
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
      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);
    }
  }
6351 6352
  else
    create_info->data_file_name=create_info->index_file_name=0;
monty@mysql.com's avatar
monty@mysql.com committed
6353

6354 6355 6356 6357 6358 6359
  /*
    Create a table with a temporary name.
    With create_info->frm_only == 1 this creates a .frm file only.
    We don't log the statement, it will be logged later.
  */
  tmp_disable_binlog(thd);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6360
  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
6361 6362 6363
                                    create_info,
                                    alter_info,
                                    1, 0);
6364 6365
  reenable_binlog(thd);
  if (error)
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6366
    goto err;
6367 6368

  /* Open the table if we need to copy the data. */
6369
  DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
6370
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
6371
  {
6372
    if (table->s->tmp_table)
6373 6374 6375 6376
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
6377
      tbl.table_name= tbl.alias= tmp_name;
6378
      /* Table is in thd->temporary_tables */
6379 6380
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
6381 6382 6383 6384
    }
    else
    {
      char path[FN_REFLEN];
6385
      /* table is a normal table: Create temporary table in same directory */
6386 6387
      build_table_filename(path, sizeof(path), new_db, tmp_name, "",
                           FN_IS_TMP);
6388
      /* Open our intermediate table */
6389 6390 6391
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
6392
      goto err1;
6393 6394 6395 6396
    /*
      Note: In case of MERGE table, we do not attach children. We do not
      copy data for MERGE tables. Only the children have data.
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6397 6398
  }

6399
  /* Copy the data if necessary. */
6400
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6401 6402
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
6403
  copied=deleted=0;
6404 6405 6406 6407
  /*
    We do not copy data for MERGE tables. Only the children have data.
    MERGE tables have HA_NO_COPY_ON_ALTER set.
  */
6408
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
6409
  {
6410
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
monty@mysql.com's avatar
monty@mysql.com committed
6411
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
6412
    new_table->next_number_field=new_table->found_next_number_field;
6413 6414 6415
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
6416
                                    alter_info->keys_onoff,
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6417
                                    alter_info->error_if_not_empty);
6418
  }
6419
  else
6420 6421 6422
  {
    VOID(pthread_mutex_lock(&LOCK_open));
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
6423
    VOID(pthread_mutex_unlock(&LOCK_open));
6424 6425
    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
                            alter_info->keys_onoff);
6426 6427 6428
    error= ha_commit_stmt(thd);
    if (ha_commit(thd))
      error= 1;
6429
  }
6430
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6431

6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444
  /* 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"));

6445
    table->file->prepare_for_alter();
6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472
    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;
6473
        goto err1;
6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495
      }
    }
    /*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));
6496
        goto err1;
6497 6498 6499 6500 6501 6502
      }

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
6503
        goto err1;
6504 6505 6506 6507
      }
    }
    /*end of if (index_drop_count)*/

6508 6509 6510 6511
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
6512

6513 6514 6515
    /* Need to commit before a table is unlocked (NDB requirement). */
    DBUG_PRINT("info", ("Committing before unlocking table"));
    if (ha_commit_stmt(thd) || ha_commit(thd))
6516
      goto err1;
6517
    committed= 1;
6518 6519 6520
  }
  /*end of if (! new_table) for add/drop index*/

6521
  if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6522 6523 6524
  {
    /* We changed a temporary table */
    if (error)
6525
      goto err1;
6526 6527 6528 6529 6530 6531
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6532
    /* Remove link to old table and rename the new one */
6533
    close_temporary_table(thd, table, 1, 1);
6534 6535
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
6536
      goto err1;
6537
    /* We don't replicate alter table statement on temporary tables */
6538
    if (!thd->current_stmt_binlog_row_based)
6539
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6540 6541 6542
    goto end_temporary;
  }

6543 6544
  if (new_table)
  {
6545 6546 6547 6548
    /*
      Close the intermediate table that will be the new table.
      Note that MERGE tables do not have their children attached here.
    */
6549
    intern_close_table(new_table);
6550
    my_free(new_table,MYF(0));
6551
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6552 6553 6554
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
6555
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6556 6557 6558
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6559

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6560
  /*
6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573
    Data is copied. Now we:
    1) Wait until all other threads close old version of table.
    2) Close instances of table open by this thread and replace them
       with exclusive name-locks.
    3) Rename the old table to a temp name, rename the new one to the
       old name.
    4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
       we reopen new version of table.
    5) Write statement to the binary log.
    6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
       remove name-locks from list of open tables and table cache.
    7) If we are not not under LOCK TABLES we rely on close_thread_tables()
       call to remove name-locks from table cache and list of open table.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6574 6575 6576
  */

  thd->proc_info="rename result table";
6577 6578
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
6579 6580
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6581

6582 6583
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
  close_data_files_and_morph_locks(thd, db, table_name);
6584

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6585
  error=0;
6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600
  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.
  */
6601 6602 6603 6604 6605 6606
  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;
  }
6607 6608
  if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                         FN_TO_IS_TMP))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6609 6610
  {
    error=1;
6611
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6612
  }
6613
  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
6614
                              new_alias, FN_FROM_IS_TMP) ||
6615
           (new_name != table_name || new_db != db) && // we also do rename
6616
           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
6617 6618
            mysql_rename_table(save_old_db_type, db, table_name, new_db,
                               new_alias, NO_FRM_RENAME)) &&
6619 6620
           Table_triggers_list::change_table_name(thd, db, table_name,
                                                  new_db, new_alias))
6621 6622
  {
    /* Try to get everything back. */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6623
    error=1;
6624 6625 6626 6627
    VOID(quick_rm_table(new_db_type,new_db,new_alias, 0));
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
    VOID(mysql_rename_table(old_db_type, db, old_name, db, alias,
                            FN_FROM_IS_TMP));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6628
  }
6629

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6630 6631
  if (error)
  {
6632 6633
    /* This shouldn't happen. But let us play it safe. */
    goto err_with_placeholders;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6634
  }
6635

6636
  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
6637
  {
6638 6639 6640
    /*
      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.
6641
      NO need to tamper with MERGE tables. The real open is done later.
6642 6643 6644 6645 6646 6647 6648 6649 6650
    */
    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);
6651 6652
      table_list->table= name_lock;
      if (reopen_name_locked_table(thd, table_list, FALSE))
6653 6654
        goto err_with_placeholders;
      t_table= table_list->table;
6655
    }
6656
    else
6657
    {
6658 6659 6660
      if (reopen_table(table))
        goto err_with_placeholders;
      t_table= table;
6661
    }
6662 6663 6664 6665 6666
    /* Tell the handler that a new frm file is in place. */
    if (t_table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
                                            create_info))
      goto err_with_placeholders;
    if (thd->locked_tables && new_name == table_name && new_db == db)
6667
    {
6668 6669 6670 6671 6672 6673 6674 6675
      /*
        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);
6676
    }
6677
  }
6678

6679 6680 6681
  VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));

  if (thd->locked_tables && new_name == table_name && new_db == db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6682
  {
6683 6684 6685 6686 6687
    thd->in_lock_tables= 1;
    error= reopen_tables(thd, 1, 0);
    thd->in_lock_tables= 0;
    if (error)
      goto err_with_placeholders;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6688
  }
6689
  VOID(pthread_mutex_unlock(&LOCK_open));
6690

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6691
  thd->proc_info="end";
6692

6693 6694
  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););

6695 6696 6697 6698
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
                      thd->query, thd->query_length,
                      db, table_name);

6699 6700
  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
                thd->current_stmt_binlog_row_based &&
6701 6702
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
6703

6704
  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
6705
  {
6706 6707 6708
    /*
      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
6709
      shutdown. But we do not need to attach MERGE children.
6710
    */
6711
    char path[FN_REFLEN];
6712
    TABLE *t_table;
6713
    build_table_filename(path, sizeof(path), new_db, table_name, "", 0);
6714 6715
    t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
    if (t_table)
6716
    {
6717
      intern_close_table(t_table);
6718
      my_free(t_table, MYF(0));
6719
    }
6720
    else
6721
      sql_print_warning("Could not open table %s.%s after rename\n",
serg@serg.mylan's avatar
serg@serg.mylan committed
6722
                        new_db,table_name);
6723
    ha_flush_logs(old_db_type);
6724
  }
6725
  table_list->table=0;				// For query cache
6726
  query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6727

6728
  if (thd->locked_tables && (new_name != table_name || new_db != db))
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6729
  {
6730 6731 6732 6733 6734 6735
    /*
      If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
      to remove placeholders for the old table and for the target table
      from the list of open tables and table cache. If we are not under
      LOCK TABLES we can rely on close_thread_tables() doing this job.
    */
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6736
    pthread_mutex_lock(&LOCK_open);
6737
    unlink_open_table(thd, table, FALSE);
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6738 6739 6740 6741
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6742
end_temporary:
6743 6744 6745
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
6746
  send_ok(thd, copied + deleted, 0L, tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6747
  thd->some_tables_deleted=0;
6748
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6749

6750
err1:
6751 6752 6753 6754 6755 6756
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
6757
    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
6758

6759
err:
6760 6761 6762 6763 6764 6765
  /*
    No default value was provided for a DATE/DATETIME field, the
    current sql_mode doesn't allow the '0000-00-00' value and
    the table to be altered isn't empty.
    Report error here.
  */
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6766
  if (alter_info->error_if_not_empty && thd->row_count)
6767
  {
6768 6769
    const char *f_val= 0;
    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6770
    switch (alter_info->datetime_field->sql_type)
6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786
    {
      case MYSQL_TYPE_DATE:
      case MYSQL_TYPE_NEWDATE:
        f_val= "0000-00-00";
        t_type= MYSQL_TIMESTAMP_DATE;
        break;
      case MYSQL_TYPE_DATETIME:
        f_val= "0000-00-00 00:00:00";
        t_type= MYSQL_TIMESTAMP_DATETIME;
        break;
      default:
        /* Shouldn't get here. */
        DBUG_ASSERT(0);
    }
    bool save_abort_on_warning= thd->abort_on_warning;
    thd->abort_on_warning= TRUE;
evgen@moonbone.local's avatar
evgen@moonbone.local committed
6787 6788
    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                                 f_val, strlength(f_val), t_type,
igor@olga.mysql.com's avatar
igor@olga.mysql.com committed
6789
                                 alter_info->datetime_field->field_name);
6790 6791
    thd->abort_on_warning= save_abort_on_warning;
  }
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
6792 6793 6794 6795 6796 6797
  if (name_lock)
  {
    pthread_mutex_lock(&LOCK_open);
    unlink_open_table(thd, name_lock, FALSE);
    pthread_mutex_unlock(&LOCK_open);
  }
6798
  DBUG_RETURN(TRUE);
6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810

err_with_placeholders:
  /*
    An error happened while we were holding exclusive name-lock on table
    being altered. To be safe under LOCK TABLES we should remove placeholders
    from list of open tables list and table cache.
  */
  unlink_open_table(thd, table, FALSE);
  if (name_lock)
    unlink_open_table(thd, name_lock, FALSE);
  VOID(pthread_mutex_unlock(&LOCK_open));
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6811
}
6812
/* mysql_alter_table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6813 6814

static int
6815
copy_data_between_tables(TABLE *from,TABLE *to,
6816
			 List<Create_field> &create,
6817
                         bool ignore,
6818
			 uint order_num, ORDER *order,
6819
			 ha_rows *copied,
andrey@example.com's avatar
andrey@example.com committed
6820
			 ha_rows *deleted,
6821 6822
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6823 6824 6825 6826 6827
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
6828
  uint length= 0;
6829 6830 6831 6832 6833
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
6834
  ha_rows examined_rows;
6835
  bool auto_increment_field_copied= 0;
6836
  ulong save_sql_mode;
6837
  ulonglong prev_insert_id;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6838 6839
  DBUG_ENTER("copy_data_between_tables");

6840 6841 6842 6843 6844 6845
  /*
    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
  */
6846
  error= ha_enable_transaction(thd, FALSE);
6847 6848
  if (error)
    DBUG_RETURN(-1);
6849
  
6850
  if (!(copy= new Copy_field[to->s->fields]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6851 6852
    DBUG_RETURN(-1);				/* purecov: inspected */

6853
  if (to->file->ha_external_lock(thd, F_WRLCK))
6854
    DBUG_RETURN(-1);
6855

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

6859 6860 6861 6862 6863
  /* 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));

6864
  from->file->info(HA_STATUS_VARIABLE);
6865
  to->file->ha_start_bulk_insert(from->file->stats.records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6866

6867 6868
  save_sql_mode= thd->variables.sql_mode;

6869 6870
  List_iterator<Create_field> it(create);
  Create_field *def;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6871 6872 6873 6874 6875
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
6876 6877
    {
      if (*ptr == to->next_number_field)
6878
      {
6879
        auto_increment_field_copied= TRUE;
6880 6881 6882 6883 6884 6885 6886 6887 6888
        /*
          If we are going to copy contents of one auto_increment column to
          another auto_increment column it is sensible to preserve zeroes.
          This condition also covers case when we are don't actually alter
          auto_increment column.
        */
        if (def->field == from->found_next_number_field)
          thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6889
      (copy_end++)->set(*ptr,def->field,0);
6890 6891
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6892 6893
  }

6894 6895
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
6896 6897
  if (order)
  {
6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915
    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;
6916

6917 6918 6919 6920 6921 6922 6923 6924 6925 6926
      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;
    }
6927 6928
  };

6929 6930
  /* Tell handler that we have values for all columns in the to table */
  to->use_all_columns();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6931
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
6932
  if (ignore)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6933
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
6934
  thd->row_count= 0;
6935
  restore_record(to, s->default_values);        // Create empty record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6936 6937 6938 6939
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
6940
      thd->send_kill_message();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6941 6942 6943
      error= 1;
      break;
    }
6944
    thd->row_count++;
6945 6946 6947 6948 6949 6950
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
6951 6952
    if (to->next_number_field)
    {
6953
      if (auto_increment_field_copied)
6954
        to->auto_increment_field_not_null= TRUE;
6955 6956 6957
      else
        to->next_number_field->reset();
    }
6958
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6959
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
6960
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6961
      copy_ptr->do_copy(copy_ptr);
6962
    }
6963
    prev_insert_id= to->file->next_insert_id;
6964
    error=to->file->write_row(to->record[0]);
6965 6966
    to->auto_increment_field_not_null= FALSE;
    if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6967
    {
6968
      if (!ignore ||
6969
          to->file->is_fatal_error(error, HA_CHECK_DUP))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6970
      {
6971
         if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
6972 6973 6974 6975
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
6976
             const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
6977
             if (key_nr == 0 &&
monty@mysql.com's avatar
monty@mysql.com committed
6978 6979
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
6980
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
monty@mysql.com's avatar
monty@mysql.com committed
6981
             to->file->print_keydup_error(key_nr, err_msg);
6982 6983 6984 6985
             break;
           }
         }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6986 6987 6988
	to->file->print_error(error,MYF(0));
	break;
      }
6989
      to->file->restore_auto_increment(prev_insert_id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6990 6991 6992
      delete_count++;
    }
    else
6993
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6994 6995
  }
  end_read_record(&info);
6996
  free_io_cache(from);
6997
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
6998

6999
  if (to->file->ha_end_bulk_insert() && error <= 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7000
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
7001
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7002 7003
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
7004
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
7005

7006 7007 7008 7009 7010 7011
  if (ha_enable_transaction(thd, TRUE))
  {
    error= 1;
    goto err;
  }
  
7012 7013 7014 7015 7016 7017 7018 7019
  /*
    Ensure that the new table is saved properly to disk so that we
    can do a rename
  */
  if (ha_commit_stmt(thd))
    error=1;
  if (ha_commit(thd))
    error=1;
7020

7021
 err:
7022
  thd->variables.sql_mode= save_sql_mode;
7023
  thd->abort_on_warning= 0;
7024
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7025 7026
  *copied= found_count;
  *deleted=delete_count;
7027
  to->file->ha_release_auto_increment();
7028
  if (to->file->ha_external_lock(thd,F_UNLCK))
7029
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7030 7031
  DBUG_RETURN(error > 0 ? -1 : 0);
}
7032

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

7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate

 RETURN
    Like mysql_alter_table().
*/
7045
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
7046 7047
{
  HA_CREATE_INFO create_info;
7048 7049 7050
  Alter_info alter_info;

  DBUG_ENTER("mysql_recreate_table");
7051 7052 7053 7054 7055 7056
  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;
7057 7058

  bzero((char*) &create_info, sizeof(create_info));
7059
  create_info.db_type= 0;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
7060
  create_info.row_type=ROW_TYPE_NOT_USED;
7061
  create_info.default_table_charset=default_charset_info;
monty@mysql.com's avatar
monty@mysql.com committed
7062
  /* Force alter table to recreate table */
7063
  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
7064
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
7065 7066
                                table_list, &alter_info, 0,
                                (ORDER *) 0, 0));
7067 7068 7069
}


7070 7071
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
7072 7073 7074 7075 7076
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
7077
  DBUG_ENTER("mysql_checksum_table");
7078 7079 7080

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
7081 7082
  field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                          MY_INT64_NUM_DECIMAL_DIGITS));
7083
  item->maybe_null= 1;
7084 7085
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
7086
    DBUG_RETURN(TRUE);
7087

7088
  /* Open one table after the other to keep lock time as short as possible. */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7089
  for (table= tables; table; table= table->next_local)
7090 7091
  {
    char table_name[NAME_LEN*2+2];
7092
    TABLE *t;
7093

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

7096
    t= table->table= open_n_lock_single_table(thd, table, TL_READ);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7097
    thd->clear_error();			// these errors shouldn't get client
7098 7099 7100 7101

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

7102
    if (!t)
7103
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7104
      /* Table didn't exist */
7105
      protocol->store_null();
7106
      thd->clear_error();
7107 7108 7109
    }
    else
    {
7110
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
7111 7112
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
7113
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7114
	       (check_opt->flags & T_QUICK))
7115
	protocol->store_null();
7116 7117
      else
      {
7118 7119
	/* calculating table's checksum */
	ha_checksum crc= 0;
7120
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
7121

7122
        t->use_all_columns();
7123

7124
	if (t->file->ha_rnd_init(1))
7125 7126 7127
	  protocol->store_null();
	else
	{
7128
	  for (;;)
7129 7130
	  {
	    ha_checksum row_crc= 0;
7131 7132 7133 7134 7135 7136 7137
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
7138 7139 7140 7141
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
serg@mysql.com's avatar
serg@mysql.com committed
7142 7143 7144
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

7145 7146
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
7147

7148
	    for (uint i= 0; i < t->s->fields; i++ )
7149 7150
	    {
	      Field *f= t->field[i];
7151
	      if ((f->type() == MYSQL_TYPE_BLOB) ||
7152
                  (f->type() == MYSQL_TYPE_VARCHAR))
7153 7154 7155
	      {
		String tmp;
		f->val_str(&tmp);
7156
		row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
7157 7158
	      }
	      else
7159
		row_crc= my_checksum(row_crc, f->ptr,
7160
				     f->pack_length());
7161
	    }
7162

7163 7164 7165
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
7166
          t->file->ha_rnd_end();
7167
	}
7168
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7169
      thd->clear_error();
7170 7171 7172 7173 7174 7175 7176 7177
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
7178
  DBUG_RETURN(FALSE);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7179

7180 7181 7182 7183
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
7184
  DBUG_RETURN(TRUE);
7185
}
7186 7187

static bool check_engine(THD *thd, const char *table_name,
7188
                         HA_CREATE_INFO *create_info)
7189
{
7190
  handlerton **new_engine= &create_info->db_type;
7191
  handlerton *req_engine= *new_engine;
7192
  bool no_substitution=
7193
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
7194 7195
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
7196 7197
    return TRUE;

7198
  if (req_engine && req_engine != *new_engine)
7199
  {
serg@janus.mylan's avatar
serg@janus.mylan committed
7200
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
7201 7202
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
7203
                       ha_resolve_storage_engine_name(*new_engine),
7204 7205
                       table_name);
  }
7206 7207 7208 7209 7210
  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)
    {
7211
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7212
               ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
7213 7214 7215
      *new_engine= 0;
      return TRUE;
    }
7216
    *new_engine= myisam_hton;
7217
  }
7218 7219
  return FALSE;
}