sql_table.cc 198 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
    return my_snprintf(to, to_length, "%s", from + 9);
79 80 81 82 83 84 85 86 87
  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;
88 89 90
}


91
/*
92
  Creates path to a file: mysql_data_dir/db/table.ext
93 94

  SYNOPSIS
95 96 97 98 99 100 101 102 103 104 105 106 107
   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.
108 109 110

  RETURN

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


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


129
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
130
{
131 132
  uint length;
  char tbbuff[FN_REFLEN];
133 134 135 136 137 138 139
  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);
140 141
  length= unpack_filename(buff, buff);
  return length;
142 143
}

144 145 146 147 148 149 150
/*
  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

151

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
/*
  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,
169
                                  List<create_field> *new_create_list)
170 171 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
{
  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;
209
  DBUG_ENTER("mysql_copy_key_list");
210 211 212 213 214 215 216 217 218 219 220

  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;
221 222

      if (!(prep_key_part= new key_part_spec(*prep_col)))
223 224 225 226 227 228 229 230 231 232
      {
        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);
      }
    }
233
    if (!(temp_key= new Key(prep_key->type, prep_key->name,
234
                            &prep_key->key_create_info,
235
                            prep_key->generated,
236
                            prep_columns)))
237 238 239 240 241 242 243 244 245 246 247 248 249
    {
      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);
}

250 251 252
/*
--------------------------------------------------------------------------

253
   MODULE: DDL log
254 255 256 257 258 259 260 261
   -----------------

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

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

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

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


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

298
GLOBAL_DDL_LOG global_ddl_log;
299

300
pthread_mutex_t LOCK_gdl;
301

302 303 304 305 306
#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
307

308 309
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
310
#define DDL_LOG_IO_SIZE_POS 8
311

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

322
static bool read_ddl_log_file_entry(uint entry_no)
323 324
{
  bool error= FALSE;
325 326 327 328
  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");
329

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


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

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

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


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

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

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


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

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


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

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

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


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

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

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


/*
503
  Initialise ddl log
504
  SYNOPSIS
505
    init_ddl_log()
506

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

  RETURN VALUES
    TRUE                     Error
    FALSE                    Success
514 515
*/

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

522
  if (global_ddl_log.inited)
523 524
    goto end;

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

end:
545
  DBUG_RETURN(FALSE);
546 547 548 549
}


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

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

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

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


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

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

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

  *active_entry= used_entry;
724
  DBUG_RETURN(FALSE);
725 726 727 728
}


/*
729
  External interface methods for the DDL log Module
730 731 732 733
  ---------------------------------------------------
*/

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

739
  RETURN VALUES
740 741 742 743
    TRUE                      Error
    FALSE                     Success

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

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

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


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

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

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

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


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

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

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


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

960
bool sync_ddl_log()
961 962
{
  bool error= FALSE;
963
  DBUG_ENTER("sync_ddl_log");
964

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


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

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

996
  global_ddl_log.first_free= log_entry;
997 998 999 1000 1001
  log_entry->next_log_entry= first_free;

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


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

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

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

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

1052

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

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

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

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

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


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

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

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


1153 1154 1155
/*
---------------------------------------------------------------------------

1156
  END MODULE DDL log
1157 1158 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

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

1200 1201 1202
  /*
    Build shadow frm file name
  */
1203
  build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
1204
                       lpt->table_name, "#");
1205
  strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
1206
  if (flags & WFRM_WRITE_SHADOW)
1207
  {
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
    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,
1220
                            /*select_field_count*/ 0))
1221 1222 1223 1224 1225
    {
      DBUG_RETURN(TRUE);
    }
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
1226 1227 1228 1229 1230
      partition_info *part_info= lpt->table->part_info;
      char *part_syntax_buf;
      uint syntax_len;

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

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

1359

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

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

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

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

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

  /*
    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
1411
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
1412 1413 1414 1415 1416 1417 1418 1419

  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);

1420 1421 1422
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

1423
  if (error)
1424
    DBUG_RETURN(TRUE);
1425
  send_ok(thd);
1426
  DBUG_RETURN(FALSE);
1427 1428
}

1429 1430 1431 1432 1433

/*
 delete (drop) tables.

  SYNOPSIS
1434 1435 1436 1437 1438
    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
1439
                        generate warnings if the handler files doesn't exists
1440 1441 1442 1443 1444 1445 1446 1447 1448 1449

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

1450 1451
int mysql_rm_table_part2_with_lock(THD *thd,
				   TABLE_LIST *tables, bool if_exists,
1452
				   bool drop_temporary, bool dont_log_query)
1453 1454 1455 1456 1457 1458
{
  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
1459 1460
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
			      dont_log_query);
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470

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

1471

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

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

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

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

1516 1517 1518 1519
  LINT_INIT(alias);
  LINT_INIT(path_length);
  safe_mutex_assert_owner(&LOCK_open);

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

1542
  if (lock_table_names(thd, tables))
1543
    DBUG_RETURN(1);
1544

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

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

1561 1562 1563 1564 1565 1566
    /*
      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.
      */
1567
    if (thd->current_stmt_binlog_row_based && !dont_log_query)
1568
    {
1569
      non_temp_tables_count++;
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584
      /*
        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
1585
    error=0;
1586
    table_type= table->db_type;
1587
    if (!drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1588
    {
1589
      TABLE *locked_table;
1590 1591 1592 1593
      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);
1594 1595 1596 1597 1598 1599 1600
      /*
        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;

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

  if (some_tables_deleted || tmp_table_deleted || !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1680
  {
1681
    query_cache_invalidate3(thd, tables, 0);
1682
    if (!dont_log_query)
1683
    {
1684
      if (!thd->current_stmt_binlog_row_based ||
1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695
          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);
      }
1696
      else if (thd->current_stmt_binlog_row_based &&
1697 1698 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
               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.
      */
1724
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1725
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1726

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


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

1740 1741
  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
1742
  if (my_delete(path,MYF(0)))
1743
    error= 1; /* purecov: inspected */
1744
  path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
1745 1746
  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
1747 1748
}

1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766
/*
  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;
1767
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
1768 1769
    {
      /* Sort NOT NULL keys before other keys */
1770
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
    }
    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;
  }
1784
  /*
1785
    Prefer original key order.	usable_key_parts contains here
1786 1787 1788 1789 1790
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
1791 1792
}

1793 1794
/*
  Check TYPELIB (set or enum) for duplicates
1795

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

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

  RETURN VALUES
    void
*/

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

1833 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

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


1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887
/*
  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
1888 1889
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
1890 1891 1892
			 uint table_flags)
{
  DBUG_ENTER("prepare_field");
monty@mysql.com's avatar
monty@mysql.com committed
1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911

  /*
    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;
1912
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
1913 1914
    break;
  case FIELD_TYPE_GEOMETRY:
1915
#ifdef HAVE_SPATIAL
monty@mysql.com's avatar
monty@mysql.com committed
1916 1917 1918 1919
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
1920
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
1921 1922 1923 1924 1925 1926 1927 1928
    }
    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;
1929
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
1930 1931 1932 1933 1934
    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);
1935
#endif /*HAVE_SPATIAL*/
monty@mysql.com's avatar
monty@mysql.com committed
1936
  case MYSQL_TYPE_VARCHAR:
1937
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
monty@mysql.com's avatar
monty@mysql.com committed
1938 1939 1940 1941 1942 1943 1944 1945
    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)
1946
      {
monty@mysql.com's avatar
monty@mysql.com committed
1947 1948
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
1949 1950
        DBUG_RETURN(1);
      }
monty@mysql.com's avatar
monty@mysql.com committed
1951 1952 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
    }
#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
1987 1988 1989
    /* 
      We have sql_field->pack_flag already set here, see mysql_prepare_table().
    */
monty@mysql.com's avatar
monty@mysql.com committed
1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002
    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)
    {
2003
      if (!*timestamps)
2004
      {
monty@mysql.com's avatar
monty@mysql.com committed
2005
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
2006
        (*timestamps_with_niladic)++;
2007
      }
monty@mysql.com's avatar
monty@mysql.com committed
2008 2009 2010 2011
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
2012
      (*timestamps_with_niladic)++;
monty@mysql.com's avatar
monty@mysql.com committed
2013

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

2033
/*
2034
  Preparation for table creation
2035 2036

  SYNOPSIS
2037
    mysql_prepare_table()
2038 2039 2040 2041 2042 2043 2044 2045 2046 2047
      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.
2048

2049
  DESCRIPTION
2050
    Prepares the table and key structures for table creation.
2051

2052
  NOTES
2053
    sets create_info->varchar if the table has a varchar
2054

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

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

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

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

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

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

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

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

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

2160
          // Strip trailing spaces.
2161 2162
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
2163 2164
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
2165 2166 2167 2168 2169 2170
          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))
            {
2171
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
2172 2173 2174
              DBUG_RETURN(-1);
            }
          }
2175
        }
2176
        sql_field->interval_list.empty(); // Don't need interval_list anymore
2177 2178 2179 2180 2181 2182
      }

      /*
        Convert the default value from client character
        set into the column character set if necessary.
      */
bar@mysql.com's avatar
bar@mysql.com committed
2183
      if (sql_field->def && cs != sql_field->def->collation.collation)
2184
      {
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2185 2186
        Query_arena backup_arena;
        bool need_to_change_arena= !thd->stmt_arena->is_conventional();
2187 2188 2189
        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
2190 2191 2192
          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);
2193 2194 2195 2196 2197
        }

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

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

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

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

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

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

2286
    sql_field->create_length_to_internal_length();
2287 2288
    if (prepare_blob_field(thd, sql_field))
      DBUG_RETURN(-1);
2289

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

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

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

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

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

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

2391
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2392
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2393 2394
    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
2395 2396 2397 2398
    DBUG_RETURN(-1);
  }

  /* Create keys */
2399

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

2408
  /* Calculate number of key segements */
2409
  *key_count= 0;
2410

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

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

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

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

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

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

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

2563
    /* TODO: Add proper checks if handler supports key_type and algorithm */
2564
    if (key_info->flags & HA_SPATIAL)
2565 2566 2567
    {
      if (key_info->key_parts != 1)
      {
2568
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
2569
	DBUG_RETURN(-1);
2570
      }
2571
    }
2572
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
2573
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2574
#ifdef HAVE_RTREE_KEYS
2575 2576
      if ((key_info->key_parts & 1) == 1)
      {
2577
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
2578
	DBUG_RETURN(-1);
2579
      }
2580
      /* TODO: To be deleted */
2581
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
2582
      DBUG_RETURN(-1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2583
#else
2584 2585
      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
2586 2587
      DBUG_RETURN(-1);
#endif
2588
    }
2589

2590 2591 2592 2593 2594
    /* 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
    */
2595 2596
    key_info->block_size= (key->key_create_info.block_size ?
                           key->key_create_info.block_size :
2597 2598 2599 2600 2601
                           create_info->key_block_size);

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

2602
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
2603
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2604 2605
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
2606
      uint length;
2607 2608
      key_part_spec *dup_column;

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2719 2720 2721
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
2722 2723
      length= sql_field->key_length;

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

2859 2860 2861
  DBUG_RETURN(0);
}

2862

2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882
/*
  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];
2883 2884
    /* Abuse build_table_filename() to build the path to the db.opt file */
    build_table_filename(path, sizeof(path), db, "", MY_DB_OPT_FILE);
2885 2886 2887 2888 2889 2890
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }
}


2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913
/*
  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];

2914 2915
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
2916 2917 2918 2919 2920 2921 2922 2923
    {
      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,
2924
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943
            (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);
}


2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 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
/*
  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();
2988 2989 2990 2991
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
2992 2993


2994 2995 2996 2997
/*
  Create a table

  SYNOPSIS
2998
    mysql_create_table_internal()
2999 3000 3001 3002 3003 3004
    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
3005
    internal_tmp_table  Set to 1 if this is an internal temporary table
3006
			(From ALTER TABLE)
3007 3008

  DESCRIPTION
3009
    If one creates a temporary table, this is automatically opened
3010 3011 3012 3013 3014 3015 3016

    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
3017 3018
    FALSE OK
    TRUE  error
3019 3020
*/

3021 3022 3023 3024 3025 3026
bool mysql_create_table_internal(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)
3027
{
3028
  char		path[FN_REFLEN];
3029
  uint          path_length;
3030 3031 3032 3033
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
3034
  bool		error= TRUE;
3035
  DBUG_ENTER("mysql_create_table_internal");
3036 3037 3038 3039

  /* 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
3040 3041
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
3042
    DBUG_RETURN(TRUE);
3043
  }
3044
  if (check_engine(thd, table_name, create_info))
3045
    DBUG_RETURN(TRUE);
3046
  db_options= create_info->table_options;
3047 3048 3049
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
3050 3051
  if (!(file=get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
                             create_info->db_type)))
3052
  {
3053
    mem_alloc_error(sizeof(handler));
3054 3055
    DBUG_RETURN(TRUE);
  }
3056
#ifdef WITH_PARTITION_STORAGE_ENGINE
3057 3058
  partition_info *part_info= thd->work_part_info;

3059 3060 3061 3062 3063 3064 3065 3066
  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.
    */
3067
    thd->work_part_info= part_info= new partition_info();
3068 3069 3070 3071 3072 3073
    if (!part_info)
    {
      mem_alloc_error(sizeof(partition_info));
      DBUG_RETURN(TRUE);
    }
    file->set_auto_partitions(part_info);
3074
    part_info->default_engine_type= create_info->db_type;
3075
  }
3076 3077 3078
  if (part_info)
  {
    /*
3079 3080 3081 3082 3083 3084 3085 3086 3087
      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.
3088
    */
3089 3090
    List_iterator<Key> key_iterator(keys);
    Key *key;
3091
    handlerton *part_engine_type= create_info->db_type;
3092 3093
    char *part_syntax_buf;
    uint syntax_len;
3094
    handlerton *engine_type;
3095 3096 3097 3098 3099
    if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
    {
      my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
      goto err;
    }
3100 3101 3102 3103 3104 3105 3106 3107
    while ((key= key_iterator++))
    {
      if (key->type == Key::FOREIGN_KEY)
      {
        my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
        goto err;
      }
    }
3108 3109
    if ((part_engine_type == &partition_hton) &&
        part_info->default_engine_type)
3110 3111 3112 3113 3114 3115
    {
      /*
        This only happens at ALTER TABLE.
        default_engine_type was assigned from the engine set in the ALTER
        TABLE command.
      */
3116
      ;
3117
    }
3118 3119
    else
    {
3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131
      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);
        }
      }
3132
    }
3133 3134
    DBUG_PRINT("info", ("db_type = %d",
                         ha_legacy_type(part_info->default_engine_type)));
3135
    if (part_info->check_partition_info( &engine_type, file,
3136
                             create_info->max_rows))
3137
      goto err;
3138
    part_info->default_engine_type= engine_type;
3139

3140 3141 3142 3143 3144 3145
    /*
      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,
3146
                                                     TRUE, FALSE)))
3147
      goto err;
3148 3149
    part_info->part_info_string= part_syntax_buf;
    part_info->part_info_len= syntax_len;
3150 3151
    if ((!(engine_type->partition_flags &&
           engine_type->partition_flags() & HA_CAN_PARTITION)) ||
3152
        create_info->db_type == &partition_hton)
3153 3154 3155 3156 3157
    {
      /*
        The handler assigned to the table cannot handle partitioning.
        Assign the partition handler as the handler of the table.
      */
3158 3159
      DBUG_PRINT("info", ("db_type: %d",
                          ha_legacy_type(create_info->db_type)));
3160
      delete file;
3161
      create_info->db_type= &partition_hton;
3162 3163 3164 3165
      if (!(file= get_ha_partition(part_info)))
      {
        DBUG_RETURN(TRUE);
      }
3166 3167 3168 3169 3170 3171 3172 3173
      /*
        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 &&
3174
          (int)part_info->no_parts != file->get_default_no_partitions(0ULL))
3175
      {
3176
        uint i;
3177
        List_iterator<partition_element> part_it(part_info->partitions);
3178 3179 3180 3181
        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;
3182 3183 3184 3185
      }
      else if (part_info->is_sub_partitioned() &&
               part_info->use_default_no_subpartitions &&
               part_info->no_subparts &&
3186 3187
               (int)part_info->no_subparts !=
                 file->get_default_no_partitions(0ULL))
3188
      {
3189
        DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200
        part_info->no_subparts= file->get_default_no_partitions(0ULL);
      }
    }
    else if (create_info->db_type != engine_type)
    {
      delete file;
      if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, engine_type)))
      {
        mem_alloc_error(sizeof(handler));
        DBUG_RETURN(TRUE);
      }
3201 3202 3203
    }
  }
#endif
3204

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

3207 3208 3209
  if (mysql_prepare_table(thd, create_info, &fields,
			  &keys, internal_tmp_table, &db_options, file,
			  &key_info_buffer, &key_count,
3210
			  select_field_count))
3211
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3212 3213 3214 3215

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
3216
    path_length= build_tmptable_filename(thd, path, sizeof(path));
3217
    if (lower_case_table_names)
3218
      my_casedn_str(files_charset_info, path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3219 3220 3221
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
3222
    path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext);
3223

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3224
  /* Check if table already exists */
3225 3226
  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
3227
  {
3228
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3229 3230
    {
      create_info->table_existed= 1;		// Mark that table existed
3231 3232 3233
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
3234 3235
      error= 0;
      goto err;
3236
    }
monty@mysql.com's avatar
monty@mysql.com committed
3237
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
3238
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3239
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
3240
  if (wait_if_global_read_lock(thd, 0, 1))
3241
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3242
  VOID(pthread_mutex_lock(&LOCK_open));
3243
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3244 3245 3246 3247
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
3248 3249
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3250
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3251
    }
3252
    DBUG_ASSERT(get_cached_table_share(db, alias) == 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3253 3254
  }

3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267
  /*
    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;
3268
    if (ha_table_exists_in_engine(thd, db, table_name))
3269
    {
3270
      DBUG_PRINT("info", ("Table with same name already existed in handler"));
3271 3272

      if (create_if_not_exists)
3273 3274
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
3275
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3276 3277 3278 3279
    }
  }

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3282
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
3283
    create_info->data_file_name= create_info->index_file_name= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3284
  create_info->table_options=db_options;
3285

3286
  path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
3287 3288
  if (rea_create_table(thd, path, db, table_name, create_info, fields,
                       key_count, key_info_buffer, file))
3289
    goto unlock_and_end;
3290

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3291 3292 3293 3294 3295 3296
  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);
3297
      goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3298
    }
3299
    thd->tmp_table_used= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3300
  }
monty@mysql.com's avatar
monty@mysql.com committed
3301

3302 3303 3304 3305 3306
  /*
    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.
3307
    Otherwise, the statement shall be binlogged.
3308 3309
   */
  if (!internal_tmp_table &&
3310 3311
      (!thd->current_stmt_binlog_row_based ||
       (thd->current_stmt_binlog_row_based &&
3312
        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
3313
    write_bin_log(thd, TRUE, thd->query, thd->query_length);
3314
  error= FALSE;
3315
unlock_and_end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3316
  VOID(pthread_mutex_unlock(&LOCK_open));
3317
  start_waiting_global_read_lock(thd);
3318 3319

err:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3320
  thd->proc_info="After create";
3321
  delete file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3322
  DBUG_RETURN(error);
3323 3324

warn:
3325
  error= FALSE;
3326 3327 3328 3329
  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
3330
  goto unlock_and_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3331 3332
}

3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375

/*
  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
3376 3377 3378 3379 3380 3381 3382 3383
/*
** 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++)
3384
    if (!my_strcasecmp(system_charset_info,name,key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3385 3386 3387 3388 3389 3390 3391 3392 3393 3394
      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;

3395 3396
  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
3397
    return (char*) field_name;			// Use fieldname
3398 3399 3400 3401 3402 3403
  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
  */
3404
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3405
  {
3406 3407
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3408 3409 3410
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
3411
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3412 3413
}

3414

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3415 3416 3417 3418 3419
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3420
			       TABLE_LIST *create_table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3421 3422 3423
			       List<create_field> *extra_fields,
			       List<Key> *keys,
			       List<Item> *items,
3424 3425
			       MYSQL_LOCK **lock,
                               TABLEOP_HOOKS *hooks)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3426 3427
{
  TABLE tmp_table;		// Used during 'create_field()'
3428
  TABLE_SHARE share;
3429
  TABLE *table= 0;
3430
  uint select_field_count= items->elements;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3431
  /* Add selected items to field list */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3432
  List_iterator_fast<Item> it(*items);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3433 3434
  Item *item;
  Field *tmp_field;
3435
  bool not_used;
3436 3437 3438
  DBUG_ENTER("create_table_from_items");

  tmp_table.alias= 0;
3439
  tmp_table.timestamp_field= 0;
3440 3441 3442
  tmp_table.s= &share;
  init_tmp_table_share(&share, "", 0, "", "");

3443 3444
  tmp_table.s->db_create_options=0;
  tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
3445 3446 3447
  tmp_table.s->db_low_byte_first= 
        test(create_info->db_type == &myisam_hton ||
             create_info->db_type == &heap_hton);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3448 3449 3450 3451 3452
  tmp_table.null_row=tmp_table.maybe_null=0;

  while ((item=it++))
  {
    create_field *cr_field;
3453 3454 3455 3456 3457
    Field *field;
    if (item->type() == Item::FUNC_ITEM)
      field=item->tmp_table_field(&tmp_table);
    else
      field=create_tmp_field(thd, &tmp_table, item, item->type(),
evgen@sunlight.local's avatar
evgen@sunlight.local committed
3458
                             (Item ***) 0, &tmp_field, 0, 0, 0, 0, 0);
3459 3460
    if (!field ||
	!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
3461 3462
					   ((Item_field *)item)->field :
					   (Field*) 0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3463
      DBUG_RETURN(0);
3464 3465
    if (item->maybe_null)
      cr_field->flags &= ~NOT_NULL_FLAG;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3466 3467
    extra_fields->push_back(cr_field);
  }
3468
  /*
monty@mysql.com's avatar
monty@mysql.com committed
3469 3470
    create and lock table

3471
    We don't log the statement, it will be logged later.
monty@mysql.com's avatar
monty@mysql.com committed
3472

3473 3474 3475 3476
    If this is a HEAP table, the automatic DELETE FROM which is written to the
    binlog when a HEAP table is opened for the first time since startup, must
    not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we
    don't want to delete from it) 2) it would be written before the CREATE
3477 3478
    TABLE, which is a wrong order. So we keep binary logging disabled when we
    open_table().
monty@mysql.com's avatar
monty@mysql.com committed
3479
    TODO: create and open should be done atomic !
3480
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3481
  {
monty@mysql.com's avatar
monty@mysql.com committed
3482
    tmp_disable_binlog(thd);
3483
    if (!mysql_create_table(thd, create_table->db, create_table->table_name,
monty@mysql.com's avatar
monty@mysql.com committed
3484 3485 3486
                            create_info, *extra_fields, *keys, 0,
                            select_field_count))
    {
3487 3488
      if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
                               MYSQL_LOCK_IGNORE_FLUSH)))
monty@mysql.com's avatar
monty@mysql.com committed
3489
        quick_rm_table(create_info->db_type, create_table->db,
3490
                       table_case_name(create_info, create_table->table_name));
monty@mysql.com's avatar
monty@mysql.com committed
3491 3492 3493 3494
    }
    reenable_binlog(thd);
    if (!table)                                   // open failed
      DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3495
  }
monty@mysql.com's avatar
monty@mysql.com committed
3496

3497 3498 3499 3500 3501 3502
  /*
    FIXME: What happens if trigger manages to be created while we are
           obtaining this lock ? May be it is sensible just to disable
           trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
           save us from that ?
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3503
  table->reginfo.lock_type=TL_WRITE;
3504
  hooks->prelock(&table, 1);                    // Call prelock hooks
3505 3506
  if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
                                    MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3507
  {
3508
    VOID(pthread_mutex_lock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3509
    hash_delete(&open_cache,(byte*) table);
3510
    VOID(pthread_mutex_unlock(&LOCK_open));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3511
    quick_rm_table(create_info->db_type, create_table->db,
3512
		   table_case_name(create_info, create_table->table_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523
    DBUG_RETURN(0);
  }
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  DBUG_RETURN(table);
}


/****************************************************************************
** Alter a table definition
****************************************************************************/

3524
bool
3525
mysql_rename_table(handlerton *base,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3526
		   const char *old_db,
3527
		   const char *old_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3528
		   const char *new_db,
3529
		   const char *new_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3530
{
3531
  THD *thd= current_thd;
3532 3533 3534
  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];
3535
  handler *file;
3536
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3537
  DBUG_ENTER("mysql_rename_table");
3538

3539
  file= (base == NULL ? 0 :
3540 3541
         get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));

3542 3543
  build_table_filename(from, sizeof(from), old_db, old_name, "");
  build_table_filename(to, sizeof(to), new_db, new_name, "");
3544 3545 3546 3547 3548 3549

  /*
    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.
   */
3550 3551
  if (lower_case_table_names == 2 && file &&
      !(file->table_flags() & HA_FILE_BASED))
3552
  {
3553 3554
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
3555
    build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "");
3556
    from_base= lc_from;
3557

3558 3559
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
3560
    build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "");
3561
    to_base= lc_to;
3562 3563
  }

3564
  if (!file || !(error=file->rename_table(from_base, to_base)))
3565 3566 3567
  {
    if (rename_file_ext(from,to,reg_ext))
    {
3568
      error=my_errno;
3569
      /* Restore old file name */
3570
      if (file)
3571
        file->rename_table(to_base, from_base);
3572 3573
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3574
  delete file;
3575 3576 3577
  if (error)
    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
3578 3579
}

3580

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3581
/*
3582 3583 3584 3585 3586 3587
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
3588
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
3589
			HA_EXTRA_FORCE_REOPEN if table is not be used
3590 3591 3592 3593 3594 3595 3596
  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
3597 3598
*/

3599 3600
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
3601
{
3602
  DBUG_ENTER("wait_while_table_is_used");
3603 3604 3605
  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
3606

3607
  VOID(table->file->extra(function));
3608
  /* Mark all tables that are in use as 'old' */
3609
  mysql_lock_abort(thd, table, TRUE);	/* end threads waiting on lock */
3610 3611

  /* Wait until all there are no other threads that has this table open */
3612 3613 3614
  remove_table_from_cache(thd, table->s->db.str,
                          table->s->table_name.str,
                          RTFC_WAIT_OTHER_THREAD_FLAG);
3615 3616
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3617

3618 3619
/*
  Close a cached table
3620

3621
  SYNOPSIS
3622
    close_cached_table()
3623 3624 3625 3626 3627 3628
    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.
3629

3630 3631 3632 3633
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
3634

3635
void close_cached_table(THD *thd, TABLE *table)
3636 3637
{
  DBUG_ENTER("close_cached_table");
3638

3639
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
3640 3641
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
3642
  {
3643 3644
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3645
  }
3646 3647 3648 3649 3650
  /* 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
3651
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3652 3653
}

3654
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
3655
			     const char* operator_name, const char* errmsg)
3656

3657
{
3658 3659
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
3660 3661
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
3662
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
3663
  protocol->store(errmsg, system_charset_info);
3664
  thd->clear_error();
3665
  if (protocol->write())
3666 3667 3668 3669
    return -1;
  return 1;
}

3670

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3671
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
3672
			       HA_CHECK_OPT *check_opt)
3673
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3674
  DBUG_ENTER("prepare_for_restore");
3675

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3676 3677 3678 3679 3680 3681
  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"
				  ));
  }
3682
  else
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3683
  {
3684
    char* backup_dir= thd->lex->backup_dir;
3685
    char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
3686 3687
    char* table_name= table->table_name;
    char* db= table->db;
3688

3689 3690 3691
    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
3692
      DBUG_RETURN(-1); // protect buffer overflow
3693

3694
    build_table_filename(dst_path, sizeof(dst_path), db, table_name, reg_ext);
3695

3696
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3697
      DBUG_RETURN(-1);
3698

3699
    if (my_copy(src_path, dst_path, MYF(MY_WME)))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3700
    {
3701
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3702
      unlock_table_name(thd, table);
3703
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3704 3705 3706
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
3707
    if (mysql_truncate(thd, table, 1))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3708
    {
3709
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3710
      unlock_table_name(thd, table);
3711
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3712 3713
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
3714
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3715
  }
3716

3717 3718 3719 3720
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
3721 3722
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table))
3723
  {
3724
    unlock_table_name(thd, table);
3725
    pthread_mutex_unlock(&LOCK_open);
3726 3727
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
3728
  }
3729
  pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3730
  DBUG_RETURN(0);
3731
}
3732

3733

3734
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
3735
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3736
{
3737 3738
  int error= 0;
  TABLE tmp_table, *table;
3739 3740 3741 3742
  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
3743 3744 3745 3746
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
3747 3748

  if (!(table= table_list->table))		/* if open_ltable failed */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3749
  {
3750 3751 3752 3753 3754 3755 3756 3757 3758
    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);
3759
      DBUG_RETURN(0);				// Can't open frm file
3760 3761
    }

3762
    if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
3763 3764 3765 3766 3767
    {
      release_table_share(share, RELEASE_NORMAL);
      pthread_mutex_unlock(&LOCK_open);
      DBUG_RETURN(0);                           // Out of memory
    }
3768
    table= &tmp_table;
3769
    pthread_mutex_unlock(&LOCK_open);
3770
  }
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3771

3772 3773 3774 3775 3776 3777 3778 3779 3780
  /*
    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
3781

3782 3783 3784 3785
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
3786
  ext= table->file->bas_ext();
3787 3788
  if (!ext[0] || !ext[1])
    goto end;					// No data file
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3789

3790 3791
  // Name of data file
  strxmov(from, table->s->normalized_path.str, ext[1], NullS);
3792 3793
  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
3794

3795 3796
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
3797

3798 3799 3800 3801 3802 3803 3804
  /* 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);
  }
3805
  if (lock_and_wait_for_table_name(thd,table_list))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3806
  {
3807 3808
    error= -1;
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3809
  }
3810
  if (my_rename(from, tmp, MYF(MY_WME)))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3811
  {
3812
    pthread_mutex_lock(&LOCK_open);
3813
    unlock_table_name(thd, table_list);
3814
    pthread_mutex_unlock(&LOCK_open);
3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835
    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
3836 3837
  }

3838 3839 3840 3841
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
3842 3843
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table_list))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3844
  {
3845
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3846
    pthread_mutex_unlock(&LOCK_open);
3847 3848 3849
    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
3850
  }
3851
  pthread_mutex_unlock(&LOCK_open);
3852 3853 3854

end:
  if (table == &tmp_table)
3855 3856 3857 3858 3859
  {
    pthread_mutex_lock(&LOCK_open);
    closefrm(table, 1);				// Free allocated memory
    pthread_mutex_unlock(&LOCK_open);
  }
3860
  DBUG_RETURN(error);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3861
}
3862

3863

3864

3865 3866
/*
  RETURN VALUES
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
3867 3868 3869
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
3870
*/
3871 3872 3873 3874 3875
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,
3876
                              bool no_warnings_for_error,
3877 3878 3879
                              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
3880 3881 3882
                              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
3883
{
3884 3885
  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
3886
  List<Item> field_list;
3887 3888
  Item *item;
  Protocol *protocol= thd->protocol;
3889
  LEX *lex= thd->lex;
3890
  int result_code;
3891
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3892 3893 3894 3895 3896 3897 3898 3899 3900

  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;
3901 3902
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
3903
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3904

3905
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3906
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3907 3908
  {
    char table_name[NAME_LEN*2+2];
3909
    char* db = table->db;
3910
    bool fatal_error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3911

serg@serg.mylan's avatar
serg@serg.mylan committed
3912
    strxmov(table_name, db, ".", table->table_name, NullS);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3913
    thd->open_options|= extra_open_options;
3914 3915
    table->lock_type= lock_type;
    /* open only one table from local list of command */
3916
    save_next_global= table->next_global;
3917
    table->next_global= 0;
3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928
    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;
3929
    lex->query_tables_own_last= 0;
3930
    thd->no_warnings_for_error= no_warnings_for_error;
3931
    if (view_operator_func == NULL)
3932
      table->required_type=FRMTYPE_TABLE;
3933
    open_and_lock_tables(thd, table);
3934
    thd->no_warnings_for_error= 0;
3935 3936
    table->next_global= save_next_global;
    table->next_local= save_next_local;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3937
    thd->open_options&= ~extra_open_options;
3938

3939
    if (prepare_func)
3940
    {
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3941
      switch ((*prepare_func)(thd, table, check_opt)) {
3942 3943 3944 3945 3946 3947 3948
      case  1:           // error, message written to net
        close_thread_tables(thd);
        continue;
      case -1:           // error, message could be written to net
        goto err;
      default:           // should be 0 otherwise
        ;
3949
      }
3950
    }
3951

3952
    /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3953 3954 3955 3956 3957 3958
      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)
3959
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3960 3961
    if (!table->table)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3962
      char buf[ERRMSGSIZE+ERRMSGSIZE+2];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3963
      const char *err_msg;
3964
      protocol->prepare_for_resend();
3965 3966
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
3967
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3968 3969
      if (!(err_msg=thd->net.last_error))
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
3970 3971 3972 3973
      /* 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
3974
        strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
3975 3976
        err_msg= (const char *)buf;
      }
3977
      protocol->store(err_msg, system_charset_info);
3978
      lex->cleanup_after_one_table_open();
3979
      thd->clear_error();
3980 3981 3982 3983 3984
      /*
        View opening can be interrupted in the middle of process so some
        tables can be left opening
      */
      close_thread_tables(thd);
3985
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3986 3987 3988
	goto err;
      continue;
    }
3989 3990 3991 3992 3993 3994 3995

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

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
3996
    table->table->pos_in_table_list= table;
3997
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3998
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3999
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
4000
      uint length;
4001
      protocol->prepare_for_resend();
4002 4003
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4004
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4005 4006 4007
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
4008
      close_thread_tables(thd);
4009
      table->table=0;				// For query cache
4010
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4011 4012 4013 4014
	goto err;
      continue;
    }

4015
    /* Close all instances of the table to allow repair to rename files */
4016 4017
    if (lock_type == TL_WRITE && table->table->s->version &&
        !table->table->s->log_table)
4018 4019
    {
      pthread_mutex_lock(&LOCK_open);
4020 4021
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
4022
      mysql_lock_abort(thd,table->table, TRUE);
4023 4024
      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
4025 4026
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
4027
      thd->exit_cond(old_message);
4028 4029
      if (thd->killed)
	goto err;
4030 4031 4032
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
4033 4034
    }

4035
    if (table->table->s->crashed && operator_func == &handler::ha_check)
4036 4037 4038 4039
    {
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
4040 4041 4042
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
4043 4044 4045 4046
      if (protocol->write())
        goto err;
    }

4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061
    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))
      {
        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;
      }

    }

4062 4063 4064 4065
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

4066
    lex->cleanup_after_one_table_open();
4067
    thd->clear_error();  // these errors shouldn't get client
4068
    protocol->prepare_for_resend();
4069 4070
    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
4071

4072 4073 4074
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
4075 4076
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
4077
      {
4078 4079
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
4080
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
4081
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
4082
	protocol->store(buf, length, system_charset_info);
4083
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4084 4085
      break;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4086 4087
    case HA_ADMIN_NOT_BASE_TABLE:
      {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4088 4089
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4090
                                 ER(ER_BAD_TABLE_ERROR), table_name);
4091
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4092
        protocol->store(buf, length, system_charset_info);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4093 4094 4095
      }
      break;

4096
    case HA_ADMIN_OK:
4097 4098
      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
4099 4100
      break;

4101
    case HA_ADMIN_FAILED:
4102 4103 4104
      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
4105 4106
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4107
    case HA_ADMIN_REJECT:
4108 4109 4110
      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
4111
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
4112 4113
      break;

4114
    case HA_ADMIN_ALREADY_DONE:
4115 4116 4117
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
4118 4119
      break;

4120
    case HA_ADMIN_CORRUPT:
4121 4122
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
4123
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4124 4125
      break;

4126
    case HA_ADMIN_INVALID:
4127 4128 4129
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
4130 4131
      break;

4132 4133 4134 4135 4136 4137 4138 4139
    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.
      */
      close_thread_tables(thd);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4140 4141 4142
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
4143
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
4144
      result_code= mysql_recreate_table(thd, table, 0);
4145
      reenable_binlog(thd);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4146
      close_thread_tables(thd);
4147 4148 4149 4150 4151 4152
      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
      }
4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164
      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. */
4165
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4166 4167 4168 4169 4170 4171 4172 4173 4174
            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);
          }
        }
      }
4175
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4176 4177
      table->next_local= save_next_local;
      table->next_global= save_next_global;
4178 4179
      goto send_result_message;
    }
4180 4181
    case HA_ADMIN_WRONG_CHECKSUM:
    {
4182
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4183 4184
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
4185 4186
      break;
    }
4187

4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200
    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;
    }

4201
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
4202 4203 4204 4205 4206
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
4207
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
4208 4209 4210 4211
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4212
    }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4213
    if (table->table)
4214
    {
4215
      /* in the below check we do not refresh the log tables */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4216 4217
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
4218
      else if (open_for_modify && !table->table->s->log_table)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4219 4220
      {
        pthread_mutex_lock(&LOCK_open);
4221 4222
        remove_table_from_cache(thd, table->table->s->db.str,
                                table->table->s->table_name.str, RTFC_NO_FLAG);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4223 4224 4225 4226
        pthread_mutex_unlock(&LOCK_open);
        /* Something may be modified, that's why we have to invalidate cache */
        query_cache_invalidate3(thd, table->table, 0);
      }
4227
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4228
    close_thread_tables(thd);
4229
    table->table=0;				// For query cache
4230
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4231 4232 4233
      goto err;
  }

4234
  send_eof(thd);
4235
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4236
 err:
4237
  close_thread_tables(thd);			// Shouldn't be needed
4238 4239
  if (table)
    table->table=0;
4240
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4241 4242
}

4243

4244
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
4245 4246 4247
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4248
				"backup", TL_READ, 0, 0, 0, 0,
4249
				&handler::backup, 0));
4250
}
4251

4252

4253
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
4254 4255 4256
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
4257
				"restore", TL_WRITE, 1, 1, 0,
4258
				&prepare_for_restore,
4259
				&handler::restore, 0));
4260
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4261

4262

4263
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4264 4265 4266
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4267 4268 4269
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
4270
				&prepare_for_repair,
4271
				&handler::ha_repair, 0));
4272 4273
}

4274

4275
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4276 4277 4278
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4279
				"optimize", TL_WRITE, 1,0,0,0,
4280
				&handler::optimize, 0));
4281 4282 4283
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4284 4285 4286 4287 4288
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
4289 4290
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4291 4292

  RETURN VALUES
4293 4294
   FALSE ok
   TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4295 4296
*/

4297
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
4298
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4299
{
4300
  HA_CHECK_OPT check_opt;
4301
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4302
  DBUG_ENTER("mysql_assign_to_keycache");
4303 4304 4305 4306 4307 4308 4309

  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);
4310
    DBUG_RETURN(TRUE);
4311 4312 4313 4314
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
4315
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
4316
				0, 0, &handler::assign_to_keycache, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4317 4318
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4319 4320 4321 4322 4323 4324

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

  SYNOPSIS
    reassign_keycache_tables()
4325 4326 4327
    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
4328

4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341
  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
4342 4343 4344
    0	  ok
*/

4345 4346
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
4347 4348 4349
{
  DBUG_ENTER("reassign_keycache_tables");

4350 4351
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
4352
  src_cache->param_buff_size= 0;		// Free key cache
4353 4354
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
4355
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4356 4357 4358
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4359 4360 4361 4362 4363
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
4364 4365
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4366 4367

  RETURN VALUES
4368 4369
    FALSE ok
    TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4370 4371
*/

4372
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4373 4374 4375
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
4376
				"preload_keys", TL_READ, 0, 0, 0, 0,
4377
				&handler::preload_keys, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4378 4379 4380
}


venu@myvenu.com's avatar
venu@myvenu.com committed
4381 4382 4383 4384 4385
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
4386 4387
    thd		Thread object
    table	Table list (one table only)
venu@myvenu.com's avatar
venu@myvenu.com committed
4388 4389 4390 4391
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
4392 4393
    FALSE OK
    TRUE  error
venu@myvenu.com's avatar
venu@myvenu.com committed
4394 4395
*/

4396 4397 4398
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
4399
{
4400
  TABLE *tmp_table;
4401
  char src_path[FN_REFLEN], dst_path[FN_REFLEN], tmp_path[FN_REFLEN];
4402
  uint dst_path_length;
venu@myvenu.com's avatar
venu@myvenu.com committed
4403
  char *db= table->db;
4404
  char *table_name= table->table_name;
4405
  char *src_db;
venu@myvenu.com's avatar
venu@myvenu.com committed
4406
  char *src_table= table_ident->table.str;
4407 4408
  int  err;
  bool res= TRUE;
4409
  enum legacy_db_type not_used;
4410

4411
  TABLE_LIST src_tables_list;
venu@myvenu.com's avatar
venu@myvenu.com committed
4412
  DBUG_ENTER("mysql_create_like_table");
4413
  src_db= table_ident->db.str ? table_ident->db.str : thd->db;
venu@myvenu.com's avatar
venu@myvenu.com committed
4414 4415 4416 4417 4418 4419

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
4420
       check_table_name(src_table,table_ident->table.length)))
venu@myvenu.com's avatar
venu@myvenu.com committed
4421
  {
4422
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
4423
    DBUG_RETURN(TRUE);
venu@myvenu.com's avatar
venu@myvenu.com committed
4424
  }
4425 4426 4427 4428 4429
  if (!src_db || check_db_name(src_db))
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
    DBUG_RETURN(-1);
  }
4430

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4431
  bzero((gptr)&src_tables_list, sizeof(src_tables_list));
4432
  src_tables_list.db= src_db;
acurtis@xiphis.org's avatar
Merge  
acurtis@xiphis.org committed
4433
  src_tables_list.table_name= src_table;
4434

4435 4436
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
4437 4438

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
4439
    strxmov(src_path, tmp_table->s->path.str, reg_ext, NullS);
venu@myvenu.com's avatar
venu@myvenu.com committed
4440 4441
  else
  {
4442 4443
    build_table_filename(src_path, sizeof(src_path),
                         src_db, src_table, reg_ext);
4444
    /* Resolve symlinks (for windows) */
4445
    unpack_filename(src_path, src_path);
4446 4447
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
4448 4449 4450
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
4451
      goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
4452 4453 4454
    }
  }

4455 4456 4457
  /* 
     create like should be not allowed for Views, Triggers, ... 
  */
4458
  if (mysql_frm_type(thd, src_path, &not_used) != FRMTYPE_TABLE)
4459
  {
4460
    my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE");
4461 4462 4463
    goto err;
  }

venu@myvenu.com's avatar
venu@myvenu.com committed
4464 4465 4466
  /*
    Validate the destination table

4467
    skip the destination table name checking as this is already
venu@myvenu.com's avatar
venu@myvenu.com committed
4468 4469 4470 4471 4472 4473
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
4474
    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
4475 4476
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
4477 4478 4479 4480
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
4481 4482
    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
4483 4484 4485 4486
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

4487
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
4488
    Create a new table by copying from source table
4489
  */
4490 4491 4492 4493 4494 4495
  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);
4496
    goto err;
4497
  }
venu@myvenu.com's avatar
venu@myvenu.com committed
4498 4499

  /*
4500 4501
    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
4502 4503
    and temporary tables).
  */
4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516
#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
4517
  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
4518
  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
4519

venu@myvenu.com's avatar
venu@myvenu.com committed
4520 4521 4522 4523
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
4524 4525
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
4526
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
4527 4528 4529 4530
    }
  }
  else if (err)
  {
4531 4532 4533
    (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
4534
  }
4535

4536 4537 4538
  /*
    We have to write the query before we unlock the tables.
  */
4539
  if (thd->current_stmt_binlog_row_based)
4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589
  {
    /*
       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
    */
  }
4590
  else
4591 4592
    write_bin_log(thd, TRUE, thd->query, thd->query_length);

4593
  res= FALSE;
4594
  goto err;
4595

venu@myvenu.com's avatar
venu@myvenu.com committed
4596 4597 4598 4599
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
4600 4601
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
4602
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4603
		 ER_TABLE_EXISTS_ERROR,warn_buff);
4604
    res= FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
4605
  }
4606 4607 4608 4609 4610 4611 4612 4613
  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
4614 4615 4616
}


4617
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
4618
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4619 4620
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4621 4622
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
4623
				"analyze", lock_type, 1, 0, 0, 0,
4624
				&handler::analyze, 0));
4625 4626 4627
}


4628
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
4629
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4630 4631
  thr_lock_type lock_type = TL_READ_NO_INSERT;

4632 4633
  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
4634
				"check", lock_type,
4635
				0, HA_OPEN_FOR_REPAIR, 0, 0,
4636
				&handler::ha_check, &view_checksum));
4637 4638
}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4640
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
4641 4642 4643 4644
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
4645 4646 4647 4648 4649 4650
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
4651 4652 4653 4654
  /*
    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
4655 4656 4657

  thd->proc_info="discard_or_import_tablespace";

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

monty@mysql.com's avatar
monty@mysql.com committed
4660 4661 4662 4663 4664
 /*
   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
4665 4666 4667 4668 4669
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
4670

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4671 4672 4673 4674 4675 4676 4677
  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
4678 4679 4680 4681
  /*
    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
4682 4683 4684 4685 4686 4687 4688 4689
  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;
4690
  write_bin_log(thd, FALSE, thd->query, thd->query_length);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4691 4692
err:
  close_thread_tables(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4693
  thd->tablespace_op=FALSE;
4694
  
monty@mysql.com's avatar
monty@mysql.com committed
4695 4696
  if (error == 0)
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4697
    send_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
4698
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4699
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4700

4701 4702
  table->file->print_error(error, MYF(0));
    
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4703
  DBUG_RETURN(-1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4704
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4705

4706

4707 4708
/*
  SYNOPSIS
4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720
    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.
4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731

  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.

4732 4733 4734 4735 4736
    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.

4737
  RETURN VALUES
4738 4739 4740
    0                           No copy needed
    ALTER_TABLE_DATA_CHANGED    Data changes, copy needed
    ALTER_TABLE_INDEX_CHANGED   Index changes, copy might be needed
4741 4742
*/

4743 4744 4745 4746 4747 4748
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,
                           uint *index_add_buffer, uint *index_add_count)
4749 4750 4751 4752 4753
{
  Field **f_ptr, *field;
  uint changes= 0, tmp;
  List_iterator_fast<create_field> new_field_it(*create_list);
  create_field *new_field;
4754 4755
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
4756
  DBUG_ENTER("compare_tables");
4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781

  /*
    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.
  */
  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 ||
4782
      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
4783
      order_num)
4784
    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795

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

4797 4798 4799
    /* Check that NULL behavior is same for old and new fields */
    if ((new_field->flags & NOT_NULL_FLAG) !=
	(uint) (field->flags & NOT_NULL_FLAG))
4800
      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
4801 4802 4803 4804 4805 4806 4807 4808 4809 4810

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

    /* Evaluate changes bitmap and send to check_if_incompatible_data() */
    if (!(tmp= field->is_equal(new_field)))
4811
      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
4812 4813
    // Clear indexed marker
    field->add_index= 0;
4814 4815 4816 4817 4818 4819 4820
    changes|= tmp;
  }

  /*
    Go through keys and check if the original ones are compatible
    with new table.
  */
4821 4822 4823 4824
  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;
4825

4826 4827 4828 4829 4830 4831 4832 4833
  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++)
4834
  {
4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858
    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;
4859 4860 4861 4862 4863

    /*
      Check that the key parts remain compatible between the old and
      new tables.
    */
4864 4865 4866
    for (table_part= table_key->key_part, new_part= new_key->key_part;
         table_part < table_part_end;
         table_part++, new_part++)
4867 4868 4869
    {
      /*
	Key definition has changed if we are using a different field or
4870 4871
	if the used key part length is different. We know that the fields
        did not change. Comparing field numbers is sufficient.
4872
      */
4873 4874 4875
      if ((table_part->length != new_part->length) ||
          (table_part->fieldnr - 1 != new_part->fieldnr))
	goto index_changed;
4876
    }
4877 4878 4879 4880 4881 4882
    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;
4883 4884 4885 4886 4887 4888 4889 4890
    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];
      field->add_index= 1;
    }
4891
    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
4892
  }
4893
  /*end of for (; table_key < table_key_end;) */
4894

4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909
  /*
    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;
4910 4911 4912 4913 4914 4915 4916 4917
      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];
        field->add_index= 1;
      }
marty@linux.site's avatar
marty@linux.site committed
4918
      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
4919 4920
    }
  }
4921 4922 4923 4924 4925

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

4926 4927 4928 4929
  if (*index_drop_count || *index_add_count)
    DBUG_RETURN(ALTER_TABLE_INDEX_CHANGED);

  DBUG_RETURN(0); // Tables are compatible
4930 4931 4932
}


4933 4934 4935
/*
  Alter table
*/
4936

4937 4938 4939 4940 4941
bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
                       HA_CREATE_INFO *create_info,
                       TABLE_LIST *table_list,
                       List<create_field> &fields, List<Key> &keys,
                       uint order_num, ORDER *order,
4942
                       enum enum_duplicates handle_duplicates, bool ignore,
4943
                       ALTER_INFO *alter_info, bool do_send_ok)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4944
{
4945
  TABLE *table,*new_table=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4946
  int error;
4947 4948
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
4949
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
4950
  char path[FN_REFLEN];
4951
  char reg_path[FN_REFLEN+1];
4952 4953
  ha_rows copied,deleted;
  ulonglong next_insert_id;
4954
  uint db_create_options, used_fields;
4955
  handlerton *old_db_type, *new_db_type;
4956
  uint need_copy_table= 0;
4957
  bool no_table_reopen= FALSE;
4958
#ifdef WITH_PARTITION_STORAGE_ENGINE
4959
  uint fast_alter_partition= 0;
4960 4961
  bool partition_changed= FALSE;
#endif
4962 4963 4964 4965 4966 4967 4968 4969 4970 4971
  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;
4972
  bool committed= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4973 4974
  DBUG_ENTER("mysql_alter_table");

4975 4976 4977 4978 4979
  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
4980
  thd->proc_info="init";
4981
  table_name=table_list->table_name;
4982
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4983
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
4984
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
4985
    new_db= db;
4986
  build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext);
4987
  build_table_filename(path, sizeof(path), db, table_name, "");
4988

4989
  used_fields=create_info->used_fields;
4990

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4993
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
4994
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4995
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
4996
						   alter_info->tablespace_op));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4997
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
4998
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4999 5000 5001 5002 5003

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

5054
  old_db_type= table->s->db_type;
5055
  if (create_info->db_type == (handlerton*) &default_hton)
5056
    create_info->db_type= old_db_type;
5057

5058
#ifdef WITH_PARTITION_STORAGE_ENGINE
5059 5060
  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
                            &partition_changed, &fast_alter_partition))
5061
  {
5062
    DBUG_RETURN(TRUE);
5063 5064
  }
#endif
5065
  if (check_engine(thd, new_name, create_info))
5066 5067
    DBUG_RETURN(TRUE);
  new_db_type= create_info->db_type;
5068
  if (create_info->row_type == ROW_TYPE_NOT_USED)
5069
    create_info->row_type= table->s->row_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5070

5071 5072 5073
  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
5074 5075
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
5076 5077 5078 5079 5080 5081
  {
    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
5082
  thd->proc_info="setup";
5083
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
5084
      !table->s->tmp_table) // no need to touch frm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5085 5086
  {
    error=0;
5087
    if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5088
    {
5089 5090 5091 5092 5093 5094
      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))
      {
5095
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
5096
	error= -1;
5097 5098 5099
      }
      else
      {
5100
	*fn_ext(new_name)=0;
5101
        table->s->version= 0;                   // Force removal of table def
5102 5103
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
5104
	  error= -1;
5105 5106 5107 5108 5109 5110 5111
        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;
        }
5112 5113
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5114
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5115

5116
    if (!error)
5117
    {
5118
      switch (alter_info->keys_onoff) {
5119
      case LEAVE_AS_IS:
5120
        break;
5121
      case ENABLE:
5122 5123 5124 5125 5126 5127
        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;
5128
      case DISABLE:
5129 5130 5131 5132 5133 5134
        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;
5135
      }
5136
    }
5137

5138
    if (error == HA_ERR_WRONG_COMMAND)
serg@serg.mylan's avatar
serg@serg.mylan committed
5139 5140
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
5141
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
5142
			  table->alias);
serg@serg.mylan's avatar
serg@serg.mylan committed
5143 5144
      error=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5145 5146
    if (!error)
    {
5147
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
5148 5149
      if (do_send_ok)
        send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5150
    }
5151
    else if (error > 0)
5152 5153
    {
      table->file->print_error(error, MYF(0));
5154
      error= -1;
5155
    }
5156 5157
    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
5158 5159 5160 5161
    DBUG_RETURN(error);
  }

  /* Full alter table */
5162

5163
  /* Let new create options override the old ones */
5164
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
5165
    create_info->min_rows= table->s->min_rows;
5166
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
5167
    create_info->max_rows= table->s->max_rows;
5168
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
5169
    create_info->avg_row_length= table->s->avg_row_length;
5170
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
5171
    create_info->default_table_charset= table->s->table_charset;
5172 5173
  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
    create_info->key_block_size= table->s->key_block_size;
5174

5175
  restore_record(table, s->default_values);     // Empty record for DEFAULT
5176
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5177
  List_iterator<create_field> def_it(fields);
5178
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5179 5180
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
5181 5182
  create_field *def;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5183
  /*
5184
    First collect all fields from table which isn't in drop_list
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5185 5186 5187 5188 5189
  */

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

  /*
5299 5300
    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
5301 5302 5303 5304 5305 5306 5307
  */

  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;
5308
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5309
  {
5310
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5311 5312 5313 5314 5315
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
5316
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337
	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)
	{
5338 5339
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5340 5341
	    break;
	}
5342
	else if (!my_strcasecmp(system_charset_info,
5343
				key_part_name, cfield->field_name))
5344
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5345 5346 5347 5348 5349
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
5350 5351 5352 5353 5354 5355 5356
      {
        /*
          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.

5357 5358 5359
          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.

5360 5361 5362 5363 5364
          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
5365 5366
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
5367 5368 5369
	    (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
5370
      }
5371
      key_part_length /= key_part->field->charset()->mbmaxlen;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5372 5373 5374 5375
      key_parts.push_back(new key_part_spec(cfield->field_name,
					    key_part_length));
    }
    if (key_parts.elements)
5376 5377 5378 5379 5380 5381 5382 5383 5384 5385
    {
      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;

5386
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
5387
				 (key_info->flags & HA_NOSAME ?
5388
				 (!my_strcasecmp(system_charset_info,
5389 5390
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
5391 5392 5393
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
5394
                                 &key_create_info,
5395
                                 test(key_info->flags & HA_GENERATED_KEY),
5396 5397
				 key_parts));
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5398 5399 5400 5401
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
5402 5403 5404
    {
      if (key->type != Key::FOREIGN_KEY)
	key_list.push_back(key);
5405 5406 5407 5408
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
5409
	DBUG_RETURN(TRUE);
5410
      }
5411
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5412 5413
  }

5414
  if (alter_info->drop_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5415
  {
5416 5417
    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
5418 5419
    goto err;
  }
5420
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5421
  {
5422 5423
    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
5424 5425 5426
    goto err;
  }

5427
  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
5428 5429
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
5430 5431
  /* Safety fix for innodb */
  if (lower_case_table_names)
5432
    my_casedn_str(files_charset_info, tmp_name);
5433 5434 5435 5436
  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
5437 5438
  create_info->db_type=new_db_type;
  if (!create_info->comment)
5439
    create_info->comment= table->s->comment;
5440 5441 5442 5443 5444

  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
5445 5446 5447 5448 5449 5450 5451 5452 5453 5454
    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;

5455
  if (table->s->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5456 5457
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

5458 5459
  set_table_default_charset(thd, create_info, db);

5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492
  {
    /*
      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,
5493
                                          &prep_key->key_create_info,
5494
                                          prep_key->generated, prep_columns));
5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506
    }

    /* 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)
5507
#ifdef WITH_PARTITION_STORAGE_ENGINE
5508
      || partition_changed
5509
#endif
5510
     )
5511 5512
    need_copy_table= 1;
  else
5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535
  {
    /* 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,
                                    index_add_buffer, &index_add_count);
  }

  /*
    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;
5536
    ulong alter_flags= 0;
5537 5538 5539 5540 5541 5542
    ulong needed_online_flags= 0;
    ulong needed_fast_flags= 0;
    KEY   *key;
    uint  *idx_p;
    uint  *idx_end_p;

5543 5544 5545
    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));
5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 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
    /* 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));
  }
5635

5636 5637
  /*
    better have a negative test here, instead of positive, like
monty@mysql.com's avatar
monty@mysql.com committed
5638
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
5639 5640
    so that ALTER TABLE won't break when somebody will add new flag
  */
5641 5642
  if (!need_copy_table)
    create_info->frm_only= 1;
5643

5644
#ifdef WITH_PARTITION_STORAGE_ENGINE
5645
  if (fast_alter_partition)
5646
  {
5647 5648 5649 5650 5651
    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                           create_info, table_list,
                                           &create_list, &key_list,
                                           db, table_name,
                                           fast_alter_partition));
5652
  }
5653
#endif
5654

5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697
  /*
    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);
    }
  }
5698 5699
  else
    create_info->data_file_name=create_info->index_file_name=0;
monty@mysql.com's avatar
monty@mysql.com committed
5700

5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713
  /*
    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. */
5714
  if (need_copy_table)
5715
  {
5716
    if (table->s->tmp_table)
5717 5718 5719 5720
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
5721
      tbl.table_name= tbl.alias= tmp_name;
5722
      /* Table is in thd->temporary_tables */
5723 5724
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
5725 5726 5727 5728
    }
    else
    {
      char path[FN_REFLEN];
5729
      /* table is a normal table: Create temporary table in same directory */
5730
      build_table_filename(path, sizeof(path), new_db, tmp_name, "");
5731 5732 5733
      new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
    if (!new_table)
5734
      goto err1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5735 5736
  }

5737
  /* Copy the data if necessary. */
5738
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5739 5740
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
5741
  next_insert_id=thd->next_insert_id;		// Remember for logging
5742
  copied=deleted=0;
5743
  if (new_table && !(new_table->file->table_flags() & HA_NO_COPY_ON_ALTER))
5744
  {
5745
    /* We don't want update TIMESTAMP fields during ALTER TABLE. */
monty@mysql.com's avatar
monty@mysql.com committed
5746
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
5747
    new_table->next_number_field=new_table->found_next_number_field;
5748
    error=copy_data_between_tables(table,new_table,create_list,
5749
				   handle_duplicates, ignore,
5750
				   order_num, order, &copied, &deleted);
5751
  }
5752
  thd->last_insert_id=next_insert_id;		// Needed for correct log
5753
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5754

5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767
  /* 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"));

5768
    table->file->prepare_for_alter();
5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783
    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) ||
5784
              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
5785
                                                create_info));
5786 5787
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (error)
5788
        goto err1;
5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815
#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;
5816
        goto err1;
5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830
      }
    }
    /*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) ||
5831
              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
5832
                                                create_info));
5833 5834
      VOID(pthread_mutex_unlock(&LOCK_open));
      if (error)
5835
        goto err1;
5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868

      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));
5869
        goto err1;
5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896
      }

#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));
5897
        goto err1;
5898 5899 5900 5901
      }
    }
    /*end of if (index_drop_count)*/

5902 5903 5904 5905
    /*
      The final .frm file is already created as a temporary file
      and will be renamed to the original table name later.
    */
5906

5907 5908 5909
    /* 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))
5910
      goto err1;
5911
    committed= 1;
5912 5913 5914
  }
  /*end of if (! new_table) for add/drop index*/

5915
  if (table->s->tmp_table != NO_TMP_TABLE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5916 5917 5918
  {
    /* We changed a temporary table */
    if (error)
5919
      goto err1;
5920 5921 5922 5923 5924 5925
    /* 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
5926
    /* Remove link to old table and rename the new one */
5927
    close_temporary_table(thd, table, 1, 1);
5928 5929
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
5930
      goto err1;
5931
    /* We don't replicate alter table statement on temporary tables */
5932
    if (!thd->current_stmt_binlog_row_based)
5933
      write_bin_log(thd, TRUE, thd->query, thd->query_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5934 5935 5936
    goto end_temporary;
  }

5937 5938
  if (new_table)
  {
5939 5940
    /* close temporary table that will be the new table */
    intern_close_table(new_table);
5941 5942
    my_free((gptr) new_table,MYF(0));
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5943 5944 5945 5946 5947 5948 5949
  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;
  }
5950

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5951
  /*
5952 5953
    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
5954
    from the cache, free all locks, close the old table and remove it.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5955 5956 5957
  */

  thd->proc_info="rename result table";
5958 5959
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
5960 5961
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
5962
  if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5963 5964 5965 5966
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
5967
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5968 5969 5970 5971 5972 5973
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

5974
#if !defined( __WIN__)
5975 5976 5977 5978
  if (table->file->has_transactions())
#endif
  {
    /*
5979
      Win32 and InnoDB can't drop a table that is in use, so we must
5980
      close the original table at before doing the rename
5981
    */
5982
    table->s->version= 0;                	// Force removal of table def
5983
    close_cached_table(thd, table);
5984
    table=0;					// Marker that table is closed
5985
    no_table_reopen= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5986
  }
5987
#if !defined( __WIN__)
5988 5989
  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
5990 5991
#endif

5992

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5993
  error=0;
5994
  if (!need_copy_table)
5995
    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
5996 5997 5998 5999 6000 6001
  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,
6002 6003 6004 6005 6006
			      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
6007 6008
  {						// Try to get everything back
    error=1;
6009
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6010
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
6011
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6012 6013 6014
  }
  if (error)
  {
6015 6016 6017 6018
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
6019
    if (table)
6020 6021
    {
      table->s->version= 0;            	        // Force removal of table def
6022
      close_cached_table(thd,table);
6023
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6024 6025 6026
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
6027 6028 6029 6030 6031 6032 6033 6034 6035 6036
  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. */
6037
    if (table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
6038
                                          create_info))
6039 6040 6041 6042 6043
    {
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
6044
  if (thd->lock || new_name != table_name || no_table_reopen)  // True if WIN32
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6045
  {
6046
    /*
6047 6048
      Not table locking or alter table with rename.
      Free locks and remove old table
6049
    */
6050
    if (table)
6051 6052
    {
      table->s->version= 0;              	// Force removal of table def
6053
      close_cached_table(thd,table);
6054
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6055 6056 6057 6058
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
6059 6060 6061 6062 6063
    /*
      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
6064 6065 6066
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
monty@mysql.com's avatar
monty@mysql.com committed
6067
      /* Mark in-use copies old */
6068
      remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
monty@mysql.com's avatar
monty@mysql.com committed
6069
      /* end threads waiting on lock */
6070
      mysql_lock_abort(thd,table, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6071 6072 6073 6074 6075
    }
    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
6076
      if (table)
6077 6078
      {
        table->s->version= 0;                   // Force removal of table def
6079
	close_cached_table(thd,table);		// Remove lock for table
6080
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6081 6082 6083 6084
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
6085 6086 6087 6088 6089 6090 6091 6092
  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.
  */
6093 6094 6095 6096 6097 6098 6099 6100
  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
6101
  thd->proc_info="end";
6102

6103 6104 6105 6106
  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
                      thd->query, thd->query_length,
                      db, table_name);

6107
  DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->current_stmt_binlog_row_based &&
6108 6109
                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
6110 6111 6112 6113
  /*
    TODO RONM: This problem needs to handled for Berkeley DB partitions
    as well
  */
6114
  if (ha_check_storage_engine_flag(old_db_type,HTON_FLUSH_AFTER_RENAME))
6115
  {
6116 6117 6118 6119 6120
    /*
      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.
    */
6121
    char path[FN_REFLEN];
6122
    build_table_filename(path, sizeof(path), new_db, table_name, "");
6123 6124
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
6125
    {
6126 6127
      intern_close_table(table);
      my_free((char*) table, MYF(0));
6128
    }
6129
    else
6130
      sql_print_warning("Could not open table %s.%s after rename\n",
serg@serg.mylan's avatar
serg@serg.mylan committed
6131
                        new_db,table_name);
6132
    ha_flush_logs(old_db_type);
6133
  }
6134
  table_list->table=0;				// For query cache
6135
  query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6136 6137

end_temporary:
6138 6139 6140
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
6141 6142
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6143
  thd->some_tables_deleted=0;
6144
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6145

6146 6147 6148 6149 6150 6151 6152 6153 6154
 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
6155
 err:
6156
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6157
}
6158
/* mysql_alter_table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6159 6160

static int
6161
copy_data_between_tables(TABLE *from,TABLE *to,
6162
			 List<create_field> &create,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6163
			 enum enum_duplicates handle_duplicates,
6164
                         bool ignore,
6165
			 uint order_num, ORDER *order,
6166
			 ha_rows *copied,
6167
			 ha_rows *deleted)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6168 6169 6170 6171 6172
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
6173 6174 6175 6176 6177 6178
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
6179
  ha_rows examined_rows;
6180
  bool auto_increment_field_copied= 0;
6181
  ulong save_sql_mode;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6182 6183
  DBUG_ENTER("copy_data_between_tables");

6184 6185 6186 6187 6188 6189
  /*
    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
  */
6190
  error= ha_enable_transaction(thd, FALSE);
6191 6192
  if (error)
    DBUG_RETURN(-1);
6193
  
6194
  if (!(copy= new Copy_field[to->s->fields]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6195 6196
    DBUG_RETURN(-1);				/* purecov: inspected */

6197
  if (to->file->ha_external_lock(thd, F_WRLCK))
6198
    DBUG_RETURN(-1);
6199 6200 6201 6202 6203 6204 6205

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

6206
  from->file->info(HA_STATUS_VARIABLE);
6207
  to->file->start_bulk_insert(from->file->records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6208

6209 6210
  save_sql_mode= thd->variables.sql_mode;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6211 6212 6213 6214 6215 6216 6217
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
6218 6219
    {
      if (*ptr == to->next_number_field)
6220
      {
6221
        auto_increment_field_copied= TRUE;
6222 6223 6224 6225 6226 6227 6228 6229 6230
        /*
          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
6231
      (copy_end++)->set(*ptr,def->field,0);
6232 6233
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6234 6235
  }

6236 6237
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
6238 6239
  if (order)
  {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
6240
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
6241
					      MYF(MY_FAE | MY_ZEROFILL));
6242
    bzero((char*) &tables,sizeof(tables));
6243
    tables.table= from;
6244 6245
    tables.alias= tables.table_name= from->s->table_name.str;
    tables.db=    from->s->db.str;
6246 6247
    error=1;

pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
6248
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
6249
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
6250
		    &tables, fields, all_fields, order) ||
6251 6252 6253
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
					     (SQL_SELECT *) 0, HA_POS_ERROR,
monty@mysql.com's avatar
monty@mysql.com committed
6254 6255
					     &examined_rows)) ==
	HA_POS_ERROR)
6256 6257 6258
      goto err;
  };

6259 6260 6261 6262 6263
  /*
    Handler must be told explicitly to retrieve all columns, because
    this function does not set field->query_id in the columns to the
    current query id
  */
6264
  to->file->ha_set_all_bits_in_write_set();
6265
  from->file->ha_retrieve_all_cols();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6266
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
6267
  if (ignore ||
6268
      handle_duplicates == DUP_REPLACE)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6269
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
6270
  thd->row_count= 0;
6271
  restore_record(to, s->default_values);        // Create empty record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6272 6273 6274 6275
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
6276
      thd->send_kill_message();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6277 6278 6279
      error= 1;
      break;
    }
6280
    thd->row_count++;
6281 6282
    if (to->next_number_field)
    {
6283
      if (auto_increment_field_copied)
6284
        to->auto_increment_field_not_null= TRUE;
6285 6286 6287
      else
        to->next_number_field->reset();
    }
6288
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6289
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
6290
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6291
      copy_ptr->do_copy(copy_ptr);
6292
    }
6293
    if ((error=to->file->ha_write_row((byte*) to->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6294
    {
6295
      if ((!ignore &&
6296
	   handle_duplicates != DUP_REPLACE) ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6297 6298 6299 6300 6301 6302
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
      {
	to->file->print_error(error,MYF(0));
	break;
      }
6303
      to->file->restore_auto_increment();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6304 6305 6306
      delete_count++;
    }
    else
6307
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6308 6309
  }
  end_read_record(&info);
6310
  free_io_cache(from);
6311
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
6312

6313
  if (to->file->end_bulk_insert() && error <= 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6314
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
6315
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6316 6317
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
6318
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
6319

6320
  ha_enable_transaction(thd,TRUE);
6321

6322 6323 6324 6325 6326 6327 6328 6329
  /*
    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;
6330

6331
 err:
6332
  thd->variables.sql_mode= save_sql_mode;
6333
  thd->abort_on_warning= 0;
6334
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6335 6336
  *copied= found_count;
  *deleted=delete_count;
6337
  if (to->file->ha_external_lock(thd,F_UNLCK))
6338
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6339 6340
  DBUG_RETURN(error > 0 ? -1 : 0);
}
6341

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

6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354
/*
  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().
*/
6355 6356
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                          bool do_send_ok)
6357 6358 6359 6360 6361 6362 6363 6364 6365
{
  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));
6366
  create_info.db_type= (handlerton*) &default_hton;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
6367
  create_info.row_type=ROW_TYPE_NOT_USED;
6368
  create_info.default_table_charset=default_charset_info;
monty@mysql.com's avatar
monty@mysql.com committed
6369
  /* Force alter table to recreate table */
6370
  lex->alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
6371 6372 6373
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
6374
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
6375 6376 6377
}


6378
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
6379 6380 6381 6382 6383
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
6384
  DBUG_ENTER("mysql_checksum_table");
6385 6386 6387 6388 6389

  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;
6390 6391
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
6392
    DBUG_RETURN(TRUE);
6393

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6394
  for (table= tables; table; table= table->next_local)
6395 6396
  {
    char table_name[NAME_LEN*2+2];
6397
    TABLE *t;
6398

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

6401
    t= table->table= open_ltable(thd, table, TL_READ);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6402
    thd->clear_error();			// these errors shouldn't get client
6403 6404 6405 6406

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

6407
    if (!t)
6408
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6409
      /* Table didn't exist */
6410
      protocol->store_null();
6411
      thd->clear_error();
6412 6413 6414
    }
    else
    {
6415
      t->pos_in_table_list= table;
6416

6417
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
6418 6419
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
6420
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6421
	       (check_opt->flags & T_QUICK))
6422
	protocol->store_null();
6423 6424
      else
      {
6425 6426
	/* calculating table's checksum */
	ha_checksum crc= 0;
6427
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
6428

6429 6430 6431 6432 6433
        /*
          Set all bits in read set and inform InnoDB that we are reading all
          fields
        */
        t->file->ha_retrieve_all_cols();
6434

6435
	if (t->file->ha_rnd_init(1))
6436 6437 6438
	  protocol->store_null();
	else
	{
6439
	  for (;;)
6440 6441
	  {
	    ha_checksum row_crc= 0;
6442 6443 6444 6445 6446 6447 6448
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
6449 6450 6451 6452
	    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
6453 6454 6455
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

6456 6457
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
6458

6459
	    for (uint i= 0; i < t->s->fields; i++ )
6460 6461
	    {
	      Field *f= t->field[i];
6462 6463
	      if ((f->type() == FIELD_TYPE_BLOB) ||
                  (f->type() == MYSQL_TYPE_VARCHAR))
6464 6465 6466 6467 6468 6469 6470
	      {
		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,
6471
				     f->pack_length());
6472
	    }
6473

6474 6475 6476
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
6477
          t->file->ha_rnd_end();
6478
	}
6479
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6480
      thd->clear_error();
6481 6482 6483 6484 6485 6486 6487 6488
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

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

6491 6492 6493 6494
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
6495
  DBUG_RETURN(TRUE);
6496
}
6497 6498

static bool check_engine(THD *thd, const char *table_name,
6499
                         HA_CREATE_INFO *create_info)
6500
{
6501
  handlerton **new_engine= &create_info->db_type;
6502
  handlerton *req_engine= *new_engine;
6503
  bool no_substitution=
6504
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
6505 6506
  if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
                                  no_substitution, 1)))
6507 6508
    return TRUE;

6509
  if (req_engine != (handlerton*) &default_hton && req_engine != *new_engine)
6510 6511 6512 6513
  {
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
6514
                       ha_resolve_storage_engine_name(*new_engine),
6515 6516
                       table_name);
  }
6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527
  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)
    {
      my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), (*new_engine)->name, "TEMPORARY");
      *new_engine= 0;
      return TRUE;
    }
    *new_engine= &myisam_hton;
  }
6528 6529
  return FALSE;
}