sql_table.cc 199 KB
Newer Older
1
/* Copyright (C) 2000-2004 MySQL AB
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

   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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   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"
20
#include <hash.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21
#include <myisam.h>
22
#include <my_dir.h>
23 24
#include "sp_head.h"
#include "sql_trigger.h"
25
#include "sql_show.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
26 27 28 29 30

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

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

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

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,
				    List<create_field> &create,
				    enum enum_duplicates handle_duplicates,
40
                                    bool ignore,
41
				    uint order_num, ORDER *order,
42
				    ha_rows *copied,ha_rows *deleted);
43
static bool prepare_blob_field(THD *thd, create_field *sql_field);
44
static bool check_engine(THD *thd, const char *table_name,
45
                         HA_CREATE_INFO *create_info);                             
46 47 48 49 50 51
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
                               List<create_field> *fields,
                               List<Key> *keys, 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
52

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#define MYSQL50_TABLE_NAME_PREFIX         "#mysql50#"
#define MYSQL50_TABLE_NAME_PREFIX_LENGTH  9

uint filename_to_tablename(const char *from, char *to, uint to_length)
{
  uint errors, 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.
    */
  }
  return res;
}


uint tablename_to_filename(const char *from, char *to, uint to_length)
{
75
  uint errors, length;
bar@mysql.com's avatar
bar@mysql.com committed
76 77
  if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
                                 MYSQL50_TABLE_NAME_PREFIX_LENGTH))
78 79 80
    return (uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
                           to_length-1) -
                   (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH));
81 82 83 84 85 86 87 88 89
  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;
  }
  return length;
90 91 92
}


93
/*
94
  Creates path to a file: mysql_data_dir/db/table.ext
95 96

  SYNOPSIS
97 98 99 100 101 102 103 104 105 106 107 108 109
   build_table_filename()
   buff			where to write result
   bufflen              buff size
   db                   database name, in system_charset_info
   table                table name, in system_charset_info
   ext                  file extension

  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".
    'ext' is not converted.
110 111 112

  RETURN

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
*/


uint build_table_filename(char *buff, size_t bufflen, const char *db,
                          const char *table, const char *ext)
{
  uint length;
  char dbbuff[FN_REFLEN];
  char tbbuff[FN_REFLEN];
  VOID(tablename_to_filename(table, tbbuff, sizeof(tbbuff)));
  VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
  strxnmov(buff, bufflen,
           mysql_data_home, "/", dbbuff, "/", tbbuff, ext, NullS);
  length= unpack_filename(buff, buff);
  return length;
}


131
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
132
{
133 134
  uint length;
  char tbbuff[FN_REFLEN];
135 136 137 138 139 140 141
  char tmp_table_name[tmp_file_prefix_length+22+22+22+3];
  my_snprintf(tmp_table_name, sizeof(tmp_table_name),
	      "%s%lx_%lx_%x",
	      tmp_file_prefix, current_pid,
	      thd->thread_id, thd->tmp_table++);
  VOID(tablename_to_filename(tmp_table_name, tbbuff, sizeof(tbbuff)));
  strxnmov(buff, bufflen, mysql_tmpdir, "/", tbbuff, reg_ext, NullS);
142 143
  length= unpack_filename(buff, buff);
  return length;
144 145
}

146 147 148 149 150 151 152
/*
  Return values for compare_tables().
  If you make compare_tables() non-static, move them to a header file.
*/
#define ALTER_TABLE_DATA_CHANGED  1
#define ALTER_TABLE_INDEX_CHANGED 2

153

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
/*
  SYNOPSIS
    mysql_copy_create_list()
    orig_create_list          Original list of created fields
    inout::new_create_list    Copy of original list

  RETURN VALUES
    FALSE                     Success
    TRUE                      Memory allocation error

  DESCRIPTION
    mysql_prepare_table destroys the create_list and in some cases we need
    this lists for more purposes. Thus we copy it specifically for use
    by mysql_prepare_table
*/

static int mysql_copy_create_list(List<create_field> *orig_create_list,
171
                                  List<create_field> *new_create_list)
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
{
  List_iterator<create_field> prep_field_it(*orig_create_list);
  create_field *prep_field;
  DBUG_ENTER("mysql_copy_create_list");

  while ((prep_field= prep_field_it++))
  {
    create_field *field= new create_field(*prep_field);
    if (!field || new_create_list->push_back(field))
    {
      mem_alloc_error(2);
      DBUG_RETURN(TRUE);
    }
  }
  DBUG_RETURN(FALSE);
}


/*
  SYNOPSIS
    mysql_copy_key_list()
    orig_key                  Original list of keys
    inout::new_key            Copy of original list

  RETURN VALUES
    FALSE                     Success
    TRUE                      Memory allocation error

  DESCRIPTION
    mysql_prepare_table destroys the key list and in some cases we need
    this lists for more purposes. Thus we copy it specifically for use
    by mysql_prepare_table
*/

static int mysql_copy_key_list(List<Key> *orig_key,
                               List<Key> *new_key)
{
  List_iterator<Key> prep_key_it(*orig_key);
  Key *prep_key;
211
  DBUG_ENTER("mysql_copy_key_list");
212 213 214 215 216 217 218 219 220 221 222

  while ((prep_key= prep_key_it++))
  {
    List<key_part_spec> prep_columns;
    List_iterator<key_part_spec> prep_col_it(prep_key->columns);
    key_part_spec *prep_col;
    Key *temp_key;

    while ((prep_col= prep_col_it++))
    {
      key_part_spec *prep_key_part;
223 224

      if (!(prep_key_part= new key_part_spec(*prep_col)))
225 226 227 228 229 230 231 232 233 234
      {
        mem_alloc_error(sizeof(key_part_spec));
        DBUG_RETURN(TRUE);
      }
      if (prep_columns.push_back(prep_key_part))
      {
        mem_alloc_error(2);
        DBUG_RETURN(TRUE);
      }
    }
235
    if (!(temp_key= new Key(prep_key->type, prep_key->name,
236
                            &prep_key->key_create_info,
237
                            prep_key->generated,
238
                            prep_columns)))
239 240 241 242 243 244 245 246 247 248 249 250 251
    {
      mem_alloc_error(sizeof(Key));
      DBUG_RETURN(TRUE);
    }
    if (new_key->push_back(temp_key))
    {
      mem_alloc_error(2);
      DBUG_RETURN(TRUE);
    }
  }
  DBUG_RETURN(FALSE);
}

252 253 254
/*
--------------------------------------------------------------------------

255
   MODULE: DDL log
256 257 258 259 260 261 262 263
   -----------------

   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
264
   ddl log while we are executing. These entries are dropped when the
265 266 267 268
   operation is completed.

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

269
   There is only one ddl log in the system and it is protected by a mutex
270 271 272
   and there is a global struct that contains information about its current
   state.

273 274
   History:
   First version written in 2006 by Mikael Ronstrom
275 276 277 278
--------------------------------------------------------------------------
*/


279
typedef struct st_global_ddl_log
280
{
281 282 283 284 285 286
  /*
    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.
  */
287
  char file_entry_buf[4*IO_SIZE];
288 289
  char file_name_str[FN_REFLEN];
  char *file_name;
290 291 292
  DDL_LOG_MEMORY_ENTRY *first_free;
  DDL_LOG_MEMORY_ENTRY *first_used;
  uint num_entries;
293 294
  File file_id;
  uint name_len;
295
  uint io_size;
296
  bool inited;
297
  bool recovery_phase;
298
} GLOBAL_DDL_LOG;
299

300
GLOBAL_DDL_LOG global_ddl_log;
301

302
pthread_mutex_t LOCK_gdl;
303

304 305 306 307 308
#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
309

310 311
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
312
#define DDL_LOG_IO_SIZE_POS 8
313

314
/*
315
  Read one entry from ddl log file
316
  SYNOPSIS
317
    read_ddl_log_file_entry()
318
    entry_no                     Entry number to read
319
  RETURN VALUES
320 321
    TRUE                         Error
    FALSE                        Success
322 323
*/

324
static bool read_ddl_log_file_entry(uint entry_no)
325 326
{
  bool error= FALSE;
327 328 329 330
  File file_id= global_ddl_log.file_id;
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  uint io_size= global_ddl_log.io_size;
  DBUG_ENTER("read_ddl_log_file_entry");
331

Magnus@hugo's avatar
Magnus@hugo committed
332
  if (my_pread(file_id, (byte*)file_entry_buf, io_size, io_size * entry_no,
333
               MYF(MY_WME)) != io_size)
334 335 336 337 338 339
    error= TRUE;
  DBUG_RETURN(error);
}


/*
340
  Write one entry from ddl log file
341
  SYNOPSIS
342
    write_ddl_log_file_entry()
343 344 345 346 347 348
    entry_no                     Entry number to read
  RETURN VALUES
    TRUE                         Error
    FALSE                        Success
*/

349
static bool write_ddl_log_file_entry(uint entry_no)
350 351
{
  bool error= FALSE;
352 353 354
  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");
355

Magnus@hugo's avatar
Magnus@hugo committed
356
  if (my_pwrite(file_id, (byte*)file_entry_buf,
357
                IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
358 359 360 361 362 363
    error= TRUE;
  DBUG_RETURN(error);
}


/*
364
  Write ddl log header
365
  SYNOPSIS
366
    write_ddl_log_header()
367 368 369 370 371
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

372
static bool write_ddl_log_header()
373 374
{
  uint16 const_var;
375
  bool error= FALSE;
376
  DBUG_ENTER("write_ddl_log_header");
377

378 379
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
            global_ddl_log.num_entries);
380
  const_var= FN_LEN;
381
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
382
            const_var);
383
  const_var= IO_SIZE;
384
  int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
385
            const_var);
386
  if (write_ddl_log_file_entry(0UL))
387 388 389 390 391
  {
    sql_print_error("Error writing ddl log header");
    DBUG_RETURN(TRUE);
  }
  VOID(sync_ddl_log());
392
  DBUG_RETURN(error);
393 394 395
}


396
/*
397
  Create ddl log file name
398
  SYNOPSIS
399
    create_ddl_log_file_name()
400 401 402 403 404
    file_name                   Filename setup
  RETURN VALUES
    NONE
*/

405
static inline void create_ddl_log_file_name(char *file_name)
406
{
407
  strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
408 409 410
}


411
/*
412
  Read header of ddl log file
413
  SYNOPSIS
414
    read_ddl_log_header()
415
  RETURN VALUES
416 417
    > 0                  Last entry in ddl log
    0                    No entries in ddl log
418
  DESCRIPTION
419 420 421
    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.
422 423
*/

424
static uint read_ddl_log_header()
425
{
426
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
427
  char file_name[FN_REFLEN];
428
  uint entry_no;
429
  bool successful_open= FALSE;
430
  DBUG_ENTER("read_ddl_log_header");
431

432
  create_ddl_log_file_name(file_name);
433 434
  if ((global_ddl_log.file_id= my_open(file_name,
                                        O_RDWR | O_BINARY, MYF(MY_WME))) >= 0)
435
  {
436
    if (read_ddl_log_file_entry(0UL))
437
    {
438 439
      /* Write message into error log */
      sql_print_error("Failed to read ddl log file in recovery");
440
    }
441 442
    else
      successful_open= TRUE;
443
  }
444 445
  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]);
446 447
  if (successful_open)
  {
448
    global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
449 450 451
    DBUG_ASSERT(global_ddl_log.io_size <=
                sizeof(global_ddl_log.file_entry_buf));
  }
452
  else
453 454 455
  {
    entry_no= 0;
  }
456 457 458 459
  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));
460
  DBUG_RETURN(entry_no);
461 462 463 464
}


/*
465
  Read a ddl log entry
466
  SYNOPSIS
467
    read_ddl_log_entry()
468 469 470 471 472 473
    read_entry               Number of entry to read
    out:entry_info           Information from entry
  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
  DESCRIPTION
474
    Read a specified entry in the ddl log
475 476
*/

477
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
478
{
479
  char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
480
  uint inx;
481
  uchar single_char;
482
  DBUG_ENTER("read_ddl_log_entry");
483

484
  if (read_ddl_log_file_entry(read_entry))
485 486 487
  {
    DBUG_RETURN(TRUE);
  }
488
  ddl_log_entry->entry_pos= read_entry;
489 490 491 492
  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;
493 494 495 496 497 498 499
  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];
500 501 502 503 504
  DBUG_RETURN(FALSE);
}


/*
505
  Initialise ddl log
506
  SYNOPSIS
507
    init_ddl_log()
508

509
  DESCRIPTION
510
    Write the header of the ddl log file and length of names. Also set
511
    number of entries to zero.
512 513 514 515

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
516 517
*/

518
static bool init_ddl_log()
519
{
520
  bool error= FALSE;
521
  char file_name[FN_REFLEN];
522
  DBUG_ENTER("init_ddl_log");
523

524
  if (global_ddl_log.inited)
525 526
    goto end;

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

end:
547
  DBUG_RETURN(FALSE);
548 549 550 551
}


/*
552
  Execute one action in a ddl log entry
553
  SYNOPSIS
554 555
    execute_ddl_log_action()
    ddl_log_entry              Information in action entry to execute
556
  RETURN VALUES
557 558
    TRUE                       Error
    FALSE                      Success
559 560
*/

561
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
562
{
563 564
  bool frm_action= FALSE;
  LEX_STRING handler_name;
565
  handler *file= NULL;
566
  MEM_ROOT mem_root;
567
  int error= TRUE;
568
  char to_path[FN_REFLEN];
569
  char from_path[FN_REFLEN];
570 571
  char *par_ext= (char*)".par";
  handlerton *hton;
572
  DBUG_ENTER("execute_ddl_log_action");
573

574
  if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
575 576 577
  {
    DBUG_RETURN(FALSE);
  }
578 579
  handler_name.str= (char*)ddl_log_entry->handler_name;
  handler_name.length= strlen(ddl_log_entry->handler_name);
580
  init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 
581
  if (!strcmp(ddl_log_entry->handler_name, reg_ext))
582 583 584
    frm_action= TRUE;
  else
  {
585
    TABLE_SHARE dummy;
586 587 588 589 590 591 592

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


681
/*
682
  Get a free entry in the ddl log
683
  SYNOPSIS
684 685
    get_free_ddl_log_entry()
    out:active_entry                A ddl log memory entry returned
686
  RETURN VALUES
687 688
    TRUE                       Error
    FALSE                      Success
689 690
*/

691 692
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
                                   bool *write_header)
693
{
694 695 696
  DDL_LOG_MEMORY_ENTRY *used_entry;
  DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
  DBUG_ENTER("get_free_ddl_log_entry");
697

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

  *active_entry= used_entry;
726
  DBUG_RETURN(FALSE);
727 728 729 730
}


/*
731
  External interface methods for the DDL log Module
732 733 734 735
  ---------------------------------------------------
*/

/*
736
  SYNOPSIS
737 738 739
    write_ddl_log_entry()
    ddl_log_entry         Information about log entry
    out:entry_written     Entry information written into   
740

741
  RETURN VALUES
742 743 744 745
    TRUE                      Error
    FALSE                     Success

  DESCRIPTION
746
    A careful write of the ddl log is performed to ensure that we can
747
    handle crashes occurring during CREATE and ALTER TABLE processing.
748 749
*/

750 751
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
                         DDL_LOG_MEMORY_ENTRY **active_entry)
752
{
753
  bool error, write_header;
754 755 756 757 758 759
  DBUG_ENTER("write_ddl_log_entry");

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


/*
806
  Write final entry in the ddl log
807
  SYNOPSIS
808
    write_execute_ddl_log_entry()
809 810 811 812
    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.
813 814 815
    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
816 817 818
                                   is written first time and needs to be
                                   returned. In this case the entry written
                                   is returned in this parameter
819
  RETURN VALUES
820 821 822 823
    TRUE                           Error
    FALSE                          Success

  DESCRIPTION
824
    This is the last write in the ddl log. The previous log entries have
825
    already been written but not yet synched to disk.
826 827 828 829
    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 
830
*/ 
831

832 833 834
bool write_execute_ddl_log_entry(uint first_entry,
                                 bool complete,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
835
{
836
  bool write_header= FALSE;
837 838
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("write_execute_ddl_log_entry");
839

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


889
/*
890
  For complex rename operations we need to deactivate individual entries.
891
  SYNOPSIS
892
    deactivate_ddl_log_entry()
893 894 895 896 897 898 899 900
    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
901
    do in a safe manner unless the ddl log is informed of the phases in
902 903 904 905 906 907 908 909 910 911 912
    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.
*/

913
bool deactivate_ddl_log_entry(uint entry_no)
914
{
915 916
  char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
  DBUG_ENTER("deactivate_ddl_log_entry");
917

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


/*
954
  Sync ddl log file
955
  SYNOPSIS
956
    sync_ddl_log()
957 958 959 960 961
  RETURN VALUES
    TRUE                      Error
    FALSE                     Success
*/

962
bool sync_ddl_log()
963 964
{
  bool error= FALSE;
965
  DBUG_ENTER("sync_ddl_log");
966

967 968
  if ((!global_ddl_log.recovery_phase) &&
      init_ddl_log())
969 970 971 972
  {
    DBUG_RETURN(TRUE);
  }
  if (my_sync(global_ddl_log.file_id, MYF(0)))
973 974
  {
    /* Write to error log */
975
    sql_print_error("Failed to sync ddl log");
976 977 978 979 980 981
    error= TRUE;
  }
  DBUG_RETURN(error);
}


982 983 984
/*
  Release a log memory entry
  SYNOPSIS
985
    release_ddl_log_memory_entry()
986 987 988 989 990
    log_memory_entry                Log memory entry to release
  RETURN VALUES
    NONE
*/

991
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
992
{
993 994 995 996
  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");
997

998
  global_ddl_log.first_free= log_entry;
999 1000 1001 1002 1003
  log_entry->next_log_entry= first_free;

  if (prev_log_entry)
    prev_log_entry->next_log_entry= next_log_entry;
  else
1004
    global_ddl_log.first_used= next_log_entry;
1005 1006
  if (next_log_entry)
    next_log_entry->prev_log_entry= prev_log_entry;
1007
  DBUG_VOID_RETURN;
1008 1009 1010
}


1011
/*
1012
  Execute one entry in the ddl log. Executing an entry means executing
1013 1014
  a linked list of actions.
  SYNOPSIS
1015
    execute_ddl_log_entry()
1016 1017 1018 1019 1020 1021
    first_entry                Reference to first action in entry
  RETURN VALUES
    TRUE                       Error
    FALSE                      Success
*/

1022
bool execute_ddl_log_entry(THD *thd, uint first_entry)
1023
{
1024
  DDL_LOG_ENTRY ddl_log_entry;
1025
  uint read_entry= first_entry;
1026
  DBUG_ENTER("execute_ddl_log_entry");
1027

1028
  pthread_mutex_lock(&LOCK_gdl);
1029 1030
  do
  {
1031
    if (read_ddl_log_entry(read_entry, &ddl_log_entry))
1032 1033
    {
      /* Write to error log and continue with next log entry */
1034 1035
      sql_print_error("Failed to read entry = %u from ddl log",
                      read_entry);
1036 1037
      break;
    }
1038 1039
    DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
                ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
1040

1041
    if (execute_ddl_log_action(thd, &ddl_log_entry))
1042
    {
1043
      /* Write to error log and continue with next log entry */
1044 1045
      sql_print_error("Failed to execute action for entry = %u from ddl log",
                      read_entry);
1046
      break;
1047
    }
1048
    read_entry= ddl_log_entry.next_entry;
1049
  } while (read_entry);
1050
  pthread_mutex_unlock(&LOCK_gdl);
1051 1052 1053
  DBUG_RETURN(FALSE);
}

1054

1055
/*
1056
  Execute the ddl log at recovery of MySQL Server
1057
  SYNOPSIS
1058
    execute_ddl_log_recovery()
1059 1060 1061 1062
  RETURN VALUES
    NONE
*/

1063
void execute_ddl_log_recovery()
1064
{
1065
  uint num_entries, i;
1066
  THD *thd;
1067
  DDL_LOG_ENTRY ddl_log_entry;
1068
  char file_name[FN_REFLEN];
1069
  DBUG_ENTER("execute_ddl_log_recovery");
1070

1071 1072 1073 1074 1075 1076 1077
  /*
    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
1078
  global_ddl_log.file_id= (File) -1;
1079

1080 1081 1082 1083 1084 1085 1086 1087
  /*
    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();

1088
  num_entries= read_ddl_log_header();
1089
  for (i= 1; i < num_entries + 1; i++)
1090
  {
1091
    if (read_ddl_log_entry(i, &ddl_log_entry))
1092
    {
1093 1094 1095
      sql_print_error("Failed to read entry no = %u from ddl log",
                       i);
      continue;
1096
    }
1097
    if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
1098
    {
1099
      if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
1100
      {
1101 1102
        /* Real unpleasant scenario but we continue anyways.  */
        continue;
1103 1104 1105
      }
    }
  }
1106 1107 1108 1109 1110 1111
  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);
1112
  DBUG_VOID_RETURN;
1113 1114 1115 1116
}


/*
1117
  Release all memory allocated to the ddl log
1118
  SYNOPSIS
1119
    release_ddl_log()
1120 1121 1122 1123
  RETURN VALUES
    NONE
*/

1124
void release_ddl_log()
1125
{
1126 1127 1128
  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");
1129

1130
  pthread_mutex_lock(&LOCK_gdl);
1131 1132
  while (used_list)
  {
1133
    DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
1134
    my_free((char*)used_list, MYF(0));
1135
    used_list= tmp;
1136 1137 1138
  }
  while (free_list)
  {
1139
    DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
1140
    my_free((char*)free_list, MYF(0));
1141
    free_list= tmp;
1142
  }
monty@mysql.com's avatar
monty@mysql.com committed
1143
  if (global_ddl_log.file_id >= 0)
1144 1145
  {
    VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
monty@mysql.com's avatar
monty@mysql.com committed
1146
    global_ddl_log.file_id= (File) -1;
1147
  }
monty@mysql.com's avatar
monty@mysql.com committed
1148
  global_ddl_log.inited= 0;
1149
  pthread_mutex_unlock(&LOCK_gdl);
1150
  VOID(pthread_mutex_destroy(&LOCK_gdl));
1151
  DBUG_VOID_RETURN;
1152 1153 1154
}


1155 1156 1157
/*
---------------------------------------------------------------------------

1158
  END MODULE DDL log
1159 1160 1161 1162 1163
  --------------------

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

1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196

/*
  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];
1197 1198
  char shadow_path[FN_REFLEN+1];
  char shadow_frm_name[FN_REFLEN+1];
1199 1200 1201
  char frm_name[FN_REFLEN+1];
  DBUG_ENTER("mysql_write_frm");

1202 1203 1204
  /*
    Build shadow frm file name
  */
1205
  build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
1206
                       lpt->table_name, "#");
1207
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1208
  if (flags & WFRM_WRITE_SHADOW)
1209
  {
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
    if (mysql_copy_create_list(lpt->create_list,
                               &lpt->new_create_list) ||
        mysql_copy_key_list(lpt->key_list,
                            &lpt->new_key_list) ||
        mysql_prepare_table(lpt->thd, lpt->create_info,
                            &lpt->new_create_list,
                            &lpt->new_key_list,
                            /*tmp_table*/ 1,
                            &lpt->db_options,
                            lpt->table->file,
                            &lpt->key_info_buffer,
                            &lpt->key_count,
1222
                            /*select_field_count*/ 0))
1223 1224 1225 1226 1227
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1228 1229 1230 1231 1232
      partition_info *part_info= lpt->table->part_info;
      char *part_syntax_buf;
      uint syntax_len;

      if (part_info)
1233
      {
1234 1235
        if (!(part_syntax_buf= generate_partition_syntax(part_info,
                                                         &syntax_len,
1236
                                                         TRUE, TRUE)))
1237 1238 1239 1240 1241
        {
          DBUG_RETURN(TRUE);
        }
        part_info->part_info_string= part_syntax_buf;
        part_info->part_info_len= syntax_len;
1242 1243 1244
      }
    }
#endif
1245 1246 1247 1248 1249 1250
    /* 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,
                          lpt->new_create_list, lpt->key_count,
                          lpt->key_info_buffer, lpt->table->file)) ||
1251 1252
         lpt->table->file->create_handler_files(shadow_path, NULL,
                                                CHF_CREATE_FLAG,
1253
                                                lpt->create_info))
1254 1255 1256 1257 1258
    {
      my_delete(shadow_frm_name, MYF(0));
      error= 1;
      goto end;
    }
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
  }
  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.
    */
    const void *data= 0;
    uint length= 0;
1270
    if (readfrm(shadow_path, &data, &length) ||
1271 1272 1273 1274 1275 1276 1277 1278
        packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
    {
      my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*)lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
      mem_alloc_error(length);
      error= 1;
      goto end;
    }
1279
    error= my_delete(shadow_frm_name, MYF(MY_WME));
1280
  }
1281 1282
  if (flags & WFRM_INSTALL_SHADOW)
  {
1283 1284 1285
#ifdef WITH_PARTITION_STORAGE_ENGINE
    partition_info *part_info= lpt->part_info;
#endif
1286 1287 1288 1289 1290 1291 1292 1293 1294
    /*
      Build frm file name
    */
    build_table_filename(path, sizeof(path), lpt->db,
                         lpt->table_name, "");
    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.
1295 1296 1297 1298 1299 1300
      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.
1301 1302 1303
    */
    VOID(pthread_mutex_lock(&LOCK_open));
    if (my_delete(frm_name, MYF(MY_WME)) ||
1304
#ifdef WITH_PARTITION_STORAGE_ENGINE
1305
        lpt->table->file->create_handler_files(path, shadow_path,
1306
                                               CHF_DELETE_FLAG, NULL) ||
1307 1308
        deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
        (sync_ddl_log(), FALSE) ||
1309
#endif
1310
#ifdef WITH_PARTITION_STORAGE_ENGINE
1311
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
1312
        lpt->table->file->create_handler_files(path, shadow_path,
1313
                                               CHF_RENAME_FLAG, NULL))
1314 1315 1316
#else
        my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
1317 1318 1319 1320
    {
      error= 1;
    }
    VOID(pthread_mutex_unlock(&LOCK_open));
1321
#ifdef WITH_PARTITION_STORAGE_ENGINE
1322
    deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
1323
    part_info->frm_log_entry= NULL;
1324
    VOID(sync_ddl_log());
1325
#endif
1326
  }
1327

1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
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);
  }
}

1361

1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
/*
 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
1380 1381
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
1382 1383

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

1385 1386
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
1387
{
1388
  bool error= FALSE, need_start_waiters= FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1389 1390 1391 1392
  DBUG_ENTER("mysql_rm_table");

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

1393
  if (!drop_temporary)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1394
  {
1395
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
1396
    {
1397
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
1398
      DBUG_RETURN(TRUE);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1399
    }
1400 1401
    else
      need_start_waiters= TRUE;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1402
  }
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412

  /*
    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.
  */
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1413
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1414 1415 1416 1417 1418 1419 1420 1421

  pthread_mutex_unlock(&LOCK_open);

  pthread_mutex_lock(&thd->mysys_var->mutex);
  thd->mysys_var->current_mutex= 0;
  thd->mysys_var->current_cond= 0;
  pthread_mutex_unlock(&thd->mysys_var->mutex);

1422 1423 1424
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1425
  if (error)
1426
    DBUG_RETURN(TRUE);
1427
  send_ok(thd);
1428
  DBUG_RETURN(FALSE);
1429 1430
}

1431 1432 1433 1434 1435

/*
 delete (drop) tables.

  SYNOPSIS
1436 1437 1438 1439 1440
    mysql_rm_table_part2_with_lock()
    thd			Thread handle
    tables		List of tables to delete
    if_exists		If 1, don't give error if one table doesn't exists
    dont_log_query	Don't write query to log files. This will also not
1441
                        generate warnings if the handler files doesn't exists
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451

 NOTES
   Works like documented in mysql_rm_table(), but don't check
   global_read_lock and don't send_ok packet to server.

 RETURN
  0	ok
  1	error
*/

1452 1453
int mysql_rm_table_part2_with_lock(THD *thd,
				   TABLE_LIST *tables, bool if_exists,
1454
				   bool drop_temporary, bool dont_log_query)
1455 1456 1457 1458 1459 1460
{
  int error;
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1461 1462
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
			      dont_log_query);
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472

  pthread_mutex_unlock(&LOCK_open);

  pthread_mutex_lock(&thd->mysys_var->mutex);
  thd->mysys_var->current_mutex= 0;
  thd->mysys_var->current_cond= 0;
  pthread_mutex_unlock(&thd->mysys_var->mutex);
  return error;
}

1473

1474
/*
1475 1476 1477 1478 1479 1480 1481 1482 1483
  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
1484
    drop_view		Allow to delete VIEW .frm
1485 1486
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
1487

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

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

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
1505 1506
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
1507 1508
{
  TABLE_LIST *table;
1509 1510
  char path[FN_REFLEN], *alias;
  uint path_length;
1511 1512
  String wrong_tables;
  int error;
1513
  int non_temp_tables_count= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1514
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
1515
  String built_query;
1516 1517
  DBUG_ENTER("mysql_rm_table_part2");

1518 1519 1520 1521
  LINT_INIT(alias);
  LINT_INIT(path_length);
  safe_mutex_assert_owner(&LOCK_open);

1522
  if (thd->current_stmt_binlog_row_based && !dont_log_query)
1523 1524 1525 1526 1527 1528 1529
  {
    built_query.set_charset(system_charset_info);
    if (if_exists)
      built_query.append("DROP TABLE IF EXISTS ");
    else
      built_query.append("DROP TABLE ");
  }
1530 1531 1532 1533 1534 1535 1536 1537 1538
  /*
    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;
1539
    table->db_type= NULL;
1540 1541 1542 1543
    if ((share= get_cached_table_share(table->db, table->table_name)))
      table->db_type= share->db_type;
  }

1544
  if (lock_table_names(thd, tables))
1545
    DBUG_RETURN(1);
1546

1547 1548 1549
  /* 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
1550
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1551
  {
1552
    char *db=table->db;
1553 1554
    handlerton *table_type;
    enum legacy_db_type frm_db_type;
1555

1556
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
1557
    if (!close_temporary_table(thd, table))
1558
    {
1559
      tmp_table_deleted=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1560
      continue;					// removed temporary table
1561
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1562

1563 1564 1565 1566 1567 1568
    /*
      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.
      */
1569
    if (thd->current_stmt_binlog_row_based && !dont_log_query)
1570
    {
1571
      non_temp_tables_count++;
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
      /*
        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("`,");
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1587
    error=0;
1588
    table_type= table->db_type;
1589
    if (!drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1590
    {
1591
      TABLE *locked_table;
1592 1593 1594 1595
      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);
1596 1597 1598 1599 1600 1601 1602
      /*
        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;

1603
      if (thd->killed)
1604 1605
      {
        thd->no_warnings_for_error= 0;
1606
	DBUG_RETURN(-1);
1607
      }
1608
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
1609
      /* remove .frm file and engine files */
1610 1611
      path_length= build_table_filename(path, sizeof(path),
                                        db, alias, reg_ext);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1612
    }
1613 1614
    if (drop_temporary ||
        (table_type == NULL &&        
1615 1616 1617
         (access(path, F_OK) &&
          ha_create_table_from_engine(thd, db, alias)) ||
         (!drop_view &&
1618
          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1619
    {
1620
      // Table was not found on disk and table can't be created from engine
1621
      if (if_exists)
1622 1623
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
1624
			    table->table_name);
1625
      else
1626
        error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1627 1628 1629
    }
    else
    {
1630
      char *end;
1631 1632 1633 1634 1635
      if (table_type == NULL)
      {
	mysql_frm_type(thd, path, &frm_db_type);
        table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
      }
1636 1637
      // Remove extension for delete
      *(end= path + path_length - reg_ext_length)= '\0';
1638
      error= ha_delete_table(thd, table_type, path, db, table->table_name,
1639
                             !dont_log_query);
1640
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
1641
	  (if_exists || table_type == NULL))
1642
	error= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1643
      if (error == HA_ERR_ROW_IS_REFERENCED)
monty@mysql.com's avatar
monty@mysql.com committed
1644 1645
      {
	/* the table is referenced by a foreign key constraint */
1646
	foreign_key_error=1;
monty@mysql.com's avatar
monty@mysql.com committed
1647
      }
1648
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
1649
      {
1650
        int new_error;
1651 1652
	/* Delete the table definition file */
	strmov(end,reg_ext);
1653
	if (!(new_error=my_delete(path,MYF(MY_WME))))
1654
        {
1655
	  some_tables_deleted=1;
1656 1657
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
1658
        }
1659
        error|= new_error;
1660
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1661 1662 1663 1664 1665
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
1666
      wrong_tables.append(String(table->table_name,system_charset_info));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1667 1668
    }
  }
1669
  thd->tmp_table_used= tmp_table_deleted;
1670 1671 1672 1673
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
1674
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
1675
                      wrong_tables.c_ptr());
1676
    else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1677
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
1678 1679 1680 1681
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1682
  {
1683
    query_cache_invalidate3(thd, tables, 0);
1684
    if (!dont_log_query)
1685
    {
1686
      if (!thd->current_stmt_binlog_row_based ||
1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
          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);
      }
1698
      else if (thd->current_stmt_binlog_row_based &&
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725
               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.
      */
1726
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1727
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1728

monty@mysql.com's avatar
monty@mysql.com committed
1729
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
1730
  thd->no_warnings_for_error= 0;
1731
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1732 1733 1734
}


1735
bool quick_rm_table(handlerton *base,const char *db,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1736 1737 1738
		   const char *table_name)
{
  char path[FN_REFLEN];
1739 1740 1741
  bool error= 0;
  DBUG_ENTER("quick_rm_table");

1742 1743
  uint path_length= build_table_filename(path, sizeof(path),
                                         db, table_name, reg_ext);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1744
  if (my_delete(path,MYF(0)))
1745
    error= 1; /* purecov: inspected */
1746
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
1747 1748
  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
1749 1750
}

1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768
/*
  Sort keys in the following order:
  - PRIMARY KEY
  - UNIQUE keyws where all column are NOT NULL
  - 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)
{
  if (a->flags & HA_NOSAME)
  {
    if (!(b->flags & HA_NOSAME))
      return -1;
1769
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
1770 1771
    {
      /* Sort NOT NULL keys before other keys */
1772
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
  }
  else if (b->flags & HA_NOSAME)
    return 1;					// Prefer b

  if ((a->flags ^ b->flags) & HA_FULLTEXT)
  {
    return (a->flags & HA_FULLTEXT) ? 1 : -1;
  }
1786
  /*
1787
    Prefer original key order.	usable_key_parts contains here
1788 1789 1790 1791 1792
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
1793 1794
}

1795 1796
/*
  Check TYPELIB (set or enum) for duplicates
1797

1798 1799 1800
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
1801 1802
    name	  name of the checked column
    typelib	  list of values for the column
1803 1804

  DESCRIPTION
1805
    This function prints an warning for each value in list
1806 1807 1808 1809 1810 1811 1812
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
1813 1814
                                  const char *name, TYPELIB *typelib,
                                  CHARSET_INFO *cs)
1815
{
1816
  TYPELIB tmp= *typelib;
1817
  const char **cur_value= typelib->type_names;
1818 1819 1820
  unsigned int *cur_length= typelib->type_lengths;
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
1821
  {
1822 1823 1824 1825
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
1826
    {
monty@mysql.com's avatar
monty@mysql.com committed
1827
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
1828 1829 1830 1831 1832 1833
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
    }
  }
}
1834

1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869

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


1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889
/*
  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
    This function prepares a create_field instance.
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

int prepare_create_field(create_field *sql_field, 
monty@mysql.com's avatar
monty@mysql.com committed
1890 1891
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
1892 1893 1894
			 uint table_flags)
{
  DBUG_ENTER("prepare_field");
monty@mysql.com's avatar
monty@mysql.com committed
1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913

  /*
    This code came from mysql_prepare_table.
    Indent preserved to make patching easier
  */
  DBUG_ASSERT(sql_field->charset);

  switch (sql_field->sql_type) {
  case FIELD_TYPE_BLOB:
  case FIELD_TYPE_MEDIUM_BLOB:
  case FIELD_TYPE_TINY_BLOB:
  case FIELD_TYPE_LONG_BLOB:
    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;
1914
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
1915 1916
    break;
  case FIELD_TYPE_GEOMETRY:
1917
#ifdef HAVE_SPATIAL
monty@mysql.com's avatar
monty@mysql.com committed
1918 1919 1920 1921
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
1922
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
1923 1924 1925 1926 1927 1928 1929 1930
    }
    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;
1931
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
1932 1933 1934 1935 1936
    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);
1937
#endif /*HAVE_SPATIAL*/
monty@mysql.com's avatar
monty@mysql.com committed
1938
  case MYSQL_TYPE_VARCHAR:
1939
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
monty@mysql.com's avatar
monty@mysql.com committed
1940 1941 1942 1943 1944 1945 1946 1947
    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)
1948
      {
monty@mysql.com's avatar
monty@mysql.com committed
1949 1950
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
1951 1952
        DBUG_RETURN(1);
      }
monty@mysql.com's avatar
monty@mysql.com committed
1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988
    }
#endif
    /* fall through */
  case FIELD_TYPE_STRING:
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
  case FIELD_TYPE_ENUM:
    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;
    check_duplicates_in_interval("ENUM",sql_field->field_name,
                                 sql_field->interval,
                                 sql_field->charset);
    break;
  case FIELD_TYPE_SET:
    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;
    check_duplicates_in_interval("SET",sql_field->field_name,
                                 sql_field->interval,
                                 sql_field->charset);
    break;
  case FIELD_TYPE_DATE:			// Rest of string types
  case FIELD_TYPE_NEWDATE:
  case FIELD_TYPE_TIME:
  case FIELD_TYPE_DATETIME:
  case FIELD_TYPE_NULL:
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
  case FIELD_TYPE_BIT:
ramil@mysql.com's avatar
ramil@mysql.com committed
1989 1990 1991
    /* 
      We have sql_field->pack_flag already set here, see mysql_prepare_table().
    */
monty@mysql.com's avatar
monty@mysql.com committed
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
    break;
  case FIELD_TYPE_NEWDECIMAL:
    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;
  case FIELD_TYPE_TIMESTAMP:
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
2005
      if (!*timestamps)
2006
      {
monty@mysql.com's avatar
monty@mysql.com committed
2007
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2008
        (*timestamps_with_niladic)++;
2009
      }
monty@mysql.com's avatar
monty@mysql.com committed
2010 2011 2012 2013
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2014
      (*timestamps_with_niladic)++;
monty@mysql.com's avatar
monty@mysql.com committed
2015

2016
    (*timestamps)++;
monty@mysql.com's avatar
monty@mysql.com committed
2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031
    /* 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;
2032 2033 2034
  DBUG_RETURN(0);
}

2035
/*
2036
  Preparation for table creation
2037 2038

  SYNOPSIS
2039
    mysql_prepare_table()
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049
      thd                       Thread object.
      create_info               Create information (like MAX_ROWS).
      fields                    List of fields to create.
      keys                      List of keys to create.
      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.
2050

2051
  DESCRIPTION
2052
    Prepares the table and key structures for table creation.
2053

2054
  NOTES
2055
    sets create_info->varchar if the table has a varchar
2056

2057 2058 2059 2060
  RETURN VALUES
    0	ok
    -1	error
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2061

2062 2063 2064 2065 2066 2067
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
                               List<create_field> *fields,
                               List<Key> *keys, 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
2068
{
2069
  const char	*key_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2070
  create_field	*sql_field,*dup_field;
monty@mysql.com's avatar
monty@mysql.com committed
2071
  uint		field,null_fields,blob_columns,max_key_length;
monty@mysql.com's avatar
monty@mysql.com committed
2072
  ulong		record_offset= 0;
2073
  KEY		*key_info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2074
  KEY_PART_INFO *key_part_info;
2075 2076 2077
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
2078
  List_iterator<create_field> it(*fields),it2(*fields);
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
2079
  uint total_uneven_bit_length= 0;
2080
  DBUG_ENTER("mysql_prepare_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2081

2082
  select_field_pos= fields->elements - select_field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2083
  null_fields=blob_columns=0;
2084
  create_info->varchar= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2085
  max_key_length= file->max_key_length();
2086

2087
  for (field_no=0; (sql_field=it++) ; field_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2088
  {
2089 2090
    CHARSET_INFO *save_cs;

2091 2092 2093 2094 2095 2096
    /*
      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;
2097
    if (!sql_field->charset)
2098 2099 2100
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
2101 2102 2103
      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.
2104
    */
2105
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
2106
      sql_field->charset= create_info->table_charset;
2107

2108
    save_cs= sql_field->charset;
2109 2110 2111 2112 2113
    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];
2114 2115
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
2116 2117 2118
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
2119

2120 2121
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
2122 2123 2124
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
2125
      TYPELIB *interval= sql_field->interval;
2126 2127 2128 2129 2130 2131

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
2132
      if (!interval)
2133
      {
2134 2135 2136 2137
        /*
          Create the typelib in prepared statement memory if we're
          executing one.
        */
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2138
        MEM_ROOT *stmt_root= thd->stmt_arena->mem_root;
2139 2140 2141

        interval= sql_field->interval= typelib(stmt_root,
                                               sql_field->interval_list);
2142 2143
        List_iterator<String> it(sql_field->interval_list);
        String conv, *tmp;
2144 2145 2146 2147 2148
        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);
2149
        for (uint i= 0; (tmp= it++); i++)
2150
        {
2151
          uint lengthsp;
2152 2153 2154 2155 2156
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
2157
            interval->type_names[i]= strmake_root(stmt_root, conv.ptr(),
2158
                                                  conv.length());
2159 2160
            interval->type_lengths[i]= conv.length();
          }
2161

2162
          // Strip trailing spaces.
2163 2164
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2165 2166
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2167 2168 2169 2170 2171 2172
          if (sql_field->sql_type == FIELD_TYPE_SET)
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
2173
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2174 2175 2176
              DBUG_RETURN(-1);
            }
          }
2177
        }
2178
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2179 2180 2181 2182 2183 2184
      }

      /*
        Convert the default value from client character
        set into the column character set if necessary.
      */
bar@mysql.com's avatar
bar@mysql.com committed
2185
      if (sql_field->def && cs != sql_field->def->collation.collation)
2186
      {
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2187 2188
        Query_arena backup_arena;
        bool need_to_change_arena= !thd->stmt_arena->is_conventional();
2189 2190 2191
        if (need_to_change_arena)
        {
          /* Asser that we don't do that at every PS execute */
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2192 2193 2194
          DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() ||
                      thd->stmt_arena->is_first_sp_execute());
          thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena);
2195 2196 2197 2198 2199
        }

        sql_field->def= sql_field->def->safe_charset_converter(cs);

        if (need_to_change_arena)
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2200
          thd->restore_active_arena(thd->stmt_arena, &backup_arena);
2201

2202
        if (sql_field->def == NULL)
bar@mysql.com's avatar
bar@mysql.com committed
2203 2204 2205 2206 2207
        {
          /* Could not convert */
          my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
          DBUG_RETURN(-1);
        }
2208 2209 2210 2211
      }

      if (sql_field->sql_type == FIELD_TYPE_SET)
      {
2212
        uint32 field_length;
2213
        if (sql_field->def != NULL)
2214 2215 2216 2217 2218
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236
          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);
              DBUG_RETURN(-1);
            }

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

2237 2238 2239 2240 2241 2242
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
2243 2244
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
2245 2246 2247
      }
      else  /* FIELD_TYPE_ENUM */
      {
2248
        uint32 field_length;
2249 2250
        DBUG_ASSERT(sql_field->sql_type == FIELD_TYPE_ENUM);
        if (sql_field->def != NULL)
2251 2252
        {
          String str, *def= sql_field->def->val_str(&str);
2253
          if (def == NULL) /* SQL "NULL" maps to NULL */
2254
          {
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }

            /* 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);
              DBUG_RETURN(-1);
            }
2271 2272
          }
        }
2273 2274
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
2275 2276 2277 2278
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

2279 2280
    if (sql_field->sql_type == FIELD_TYPE_BIT)
    { 
ramil@mysql.com's avatar
ramil@mysql.com committed
2281
      sql_field->pack_flag= FIELDFLAG_NUMBER;
2282
      if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
2283 2284 2285 2286 2287
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

2288
    sql_field->create_length_to_internal_length();
2289 2290
    if (prepare_blob_field(thd, sql_field))
      DBUG_RETURN(-1);
2291

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

2295 2296
    if (check_column_name(sql_field->field_name))
    {
2297
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
2298 2299
      DBUG_RETURN(-1);
    }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2300

2301 2302
    /* 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
2303
    {
2304
      if (my_strcasecmp(system_charset_info,
2305 2306
			sql_field->field_name,
			dup_field->field_name) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2307
      {
2308 2309 2310 2311
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
2312 2313
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
2314
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
2315 2316 2317 2318
	  DBUG_RETURN(-1);
	}
	else
	{
2319
	  /* Field redefined */
2320
	  sql_field->def=		dup_field->def;
2321
	  sql_field->sql_type=		dup_field->sql_type;
2322 2323 2324
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
2325
	  sql_field->length=		dup_field->char_length;
2326
          sql_field->pack_length=	dup_field->pack_length;
2327
          sql_field->key_length=	dup_field->key_length;
2328
	  sql_field->create_length_to_internal_length();
2329 2330
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->unireg_check=	dup_field->unireg_check;
2331 2332 2333 2334 2335 2336 2337 2338
          /* 
            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
2339
          sql_field->interval=          dup_field->interval;
2340 2341 2342
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
2343
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2344 2345
      }
    }
2346 2347 2348 2349
    /* 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)
2350
      (*db_options)|= HA_OPTION_PACK_RECORD;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2351 2352
    it2.rewind();
  }
2353 2354 2355

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
monty@mysql.com's avatar
monty@mysql.com committed
2356
  null_fields+= total_uneven_bit_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2357 2358 2359 2360

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

monty@mysql.com's avatar
monty@mysql.com committed
2363 2364
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
2365
			     file->ha_table_flags()))
hf@deer.(none)'s avatar
hf@deer.(none) committed
2366
      DBUG_RETURN(-1);
2367
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
2368
      create_info->varchar= 1;
2369
    sql_field->offset= record_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2370 2371
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
2372
    record_offset+= sql_field->pack_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2373
  }
2374 2375
  if (timestamps_with_niladic > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2376 2377
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
2378 2379
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2380 2381
  if (auto_increment > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2382
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2383 2384 2385
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
2386
      (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2387
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2388 2389
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2390 2391 2392
    DBUG_RETURN(-1);
  }

2393
  if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2394
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2395 2396
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2397 2398 2399 2400
    DBUG_RETURN(-1);
  }

  /* Create keys */
2401

2402
  List_iterator<Key> key_iterator(*keys), key_iterator2(*keys);
2403
  uint key_parts=0, fk_key_count=0;
2404
  bool primary_key=0,unique_key=0;
2405
  Key *key, *key2;
2406
  uint tmp, key_number;
2407 2408
  /* special marker for keys to be ignored */
  static char ignore_key[1];
2409

2410
  /* Calculate number of key segements */
2411
  *key_count= 0;
2412

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2413 2414
  while ((key=key_iterator++))
  {
2415 2416
    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
                        "(none)" , key->type));
2417 2418 2419 2420 2421 2422 2423
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
2424 2425 2426
        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));
2427 2428 2429 2430
	DBUG_RETURN(-1);
      }
      continue;
    }
2431
    (*key_count)++;
2432
    tmp=file->max_key_parts();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2433 2434 2435 2436 2437
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
2438
    if (key->name && strlen(key->name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2439
    {
2440
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2441 2442
      DBUG_RETURN(-1);
    }
2443
    key_iterator2.rewind ();
2444
    if (key->type != Key::FOREIGN_KEY)
2445
    {
2446
      while ((key2 = key_iterator2++) != key)
2447
      {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2448
	/*
2449 2450 2451
          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
2452
        */
2453 2454 2455
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
2456
        {
2457
          /* TODO: issue warning message */
2458 2459 2460 2461 2462 2463 2464
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
2465 2466 2467
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
2468 2469 2470
          }
          break;
        }
2471 2472 2473 2474 2475 2476
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
2477
    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
2478 2479 2480 2481 2482
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
2483
  }
2484
  tmp=file->max_keys();
2485
  if (*key_count > tmp)
2486 2487 2488 2489
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
2490

2491
  (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
2492
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
2493
  if (!*key_info_buffer || ! key_part_info)
2494 2495
    DBUG_RETURN(-1);				// Out of memory

2496
  key_iterator.rewind();
2497
  key_number=0;
2498
  for (; (key=key_iterator++) ; key_number++)
2499 2500 2501 2502
  {
    uint key_length=0;
    key_part_spec *column;

2503 2504 2505 2506 2507 2508 2509 2510 2511 2512
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

2513
    switch (key->type) {
2514
    case Key::MULTIPLE:
2515
	key_info->flags= 0;
2516
	break;
2517
    case Key::FULLTEXT:
2518
	key_info->flags= HA_FULLTEXT;
2519
	if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
2520
          key_info->flags|= HA_USES_PARSER;
2521 2522
        else
          key_info->parser_name= 0;
2523
	break;
2524
    case Key::SPATIAL:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2525
#ifdef HAVE_SPATIAL
2526
	key_info->flags= HA_SPATIAL;
2527
	break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2528
#else
2529 2530
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2531 2532
	DBUG_RETURN(-1);
#endif
2533 2534 2535 2536
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
2537 2538
      key_info->flags = HA_NOSAME;
      break;
2539
    }
2540 2541
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
2542

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2543 2544
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
2545
    key_info->usable_key_parts= key_number;
2546
    key_info->algorithm= key->key_create_info.algorithm;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2547

2548 2549
    if (key->type == Key::FULLTEXT)
    {
2550
      if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
2551
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2552 2553
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
2554
	DBUG_RETURN(-1);
2555 2556
      }
    }
2557 2558 2559
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
2560
       actually should be replaced by special GEOM type
2561 2562 2563
       in near future when new frm file is ready
       checking for proper key parts number:
    */
2564

2565
    /* TODO: Add proper checks if handler supports key_type and algorithm */
2566
    if (key_info->flags & HA_SPATIAL)
2567
    {
2568
      if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
2569 2570 2571 2572 2573
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
        DBUG_RETURN(-1);
      }
2574 2575
      if (key_info->key_parts != 1)
      {
2576
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
2577
	DBUG_RETURN(-1);
2578
      }
2579
    }
2580
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
2581
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2582
#ifdef HAVE_RTREE_KEYS
2583 2584
      if ((key_info->key_parts & 1) == 1)
      {
2585
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
2586
	DBUG_RETURN(-1);
2587
      }
2588
      /* TODO: To be deleted */
2589
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
2590
      DBUG_RETURN(-1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2591
#else
2592 2593
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2594 2595
      DBUG_RETURN(-1);
#endif
2596
    }
2597

2598 2599 2600 2601 2602
    /* 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
    */
2603 2604
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2605 2606 2607 2608 2609
                           create_info->key_block_size);

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

2610
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
2611
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2612 2613
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2614
      uint length;
2615 2616
      key_part_spec *dup_column;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2617 2618 2619
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
2620
	     my_strcasecmp(system_charset_info,
2621 2622
			   column->field_name,
			   sql_field->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2623 2624 2625
	field++;
      if (!sql_field)
      {
2626
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2627 2628
	DBUG_RETURN(-1);
      }
2629
      while ((dup_column= cols2++) != column)
2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640
      {
        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);
	  DBUG_RETURN(-1);
	}
      }
      cols2.rewind();
2641
      if (key->type == Key::FULLTEXT)
2642
      {
2643 2644
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
2645 2646
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
2647
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
2648 2649
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
2650
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661
	    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));
2662
      }
2663
      else
2664
      {
2665 2666
	column->length*= sql_field->charset->mbmaxlen;

2667 2668
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
2669
	{
2670
	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
2671
	  {
2672
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
2673 2674
	    DBUG_RETURN(-1);
	  }
2675 2676 2677
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
            column->length= 21;
2678 2679
	  if (!column->length)
	  {
2680
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
2681 2682 2683
	    DBUG_RETURN(-1);
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
2684
#ifdef HAVE_SPATIAL
2685
	if (key->type == Key::SPATIAL)
2686
	{
2687
	  if (!column->length)
2688 2689
	  {
	    /*
2690 2691
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
2692
	    */
2693
	    column->length= 4*sizeof(double);
2694 2695
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
2696
#endif
2697 2698 2699 2700 2701 2702 2703
	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
2704
            null_fields--;
2705 2706
	  }
	  else
2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720
          {
            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);
              DBUG_RETURN(-1);
            }
            if (key->type == Key::SPATIAL)
            {
              my_message(ER_SPATIAL_CANT_HAVE_NULL,
                         ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
              DBUG_RETURN(-1);
            }
          }
2721 2722 2723
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
2724
	  if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
2725 2726
	    auto_increment--;			// Field is used
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2727
      }
2728

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2729 2730 2731
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
2732 2733
      length= sql_field->key_length;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2734 2735 2736 2737
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
monty@mysql.com's avatar
monty@mysql.com committed
2738
	  if ((length=column->length) > max_key_length ||
2739
	      length > file->max_key_part_length())
2740
	  {
monty@mysql.com's avatar
monty@mysql.com committed
2741
	    length=min(max_key_length, file->max_key_part_length());
2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756
	    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);
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
	      DBUG_RETURN(-1);
	    }
	  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2757
	}
2758
	else if (!f_is_geom(sql_field->pack_flag) &&
2759 2760
		  (column->length > length ||
		   ((f_is_packed(sql_field->pack_flag) ||
2761
		     ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
2762 2763 2764
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2765
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
2766 2767
	  DBUG_RETURN(-1);
	}
2768
	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
2769
	  length=column->length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2770 2771 2772
      }
      else if (length == 0)
      {
2773
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2774 2775
	  DBUG_RETURN(-1);
      }
2776
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
2777
      {
2778 2779 2780
        length= file->max_key_part_length();
        /* Align key length to multibyte char boundary */
        length-= length % sql_field->charset->mbmaxlen;
2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794
	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);
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
	  DBUG_RETURN(-1);
	}
2795 2796
      }
      key_part_info->length=(uint16) length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2797
      /* Use packed keys for long strings on the first column */
2798
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2799
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
2800 2801
	   (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
2802 2803
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
2804 2805 2806
	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
2807 2808 2809 2810 2811 2812 2813 2814 2815 2816
	else
	  key_info->flags|= HA_PACK_KEY;
      }
      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)
2817 2818 2819
	{
	  if (primary_key)
	  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2820 2821
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
2822 2823 2824 2825 2826
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
2827
	else if (!(key_name = key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2828
	  key_name=make_unique_key_name(sql_field->field_name,
2829 2830
					*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
2831
	{
2832
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2833 2834 2835 2836 2837
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
      }
    }
2838 2839
    if (!key_info->name || check_column_name(key_info->name))
    {
2840
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
2841 2842
      DBUG_RETURN(-1);
    }
2843 2844
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2845
    key_info->key_length=(uint16) key_length;
2846
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2847
    {
2848
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2849 2850
      DBUG_RETURN(-1);
    }
2851
    key_info++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2852
  }
2853
  if (!unique_key && !primary_key &&
2854
      (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
2855
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2856
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
2857 2858
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2859 2860
  if (auto_increment > 0)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2861
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2862 2863
    DBUG_RETURN(-1);
  }
2864
  /* Sort keys in optimized order */
2865
  qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
2866
	(qsort_cmp) sort_keys);
monty@mysql.com's avatar
monty@mysql.com committed
2867
  create_info->null_bits= null_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2868

2869 2870 2871
  DBUG_RETURN(0);
}

2872

2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892
/*
  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)
{
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
    char path[FN_REFLEN];
2893 2894
    /* Abuse build_table_filename() to build the path to the db.opt file */
    build_table_filename(path, sizeof(path), db, "", MY_DB_OPT_FILE);
2895 2896 2897 2898 2899 2900
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923
/*
  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
*/

static bool prepare_blob_field(THD *thd, create_field *sql_field)
{
  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];

2924 2925
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
2926 2927 2928 2929 2930 2931 2932 2933
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
    sql_field->sql_type= FIELD_TYPE_BLOB;
    sql_field->flags|= BLOB_FLAG;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
2934
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953
            (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)
  {
    if (sql_field->sql_type == FIELD_TYPE_BLOB)
    {
      /* 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);
}


2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997
/*
  Preparation of create_field for SP function return values.
  Based on code used in the inner loop of mysql_prepare_table() above

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

void sp_prepare_create_field(THD *thd, create_field *sql_field)
{
  if (sql_field->sql_type == FIELD_TYPE_SET ||
      sql_field->sql_type == FIELD_TYPE_ENUM)
  {
    uint32 field_length, dummy;
    if (sql_field->sql_type == FIELD_TYPE_SET)
    {
      calculate_interval_lengths(sql_field->charset,
                                 sql_field->interval, &dummy, 
                                 &field_length);
      sql_field->length= field_length + 
                         (sql_field->interval->count - 1);
    }
    else /* FIELD_TYPE_ENUM */
    {
      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);
  }

  if (sql_field->sql_type == FIELD_TYPE_BIT)
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
2998 2999 3000 3001
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
3002 3003


3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028
/*
  Copy HA_CREATE_INFO struct
  SYNOPSIS
    copy_create_info()
    lex_create_info         The create_info struct setup by parser
  RETURN VALUES
    > 0                     A pointer to a copy of the lex_create_info
    0                       Memory allocation error
  DESCRIPTION
  Allocate memory for copy of HA_CREATE_INFO structure from parser
  to ensure we can reuse the parser struct in stored procedures
  and prepared statements.
*/

static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
{
  HA_CREATE_INFO *create_info;
  if (!(create_info= (HA_CREATE_INFO*)sql_alloc(sizeof(HA_CREATE_INFO))))
    mem_alloc_error(sizeof(HA_CREATE_INFO));
  else
    memcpy((void*)create_info, (void*)lex_create_info, sizeof(HA_CREATE_INFO));
  return create_info;
}


3029 3030 3031 3032
/*
  Create a table

  SYNOPSIS
3033
    mysql_create_table_internal()
3034 3035 3036 3037 3038 3039
    thd			Thread object
    db			Database
    table_name		Table name
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create
3040
    internal_tmp_table  Set to 1 if this is an internal temporary table
3041
			(From ALTER TABLE)
3042 3043

  DESCRIPTION
3044
    If one creates a temporary table, this is automatically opened
3045 3046 3047 3048 3049 3050 3051

    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
3052 3053
    FALSE OK
    TRUE  error
3054 3055
*/

3056 3057
bool mysql_create_table_internal(THD *thd,
                                const char *db, const char *table_name,
3058
                                HA_CREATE_INFO *lex_create_info,
3059 3060 3061
                                List<create_field> &fields,
                                List<Key> &keys,bool internal_tmp_table,
                                uint select_field_count)
3062
{
3063
  char		path[FN_REFLEN];
3064
  uint          path_length;
3065 3066 3067
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
3068
  HA_CREATE_INFO *create_info;
3069
  handler	*file;
3070
  bool		error= TRUE;
3071
  DBUG_ENTER("mysql_create_table_internal");
3072

3073 3074 3075 3076
  if (!(create_info= copy_create_info(lex_create_info)))
  {
    DBUG_RETURN(TRUE);
  }
3077 3078 3079
  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3080 3081
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
3082
    DBUG_RETURN(TRUE);
3083
  }
3084
  if (check_engine(thd, table_name, create_info))
3085
    DBUG_RETURN(TRUE);
3086
  db_options= create_info->table_options;
3087 3088 3089
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3090 3091
  if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                              create_info->db_type)))
3092
  {
3093
    mem_alloc_error(sizeof(handler));
3094 3095
    DBUG_RETURN(TRUE);
  }
3096
#ifdef WITH_PARTITION_STORAGE_ENGINE
3097 3098
  partition_info *part_info= thd->work_part_info;

3099 3100 3101 3102 3103 3104 3105 3106
  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.
    */
3107
    thd->work_part_info= part_info= new partition_info();
3108 3109 3110 3111 3112 3113
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
3114
    part_info->default_engine_type= create_info->db_type;
3115
    part_info->is_auto_partitioned= TRUE;
3116
  }
3117 3118 3119
  if (part_info)
  {
    /*
3120 3121 3122 3123 3124 3125 3126 3127 3128
      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.
3129
    */
3130 3131
    List_iterator<Key> key_iterator(keys);
    Key *key;
3132
    handlerton *part_engine_type= create_info->db_type;
3133 3134
    char *part_syntax_buf;
    uint syntax_len;
3135
    handlerton *engine_type;
3136 3137 3138 3139 3140
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
3141 3142
    while ((key= key_iterator++))
    {
3143 3144
      if (key->type == Key::FOREIGN_KEY &&
          !part_info->is_auto_partitioned)
3145 3146 3147 3148 3149
      {
        my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
        goto err;
      }
    }
3150 3151
    if ((part_engine_type == &partition_hton) &&
        part_info->default_engine_type)
3152 3153 3154 3155 3156 3157
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
3158
      ;
3159
    }
3160 3161
    else
    {
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173
      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);
        }
      }
3174
    }
3175 3176
    DBUG_PRINT("info", ("db_type = %d",
                         ha_legacy_type(part_info->default_engine_type)));
3177
    if (part_info->check_partition_info(thd, &engine_type, file,
3178
                                        create_info->max_rows))
3179
      goto err;
3180
    part_info->default_engine_type= engine_type;
3181

3182 3183 3184 3185 3186 3187
    /*
      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,
3188
                                                     TRUE, TRUE)))
3189
      goto err;
3190 3191
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
3192 3193
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3194
        create_info->db_type == &partition_hton)
3195 3196 3197 3198 3199
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
3200 3201
      DBUG_PRINT("info", ("db_type: %d",
                          ha_legacy_type(create_info->db_type)));
3202
      delete file;
3203
      create_info->db_type= &partition_hton;
3204 3205 3206 3207
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3208 3209 3210 3211 3212 3213 3214 3215
      /*
        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 &&
3216
          (int)part_info->no_parts != file->get_default_no_partitions(0ULL))
3217
      {
3218
        uint i;
3219
        List_iterator<partition_element> part_it(part_info->partitions);
3220 3221 3222 3223
        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;
3224 3225 3226 3227
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3228 3229
               (int)part_info->no_subparts !=
                 file->get_default_no_partitions(0ULL))
3230
      {
3231
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3232 3233 3234 3235 3236
        part_info->no_subparts= file->get_default_no_partitions(0ULL);
      }
    }
    else if (create_info->db_type != engine_type)
    {
3237 3238 3239 3240 3241 3242
      /*
        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.
      */
3243
      delete file;
3244 3245
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                                  engine_type)))
3246 3247 3248 3249
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3250 3251 3252
    }
  }
#endif
3253

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

3256 3257 3258
  if (mysql_prepare_table(thd, create_info, &fields,
			  &keys, internal_tmp_table, &db_options, file,
			  &key_info_buffer, &key_count,
3259
			  select_field_count))
3260
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3261 3262 3263 3264

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3265
    path_length= build_tmptable_filename(thd, path, sizeof(path));
3266
    if (lower_case_table_names)
3267
      my_casedn_str(files_charset_info, path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3268 3269 3270
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
3271
    path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext);
3272

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3273
  /* Check if table already exists */
3274 3275
  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
3276
  {
3277
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3278 3279
    {
      create_info->table_existed= 1;		// Mark that table existed
3280 3281 3282
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
3283 3284
      error= 0;
      goto err;
3285
    }
monty@mysql.com's avatar
monty@mysql.com committed
3286
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3287
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3288
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
3289
  if (wait_if_global_read_lock(thd, 0, 1))
3290
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3291
  VOID(pthread_mutex_lock(&LOCK_open));
3292
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3293 3294 3295 3296
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3297 3298
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3299
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3300
    }
3301
    DBUG_ASSERT(get_cached_table_share(db, alias) == 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3302 3303
  }

3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316
  /*
    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;
3317
    if (ha_table_exists_in_engine(thd, db, table_name))
3318
    {
3319
      DBUG_PRINT("info", ("Table with same name already existed in handler"));
3320 3321

      if (create_if_not_exists)
3322 3323
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3324
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3325 3326 3327 3328
    }
  }

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3331
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
3332
    create_info->data_file_name= create_info->index_file_name= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3333
  create_info->table_options=db_options;
3334

3335
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3336 3337
  if (rea_create_table(thd, path, db, table_name, create_info, fields,
                       key_count, key_info_buffer, file))
3338
    goto unlock_and_end;
3339

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3340 3341 3342 3343 3344 3345
  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);
3346
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3347
    }
3348
    thd->tmp_table_used= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3349
  }
monty@mysql.com's avatar
monty@mysql.com committed
3350

3351 3352 3353 3354 3355
  /*
    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.
3356
    Otherwise, the statement shall be binlogged.
3357 3358
   */
  if (!internal_tmp_table &&
3359 3360
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
3361
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3362
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
3363
  error= FALSE;
3364
unlock_and_end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3365
  VOID(pthread_mutex_unlock(&LOCK_open));
3366
  start_waiting_global_read_lock(thd);
3367 3368

err:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3369
  thd->proc_info="After create";
3370
  delete file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3371
  DBUG_RETURN(error);
3372 3373

warn:
3374
  error= FALSE;
3375 3376 3377 3378
  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
3379
  goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3380 3381
}

3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424

/*
  Database locking aware wrapper for mysql_create_table_internal(),
*/

bool mysql_create_table(THD *thd, const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
                        List<create_field> &fields,
                        List<Key> &keys,bool internal_tmp_table,
                        uint select_field_count)
{
  bool result;
  DBUG_ENTER("mysql_create_table");

  /* Wait for any database locks */
  pthread_mutex_lock(&LOCK_lock_db);
  while (!thd->killed &&
         hash_search(&lock_db_cache,(byte*) db, strlen(db)))
  {
    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);

  result= mysql_create_table_internal(thd, db, table_name, create_info,
                                      fields, keys, internal_tmp_table,
                                      select_field_count);

  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
3425 3426 3427 3428 3429 3430 3431 3432
/*
** 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++)
3433
    if (!my_strcasecmp(system_charset_info,name,key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3434 3435 3436 3437 3438 3439 3440 3441 3442 3443
      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;

3444 3445
  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
3446
    return (char*) field_name;			// Use fieldname
3447 3448 3449 3450 3451 3452
  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
  */
3453
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3454
  {
3455 3456
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3457 3458 3459
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3460
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3461 3462
}

3463

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3464 3465 3466 3467
/****************************************************************************
** Alter a table definition
****************************************************************************/

3468
bool
3469
mysql_rename_table(handlerton *base,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3470
		   const char *old_db,
3471
		   const char *old_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3472
		   const char *new_db,
3473
		   const char *new_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3474
{
3475
  THD *thd= current_thd;
3476 3477 3478
  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];
3479
  handler *file;
3480
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3481
  DBUG_ENTER("mysql_rename_table");
3482

3483
  file= (base == NULL ? 0 :
3484 3485
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

3486 3487
  build_table_filename(from, sizeof(from), old_db, old_name, "");
  build_table_filename(to, sizeof(to), new_db, new_name, "");
3488 3489 3490 3491 3492 3493

  /*
    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.
   */
3494
  if (lower_case_table_names == 2 && file &&
3495
      !(file->ha_table_flags() & HA_FILE_BASED))
3496
  {
3497 3498
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
3499
    build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "");
3500
    from_base= lc_from;
3501

3502 3503
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
3504
    build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "");
3505
    to_base= lc_to;
3506 3507
  }

3508
  if (!file || !(error=file->rename_table(from_base, to_base)))
3509 3510 3511
  {
    if (rename_file_ext(from,to,reg_ext))
    {
3512
      error=my_errno;
3513
      /* Restore old file name */
3514
      if (file)
3515
        file->rename_table(to_base, from_base);
3516 3517
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3518
  delete file;
3519 3520 3521
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
3522 3523
    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
3524 3525
}

3526

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3527
/*
3528 3529 3530 3531 3532 3533
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
3534
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
3535
			HA_EXTRA_FORCE_REOPEN if table is not be used
3536 3537 3538 3539 3540 3541 3542
  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
3543 3544
*/

3545 3546
static 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
3547
{
3548
  DBUG_ENTER("wait_while_table_is_used");
3549 3550 3551
  DBUG_PRINT("enter", ("table: '%s'  share: 0x%lx  db_stat: %u  version: %u",
                       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
3552

3553
  VOID(table->file->extra(function));
3554
  /* Mark all tables that are in use as 'old' */
3555
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
3556 3557

  /* Wait until all there are no other threads that has this table open */
3558 3559 3560
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
3561 3562
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3563

3564 3565
/*
  Close a cached table
3566

3567
  SYNOPSIS
3568
    close_cached_table()
3569 3570 3571 3572 3573 3574
    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.
3575

3576 3577 3578 3579
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
3580

3581
void close_cached_table(THD *thd, TABLE *table)
3582 3583
{
  DBUG_ENTER("close_cached_table");
3584

3585
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
3586 3587
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
3588
  {
3589 3590
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3591
  }
3592 3593 3594 3595 3596
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
  thd->open_tables=unlink_open_table(thd,thd->open_tables,table);

  /* When lock on LOCK_open is freed other threads can continue */
  pthread_cond_broadcast(&COND_refresh);
monty@mysql.com's avatar
monty@mysql.com committed
3597
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3598 3599
}

3600
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
3601
			     const char* operator_name, const char* errmsg)
3602

3603
{
3604 3605
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3606 3607
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
3608
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3609
  protocol->store(errmsg, system_charset_info);
3610
  thd->clear_error();
3611
  if (protocol->write())
3612 3613 3614 3615
    return -1;
  return 1;
}

3616

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3617
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
3618
			       HA_CHECK_OPT *check_opt)
3619
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3620
  DBUG_ENTER("prepare_for_restore");
3621

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3622 3623 3624 3625 3626 3627
  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"
				  ));
  }
3628
  else
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3629
  {
3630
    char* backup_dir= thd->lex->backup_dir;
3631
    char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
3632 3633
    char* table_name= table->table_name;
    char* db= table->db;
3634

3635 3636 3637
    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
3638
      DBUG_RETURN(-1); // protect buffer overflow
3639

3640
    build_table_filename(dst_path, sizeof(dst_path), db, table_name, reg_ext);
3641

3642
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3643
      DBUG_RETURN(-1);
3644

3645
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3646
    {
3647
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3648
      unlock_table_name(thd, table);
3649
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3650 3651 3652
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
3653
    if (mysql_truncate(thd, table, 1))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3654
    {
3655
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3656
      unlock_table_name(thd, table);
3657
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3658 3659
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
3660
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3661
  }
3662

3663 3664 3665 3666
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
3667 3668
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table))
3669
  {
3670
    unlock_table_name(thd, table);
3671
    pthread_mutex_unlock(&LOCK_open);
3672 3673
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
3674
  }
3675
  pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3676
  DBUG_RETURN(0);
3677
}
3678

3679

3680
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
3681
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3682
{
3683 3684
  int error= 0;
  TABLE tmp_table, *table;
3685 3686 3687 3688
  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
3689 3690 3691 3692
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
3693 3694

  if (!(table= table_list->table))		/* if open_ltable failed */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3695
  {
3696 3697 3698 3699 3700 3701 3702 3703 3704
    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);
3705
      DBUG_RETURN(0);				// Can't open frm file
3706 3707
    }

3708
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
3709 3710 3711 3712 3713
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
3714
    table= &tmp_table;
3715
    pthread_mutex_unlock(&LOCK_open);
3716
  }
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3717

3718 3719 3720 3721 3722 3723 3724 3725 3726
  /*
    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
3727

3728 3729 3730 3731
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
3732
  ext= table->file->bas_ext();
3733 3734
  if (!ext[0] || !ext[1])
    goto end;					// No data file
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3735

3736 3737
  // Name of data file
  strxmov(from, table->s->normalized_path.str, ext[1], NullS);
3738 3739
  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
3740

3741 3742
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
3743

3744 3745 3746 3747 3748 3749 3750
  /* 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);
  }
3751
  if (lock_and_wait_for_table_name(thd,table_list))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3752
  {
3753 3754
    error= -1;
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3755
  }
3756
  if (my_rename(from, tmp, MYF(MY_WME)))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3757
  {
3758
    pthread_mutex_lock(&LOCK_open);
3759
    unlock_table_name(thd, table_list);
3760
    pthread_mutex_unlock(&LOCK_open);
3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781
    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
3782 3783
  }

3784 3785 3786 3787
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
3788 3789
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table_list))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3790
  {
3791
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3792
    pthread_mutex_unlock(&LOCK_open);
3793 3794 3795
    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
3796
  }
3797
  pthread_mutex_unlock(&LOCK_open);
3798 3799 3800

end:
  if (table == &tmp_table)
3801 3802 3803 3804 3805
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
3806
  DBUG_RETURN(error);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3807
}
3808

3809

3810

3811 3812
/*
  RETURN VALUES
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
3813 3814 3815
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
3816
*/
3817 3818 3819 3820 3821
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,
3822
                              bool no_warnings_for_error,
3823 3824 3825
                              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
3826 3827 3828
                              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
3829
{
3830 3831
  TABLE_LIST *table, *save_next_global, *save_next_local;
  SELECT_LEX *select= &thd->lex->select_lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3832
  List<Item> field_list;
3833 3834
  Item *item;
  Protocol *protocol= thd->protocol;
3835
  LEX *lex= thd->lex;
3836
  int result_code;
3837
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3838

3839 3840
  if (end_active_trans(thd))
    DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3841 3842 3843 3844 3845 3846 3847 3848
  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  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;
3849 3850
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
3851
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3852

3853
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3854
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3855 3856
  {
    char table_name[NAME_LEN*2+2];
3857
    char* db = table->db;
3858
    bool fatal_error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3859

serg@serg.mylan's avatar
serg@serg.mylan committed
3860
    strxmov(table_name, db, ".", table->table_name, NullS);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3861
    thd->open_options|= extra_open_options;
3862 3863
    table->lock_type= lock_type;
    /* open only one table from local list of command */
3864
    save_next_global= table->next_global;
3865
    table->next_global= 0;
3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876
    save_next_local= table->next_local;
    table->next_local= 0;
    select->table_list.first= (byte*)table;
    /*
      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;
3877
    lex->query_tables_own_last= 0;
3878
    thd->no_warnings_for_error= no_warnings_for_error;
3879
    if (view_operator_func == NULL)
3880
      table->required_type=FRMTYPE_TABLE;
3881
    open_and_lock_tables(thd, table);
3882
    thd->no_warnings_for_error= 0;
3883 3884
    table->next_global= save_next_global;
    table->next_local= save_next_local;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3885
    thd->open_options&= ~extra_open_options;
3886

3887
    if (prepare_func)
3888
    {
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3889
      switch ((*prepare_func)(thd, table, check_opt)) {
3890
      case  1:           // error, message written to net
3891
        ha_autocommit_or_rollback(thd, 1);
3892 3893 3894 3895 3896 3897
        close_thread_tables(thd);
        continue;
      case -1:           // error, message could be written to net
        goto err;
      default:           // should be 0 otherwise
        ;
3898
      }
3899
    }
3900

3901
    /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3902 3903 3904 3905 3906 3907
      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)
3908
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3909 3910
    if (!table->table)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3911
      char buf[ERRMSGSIZE+ERRMSGSIZE+2];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3912
      const char *err_msg;
3913
      protocol->prepare_for_resend();
3914 3915
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
3916
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3917 3918
      if (!(err_msg=thd->net.last_error))
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
3919 3920 3921 3922
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3923
        strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
3924 3925
        err_msg= (const char *)buf;
      }
3926
      protocol->store(err_msg, system_charset_info);
3927
      lex->cleanup_after_one_table_open();
3928
      thd->clear_error();
3929 3930 3931 3932
      /*
        View opening can be interrupted in the middle of process so some
        tables can be left opening
      */
3933
      ha_autocommit_or_rollback(thd, 1);
3934
      close_thread_tables(thd);
3935
      lex->reset_query_tables_list(FALSE);
3936
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3937 3938 3939
	goto err;
      continue;
    }
3940 3941 3942 3943 3944 3945 3946

    if (table->view)
    {
      result_code= (*view_operator_func)(thd, table);
      goto send_result;
    }

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
3947
    table->table->pos_in_table_list= table;
3948
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3949
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3950
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
3951
      uint length;
3952
      protocol->prepare_for_resend();
3953 3954
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
3955
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3956 3957 3958
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
3959
      ha_autocommit_or_rollback(thd, 0);
3960
      close_thread_tables(thd);
3961
      lex->reset_query_tables_list(FALSE);
3962
      table->table=0;				// For query cache
3963
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3964 3965 3966 3967
	goto err;
      continue;
    }

3968
    /* Close all instances of the table to allow repair to rename files */
3969 3970
    if (lock_type == TL_WRITE && table->table->s->version &&
        !table->table->s->log_table)
3971 3972
    {
      pthread_mutex_lock(&LOCK_open);
3973 3974
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
3975
      mysql_lock_abort(thd,table->table, TRUE);
3976 3977
      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
3978 3979
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
3980
      thd->exit_cond(old_message);
3981 3982
      if (thd->killed)
	goto err;
3983 3984 3985
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
3986 3987
    }

3988
    if (table->table->s->crashed && operator_func == &handler::ha_check)
3989 3990 3991 3992
    {
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
3993 3994 3995
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
3996 3997 3998 3999
      if (protocol->write())
        goto err;
    }

4000 4001 4002 4003 4004 4005
    if (operator_func == &handler::ha_repair)
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
4006
        ha_autocommit_or_rollback(thd, 1);
4007 4008 4009 4010 4011 4012 4013 4014 4015
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
        result_code= mysql_recreate_table(thd, table, 0);
        reenable_binlog(thd);
        goto send_result;
      }

    }

4016 4017 4018 4019
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

4020
    lex->cleanup_after_one_table_open();
4021
    thd->clear_error();  // these errors shouldn't get client
4022
    protocol->prepare_for_resend();
4023 4024
    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
4025

4026 4027 4028
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4029 4030
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4031
      {
4032 4033
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
4034
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4035
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4036
	protocol->store(buf, length, system_charset_info);
4037
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4038 4039
      break;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4040 4041
    case HA_ADMIN_NOT_BASE_TABLE:
      {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4042 4043
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4044
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4045
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4046
        protocol->store(buf, length, system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4047 4048 4049
      }
      break;

4050
    case HA_ADMIN_OK:
4051 4052
      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
4053 4054
      break;

4055
    case HA_ADMIN_FAILED:
4056 4057 4058
      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
4059 4060
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4061
    case HA_ADMIN_REJECT:
4062 4063 4064
      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
4065
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4066 4067
      break;

4068
    case HA_ADMIN_ALREADY_DONE:
4069 4070 4071
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4072 4073
      break;

4074
    case HA_ADMIN_CORRUPT:
4075 4076
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4077
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4078 4079
      break;

4080
    case HA_ADMIN_INVALID:
4081 4082 4083
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
4084 4085
      break;

4086 4087 4088 4089 4090 4091 4092
    case HA_ADMIN_TRY_ALTER:
    {
      /*
        This is currently used only by InnoDB. ha_innobase::optimize() answers
        "try with alter", so here we close the table, do an ALTER TABLE,
        reopen the table and do ha_innobase::analyze() on it.
      */
4093
      ha_autocommit_or_rollback(thd, 0);
4094
      close_thread_tables(thd);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4095 4096 4097
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4098
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4099
      result_code= mysql_recreate_table(thd, table, 0);
4100
      reenable_binlog(thd);
4101
      ha_autocommit_or_rollback(thd, 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4102
      close_thread_tables(thd);
4103 4104 4105 4106 4107 4108
      if (!result_code) // recreation went ok
      {
        if ((table->table= open_ltable(thd, table, lock_type)) &&
            ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
          result_code= 0; // analyze went ok
      }
4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120
      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. */
4121
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4122 4123 4124 4125 4126 4127 4128 4129 4130
            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);
          }
        }
      }
4131
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4132 4133
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4134 4135
      goto send_result_message;
    }
4136 4137
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4138
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4139 4140
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4141 4142
      break;
    }
4143

4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156
    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;
    }

4157
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4158 4159 4160 4161 4162
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
4163
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4164 4165 4166 4167
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4168
    }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4169
    if (table->table)
4170
    {
4171
      /* in the below check we do not refresh the log tables */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4172 4173
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4174
      else if (open_for_modify && !table->table->s->log_table)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4175
      {
holyfoot@deer.(none)'s avatar
holyfoot@deer.(none) committed
4176
        if (table->table->s->tmp_table)
holyfoot@mysql.com's avatar
holyfoot@mysql.com committed
4177 4178 4179 4180
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
4181 4182
          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
4183 4184 4185
          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
4186 4187
        query_cache_invalidate3(thd, table->table, 0);
      }
4188
    }
4189
    ha_autocommit_or_rollback(thd, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4190
    close_thread_tables(thd);
4191
    table->table=0;				// For query cache
4192
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4193 4194 4195
      goto err;
  }

4196
  send_eof(thd);
4197
  DBUG_RETURN(FALSE);
4198

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4199
 err:
4200
  ha_autocommit_or_rollback(thd, 1);
4201
  close_thread_tables(thd);			// Shouldn't be needed
4202 4203
  if (table)
    table->table=0;
4204
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4205 4206
}

4207

4208
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
4209 4210 4211
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4212
				"backup", TL_READ, 0, 0, 0, 0,
4213
				&handler::backup, 0));
4214
}
4215

4216

4217
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
4218 4219 4220
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4221
				"restore", TL_WRITE, 1, 1, 0,
4222
				&prepare_for_restore,
4223
				&handler::restore, 0));
4224
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4225

4226

4227
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4228 4229 4230
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4231 4232 4233
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4234
				&prepare_for_repair,
4235
				&handler::ha_repair, 0));
4236 4237
}

4238

4239
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4240 4241 4242
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4243
				"optimize", TL_WRITE, 1,0,0,0,
4244
				&handler::optimize, 0));
4245 4246 4247
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4248 4249 4250 4251 4252
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4253 4254
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4255 4256

  RETURN VALUES
4257 4258
   FALSE ok
   TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4259 4260
*/

4261
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4262
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4263
{
4264
  HA_CHECK_OPT check_opt;
4265
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4266
  DBUG_ENTER("mysql_assign_to_keycache");
4267 4268 4269 4270 4271 4272 4273

  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);
4274
    DBUG_RETURN(TRUE);
4275 4276 4277 4278
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4279
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4280
				0, 0, &handler::assign_to_keycache, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4281 4282
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4283 4284 4285 4286 4287 4288

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

  SYNOPSIS
    reassign_keycache_tables()
4289 4290 4291
    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
4292

4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305
  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
4306 4307 4308
    0	  ok
*/

4309 4310
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
4311 4312 4313
{
  DBUG_ENTER("reassign_keycache_tables");

4314 4315
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
4316
  src_cache->param_buff_size= 0;		// Free key cache
4317 4318
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4319
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4320 4321 4322
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4323 4324 4325 4326 4327
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
4328 4329
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4330 4331

  RETURN VALUES
4332 4333
    FALSE ok
    TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4334 4335
*/

4336
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4337 4338 4339
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
4340
				"preload_keys", TL_READ, 0, 0, 0, 0,
4341
				&handler::preload_keys, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4342 4343 4344
}


venu@myvenu.com's avatar
venu@myvenu.com committed
4345 4346 4347 4348 4349
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
4350 4351
    thd		Thread object
    table	Table list (one table only)
venu@myvenu.com's avatar
venu@myvenu.com committed
4352 4353 4354 4355
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
4356 4357
    FALSE OK
    TRUE  error
venu@myvenu.com's avatar
venu@myvenu.com committed
4358 4359
*/

4360 4361 4362
bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
                             HA_CREATE_INFO *create_info,
                             Table_ident *table_ident)
venu@myvenu.com's avatar
venu@myvenu.com committed
4363
{
4364
  TABLE *tmp_table;
4365
  char src_path[FN_REFLEN], dst_path[FN_REFLEN], tmp_path[FN_REFLEN];
4366
  uint dst_path_length;
venu@myvenu.com's avatar
venu@myvenu.com committed
4367
  char *db= table->db;
4368
  char *table_name= table->table_name;
4369
  char *src_db;
venu@myvenu.com's avatar
venu@myvenu.com committed
4370
  char *src_table= table_ident->table.str;
4371 4372
  int  err;
  bool res= TRUE;
4373
  enum legacy_db_type not_used;
4374

4375
  TABLE_LIST src_tables_list;
venu@myvenu.com's avatar
venu@myvenu.com committed
4376
  DBUG_ENTER("mysql_create_like_table");
4377
  src_db= table_ident->db.str ? table_ident->db.str : thd->db;
venu@myvenu.com's avatar
venu@myvenu.com committed
4378 4379 4380 4381 4382 4383

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
4384
       check_table_name(src_table,table_ident->table.length)))
venu@myvenu.com's avatar
venu@myvenu.com committed
4385
  {
4386
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
4387
    DBUG_RETURN(TRUE);
venu@myvenu.com's avatar
venu@myvenu.com committed
4388
  }
4389 4390 4391 4392 4393
  if (!src_db || check_db_name(src_db))
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
    DBUG_RETURN(-1);
  }
4394

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4395
  bzero((gptr)&src_tables_list, sizeof(src_tables_list));
4396
  src_tables_list.db= src_db;
acurtis@xiphis.org's avatar
Merge  
acurtis@xiphis.org committed
4397
  src_tables_list.table_name= src_table;
4398

4399 4400
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
4401 4402

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
4403
    strxmov(src_path, tmp_table->s->path.str, reg_ext, NullS);
venu@myvenu.com's avatar
venu@myvenu.com committed
4404 4405
  else
  {
4406 4407
    build_table_filename(src_path, sizeof(src_path),
                         src_db, src_table, reg_ext);
4408
    /* Resolve symlinks (for windows) */
4409
    unpack_filename(src_path, src_path);
4410 4411
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
4412 4413 4414
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
4415
      goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
4416 4417 4418
    }
  }

4419 4420 4421
  /* 
     create like should be not allowed for Views, Triggers, ... 
  */
4422
  if (mysql_frm_type(thd, src_path, &not_used) != FRMTYPE_TABLE)
4423
  {
4424
    my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE");
4425 4426 4427
    goto err;
  }

venu@myvenu.com's avatar
venu@myvenu.com committed
4428 4429 4430
  /*
    Validate the destination table

4431
    skip the destination table name checking as this is already
venu@myvenu.com's avatar
venu@myvenu.com committed
4432 4433 4434 4435 4436 4437
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
4438
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
4439 4440
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
4441 4442 4443 4444
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
4445 4446
    dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
                                          db, table_name, reg_ext);
venu@myvenu.com's avatar
venu@myvenu.com committed
4447 4448 4449 4450
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

4451
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
4452
    Create a new table by copying from source table
4453
  */
4454 4455 4456 4457 4458 4459
  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);
4460
    goto err;
4461
  }
venu@myvenu.com's avatar
venu@myvenu.com committed
4462 4463

  /*
4464 4465
    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
4466 4467
    and temporary tables).
  */
4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480
#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
4481
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
4482
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
4483

venu@myvenu.com's avatar
venu@myvenu.com committed
4484 4485 4486 4487
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
4488 4489
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
4490
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4491 4492 4493 4494
    }
  }
  else if (err)
  {
4495 4496 4497
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4498
  }
4499

4500 4501 4502
  /*
    We have to write the query before we unlock the tables.
  */
4503
  if (thd->current_stmt_binlog_row_based)
4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553
  {
    /*
       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
           ==== ========= ========= ==============================

       The variable 'tmp_table' below is used to see if the source
       table is a temporary table: if it is set, then the source table
       was a temporary table and we can take apropriate actions.
    */
    if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
    {
      if (tmp_table)                            // Case 2
      {
        char buf[2048];
        String query(buf, sizeof(buf), system_charset_info);
        query.length(0);  // Have to zero it since constructor doesn't
        TABLE *table_ptr;
        int error;

        /*
          Let's open and lock the table: it will be closed (and
          unlocked) by close_thread_tables() at the end of the
          statement anyway.
         */
        if (!(table_ptr= open_ltable(thd, table, TL_READ_NO_INSERT)))
          goto err;

        int result= store_create_info(thd, table, &query, create_info);

        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
    */
  }
4554
  else
4555 4556
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

4557
  res= FALSE;
4558
  goto err;
4559

venu@myvenu.com's avatar
venu@myvenu.com committed
4560 4561 4562 4563
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
4564 4565
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
4566
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4567
		 ER_TABLE_EXISTS_ERROR,warn_buff);
4568
    res= FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
4569
  }
4570 4571 4572 4573 4574 4575 4576 4577
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
  pthread_mutex_lock(&LOCK_open);
  unlock_table_name(thd, &src_tables_list);
  pthread_mutex_unlock(&LOCK_open);
  DBUG_RETURN(res);
venu@myvenu.com's avatar
venu@myvenu.com committed
4578 4579 4580
}


4581
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4582
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4583 4584
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4585 4586
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4587
				"analyze", lock_type, 1, 0, 0, 0,
4588
				&handler::analyze, 0));
4589 4590 4591
}


4592
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
4593
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4594 4595
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4596 4597
  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
4598
				"check", lock_type,
4599
				0, HA_OPEN_FOR_REPAIR, 0, 0,
4600
				&handler::ha_check, &view_checksum));
4601 4602
}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4604
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
4605 4606 4607 4608
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
4609 4610 4611 4612 4613 4614
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
4615 4616 4617 4618
  /*
    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
4619 4620 4621

  thd->proc_info="discard_or_import_tablespace";

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

monty@mysql.com's avatar
monty@mysql.com committed
4624 4625 4626 4627 4628
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4629 4630 4631 4632 4633
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
4634

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4635 4636 4637 4638 4639 4640 4641
  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
4642 4643 4644 4645
  /*
    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
4646 4647 4648 4649 4650 4651 4652 4653
  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;
4654
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
4655

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4656
err:
4657
  ha_autocommit_or_rollback(thd, error);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4658
  close_thread_tables(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4659
  thd->tablespace_op=FALSE;
4660
  
monty@mysql.com's avatar
monty@mysql.com committed
4661 4662
  if (error == 0)
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4663
    send_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
4664
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4665
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4666

4667 4668
  table->file->print_error(error, MYF(0));
    
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4669
  DBUG_RETURN(-1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4670
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4671

4672

4673 4674
/*
  SYNOPSIS
4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686
    compare_tables()
      table                     The original table.
      create_list               The fields for the new table.
      key_info_buffer           An array of KEY structs for the new indexes.
      key_count                 The number of elements in the array.
      create_info               Create options for the new table.
      alter_info                Alter options.
      order_num                 Number of order list elements.
      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.
4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697

  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.

4698 4699 4700 4701 4702
    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.

4703
  RETURN VALUES
4704 4705 4706
    0                           No copy needed
    ALTER_TABLE_DATA_CHANGED    Data changes, copy needed
    ALTER_TABLE_INDEX_CHANGED   Index changes, copy might be needed
4707 4708
*/

4709 4710 4711 4712 4713
static uint compare_tables(TABLE *table, List<create_field> *create_list,
                           KEY *key_info_buffer, uint key_count,
                           HA_CREATE_INFO *create_info,
                           ALTER_INFO *alter_info, uint order_num,
                           uint *index_drop_buffer, uint *index_drop_count,
4714 4715
                           uint *index_add_buffer, uint *index_add_count,
                           bool varchar)
4716 4717 4718 4719 4720
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
  List_iterator_fast<create_field> new_field_it(*create_list);
  create_field *new_field;
4721 4722
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
4723
  DBUG_ENTER("compare_tables");
4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741

  /*
    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
4742 4743 4744 4745 4746 4747 4748 4749

    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.
4750 4751 4752 4753 4754 4755 4756
  */
  if (table->s->fields != create_list->elements ||
      table->s->db_type != create_info->db_type ||
      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 ||
4757
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
4758
      order_num ||
svoj@may.pils.ru's avatar
svoj@may.pils.ru committed
4759
      !table->s->mysql_version ||
4760
      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
4761
    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772

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

4774 4775 4776
    /* Check that NULL behavior is same for old and new fields */
    if ((new_field->flags & NOT_NULL_FLAG) !=
	(uint) (field->flags & NOT_NULL_FLAG))
4777
      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
4778 4779 4780 4781 4782 4783 4784 4785

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

4786
    /* Check if field was renamed */
4787
    field->flags&= ~FIELD_IS_RENAMED;
4788 4789 4790 4791
    if (my_strcasecmp(system_charset_info,
		      field->field_name,
		      new_field->field_name))
      field->flags|= FIELD_IS_RENAMED;      
4792

4793 4794
    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
    if (!(tmp= field->is_equal(new_field)))
4795
      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
4796
    // Clear indexed marker
4797
    field->flags&= ~FIELD_IN_ADD_INDEX;
4798 4799 4800 4801 4802 4803 4804
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
4805 4806 4807 4808
  KEY *table_key;
  KEY *table_key_end= table->key_info + table->s->keys;
  KEY *new_key;
  KEY *new_key_end= key_info_buffer + key_count;
4809

4810 4811 4812 4813 4814 4815 4816 4817
  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++)
4818
  {
4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842
    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. */
    for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
    {
      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. */
      index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
      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;
4843 4844 4845 4846 4847

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
4848 4849 4850
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
4851 4852 4853
    {
      /*
	Key definition has changed if we are using a different field or
4854 4855
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
4856
      */
4857 4858 4859
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
4860
    }
4861 4862 4863 4864 4865 4866
    continue;

  index_changed:
    /* Key modified. Add the offset of the key to both buffers. */
    index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
    index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
4867 4868 4869 4870 4871 4872
    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];
4873
      field->flags|= FIELD_IN_ADD_INDEX;
4874
    }
4875
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
4876
  }
4877
  /*end of for (; table_key < table_key_end;) */
4878

4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893
  /*
    Step through all keys of the new table and find matching old keys.
  */
  for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
  {
    /* 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. */
      index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
4894 4895 4896 4897 4898 4899
      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];
4900
        field->flags|= FIELD_IN_ADD_INDEX;
4901
      }
marty@linux.site's avatar
marty@linux.site committed
4902
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
4903 4904
    }
  }
4905 4906 4907 4908 4909

  /* Check if changes are compatible with current handler without a copy */
  if (table->file->check_if_incompatible_data(create_info, changes))
    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);

4910 4911 4912 4913
  if (*index_drop_count || *index_add_count)
    DBUG_RETURN(ALTER_TABLE_INDEX_CHANGED);

  DBUG_RETURN(0); // Tables are compatible
4914 4915 4916
}


4917 4918 4919
/*
  Alter table
*/
4920

4921
bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
4922
                       HA_CREATE_INFO *lex_create_info,
4923 4924 4925
                       TABLE_LIST *table_list,
                       List<create_field> &fields, List<Key> &keys,
                       uint order_num, ORDER *order,
4926
                       enum enum_duplicates handle_duplicates, bool ignore,
4927
                       ALTER_INFO *alter_info, bool do_send_ok)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4928
{
4929
  TABLE *table,*new_table=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4930
  int error;
4931 4932
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
4933
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
4934
  char path[FN_REFLEN];
4935
  char reg_path[FN_REFLEN+1];
4936 4937
  ha_rows copied,deleted;
  ulonglong next_insert_id;
4938
  uint db_create_options, used_fields;
4939
  handlerton *old_db_type, *new_db_type;
4940
  HA_CREATE_INFO *create_info;
4941
  uint need_copy_table= 0;
4942
  bool no_table_reopen= FALSE, varchar= FALSE;
4943
#ifdef WITH_PARTITION_STORAGE_ENGINE
4944
  uint fast_alter_partition= 0;
4945 4946
  bool partition_changed= FALSE;
#endif
4947 4948 4949 4950 4951 4952 4953 4954 4955 4956
  List<create_field> prepared_create_list;
  List<Key>          prepared_key_list;
  bool need_lock_for_indexes= TRUE;
  uint db_options= 0;
  uint key_count;
  KEY  *key_info_buffer;
  uint index_drop_count;
  uint *index_drop_buffer;
  uint index_add_count;
  uint *index_add_buffer;
4957
  bool committed= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4958 4959
  DBUG_ENTER("mysql_alter_table");

4960 4961 4962 4963 4964
  LINT_INIT(index_add_count);
  LINT_INIT(index_drop_count);
  LINT_INIT(index_add_buffer);
  LINT_INIT(index_drop_buffer);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4965
  thd->proc_info="init";
4966 4967 4968 4969
  if (!(create_info= copy_create_info(lex_create_info)))
  {
    DBUG_RETURN(TRUE);
  }
4970
  table_name=table_list->table_name;
4971
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4972
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
4973
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
4974
    new_db= db;
4975
  build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext);
4976
  build_table_filename(path, sizeof(path), db, table_name, "");
4977

4978
  used_fields=create_info->used_fields;
4979

4980
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4981

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4982
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
4983
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4984
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
4985
						   alter_info->tablespace_op));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4986
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
4987
    DBUG_RETURN(TRUE);
4988
  table->use_all_columns();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4989 4990 4991 4992 4993

  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
4994
    strmov(new_alias= new_alias_buff, new_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4995
    if (lower_case_table_names)
4996 4997 4998
    {
      if (lower_case_table_names != 2)
      {
4999
	my_casedn_str(files_charset_info, new_name_buff);
5000 5001
	new_alias= new_name;			// Create lower case table name
      }
5002
      my_casedn_str(files_charset_info, new_name);
5003
    }
5004
    if (new_db == db &&
monty@mysql.com's avatar
monty@mysql.com committed
5005
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
5006 5007
    {
      /*
5008 5009
	Source and destination table names are equal: make later check
	easier.
5010
      */
5011
      new_alias= new_name= table_name;
5012
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5013 5014
    else
    {
5015
      if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5016 5017 5018
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
5019
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
5020
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5021 5022 5023 5024
	}
      }
      else
      {
5025
	char dir_buff[FN_REFLEN];
5026 5027
	strxnmov(dir_buff, sizeof(dir_buff)-1,
                 mysql_real_data_home, new_db, NullS);
5028
	if (!access(fn_format(new_name_buff,new_name_buff,dir_buff,reg_ext,0),
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5029 5030 5031
		    F_OK))
	{
	  /* Table will be closed in do_command() */
5032
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
5033
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5034 5035 5036 5037 5038
	}
      }
    }
  }
  else
5039 5040 5041 5042
  {
    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
5043

5044
  old_db_type= table->s->db_type;
5045
  if (!create_info->db_type)
5046
  {
5047
#ifdef WITH_PARTITION_STORAGE_ENGINE
5048 5049
    if (table->part_info &&
        create_info->used_fields & HA_CREATE_USED_ENGINE)
5050 5051 5052 5053
    {
      /*
        This case happens when the user specified
        ENGINE = x where x is a non-existing storage engine
5054 5055 5056
        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.
5057
      */
5058
      create_info->db_type= table->part_info->default_engine_type;
5059
    }
5060
    else
5061
#endif
5062
      create_info->db_type= old_db_type;
5063
  }
5064

5065
#ifdef WITH_PARTITION_STORAGE_ENGINE
5066 5067
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
5068
  {
5069
    DBUG_RETURN(TRUE);
5070 5071
  }
#endif
5072
  if (check_engine(thd, new_name, create_info))
5073 5074
    DBUG_RETURN(TRUE);
  new_db_type= create_info->db_type;
5075
  if (create_info->row_type == ROW_TYPE_NOT_USED)
5076
    create_info->row_type= table->s->row_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5077

5078 5079 5080
  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
5081
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
5082 5083 5084
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED) ||
      (old_db_type != new_db_type &&
       ha_check_storage_engine_flag(new_db_type, HTON_ALTER_CANNOT_CREATE)))
5085 5086 5087 5088 5089 5090
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
    DBUG_RETURN(TRUE);
  }
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5091
  thd->proc_info="setup";
5092
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
5093
      !table->s->tmp_table) // no need to touch frm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5094 5095
  {
    error=0;
5096
    if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5097
    {
5098 5099 5100 5101 5102 5103
      thd->proc_info="rename";
      VOID(pthread_mutex_lock(&LOCK_open));
      /* Then do a 'simple' rename of the table */
      error=0;
      if (!access(new_name_buff,F_OK))
      {
5104
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
5105
	error= -1;
5106 5107 5108
      }
      else
      {
5109
	*fn_ext(new_name)=0;
5110
        table->s->version= 0;                   // Force removal of table def
5111 5112
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
5113
	  error= -1;
5114 5115 5116 5117 5118 5119 5120
        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));
          error= -1;
        }
5121 5122
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5123
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5124

5125
    if (!error)
5126
    {
5127
      switch (alter_info->keys_onoff) {
5128
      case LEAVE_AS_IS:
5129
        break;
5130
      case ENABLE:
5131 5132 5133 5134 5135 5136
        VOID(pthread_mutex_lock(&LOCK_open));
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
        VOID(pthread_mutex_unlock(&LOCK_open));
        error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
        /* COND_refresh will be signaled in close_thread_tables() */
        break;
5137
      case DISABLE:
5138 5139 5140 5141 5142 5143
        VOID(pthread_mutex_lock(&LOCK_open));
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
        VOID(pthread_mutex_unlock(&LOCK_open));
        error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
        /* COND_refresh will be signaled in close_thread_tables() */
        break;
5144
      }
5145
    }
5146

5147
    if (error == HA_ERR_WRONG_COMMAND)
serg@serg.mylan's avatar
serg@serg.mylan committed
5148 5149
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5150
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
5151
			  table->alias);
serg@serg.mylan's avatar
serg@serg.mylan committed
5152 5153
      error=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5154 5155
    if (!error)
    {
5156
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
5157 5158
      if (do_send_ok)
        send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5159
    }
5160
    else if (error > 0)
5161 5162
    {
      table->file->print_error(error, MYF(0));
5163
      error= -1;
5164
    }
5165 5166
    table_list->table=0;				// For query cache
    query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5167 5168 5169 5170
    DBUG_RETURN(error);
  }

  /* Full alter table */
5171

5172
  /* Let new create options override the old ones */
5173
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
5174
    create_info->min_rows= table->s->min_rows;
5175
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
5176
    create_info->max_rows= table->s->max_rows;
5177
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
5178
    create_info->avg_row_length= table->s->avg_row_length;
5179
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
5180
    create_info->default_table_charset= table->s->table_charset;
5181 5182
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5183

5184
  restore_record(table, s->default_values);     // Empty record for DEFAULT
5185
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5186
  List_iterator<create_field> def_it(fields);
5187
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5188 5189
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
5190 5191
  create_field *def;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5192
  /*
5193
    First collect all fields from table which isn't in drop_list
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5194 5195 5196 5197 5198
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
5199 5200
    if (field->type() == MYSQL_TYPE_STRING)
      varchar= TRUE;
5201
    /* Check if field should be dropped */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5202 5203 5204 5205 5206
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
5207
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
5208 5209 5210
      {
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
5211
	    !(used_fields & HA_CREATE_USED_AUTO))
5212 5213 5214 5215
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5216
	break;
5217
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5218 5219 5220 5221 5222 5223 5224 5225 5226 5227
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
5228
      if (def->change &&
5229
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5230 5231 5232 5233 5234
	break;
    }
    if (def)
    {						// Field is changed
      def->field=field;
5235 5236 5237 5238 5239
      if (!def->after)
      {
	create_list.push_back(def);
	def_it.remove();
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5240
    }
5241 5242 5243 5244 5245 5246
    else
    {
      /*
        This field was not dropped and not changed, add it to the list
        for the new table.
      */
5247
      create_list.push_back(def=new create_field(field,field));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5248 5249 5250 5251
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
      {
5252
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5253 5254 5255 5256
	  break;
      }
      if (alter)
      {
5257 5258
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
5259
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
5260
	  DBUG_RETURN(TRUE);
5261
	}
5262 5263 5264 5265
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5266 5267 5268 5269 5270 5271 5272 5273
	alter_it.remove();
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
  while ((def=def_it++))			// Add new columns
  {
5274
    if (def->change && ! def->field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5275
    {
5276
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
5277
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288
    }
    if (!def->after)
      create_list.push_back(def);
    else if (def->after == first_keyword)
      create_list.push_front(def);
    else
    {
      create_field *find;
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
5289
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5290 5291 5292 5293
	  break;
      }
      if (!find)
      {
5294
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
5295
	DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5296 5297 5298 5299
      }
      find_it.after(def);			// Put element after this
    }
  }
5300
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5301
  {
5302 5303
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
             alter_info->alter_list.head()->name, table_name);
5304
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5305 5306 5307
  }
  if (!create_list.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5308 5309
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
5310
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5311 5312 5313
  }

  /*
5314 5315
    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
5316 5317 5318 5319 5320 5321 5322
  */

  List_iterator<Key> key_it(keys);
  List_iterator<create_field> field_it(create_list);
  List<key_part_spec> key_parts;

  KEY *key_info=table->key_info;
5323
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5324
  {
5325
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5326 5327 5328 5329 5330
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
5331
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352
	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;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
5353 5354
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5355 5356
	    break;
	}
5357
	else if (!my_strcasecmp(system_charset_info,
5358
				key_part_name, cfield->field_name))
5359
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5360 5361 5362 5363 5364
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
5365 5366 5367 5368 5369 5370 5371
      {
        /*
          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.

5372 5373 5374
          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.

5375 5376 5377 5378 5379
          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()) ||
            !Field::type_can_have_key_part(cfield->sql_type) ||
bar@mysql.com's avatar
bar@mysql.com committed
5380 5381
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
5382 5383 5384
	    (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
5385
      }
5386
      key_part_length /= key_part->field->charset()->mbmaxlen;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5387 5388 5389 5390
      key_parts.push_back(new key_part_spec(cfield->field_name,
					    key_part_length));
    }
    if (key_parts.elements)
5391 5392 5393 5394 5395 5396 5397 5398 5399 5400
    {
      KEY_CREATE_INFO key_create_info;
      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;

5401
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
5402
				 (key_info->flags & HA_NOSAME ?
5403
				 (!my_strcasecmp(system_charset_info,
5404 5405
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
5406 5407 5408
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
5409
                                 &key_create_info,
5410
                                 test(key_info->flags & HA_GENERATED_KEY),
5411 5412
				 key_parts));
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5413 5414 5415 5416
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
5417 5418 5419
    {
      if (key->type != Key::FOREIGN_KEY)
	key_list.push_back(key);
5420 5421 5422 5423
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
5424
	DBUG_RETURN(TRUE);
5425
      }
5426
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5427 5428
  }

5429
  if (alter_info->drop_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5430
  {
5431 5432
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5433 5434
    goto err;
  }
5435
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5436
  {
5437 5438
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5439 5440 5441
    goto err;
  }

5442
  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
5443 5444
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
5445 5446
  /* Safety fix for innodb */
  if (lower_case_table_names)
5447
    my_casedn_str(files_charset_info, tmp_name);
5448 5449 5450 5451
  if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5452 5453
  create_info->db_type=new_db_type;
  if (!create_info->comment)
5454
    create_info->comment= table->s->comment;
5455 5456 5457 5458 5459

  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))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5460 5461 5462 5463 5464 5465 5466 5467 5468 5469
    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;

5470
  if (table->s->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5471 5472
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

5473 5474
  set_table_default_charset(thd, create_info, db);

5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507
  {
    /*
      For some purposes we need prepared table structures and translated
      key descriptions with proper default key name assignment.

      Unfortunately, mysql_prepare_table() modifies the field and key
      lists. mysql_create_table() needs the unmodified lists. Hence, we
      need to copy the lists and all their elements. The lists contain
      pointers to the elements only.

      We cannot copy conditionally because the partition code always
      needs prepared lists and compare_tables() needs them and is almost
      always called.
    */

    /* Copy fields. */
    List_iterator<create_field> prep_field_it(create_list);
    create_field *prep_field;
    while ((prep_field= prep_field_it++))
      prepared_create_list.push_back(new create_field(*prep_field));

    /* Copy keys and key parts. */
    List_iterator<Key> prep_key_it(key_list);
    Key *prep_key;
    while ((prep_key= prep_key_it++))
    {
      List<key_part_spec> prep_columns;
      List_iterator<key_part_spec> prep_col_it(prep_key->columns);
      key_part_spec *prep_col;

      while ((prep_col= prep_col_it++))
        prep_columns.push_back(new key_part_spec(*prep_col));
      prepared_key_list.push_back(new Key(prep_key->type, prep_key->name,
5508
                                          &prep_key->key_create_info,
5509
                                          prep_key->generated, prep_columns));
5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521
    }

    /* Create the prepared information. */
    if (mysql_prepare_table(thd, create_info, &prepared_create_list,
                            &prepared_key_list,
                            (table->s->tmp_table != NO_TMP_TABLE), &db_options,
                            table->file, &key_info_buffer, &key_count, 0))
      goto err;
  }

  if (thd->variables.old_alter_table
      || (table->s->db_type != create_info->db_type)
5522
#ifdef WITH_PARTITION_STORAGE_ENGINE
5523
      || partition_changed
5524
#endif
5525
     )
5526 5527
    need_copy_table= 1;
  else
5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539
  {
    /* Try to optimize ALTER TABLE. Allocate result buffers. */
    if (! (index_drop_buffer=
           (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
        ! (index_add_buffer=
           (uint*) thd->alloc(sizeof(uint) * prepared_key_list.elements)))
      goto err;
    /* Check how much the tables differ. */
    need_copy_table= compare_tables(table, &prepared_create_list,
                                    key_info_buffer, key_count,
                                    create_info, alter_info, order_num,
                                    index_drop_buffer, &index_drop_count,
5540 5541
                                    index_add_buffer, &index_add_count,
                                    varchar);
5542 5543 5544 5545 5546 5547 5548 5549 5550 5551
  }

  /*
    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;
5552
    ulong alter_flags= 0;
5553 5554 5555 5556 5557 5558
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

5559 5560 5561
    if (table->s->db_type->alter_table_flags)
      alter_flags= table->s->db_type->alter_table_flags(alter_info->flags);
    DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 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
    /* 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. */
        need_copy_table= 0;
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* All required fast flags are present. */
        need_copy_table= 0;
      }
    }
    DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
                        need_copy_table, need_lock_for_indexes));
  }
5651

5652 5653
  /*
    better have a negative test here, instead of positive, like
monty@mysql.com's avatar
monty@mysql.com committed
5654
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
5655 5656
    so that ALTER TABLE won't break when somebody will add new flag
  */
5657 5658
  if (!need_copy_table)
    create_info->frm_only= 1;
5659

5660
#ifdef WITH_PARTITION_STORAGE_ENGINE
5661
  if (fast_alter_partition)
5662
  {
5663 5664 5665 5666 5667
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           &create_list, &key_list,
                                           db, table_name,
                                           fast_alter_partition));
5668
  }
5669
#endif
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
  /*
    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.
      At end, rename temporary tables and symlinks to temporary table
      to final table name.
      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);
    }
  }
5714 5715
  else
    create_info->data_file_name=create_info->index_file_name=0;
monty@mysql.com's avatar
monty@mysql.com committed
5716

5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729
  /*
    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);
  error= mysql_create_table(thd, new_db, tmp_name,
                            create_info,create_list,key_list,1,0);
  reenable_binlog(thd);
  if (error)
    DBUG_RETURN(error);

  /* Open the table if we need to copy the data. */
5730
  if (need_copy_table)
5731
  {
5732
    if (table->s->tmp_table)
5733 5734 5735 5736
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
5737
      tbl.table_name= tbl.alias= tmp_name;
5738
      /* Table is in thd->temporary_tables */
5739 5740
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
5741 5742 5743 5744
    }
    else
    {
      char path[FN_REFLEN];
5745
      /* table is a normal table: Create temporary table in same directory */
5746
      build_table_filename(path, sizeof(path), new_db, tmp_name, "");
5747 5748 5749
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
5750
      goto err1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5751 5752
  }

5753
  /* Copy the data if necessary. */
5754
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5755 5756
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
5757
  next_insert_id=thd->next_insert_id;		// Remember for logging
5758
  copied=deleted=0;
5759
  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
5760
  {
5761
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
monty@mysql.com's avatar
monty@mysql.com committed
5762
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
5763
    new_table->next_number_field=new_table->found_next_number_field;
5764
    error=copy_data_between_tables(table,new_table,create_list,
5765
				   handle_duplicates, ignore,
5766
				   order_num, order, &copied, &deleted);
5767
  }
5768
  thd->last_insert_id=next_insert_id;		// Needed for correct log
5769
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5770

5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783
  /* 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"));

5784
    table->file->prepare_for_alter();
5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799
    if (index_add_count)
    {
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
      if (! need_lock_for_indexes)
      {
        /* Downgrade the write lock. */
        mysql_lock_downgrade_write(thd, table, TL_WRITE_ALLOW_WRITE);
      }

      /* Create a new .frm file for crash recovery. */
      /* TODO: Must set INDEX_TO_BE_ADDED flags in the frm file. */
      VOID(pthread_mutex_lock(&LOCK_open));
      error= (mysql_create_frm(thd, reg_path, db, table_name,
                               create_info, prepared_create_list, key_count,
                               key_info_buffer, table->file) ||
5800
              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
5801
                                                create_info));
5802 5803
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (error)
5804
        goto err1;
5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831
#endif

      /* 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;
5832
        goto err1;
5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846
      }
    }
    /*end of if (index_add_count)*/

    if (index_drop_count)
    {
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
      /* Create a new .frm file for crash recovery. */
      /* TODO: Must set INDEX_IS_ADDED in the frm file. */
      /* TODO: Must set INDEX_TO_BE_DROPPED in the frm file. */
      VOID(pthread_mutex_lock(&LOCK_open));
      error= (mysql_create_frm(thd, reg_path, db, table_name,
                               create_info, prepared_create_list, key_count,
                               key_info_buffer, table->file) ||
5847
              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
5848
                                                create_info));
5849 5850
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (error)
5851
        goto err1;
5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884

      if (! need_lock_for_indexes)
      {
        LOCK_PARAM_TYPE lpt;

        lpt.thd= thd;
        lpt.table= table;
        lpt.db= db;
        lpt.table_name= table_name;
        lpt.create_info= create_info;
        lpt.create_list= &create_list;
        lpt.key_count= key_count;
        lpt.key_info_buffer= key_info_buffer;
        abort_and_upgrade_lock(lpt);
      }
#endif

      /* 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));
5885
        goto err1;
5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912
      }

#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020
      if (! need_lock_for_indexes)
      {
        /* Downgrade the lock again. */
        if (table->reginfo.lock_type == TL_WRITE_ALLOW_READ)
        {
          LOCK_PARAM_TYPE lpt;

          lpt.thd= thd;
          lpt.table= table;
          lpt.db= db;
          lpt.table_name= table_name;
          lpt.create_info= create_info;
          lpt.create_list= &create_list;
          lpt.key_count= key_count;
          lpt.key_info_buffer= key_info_buffer;
          close_open_tables_and_downgrade(lpt);
        }
      }
#endif

      /* Tell the handler to finally drop the indexes. */
      if ((error= table->file->final_drop_index(table)))
      {
        table->file->print_error(error, MYF(0));
5913
        goto err1;
5914 5915 5916 5917
      }
    }
    /*end of if (index_drop_count)*/

5918 5919 5920 5921
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
5922

5923 5924 5925
    /* 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))
5926
      goto err1;
5927
    committed= 1;
5928 5929 5930
  }
  /*end of if (! new_table) for add/drop index*/

5931
  if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5932 5933 5934
  {
    /* We changed a temporary table */
    if (error)
5935
      goto err1;
5936 5937 5938 5939 5940 5941
    /* 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
5942
    /* Remove link to old table and rename the new one */
5943
    close_temporary_table(thd, table, 1, 1);
5944 5945
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
5946
      goto err1;
5947
    /* We don't replicate alter table statement on temporary tables */
5948
    if (!thd->current_stmt_binlog_row_based)
5949
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5950 5951 5952
    goto end_temporary;
  }

5953 5954
  if (new_table)
  {
5955 5956
    /* close temporary table that will be the new table */
    intern_close_table(new_table);
5957 5958
    my_free((gptr) new_table,MYF(0));
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5959 5960 5961 5962 5963 5964 5965
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
5966

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5967
  /*
5968 5969
    Data is copied.  Now we rename the old table to a temp name,
    rename the new one to the old name, remove all entries from the old table
5970
    from the cache, free all locks, close the old table and remove it.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5971 5972 5973
  */

  thd->proc_info="rename result table";
5974 5975
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
5976 5977
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
5978
  if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5979 5980 5981 5982
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
5983
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5984 5985 5986 5987 5988 5989
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

5990
#if !defined( __WIN__)
5991 5992 5993 5994
  if (table->file->has_transactions())
#endif
  {
    /*
5995
      Win32 and InnoDB can't drop a table that is in use, so we must
5996
      close the original table at before doing the rename
5997
    */
5998
    table->s->version= 0;                	// Force removal of table def
5999
    close_cached_table(thd, table);
6000
    table=0;					// Marker that table is closed
6001
    no_table_reopen= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6002
  }
6003
#if !defined( __WIN__)
6004 6005
  else
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6006 6007
#endif

6008

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6009
  error=0;
6010
  if (!need_copy_table)
6011
    new_db_type=old_db_type= NULL; // this type cannot happen in regular ALTER
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6012 6013 6014 6015 6016 6017
  if (mysql_rename_table(old_db_type,db,table_name,db,old_name))
  {
    error=1;
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
  }
  else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db,
6018 6019 6020 6021 6022
			      new_alias) ||
           (new_name != table_name || new_db != db) && // we also do rename
           Table_triggers_list::change_table_name(thd, db, table_name,
                                                  new_db, new_alias))
       
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6023 6024
  {						// Try to get everything back
    error=1;
6025
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6026
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
6027
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6028 6029 6030
  }
  if (error)
  {
6031 6032 6033 6034
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
6035
    if (table)
6036 6037
    {
      table->s->version= 0;            	        // Force removal of table def
6038
      close_cached_table(thd,table);
6039
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6040 6041 6042
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6043 6044 6045 6046 6047 6048 6049 6050 6051 6052
  if (! need_copy_table)
  {
    if (! table)
    {
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (! (table= open_ltable(thd, table_list, TL_WRITE_ALLOW_READ)))
        goto err;
      VOID(pthread_mutex_lock(&LOCK_open));
    }
    /* Tell the handler that a new frm file is in place. */
6053
    if (table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
6054
                                          create_info))
6055 6056 6057 6058 6059
    {
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
6060
  if (thd->lock || new_name != table_name || no_table_reopen)  // True if WIN32
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6061
  {
6062
    /*
6063 6064
      Not table locking or alter table with rename.
      Free locks and remove old table
6065
    */
6066
    if (table)
6067 6068
    {
      table->s->version= 0;              	// Force removal of table def
6069
      close_cached_table(thd,table);
6070
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6071 6072 6073 6074
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
6075 6076 6077 6078 6079
    /*
      Using LOCK TABLES without rename.
      This code is never executed on WIN32!
      Remove old renamed table, reopen table and get new locks
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6080 6081 6082
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
monty@mysql.com's avatar
monty@mysql.com committed
6083
      /* Mark in-use copies old */
6084
      remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
monty@mysql.com's avatar
monty@mysql.com committed
6085
      /* end threads waiting on lock */
6086
      mysql_lock_abort(thd,table, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6087 6088 6089 6090 6091
    }
    VOID(quick_rm_table(old_db_type,db,old_name));
    if (close_data_tables(thd,db,table_name) ||
	reopen_tables(thd,1,0))
    {						// This shouldn't happen
6092
      if (table)
6093 6094
      {
        table->s->version= 0;                   // Force removal of table def
6095
	close_cached_table(thd,table);		// Remove lock for table
6096
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6097 6098 6099 6100
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
6101 6102 6103 6104 6105 6106 6107 6108
  VOID(pthread_mutex_unlock(&LOCK_open));
  VOID(pthread_cond_broadcast(&COND_refresh));
  /*
    The ALTER TABLE is always in its own transaction.
    Commit must not be called while LOCK_open is locked. It could call
    wait_if_global_read_lock(), which could create a deadlock if called
    with LOCK_open.
  */
6109 6110 6111 6112 6113 6114 6115 6116
  if (!committed)
  {
    error = ha_commit_stmt(thd);
    if (ha_commit(thd))
      error=1;
    if (error)
      goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6117
  thd->proc_info="end";
6118

6119 6120 6121 6122
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
                      thd->query, thd->query_length,
                      db, table_name);

6123
  DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->current_stmt_binlog_row_based &&
6124 6125
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
6126 6127 6128 6129
  /*
    TODO RONM: This problem needs to handled for Berkeley DB partitions
    as well
  */
6130
  if (ha_check_storage_engine_flag(old_db_type,HTON_FLUSH_AFTER_RENAME))
6131
  {
6132 6133 6134 6135 6136
    /*
      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
      shutdown.
    */
6137
    char path[FN_REFLEN];
6138
    build_table_filename(path, sizeof(path), new_db, table_name, "");
6139 6140
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
6141
    {
6142 6143
      intern_close_table(table);
      my_free((char*) table, MYF(0));
6144
    }
6145
    else
6146
      sql_print_warning("Could not open table %s.%s after rename\n",
serg@serg.mylan's avatar
serg@serg.mylan committed
6147
                        new_db,table_name);
6148
    ha_flush_logs(old_db_type);
6149
  }
6150
  table_list->table=0;				// For query cache
6151
  query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6152 6153

end_temporary:
6154 6155 6156
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
6157 6158
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6159
  thd->some_tables_deleted=0;
6160
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6161

6162 6163 6164 6165 6166 6167 6168 6169 6170
 err1:
  if (new_table)
  {
    /* close_temporary_table() frees the new_table pointer. */
    close_temporary_table(thd, new_table, 1, 1);
  }
  else
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6171
 err:
6172
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6173
}
6174
/* mysql_alter_table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6175 6176

static int
6177
copy_data_between_tables(TABLE *from,TABLE *to,
6178
			 List<create_field> &create,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6179
			 enum enum_duplicates handle_duplicates,
6180
                         bool ignore,
6181
			 uint order_num, ORDER *order,
6182
			 ha_rows *copied,
6183
			 ha_rows *deleted)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6184 6185 6186 6187 6188
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
6189 6190 6191 6192 6193 6194
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
6195
  ha_rows examined_rows;
6196
  bool auto_increment_field_copied= 0;
6197
  ulong save_sql_mode;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6198 6199
  DBUG_ENTER("copy_data_between_tables");

6200 6201 6202 6203 6204 6205
  /*
    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
  */
6206
  error= ha_enable_transaction(thd, FALSE);
6207 6208
  if (error)
    DBUG_RETURN(-1);
6209
  
6210
  if (!(copy= new Copy_field[to->s->fields]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6211 6212
    DBUG_RETURN(-1);				/* purecov: inspected */

6213
  if (to->file->ha_external_lock(thd, F_WRLCK))
6214
    DBUG_RETURN(-1);
6215 6216 6217 6218 6219 6220 6221

  /* We can abort alter table for any table type */
  thd->no_trans_update= 0;
  thd->abort_on_warning= !ignore && test(thd->variables.sql_mode &
                                         (MODE_STRICT_TRANS_TABLES |
                                          MODE_STRICT_ALL_TABLES));

6222
  from->file->info(HA_STATUS_VARIABLE);
6223
  to->file->ha_start_bulk_insert(from->file->stats.records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6224

6225 6226
  save_sql_mode= thd->variables.sql_mode;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6227 6228 6229 6230 6231 6232 6233
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
6234 6235
    {
      if (*ptr == to->next_number_field)
6236
      {
6237
        auto_increment_field_copied= TRUE;
6238 6239 6240 6241 6242 6243 6244 6245 6246
        /*
          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
6247
      (copy_end++)->set(*ptr,def->field,0);
6248 6249
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6250 6251
  }

6252 6253
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
6254 6255
  if (order)
  {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
6256
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
6257
					      MYF(MY_FAE | MY_ZEROFILL));
6258
    bzero((char*) &tables,sizeof(tables));
6259
    tables.table= from;
6260 6261
    tables.alias= tables.table_name= from->s->table_name.str;
    tables.db=    from->s->db.str;
6262 6263
    error=1;

pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
6264
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
6265
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
6266
		    &tables, fields, all_fields, order) ||
6267 6268
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
6269
					     (SQL_SELECT *) 0, HA_POS_ERROR, 1,
monty@mysql.com's avatar
monty@mysql.com committed
6270 6271
					     &examined_rows)) ==
	HA_POS_ERROR)
6272 6273 6274
      goto err;
  };

6275 6276
  /* 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
6277
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
6278
  if (ignore ||
6279
      handle_duplicates == DUP_REPLACE)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6280
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
6281
  thd->row_count= 0;
6282
  restore_record(to, s->default_values);        // Create empty record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6283 6284 6285 6286
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
6287
      thd->send_kill_message();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6288 6289 6290
      error= 1;
      break;
    }
6291
    thd->row_count++;
6292 6293
    if (to->next_number_field)
    {
6294
      if (auto_increment_field_copied)
6295
        to->auto_increment_field_not_null= TRUE;
6296 6297 6298
      else
        to->next_number_field->reset();
    }
6299
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6300
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
6301
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6302
      copy_ptr->do_copy(copy_ptr);
6303
    }
6304
    if ((error=to->file->ha_write_row((byte*) to->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6305
    {
6306
      if ((!ignore &&
6307
	   handle_duplicates != DUP_REPLACE) ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6308 6309 6310
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
      {
6311 6312 6313 6314 6315 6316 6317
         if (error == HA_ERR_FOUND_DUPP_KEY)
         {
           uint key_nr= to->file->get_dup_key(error);
           if ((int) key_nr >= 0)
           {
             const char *err_msg= ER(ER_DUP_ENTRY);
             if (key_nr == 0 &&
monty@mysql.com's avatar
monty@mysql.com committed
6318 6319
                 (to->key_info[0].key_part[0].field->flags &
                  AUTO_INCREMENT_FLAG))
6320
               err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
monty@mysql.com's avatar
monty@mysql.com committed
6321
             to->file->print_keydup_error(key_nr, err_msg);
6322 6323 6324 6325
             break;
           }
         }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6326 6327 6328
	to->file->print_error(error,MYF(0));
	break;
      }
6329
      to->file->restore_auto_increment();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6330 6331 6332
      delete_count++;
    }
    else
6333
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6334 6335
  }
  end_read_record(&info);
6336
  free_io_cache(from);
6337
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
6338

6339
  if (to->file->ha_end_bulk_insert() && error <= 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6340
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
6341
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6342 6343
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6344
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
6345

6346
  ha_enable_transaction(thd,TRUE);
6347

6348 6349 6350 6351 6352 6353 6354 6355
  /*
    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;
6356

6357
 err:
6358
  thd->variables.sql_mode= save_sql_mode;
6359
  thd->abort_on_warning= 0;
6360
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6361 6362
  *copied= found_count;
  *deleted=delete_count;
6363
  if (to->file->ha_external_lock(thd,F_UNLCK))
6364
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6365 6366
  DBUG_RETURN(error > 0 ? -1 : 0);
}
6367

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

6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate
    do_send_ok          If we should send_ok() or leave it to caller

 RETURN
    Like mysql_alter_table().
*/
6381 6382
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                          bool do_send_ok)
6383 6384 6385 6386 6387 6388 6389 6390 6391
{
  DBUG_ENTER("mysql_recreate_table");
  LEX *lex= thd->lex;
  HA_CREATE_INFO create_info;
  lex->create_list.empty();
  lex->key_list.empty();
  lex->col_list.empty();
  lex->alter_info.reset();
  bzero((char*) &create_info,sizeof(create_info));
6392
  create_info.db_type= 0;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
6393
  create_info.row_type=ROW_TYPE_NOT_USED;
6394
  create_info.default_table_charset=default_charset_info;
monty@mysql.com's avatar
monty@mysql.com committed
6395
  /* Force alter table to recreate table */
6396
  lex->alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
6397 6398 6399
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
6400
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
6401 6402 6403
}


6404 6405
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
                          HA_CHECK_OPT *check_opt)
6406 6407 6408 6409 6410
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
6411
  DBUG_ENTER("mysql_checksum_table");
6412 6413 6414 6415 6416

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
  field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21));
  item->maybe_null= 1;
6417 6418
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
6419
    DBUG_RETURN(TRUE);
6420

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6421
  for (table= tables; table; table= table->next_local)
6422 6423
  {
    char table_name[NAME_LEN*2+2];
6424
    TABLE *t;
6425

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

6428
    t= table->table= open_ltable(thd, table, TL_READ);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6429
    thd->clear_error();			// these errors shouldn't get client
6430 6431 6432 6433

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

6434
    if (!t)
6435
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6436
      /* Table didn't exist */
6437
      protocol->store_null();
6438
      thd->clear_error();
6439 6440 6441
    }
    else
    {
6442
      t->pos_in_table_list= table;
6443

6444
      if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
6445 6446
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
6447
      else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6448
	       (check_opt->flags & T_QUICK))
6449
	protocol->store_null();
6450 6451
      else
      {
6452 6453
	/* calculating table's checksum */
	ha_checksum crc= 0;
6454
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
6455

6456
        t->use_all_columns();
6457

6458
	if (t->file->ha_rnd_init(1))
6459 6460 6461
	  protocol->store_null();
	else
	{
6462
	  for (;;)
6463 6464
	  {
	    ha_checksum row_crc= 0;
6465 6466 6467 6468 6469 6470 6471
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
6472 6473 6474 6475
	    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
6476 6477 6478
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

6479 6480
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
6481

6482
	    for (uint i= 0; i < t->s->fields; i++ )
6483 6484
	    {
	      Field *f= t->field[i];
6485 6486
	      if ((f->type() == FIELD_TYPE_BLOB) ||
                  (f->type() == MYSQL_TYPE_VARCHAR))
6487 6488 6489 6490 6491 6492 6493
	      {
		String tmp;
		f->val_str(&tmp);
		row_crc= my_checksum(row_crc, (byte*) tmp.ptr(), tmp.length());
	      }
	      else
		row_crc= my_checksum(row_crc, (byte*) f->ptr,
6494
				     f->pack_length());
6495
	    }
6496

6497 6498 6499
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
6500
          t->file->ha_rnd_end();
6501
	}
6502
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6503
      thd->clear_error();
6504 6505 6506 6507 6508 6509 6510 6511
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

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

6514 6515 6516 6517
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
6518
  DBUG_RETURN(TRUE);
6519
}
6520 6521

static bool check_engine(THD *thd, const char *table_name,
6522
                         HA_CREATE_INFO *create_info)
6523
{
6524
  handlerton **new_engine= &create_info->db_type;
6525
  handlerton *req_engine= *new_engine;
6526
  bool no_substitution=
6527
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
6528 6529
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
6530 6531
    return TRUE;

6532
  if (req_engine && req_engine != *new_engine)
6533 6534 6535 6536
  {
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
6537
                       ha_resolve_storage_engine_name(*new_engine),
6538 6539
                       table_name);
  }
6540 6541 6542 6543 6544
  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)
    {
6545
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
6546
               hton2plugin[(*new_engine)->slot]->name.str, "TEMPORARY");
6547 6548 6549 6550 6551
      *new_engine= 0;
      return TRUE;
    }
    *new_engine= &myisam_hton;
  }
6552 6553
  return FALSE;
}