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

   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"
21
#ifdef HAVE_BERKELEY_DB
22
#include "ha_berkeley.h"
23
#endif
24
#include <hash.h>
unknown's avatar
unknown committed
25
#include <myisam.h>
26
#include <my_dir.h>
unknown's avatar
unknown committed
27 28 29 30 31

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

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

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

43 44 45 46 47 48 49 50 51 52 53 54 55 56

/*
 Build the path to a file for a table (or the base path that can
 then have various extensions stuck on to it).

  SYNOPSIS
   build_table_path()
   buff                 Buffer to build the path into
   bufflen              sizeof(buff)
   db                   Name of database
   table                Name of table
   ext                  Filename extension

  RETURN
57 58
    0                   Error
    #                   Size of path
59 60
 */

61
static uint build_table_path(char *buff, size_t bufflen, const char *db,
62 63 64 65
                             const char *table, const char *ext)
{
  strxnmov(buff, bufflen-1, mysql_data_home, "/", db, "/", table, ext,
           NullS);
66
  return unpack_filename(buff,buff);
67 68 69 70
}



unknown's avatar
unknown committed
71 72 73 74 75
/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table()
76 77 78
   thd			Thread handle
   tables		List of tables to delete
   if_exists		If 1, don't give error if one table doesn't exists
unknown's avatar
unknown committed
79 80 81 82 83 84 85 86 87 88

  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
89 90
    0		ok.  In this case ok packet is sent to user
    -1		Error  (Error message given but not sent to user)
unknown's avatar
unknown committed
91 92

*/
unknown's avatar
unknown committed
93

94
int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
95
		   my_bool drop_temporary)
unknown's avatar
unknown committed
96
{
unknown's avatar
merge  
unknown committed
97
  int error= 0;
unknown's avatar
unknown committed
98 99 100 101 102 103 104 105
  DBUG_ENTER("mysql_rm_table");

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

  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

106
  if (!drop_temporary && global_read_lock)
unknown's avatar
unknown committed
107
  {
unknown's avatar
unknown committed
108
    if (thd->global_read_lock)
109
    {
unknown's avatar
unknown committed
110
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
111
	       tables->real_name);
112
      error= 1;
unknown's avatar
unknown committed
113 114 115 116 117
      goto err;
    }
    while (global_read_lock && ! thd->killed)
    {
      (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
118
    }
unknown's avatar
unknown committed
119 120

  }
121
  error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary, 0);
122

123
 err:
124 125 126 127 128 129 130 131 132
  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);

  if (error)
    DBUG_RETURN(-1);
133
  send_ok(thd);
134 135 136
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
137 138 139 140 141 142

/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table_part2_with_lock()
143 144 145 146
   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
unknown's avatar
unknown committed
147 148 149 150 151 152

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

 RETURN
153 154
  0	ok
  1	error
unknown's avatar
unknown committed
155 156
*/

unknown's avatar
unknown committed
157
int mysql_rm_table_part2_with_lock(THD *thd,
158
				   TABLE_LIST *tables, bool if_exists,
159
				   bool drop_temporary, bool dont_log_query)
unknown's avatar
unknown committed
160 161 162 163 164 165
{
  int error;
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

166
  error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary,
167
			     dont_log_query);
unknown's avatar
unknown committed
168 169 170 171 172 173 174 175 176 177

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

unknown's avatar
unknown committed
178

179
/*
180 181 182 183
  Execute the drop of a normal or temporary table

  SYNOPSIS
    mysql_rm_table_part2()
184 185 186 187 188 189
    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
    dont_log_query	Don't log the query
190

191 192 193 194 195 196 197 198 199
  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.
200 201

 RETURN
202 203 204
   0	ok
   1	Error
   -1	Thread was killed
205
*/
206 207

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
208
			 bool drop_temporary, bool dont_log_query)
209 210
{
  TABLE_LIST *table;
211
  char	path[FN_REFLEN], *alias;
212 213
  String wrong_tables;
  int error;
unknown's avatar
unknown committed
214
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
215 216
  DBUG_ENTER("mysql_rm_table_part2");

217
  if (lock_table_names(thd, tables))
218
    DBUG_RETURN(1);
219

unknown's avatar
unknown committed
220
  for (table=tables ; table ; table=table->next)
unknown's avatar
unknown committed
221
  {
222
    char *db=table->db;
223
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
unknown's avatar
unknown committed
224
    if (!close_temporary_table(thd, db, table->real_name))
unknown's avatar
unknown committed
225
    {
226
      tmp_table_deleted=1;
227
      continue;					// removed temporary table
unknown's avatar
unknown committed
228
    }
unknown's avatar
unknown committed
229 230

    error=0;
231
    if (!drop_temporary)
unknown's avatar
unknown committed
232
    {
233
      abort_locked_tables(thd,db,table->real_name);
unknown's avatar
unknown committed
234 235 236
      remove_table_from_cache(thd,db,table->real_name,
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
237 238
      drop_locked_tables(thd,db,table->real_name);
      if (thd->killed)
239
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
240
      alias= (lower_case_table_names == 2) ? table->alias : table->real_name;
241
      /* remove form file and isam files */
242
      build_table_path(path, sizeof(path), db, alias, reg_ext);
unknown's avatar
unknown committed
243
    }
244 245 246
    if (drop_temporary ||
	(access(path,F_OK) &&
         ha_create_table_from_engine(thd, db, alias)))
unknown's avatar
unknown committed
247
    {
248
      // Table was not found on disk and table can't be created from engine
249
      if (if_exists)
250 251 252
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
			    table->real_name);
253
      else
254
        error= 1;
unknown's avatar
unknown committed
255 256 257
    }
    else
    {
unknown's avatar
unknown committed
258
      char *end;
unknown's avatar
unknown committed
259
      db_type table_type= get_table_type(path);
260
      *(end=fn_ext(path))=0;			// Remove extension for delete
unknown's avatar
unknown committed
261 262
      error=ha_delete_table(table_type, path);
      if (error == ENOENT && if_exists)
263
	error = 0;
unknown's avatar
unknown committed
264
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
265
      {
266 267
	/* the table is referenced by a foreign key constraint */
	foreign_key_error=1;
unknown's avatar
unknown committed
268
      }
unknown's avatar
unknown committed
269 270
      if (!error || error == ENOENT)
      {
271 272 273 274
	/* Delete the table definition file */
	strmov(end,reg_ext);
	if (!(error=my_delete(path,MYF(MY_WME))))
	  some_tables_deleted=1;
unknown's avatar
unknown committed
275
      }
unknown's avatar
unknown committed
276 277 278 279
    }
    if (error)
    {
      if (wrong_tables.length())
280
	wrong_tables.append(',');
281
      wrong_tables.append(String(table->real_name,system_charset_info));
unknown's avatar
unknown committed
282 283
    }
  }
unknown's avatar
unknown committed
284
  thd->tmp_table_used= tmp_table_deleted;
285 286 287 288
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
289
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
unknown's avatar
unknown committed
290
                     wrong_tables.c_ptr());
291 292 293 294 295 296
    else
      my_error(ER_ROW_IS_REFERENCED, MYF(0));
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
297
  {
unknown's avatar
unknown committed
298
    query_cache_invalidate3(thd, tables, 0);
unknown's avatar
unknown committed
299
    if (!dont_log_query)
300
    {
unknown's avatar
unknown committed
301 302 303
      mysql_update_log.write(thd, thd->query,thd->query_length);
      if (mysql_bin_log.is_open())
      {
304 305
        if (!error)
          thd->clear_error();
306
	Query_log_event qinfo(thd, thd->query, thd->query_length,
307 308
			      tmp_table_deleted && !some_tables_deleted, 
			      FALSE);
309
	mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
310
      }
311
    }
unknown's avatar
unknown committed
312
  }
unknown's avatar
unknown committed
313

314
  unlock_table_names(thd, tables);
315
  DBUG_RETURN(error);
unknown's avatar
unknown committed
316 317 318 319
}


int quick_rm_table(enum db_type base,const char *db,
320
		   const char *table_name)
unknown's avatar
unknown committed
321 322 323
{
  char path[FN_REFLEN];
  int error=0;
324
  build_table_path(path, sizeof(path), db, table_name, reg_ext);
unknown's avatar
unknown committed
325 326
  if (my_delete(path,MYF(0)))
    error=1; /* purecov: inspected */
327
  *fn_ext(path)= 0;                             // Remove reg_ext
unknown's avatar
unknown committed
328 329 330
  return ha_delete_table(base,path) || error;
}

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
/*
  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;
349
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
350 351
    {
      /* Sort NOT NULL keys before other keys */
352
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
353 354 355 356 357 358 359
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
  }
  else if (b->flags & HA_NOSAME)
360
    return 1;					// Prefer b
361 362 363 364 365

  if ((a->flags ^ b->flags) & HA_FULLTEXT)
  {
    return (a->flags & HA_FULLTEXT) ? 1 : -1;
  }
unknown's avatar
unknown committed
366
  /*
367
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
368 369 370
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
371 372
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
373 374
}

375 376
/*
  Check TYPELIB (set or enum) for duplicates
377

378 379 380
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
381 382
    name	  name of the checked column
    typelib	  list of values for the column
383 384

  DESCRIPTION
385
    This function prints an warning for each value in list
386 387 388 389 390 391 392
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
393 394
                                  const char *name, TYPELIB *typelib,
                                  CHARSET_INFO *cs)
395
{
396
  TYPELIB tmp= *typelib;
397
  const char **cur_value= typelib->type_names;
398 399 400
  unsigned int *cur_length= typelib->type_lengths;
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
401
  {
402 403 404 405
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
406
    {
unknown's avatar
unknown committed
407
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
408 409 410
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
411 412 413
    }
  }
}
414

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449

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


unknown's avatar
unknown committed
450
/*
451
  Preparation for table creation
unknown's avatar
unknown committed
452 453

  SYNOPSIS
454
    mysql_prepare_table()
455 456 457 458
    thd			Thread object
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
459

460
  DESCRIPTION
461
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
462 463

  RETURN VALUES
464 465
    0	ok
    -1	error
unknown's avatar
unknown committed
466
*/
unknown's avatar
unknown committed
467

468
int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
469 470 471 472
			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)
unknown's avatar
unknown committed
473
{
474 475 476
  const char	*key_name;
  create_field	*sql_field,*dup_field;
  uint		field,null_fields,blob_columns;
unknown's avatar
unknown committed
477
  ulong		record_offset= 0;
478
  KEY		*key_info;
unknown's avatar
unknown committed
479
  KEY_PART_INFO *key_part_info;
480 481 482
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
483
  DBUG_ENTER("mysql_prepare_table");
unknown's avatar
unknown committed
484 485

  List_iterator<create_field> it(fields),it2(fields);
486
  select_field_pos=fields.elements - select_field_count;
unknown's avatar
unknown committed
487
  null_fields=blob_columns=0;
488

489
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
490
  {
491
    if (!sql_field->charset)
492 493 494
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
495 496 497
      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.
498
    */
499
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
500
      sql_field->charset= create_info->table_charset;
501 502 503 504 505 506 507 508 509 510 511

    CHARSET_INFO *savecs= sql_field->charset;
    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];
      strmake(strmake(tmp, savecs->csname, sizeof(tmp)-4), "_bin", 4);
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
512

513 514
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
515 516 517
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
518
      TYPELIB *interval= sql_field->interval;
519 520 521 522 523 524

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
525
      if (!interval)
526
      {
527 528 529 530 531 532 533 534
        /*
          Create the typelib in prepared statement memory if we're
          executing one.
        */
        MEM_ROOT *stmt_root= thd->current_arena->mem_root;

        interval= sql_field->interval= typelib(stmt_root,
                                               sql_field->interval_list);
535 536 537
        List_iterator<String> it(sql_field->interval_list);
        String conv, *tmp;
        for (uint i= 0; (tmp= it++); i++)
538
        {
539 540 541 542 543
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
544
            char *buf= (char*) alloc_root(stmt_root, conv.length()+1);
545 546 547 548 549
            memcpy(buf, conv.ptr(), conv.length());
            buf[conv.length()]= '\0';
            interval->type_names[i]= buf;
            interval->type_lengths[i]= conv.length();
          }
550

551 552 553 554 555 556 557
          // Strip trailing spaces.
          uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                            interval->type_lengths[i]);
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
        }
        sql_field->interval_list.empty(); // Don't need interval_list anymore
558 559 560 561 562 563
      }

      /*
        Convert the default value from client character
        set into the column character set if necessary.
      */
unknown's avatar
unknown committed
564
      if (sql_field->def && cs != sql_field->def->collation.collation)
565
      {
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
        Item_arena backup_arena;
        bool need_to_change_arena=
          !thd->current_arena->is_conventional_execution();
        if (need_to_change_arena)
        {
          /* Asser that we don't do that at every PS execute */
          DBUG_ASSERT(thd->current_arena->is_first_stmt_execute());
          thd->set_n_backup_item_arena(thd->current_arena, &backup_arena);
        }

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

        if (need_to_change_arena)
          thd->restore_backup_item_arena(thd->current_arena, &backup_arena);

        if (! sql_field->def)
unknown's avatar
unknown committed
582 583 584 585 586
        {
          /* Could not convert */
          my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
          DBUG_RETURN(-1);
        }
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
      }

      if (sql_field->sql_type == FIELD_TYPE_SET)
      {
        if (sql_field->def)
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
          def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
          (void) find_set(interval, def->ptr(), def->length(),
                          cs, &not_used, &not_used2, &not_found);
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
        calculate_interval_lengths(cs, interval, &dummy, &sql_field->length);
        sql_field->length+= (interval->count - 1);
      }
      else  /* FIELD_TYPE_ENUM */
      {
        if (sql_field->def)
        {
          String str, *def= sql_field->def->val_str(&str);
          def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
          if (!find_type2(interval, def->ptr(), def->length(), cs))
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
        calculate_interval_lengths(cs, interval, &sql_field->length, &dummy);
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

626
    sql_field->create_length_to_internal_length();
627

628
    /* Don't pack keys in old tables if the user has requested this */
unknown's avatar
unknown committed
629
    if ((sql_field->flags & BLOB_FLAG) ||
630 631
	sql_field->sql_type == FIELD_TYPE_VAR_STRING &&
	create_info->row_type != ROW_TYPE_FIXED)
unknown's avatar
unknown committed
632 633 634 635 636
    {
      db_options|=HA_OPTION_PACK_RECORD;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
637

unknown's avatar
unknown committed
638 639
    if (check_column_name(sql_field->field_name))
    {
640
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
unknown's avatar
unknown committed
641 642
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
643

644 645
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
unknown's avatar
unknown committed
646
    {
647
      if (my_strcasecmp(system_charset_info,
648 649
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
650
      {
651 652 653 654 655 656 657 658 659 660 661 662
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
	  my_error(ER_DUP_FIELDNAME,MYF(0),sql_field->field_name);
	  DBUG_RETURN(-1);
	}
	else
	{
	  /* Field redefined */
663
	  sql_field->def=		dup_field->def;
664 665 666 667
	  sql_field->sql_type=		dup_field->sql_type;
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
668 669
	  sql_field->length=		dup_field->chars_length;
          sql_field->pack_length=	dup_field->pack_length;
670 671 672
	  sql_field->create_length_to_internal_length();
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->unireg_check=	dup_field->unireg_check;
673 674 675 676 677 678 679 680
          /* 
            We're making one field from two, the result field will have
            dup_field->flags as flags. If we've incremented null_fields
            because of sql_field->flags, decrement it back.
          */
          if (!(sql_field->flags & NOT_NULL_FLAG))
            null_fields--;
	  sql_field->flags=		dup_field->flags;
unknown's avatar
unknown committed
681
          sql_field->interval=          dup_field->interval;
682 683 684 685
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
	}
unknown's avatar
unknown committed
686 687 688 689
      }
    }
    it2.rewind();
  }
690 691 692

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
693 694 695 696

  it.rewind();
  while ((sql_field=it++))
  {
697 698
    DBUG_ASSERT(sql_field->charset);

unknown's avatar
unknown committed
699 700 701 702 703 704
    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 |
705 706
	pack_length_to_packflag(sql_field->pack_length -
				portable_sizeof_char_ptr);
707
      if (sql_field->charset->state & MY_CS_BINSORT)
708 709
	sql_field->pack_flag|=FIELDFLAG_BINARY;
      sql_field->length=8;			// Unireg field length
unknown's avatar
unknown committed
710 711 712
      sql_field->unireg_check=Field::BLOB_FIELD;
      blob_columns++;
      break;
unknown's avatar
unknown committed
713
    case FIELD_TYPE_GEOMETRY:
unknown's avatar
unknown committed
714
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
715
      if (!(file->table_flags() & HA_CAN_GEOMETRY))
716
      {
717 718 719
	my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
			MYF(0), "GEOMETRY");
	DBUG_RETURN(-1);
720
      }
unknown's avatar
unknown committed
721
      sql_field->pack_flag=FIELDFLAG_GEOM |
722 723
	pack_length_to_packflag(sql_field->pack_length -
				portable_sizeof_char_ptr);
unknown's avatar
unknown committed
724
      if (sql_field->charset->state & MY_CS_BINSORT)
725 726
	sql_field->pack_flag|=FIELDFLAG_BINARY;
      sql_field->length=8;			// Unireg field length
unknown's avatar
unknown committed
727 728 729
      sql_field->unireg_check=Field::BLOB_FIELD;
      blob_columns++;
      break;
unknown's avatar
unknown committed
730 731
#else
      my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
732
		      sym_group_geom.name, sym_group_geom.needed_define);
unknown's avatar
unknown committed
733 734
      DBUG_RETURN(-1);
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
735 736 737
    case FIELD_TYPE_VAR_STRING:
    case FIELD_TYPE_STRING:
      sql_field->pack_flag=0;
738
      if (sql_field->charset->state & MY_CS_BINSORT)
739
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
740 741 742
      break;
    case FIELD_TYPE_ENUM:
      sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
743
	FIELDFLAG_INTERVAL;
744
      if (sql_field->charset->state & MY_CS_BINSORT)
745
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
746
      sql_field->unireg_check=Field::INTERVAL_FIELD;
747
      check_duplicates_in_interval("ENUM",sql_field->field_name,
748 749
                                   sql_field->interval,
                                   sql_field->charset);
unknown's avatar
unknown committed
750 751 752
      break;
    case FIELD_TYPE_SET:
      sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
753
	FIELDFLAG_BITFIELD;
754
      if (sql_field->charset->state & MY_CS_BINSORT)
755
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
756
      sql_field->unireg_check=Field::BIT_FIELD;
757
      check_duplicates_in_interval("SET",sql_field->field_name,
758 759
                                   sql_field->interval,
                                   sql_field->charset);
unknown's avatar
unknown committed
760
      break;
761
    case FIELD_TYPE_DATE:			// Rest of string types
unknown's avatar
unknown committed
762 763 764 765 766 767 768
    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_TIMESTAMP:
769 770 771
      /* We should replace old TIMESTAMP fields with their newer analogs */
      if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
      {
772 773 774 775 776 777 778
	if (!timestamps)
	{
	  sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
	  timestamps_with_niladic++;
	}
	else
	  sql_field->unireg_check= Field::NONE;
779
      }
780
      else if (sql_field->unireg_check != Field::NONE)
781 782
	timestamps_with_niladic++;

783
      timestamps++;
784
      /* fall-through */
unknown's avatar
unknown committed
785 786
    default:
      sql_field->pack_flag=(FIELDFLAG_NUMBER |
787 788 789 790 791 792
			    (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));
unknown's avatar
unknown committed
793 794 795 796
      break;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      sql_field->pack_flag|=FIELDFLAG_MAYBE_NULL;
797
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
798 799
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
800
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
801
  }
802 803 804 805 806
  if (timestamps_with_niladic > 1)
  {
    my_error(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,MYF(0));
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
807 808 809 810 811 812
  if (auto_increment > 1)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
813
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
814 815 816 817 818
  {
    my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0));
    DBUG_RETURN(-1);
  }

819
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
820 821 822 823 824 825
  {
    my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0));
    DBUG_RETURN(-1);
  }

  /* Create keys */
826

827
  List_iterator<Key> key_iterator(keys), key_iterator2(keys);
828
  uint key_parts=0, fk_key_count=0;
829
  bool primary_key=0,unique_key=0;
830
  Key *key, *key2;
unknown's avatar
unknown committed
831
  uint tmp, key_number;
832 833
  /* special marker for keys to be ignored */
  static char ignore_key[1];
834

835
  /* Calculate number of key segements */
836
  *key_count= 0;
837

unknown's avatar
unknown committed
838 839
  while ((key=key_iterator++))
  {
840 841 842 843 844
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
845
	  fk_key->ref_columns.elements != fk_key->columns.elements)
846
      {
847 848 849 850
	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));
	DBUG_RETURN(-1);
851 852 853
      }
      continue;
    }
854
    (*key_count)++;
unknown's avatar
unknown committed
855
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
856 857 858 859 860
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
861
    if (key->name && strlen(key->name) > NAME_LEN)
unknown's avatar
unknown committed
862
    {
863
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
unknown's avatar
unknown committed
864 865
      DBUG_RETURN(-1);
    }
866
    key_iterator2.rewind ();
867
    if (key->type != Key::FOREIGN_KEY)
868
    {
869
      while ((key2 = key_iterator2++) != key)
870
      {
unknown's avatar
unknown committed
871
	/*
872 873 874
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
unknown's avatar
unknown committed
875
        */
876 877 878
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
879
        {
880
          /* TODO: issue warning message */
881 882 883 884 885 886 887 888 889 890 891 892 893
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
          }
          break;
        }
894 895 896 897 898 899
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
900
    if (key->name && !tmp_table &&
901
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
902 903 904 905
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
906
  }
unknown's avatar
unknown committed
907
  tmp=file->max_keys();
908
  if (*key_count > tmp)
909 910 911 912
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
913

914
  key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)* *key_count);
915 916
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
  if (!key_info_buffer || ! key_part_info)
917
    DBUG_RETURN(-1);				// Out of memory
918

919
  key_iterator.rewind();
unknown's avatar
unknown committed
920
  key_number=0;
unknown's avatar
unknown committed
921
  for (; (key=key_iterator++) ; key_number++)
922 923 924 925
  {
    uint key_length=0;
    key_part_spec *column;

926 927 928 929 930 931 932 933 934 935
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

unknown's avatar
unknown committed
936
    switch(key->type){
unknown's avatar
unknown committed
937
    case Key::MULTIPLE:
938
	key_info->flags= 0;
939
	break;
unknown's avatar
unknown committed
940
    case Key::FULLTEXT:
941
	key_info->flags= HA_FULLTEXT;
942
	break;
unknown's avatar
unknown committed
943
    case Key::SPATIAL:
unknown's avatar
unknown committed
944
#ifdef HAVE_SPATIAL
945
	key_info->flags= HA_SPATIAL;
946
	break;
unknown's avatar
unknown committed
947
#else
948 949 950
	my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0),
			sym_group_geom.name, sym_group_geom.needed_define);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
951
#endif
952
    case Key::FOREIGN_KEY:
953
      key_number--;				// Skip this key
954 955
      continue;
    default:
956 957
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
958
    }
959 960
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
961

unknown's avatar
unknown committed
962 963
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
964
    key_info->usable_key_parts= key_number;
965
    key_info->algorithm=key->algorithm;
unknown's avatar
unknown committed
966

967 968
    if (key->type == Key::FULLTEXT)
    {
969
      if (!(file->table_flags() & HA_CAN_FULLTEXT))
970
      {
971 972
	my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0));
	DBUG_RETURN(-1);
973 974
      }
    }
unknown's avatar
unknown committed
975 976 977
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
978
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
979 980 981
       in near future when new frm file is ready
       checking for proper key parts number:
    */
982

983
    /* TODO: Add proper checks if handler supports key_type and algorithm */
984
    if (key_info->flags & HA_SPATIAL)
985 986 987
    {
      if (key_info->key_parts != 1)
      {
988 989 990
	my_printf_error(ER_WRONG_ARGUMENTS,
			ER(ER_WRONG_ARGUMENTS),MYF(0),"SPATIAL INDEX");
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
991
      }
992
    }
unknown's avatar
unknown committed
993
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
994
    {
unknown's avatar
unknown committed
995
#ifdef HAVE_RTREE_KEYS
996 997
      if ((key_info->key_parts & 1) == 1)
      {
998 999 1000
	my_printf_error(ER_WRONG_ARGUMENTS,
			ER(ER_WRONG_ARGUMENTS),MYF(0),"RTREE INDEX");
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
1001
      }
1002 1003
      /* TODO: To be deleted */
      my_printf_error(ER_NOT_SUPPORTED_YET, ER(ER_NOT_SUPPORTED_YET),
1004
		      MYF(0), "RTREE INDEX");
1005
      DBUG_RETURN(-1);
unknown's avatar
unknown committed
1006 1007
#else
      my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0),
1008
		      sym_group_rtree.name, sym_group_rtree.needed_define);
unknown's avatar
unknown committed
1009 1010
      DBUG_RETURN(-1);
#endif
unknown's avatar
unknown committed
1011
    }
1012

1013
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
1014
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
1015 1016
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
unknown's avatar
unknown committed
1017 1018
      key_part_spec *dup_column;

unknown's avatar
unknown committed
1019 1020 1021
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
1022 1023 1024 1025
	     my_strcasecmp(system_charset_info,
			   column->field_name,
			   sql_field->field_name))
	field++;
unknown's avatar
unknown committed
1026 1027
      if (!sql_field)
      {
1028 1029 1030 1031
	my_printf_error(ER_KEY_COLUMN_DOES_NOT_EXITS,
			ER(ER_KEY_COLUMN_DOES_NOT_EXITS),MYF(0),
			column->field_name);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
1032
      }
unknown's avatar
unknown committed
1033
      while ((dup_column= cols2++) != column)
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
      {
        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();
1045
      if (key->type == Key::FULLTEXT)
1046
      {
1047 1048 1049 1050
	if ((sql_field->sql_type != FIELD_TYPE_STRING &&
	     sql_field->sql_type != FIELD_TYPE_VAR_STRING &&
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
1051
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
	    my_printf_error(ER_BAD_FT_COLUMN,ER(ER_BAD_FT_COLUMN),MYF(0),
			    column->field_name);
	    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));
1067
      }
1068
      else
1069
      {
1070 1071 1072 1073
	column->length*= sql_field->charset->mbmaxlen;

	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
1074
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
	  {
	    my_printf_error(ER_BLOB_USED_AS_KEY,ER(ER_BLOB_USED_AS_KEY),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	  }
	  if (!column->length)
	  {
	    my_printf_error(ER_BLOB_KEY_WITHOUT_LENGTH,
			    ER(ER_BLOB_KEY_WITHOUT_LENGTH),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	  }
	}
unknown's avatar
unknown committed
1088
#ifdef HAVE_SPATIAL
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
	if (key->type  == Key::SPATIAL)
	{
	  if (!column->length )
	  {
	    /*
	    BAR: 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
		 Lately we'll extend this code to support more dimensions
	    */
	    column->length=4*sizeof(double);
	  }
	}
unknown's avatar
unknown committed
1100
#endif
1101 1102 1103 1104 1105 1106 1107
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
unknown's avatar
unknown committed
1108
            null_fields--;
1109 1110 1111
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
unknown's avatar
unknown committed
1112
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
	  {
	    my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
			    MYF(0),column->field_name);
	    DBUG_RETURN(-1);
	  }
	  if (key->type == Key::SPATIAL)
	  {
	    my_error(ER_SPATIAL_CANT_HAVE_NULL, MYF(0));
	    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
	}
unknown's avatar
unknown committed
1129
      }
1130

unknown's avatar
unknown committed
1131 1132 1133 1134 1135 1136
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
      uint length=sql_field->pack_length;
      if (column->length)
      {
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
	if (f_is_blob(sql_field->pack_flag))
	{
	  if ((length=column->length) > file->max_key_length() ||
	      length > file->max_key_part_length())
	  {
	    length=min(file->max_key_length(), file->max_key_part_length());
	    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);
	    }
	  }
	}
	else if (!f_is_geom(sql_field->pack_flag) &&
		  (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)))
	{
	  my_error(ER_WRONG_SUB_KEY,MYF(0));
	  DBUG_RETURN(-1);
	}
	else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
	  length=column->length;
unknown's avatar
unknown committed
1171 1172 1173
      }
      else if (length == 0)
      {
1174 1175 1176
	my_printf_error(ER_WRONG_KEY_COLUMN, ER(ER_WRONG_KEY_COLUMN), MYF(0),
			column->field_name);
	  DBUG_RETURN(-1);
unknown's avatar
unknown committed
1177
      }
1178 1179
      if (length > file->max_key_part_length())
      {
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
	length=file->max_key_part_length();
	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);
	}
1195 1196
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
1197 1198
      /* Use packed keys for long strings on the first column */
      if (!(db_options & HA_OPTION_NO_PACK_KEYS) &&
1199 1200 1201 1202
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
	   (sql_field->sql_type == FIELD_TYPE_STRING ||
	    sql_field->sql_type == FIELD_TYPE_VAR_STRING ||
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
unknown's avatar
unknown committed
1203
      {
1204 1205 1206 1207
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB))
	  key_info->flags|= HA_BINARY_PACK_KEY;
	else
	  key_info->flags|= HA_PACK_KEY;
unknown's avatar
unknown committed
1208 1209 1210 1211 1212 1213 1214
      }
      key_length+=length;
      key_part_info++;

      /* Create the key name based on the first column (if not given) */
      if (column_nr == 0)
      {
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
	if (key->type == Key::PRIMARY)
	{
	  if (primary_key)
	  {
	    my_error(ER_MULTIPLE_PRI_KEY,MYF(0));
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
	else if (!(key_name = key->name))
	  key_name=make_unique_key_name(sql_field->field_name,
					key_info_buffer,key_info);
	if (check_if_keyname_exists(key_name,key_info_buffer,key_info))
	{
	  my_error(ER_DUP_KEYNAME,MYF(0),key_name);
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
unknown's avatar
unknown committed
1234 1235
      }
    }
1236 1237
    if (!key_info->name || check_column_name(key_info->name))
    {
1238
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
1239 1240
      DBUG_RETURN(-1);
    }
1241 1242
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
1243
    key_info->key_length=(uint16) key_length;
unknown's avatar
unknown committed
1244
    uint max_key_length= file->max_key_length();
1245
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
1246
    {
1247
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
unknown's avatar
unknown committed
1248 1249
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
1250
    key_info++;
unknown's avatar
unknown committed
1251
  }
1252
  if (!unique_key && !primary_key &&
1253
      (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
1254 1255 1256 1257
  {
    my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1258 1259 1260 1261 1262
  if (auto_increment > 0)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
1263
  /* Sort keys in optimized order */
1264 1265
  qsort((gptr) key_info_buffer, *key_count, sizeof(KEY),
	(qsort_cmp) sort_keys);
unknown's avatar
unknown committed
1266
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
1267

1268 1269 1270
  DBUG_RETURN(0);
}

1271

1272 1273 1274 1275 1276
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1277 1278 1279 1280 1281 1282 1283 1284
    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
    tmp_table		Set to 1 if this is an internal temporary table
			(From ALTER TABLE)
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294

  DESCRIPTION
    If one creates a temporary table, this is automaticly opened

    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
1295 1296
    0	ok
    -1	error
1297 1298 1299
*/

int mysql_create_table(THD *thd,const char *db, const char *table_name,
1300 1301
		       HA_CREATE_INFO *create_info,
		       List<create_field> &fields,
1302
		       List<Key> &keys,bool tmp_table,
1303
		       uint select_field_count)
1304
{
1305 1306 1307 1308 1309 1310 1311
  char		path[FN_REFLEN];
  const char	*alias;
  int		error= -1;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
  enum db_type	new_db_type;
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
  DBUG_ENTER("mysql_create_table");

  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
    my_error(ER_TABLE_MUST_HAVE_COLUMNS,MYF(0));
    DBUG_RETURN(-1);
  }
  if ((new_db_type= ha_checktype(create_info->db_type)) !=
      create_info->db_type)
  {
    create_info->db_type= new_db_type;
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
1325 1326 1327 1328
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			table_name);
1329 1330 1331 1332 1333 1334 1335
  }
  db_options=create_info->table_options;
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
  file=get_new_handler((TABLE*) 0, create_info->db_type);

unknown's avatar
unknown committed
1336 1337 1338 1339 1340 1341 1342 1343
#ifdef NOT_USED
  /*
    if there is a technical reason for a handler not to have support
    for temp. tables this code can be re-enabled.
    Otherwise, if a handler author has a wish to prohibit usage of
    temporary tables for his handler he should implement a check in
    ::create() method
  */
1344 1345 1346 1347 1348 1349
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      (file->table_flags() & HA_NO_TEMP_TABLES))
  {
    my_error(ER_ILLEGAL_HA,MYF(0),table_name);
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1350
#endif
1351

1352 1353 1354 1355 1356 1357 1358 1359 1360
  /*
    If the table character set was not given explicitely,
    let's fetch the database default character set and
    apply it to the table.
  */
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
    char  path[FN_REFLEN];
1361 1362
    /* Abuse build_table_path() to build the path to the db.opt file */
    build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, "");
1363 1364 1365 1366
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }

1367
  if (mysql_prepare_table(thd, create_info, fields,
1368 1369 1370
			  keys, tmp_table, db_options, file,
			  key_info_buffer, &key_count,
			  select_field_count))
1371 1372
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
1373 1374 1375
      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1376 1377 1378
    my_snprintf(path, sizeof(path), "%s%s%lx_%lx_%x%s",
		mysql_tmpdir, tmp_file_prefix, current_pid, thd->thread_id,
		thd->tmp_table++, reg_ext);
1379
    if (lower_case_table_names)
1380
      my_casedn_str(files_charset_info, path);
unknown's avatar
unknown committed
1381 1382 1383
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
1384 1385
    build_table_path(path, sizeof(path), db, alias, reg_ext);

unknown's avatar
unknown committed
1386 1387 1388 1389
  /* Check if table already exists */
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
      && find_temporary_table(thd,db,table_name))
  {
1390
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1391
    {
1392
      create_info->table_existed= 1;		// Mark that table existed
1393 1394 1395
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
1396
      DBUG_RETURN(0);
1397
    }
unknown's avatar
unknown committed
1398
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
unknown's avatar
unknown committed
1399 1400
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1401
  if (wait_if_global_read_lock(thd, 0, 1))
1402
    DBUG_RETURN(error);
unknown's avatar
unknown committed
1403 1404 1405 1406 1407 1408
  VOID(pthread_mutex_lock(&LOCK_open));
  if (!tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1409 1410
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1411
      goto end;
unknown's avatar
unknown committed
1412 1413 1414
    }
  }

unknown's avatar
unknown committed
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427
  /*
    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;
1428
    if (ha_table_exists_in_engine(thd, db, table_name))
unknown's avatar
unknown committed
1429
    {
1430
      DBUG_PRINT("info", ("Table with same name already existed in handler"));
unknown's avatar
unknown committed
1431 1432

      if (create_if_not_exists)
1433 1434
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
unknown's avatar
unknown committed
1435 1436 1437 1438
      goto end;
    }
  }

unknown's avatar
unknown committed
1439
  thd->proc_info="creating table";
1440
  create_info->table_existed= 0;		// Mark that table is created
unknown's avatar
unknown committed
1441

unknown's avatar
unknown committed
1442
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1443
    create_info->data_file_name= create_info->index_file_name= 0;
unknown's avatar
unknown committed
1444
  create_info->table_options=db_options;
1445

unknown's avatar
unknown committed
1446
  if (rea_create_table(thd, path, db, table_name,
1447
                       create_info, fields, key_count,
1448
		       key_info_buffer))
unknown's avatar
unknown committed
1449 1450 1451 1452 1453 1454 1455 1456 1457
    goto end;
  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);
      goto end;
    }
unknown's avatar
unknown committed
1458
    thd->tmp_table_used= 1;
unknown's avatar
unknown committed
1459
  }
1460
  if (!tmp_table)
1461 1462 1463 1464 1465
  {
    // Must be written before unlock
    mysql_update_log.write(thd,thd->query, thd->query_length);
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
1466
      thd->clear_error();
1467
      Query_log_event qinfo(thd, thd->query, thd->query_length,
1468
			    test(create_info->options &
1469 1470
				 HA_LEX_CREATE_TMP_TABLE),
			    FALSE);
1471 1472 1473
      mysql_bin_log.write(&qinfo);
    }
  }
unknown's avatar
unknown committed
1474
  error=0;
1475 1476 1477 1478 1479 1480 1481 1482 1483
  goto end;

warn:
  error= 0;
  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                      ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                      alias);
  create_info->table_existed= 1;		// Mark that table existed

unknown's avatar
unknown committed
1484 1485
end:
  VOID(pthread_mutex_unlock(&LOCK_open));
1486
  start_waiting_global_read_lock(thd);
unknown's avatar
unknown committed
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498
  thd->proc_info="After create";
  DBUG_RETURN(error);
}

/*
** 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++)
1499
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509
      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;

1510 1511
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
1512 1513 1514 1515 1516 1517 1518
    return (char*) field_name;			// Use fieldname
  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
  */
1519
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
1520
  {
1521 1522
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
1523 1524 1525
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1526
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
1527 1528
}

1529

unknown's avatar
unknown committed
1530 1531 1532 1533 1534
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
1535 1536 1537 1538 1539
			       const char *db, const char *name,
			       List<create_field> *extra_fields,
			       List<Key> *keys,
			       List<Item> *items,
			       MYSQL_LOCK **lock)
unknown's avatar
unknown committed
1540
{
1541
  TABLE tmp_table;		// Used during 'create_field()'
1542
  TABLE *table= 0;
unknown's avatar
unknown committed
1543
  tmp_table.table_name=0;
1544
  uint select_field_count= items->elements;
unknown's avatar
unknown committed
1545 1546 1547
  DBUG_ENTER("create_table_from_items");

  /* Add selected items to field list */
unknown's avatar
unknown committed
1548
  List_iterator_fast<Item> it(*items);
unknown's avatar
unknown committed
1549 1550 1551 1552 1553 1554
  Item *item;
  Field *tmp_field;
  tmp_table.db_create_options=0;
  tmp_table.null_row=tmp_table.maybe_null=0;
  tmp_table.blob_ptr_size=portable_sizeof_char_ptr;
  tmp_table.db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
1555
				    create_info->db_type == DB_TYPE_HEAP);
unknown's avatar
unknown committed
1556 1557 1558 1559

  while ((item=it++))
  {
    create_field *cr_field;
1560 1561 1562 1563 1564
    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(),
1565
				  (Item ***) 0, &tmp_field, 0, 0, 0);
1566
    if (!field ||
1567 1568 1569
	!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
					   ((Item_field *)item)->field :
					   (Field*) 0))))
unknown's avatar
unknown committed
1570 1571 1572 1573
      DBUG_RETURN(0);
    extra_fields->push_back(cr_field);
  }
  /* create and lock table */
1574
  /* QQ: create and open should be done atomic ! */
1575
  /*
1576
    We don't log the statement, it will be logged later.
1577 1578 1579 1580
    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
1581 1582
    TABLE, which is a wrong order. So we keep binary logging disabled when we
    open_table().
1583
  */
1584
  tmp_disable_binlog(thd);
unknown's avatar
unknown committed
1585
  if (!mysql_create_table(thd,db,name,create_info,*extra_fields,
unknown's avatar
unknown committed
1586
			 *keys,0,select_field_count))
unknown's avatar
unknown committed
1587
  {
1588 1589
    if (!(table=open_table(thd,db,name,name,(bool*) 0)))
      quick_rm_table(create_info->db_type,db,table_case_name(create_info,name));
unknown's avatar
unknown committed
1590
  }
1591 1592 1593
  reenable_binlog(thd);
  if (!table)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
1594
  table->reginfo.lock_type=TL_WRITE;
1595
  if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH)))
unknown's avatar
unknown committed
1596
  {
1597
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1598
    hash_delete(&open_cache,(byte*) table);
1599
    VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
1600
    quick_rm_table(create_info->db_type,db,table_case_name(create_info, name));
unknown's avatar
unknown committed
1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
    DBUG_RETURN(0);
  }
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  DBUG_RETURN(table);
}


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

1612
bool
unknown's avatar
unknown committed
1613
mysql_rename_table(enum db_type base,
1614 1615 1616 1617
		   const char *old_db,
		   const char *old_name,
		   const char *new_db,
		   const char *new_name)
unknown's avatar
unknown committed
1618
{
1619 1620 1621
  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];
unknown's avatar
unknown committed
1622
  handler *file=get_new_handler((TABLE*) 0, base);
unknown's avatar
unknown committed
1623
  int error=0;
unknown's avatar
unknown committed
1624
  DBUG_ENTER("mysql_rename_table");
unknown's avatar
unknown committed
1625

1626 1627 1628 1629 1630 1631 1632 1633
  build_table_path(from, sizeof(from), old_db, old_name, "");
  build_table_path(to, sizeof(to), new_db, new_name, "");

  /*
    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.
   */
unknown's avatar
unknown committed
1634 1635
  if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
  {
1636 1637 1638 1639
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
    build_table_path(lc_from, sizeof(lc_from), old_db, tmp_name, "");
    from_base= lc_from;
unknown's avatar
unknown committed
1640

1641 1642 1643 1644
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
    build_table_path(lc_to, sizeof(lc_to), new_db, tmp_name, "");
    to_base= lc_to;
unknown's avatar
unknown committed
1645 1646
  }

1647
  if (!(error=file->rename_table(from_base, to_base)))
1648 1649 1650
  {
    if (rename_file_ext(from,to,reg_ext))
    {
unknown's avatar
unknown committed
1651
      error=my_errno;
1652
      /* Restore old file name */
1653
      file->rename_table(to_base, from_base);
1654 1655
    }
  }
unknown's avatar
unknown committed
1656
  delete file;
unknown's avatar
unknown committed
1657 1658 1659
  if (error)
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
1660 1661
}

unknown's avatar
unknown committed
1662

unknown's avatar
unknown committed
1663
/*
1664 1665 1666 1667
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
1668 1669 1670 1671
    thd			Thread handler
    table		Table to remove from cache
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
			HA_EXTRA_FORCE_REOPEN if table is not be used
1672 1673 1674 1675 1676 1677 1678
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

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

1681
static void wait_while_table_is_used(THD *thd,TABLE *table,
1682
				     enum ha_extra_function function)
unknown's avatar
unknown committed
1683
{
1684 1685 1686
  DBUG_PRINT("enter",("table: %s", table->real_name));
  DBUG_ENTER("wait_while_table_is_used");
  safe_mutex_assert_owner(&LOCK_open);
unknown's avatar
unknown committed
1687

1688
  VOID(table->file->extra(function));
1689
  /* Mark all tables that are in use as 'old' */
1690
  mysql_lock_abort(thd, table);			// end threads waiting on lock
1691 1692

  /* Wait until all there are no other threads that has this table open */
unknown's avatar
unknown committed
1693
  remove_table_from_cache(thd,table->table_cache_key,
unknown's avatar
unknown committed
1694
                          table->real_name, RTFC_WAIT_OTHER_THREAD_FLAG);
1695 1696
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1697

1698 1699
/*
  Close a cached table
unknown's avatar
unknown committed
1700

1701
  SYNOPSIS
unknown's avatar
unknown committed
1702
    close_cached_table()
1703 1704
    thd			Thread handler
    table		Table to remove from cache
1705 1706 1707 1708

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

1710 1711 1712 1713
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
1714

1715
static bool close_cached_table(THD *thd, TABLE *table)
1716 1717
{
  DBUG_ENTER("close_cached_table");
1718

1719
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
1720 1721
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
1722
  {
1723
    mysql_unlock_tables(thd, thd->lock);
1724
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
1725
  }
1726 1727 1728 1729 1730
  /* 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);
1731
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1732 1733
}

1734
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
1735
			     const char* operator_name, const char* errmsg)
1736

unknown's avatar
unknown committed
1737
{
1738 1739
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
1740 1741 1742 1743
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
  protocol->store("error", 5, system_charset_info);
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
1744
  thd->net.last_error[0]=0;
1745
  if (protocol->write())
unknown's avatar
unknown committed
1746 1747 1748 1749
    return -1;
  return 1;
}

1750

unknown's avatar
unknown committed
1751
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1752
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1753
{
unknown's avatar
unknown committed
1754
  DBUG_ENTER("prepare_for_restore");
1755

unknown's avatar
unknown committed
1756 1757 1758
  if (table->table) // do not overwrite existing tables on restore
  {
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1759 1760
				  "table exists, will not overwrite on restore"
				  ));
unknown's avatar
unknown committed
1761
  }
unknown's avatar
unknown committed
1762
  else
unknown's avatar
unknown committed
1763
  {
1764
    char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
1765
    char src_path[FN_REFLEN], dst_path[FN_REFLEN];
1766
    char* table_name = table->real_name;
unknown's avatar
unknown committed
1767
    char* db = thd->db ? thd->db : table->db;
1768

1769
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
1770
					reg_ext))
unknown's avatar
unknown committed
1771
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
1772

1773
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1774
		mysql_real_data_home, db, table_name);
1775

1776
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
1777
      DBUG_RETURN(-1);
1778

1779
    if (my_copy(src_path,
1780 1781
		fn_format(dst_path, dst_path,"", reg_ext, 4),
		MYF(MY_WME)))
unknown's avatar
unknown committed
1782
    {
1783
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1784
      unlock_table_name(thd, table);
1785
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1786
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1787
				    "Failed copying .frm file"));
unknown's avatar
unknown committed
1788
    }
1789
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
1790
    {
1791
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1792
      unlock_table_name(thd, table);
1793
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1794
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1795
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
1796
    }
unknown's avatar
unknown committed
1797
  }
unknown's avatar
unknown committed
1798

1799 1800 1801 1802
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
unknown's avatar
unknown committed
1803
  if (!(table->table = reopen_name_locked_table(thd, table)))
1804 1805
  {
    pthread_mutex_lock(&LOCK_open);
1806
    unlock_table_name(thd, table);
1807 1808
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1809
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1810
}
1811

1812

unknown's avatar
unknown committed
1813
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
1814
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1815
{
unknown's avatar
unknown committed
1816 1817
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
1818 1819 1820 1821
  DBUG_ENTER("prepare_for_repair");

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

1823
  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
1824
  {
unknown's avatar
unknown committed
1825
    char name[FN_REFLEN];
1826 1827
    build_table_path(name, sizeof(name), table_list->db,
                     table_list->real_name, "");
unknown's avatar
unknown committed
1828
    if (openfrm(name, "", 0, 0, 0, &tmp_table))
1829
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
1830
    table= &tmp_table;
1831
  }
unknown's avatar
unknown committed
1832

unknown's avatar
unknown committed
1833 1834 1835 1836 1837 1838 1839 1840 1841
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
unknown's avatar
unknown committed
1842

unknown's avatar
unknown committed
1843 1844 1845
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext= table->file->bas_ext();
  MY_STAT stat_info;
unknown's avatar
unknown committed
1846

unknown's avatar
unknown committed
1847 1848 1849 1850 1851
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
  if (!ext[0] || !ext[1])
1852
    goto end;					// No data file
unknown's avatar
unknown committed
1853

1854
  strxmov(from, table->path, ext[1], NullS);	// Name of data file
unknown's avatar
unknown committed
1855
  if (!my_stat(from, &stat_info, MYF(0)))
1856
    goto end;				// Can't use USE_FRM flag
unknown's avatar
unknown committed
1857

1858 1859
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
1860

1861 1862 1863 1864 1865 1866 1867
  /* 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);
  }
unknown's avatar
unknown committed
1868
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
1869
  {
unknown's avatar
unknown committed
1870 1871
    error= -1;
    goto end;
unknown's avatar
unknown committed
1872
  }
unknown's avatar
unknown committed
1873
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
1874
  {
1875
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1876
    unlock_table_name(thd, table_list);
1877
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1878
    error= send_check_errmsg(thd, table_list, "repair",
1879
			     "Failed renaming data file");
unknown's avatar
unknown committed
1880 1881 1882 1883 1884 1885 1886 1887
    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",
1888
			     "Failed generating table from .frm file");
unknown's avatar
unknown committed
1889 1890 1891 1892 1893 1894 1895 1896
    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",
1897
			     "Failed restoring .MYD file");
unknown's avatar
unknown committed
1898
    goto end;
unknown's avatar
unknown committed
1899 1900
  }

1901 1902 1903 1904
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
unknown's avatar
unknown committed
1905
  if (!(table_list->table = reopen_name_locked_table(thd, table_list)))
unknown's avatar
unknown committed
1906 1907
  {
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1908
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
1909 1910
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1911 1912 1913

end:
  if (table == &tmp_table)
1914
    closefrm(table);				// Free allocated memory
unknown's avatar
unknown committed
1915
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1916
}
1917

1918

unknown's avatar
unknown committed
1919 1920 1921 1922 1923 1924
/*
  RETURN VALUES
    0   Message sent to net (admin operation went ok)
   -1   Message should be sent by caller 
        (admin operation or network communication failed)
*/
1925
static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
1926 1927 1928 1929 1930 1931 1932 1933 1934
			     HA_CHECK_OPT* check_opt,
			     const char *operator_name,
			     thr_lock_type lock_type,
			     bool open_for_modify,
			     uint extra_open_options,
			     int (*prepare_func)(THD *, TABLE_LIST *,
						 HA_CHECK_OPT *),
			     int (handler::*operator_func)
			     (THD *, HA_CHECK_OPT *))
unknown's avatar
unknown committed
1935 1936 1937
{
  TABLE_LIST *table;
  List<Item> field_list;
1938 1939
  Item *item;
  Protocol *protocol= thd->protocol;
1940
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
1941 1942 1943 1944 1945 1946 1947 1948 1949

  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;
1950
  if (protocol->send_fields(&field_list, 1))
unknown's avatar
unknown committed
1951 1952
    DBUG_RETURN(-1);

1953
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
unknown's avatar
unknown committed
1954 1955 1956
  for (table = tables; table; table = table->next)
  {
    char table_name[NAME_LEN*2+2];
1957
    char* db = table->db;
1958
    bool fatal_error=0;
1959
    strxmov(table_name, db, ".", table->real_name, NullS);
unknown's avatar
unknown committed
1960

unknown's avatar
unknown committed
1961
    thd->open_options|= extra_open_options;
1962
    table->table = open_ltable(thd, table, lock_type);
1963 1964 1965
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
unknown's avatar
unknown committed
1966
    thd->open_options&= ~extra_open_options;
1967

unknown's avatar
unknown committed
1968
    if (prepare_func)
1969
    {
unknown's avatar
unknown committed
1970
      switch ((*prepare_func)(thd, table, check_opt)) {
1971 1972 1973 1974 1975 1976 1977
      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
        ;
unknown's avatar
unknown committed
1978
      }
1979
    }
1980

unknown's avatar
unknown committed
1981 1982 1983
    if (!table->table)
    {
      const char *err_msg;
1984
      protocol->prepare_for_resend();
1985 1986 1987
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error",5, system_charset_info);
unknown's avatar
unknown committed
1988
      if (!(err_msg=thd->net.last_error))
1989
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
1990
      protocol->store(err_msg, system_charset_info);
unknown's avatar
unknown committed
1991
      thd->net.last_error[0]=0;
1992
      if (protocol->write())
1993
	goto err;
unknown's avatar
unknown committed
1994 1995
      continue;
    }
unknown's avatar
unknown committed
1996
    table->table->pos_in_table_list= table;
1997
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
1998
    {
unknown's avatar
unknown committed
1999
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
2000
      protocol->prepare_for_resend();
2001 2002 2003
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error", 5, system_charset_info);
2004
      my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name);
2005
      protocol->store(buff, system_charset_info);
2006
      close_thread_tables(thd);
2007
      table->table=0;				// For query cache
2008
      if (protocol->write())
2009
	goto err;
unknown's avatar
unknown committed
2010 2011 2012
      continue;
    }

2013
    /* Close all instances of the table to allow repair to rename files */
2014
    if (lock_type == TL_WRITE && table->table->version)
2015 2016
    {
      pthread_mutex_lock(&LOCK_open);
2017
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
2018
					      "Waiting to get writelock");
2019
      mysql_lock_abort(thd,table->table);
unknown's avatar
unknown committed
2020
      remove_table_from_cache(thd, table->table->table_cache_key,
unknown's avatar
unknown committed
2021 2022 2023
                              table->table->real_name,
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
2024
      thd->exit_cond(old_message);
2025
      if (thd->killed)
2026
	goto err;
2027 2028 2029
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
2030 2031
    }

2032
    int result_code = (table->table->file->*operator_func)(thd, check_opt);
unknown's avatar
unknown committed
2033 2034 2035
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
2036
    protocol->prepare_for_resend();
2037 2038
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
2039

2040 2041 2042
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
2043 2044
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
2045
      {
2046 2047 2048
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
2049
	protocol->store("note", 4, system_charset_info);
2050
	protocol->store(buf, length, system_charset_info);
2051
      }
unknown's avatar
unknown committed
2052 2053
      break;

2054
    case HA_ADMIN_OK:
2055 2056
      protocol->store("status", 6, system_charset_info);
      protocol->store("OK",2, system_charset_info);
unknown's avatar
unknown committed
2057 2058
      break;

2059
    case HA_ADMIN_FAILED:
2060 2061
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation failed",16, system_charset_info);
unknown's avatar
unknown committed
2062 2063
      break;

unknown's avatar
unknown committed
2064 2065 2066
    case HA_ADMIN_REJECT:
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation need committed state",30, system_charset_info);
unknown's avatar
unknown committed
2067
      open_for_modify= FALSE;
unknown's avatar
unknown committed
2068 2069
      break;

2070
    case HA_ADMIN_ALREADY_DONE:
2071 2072
      protocol->store("status", 6, system_charset_info);
      protocol->store("Table is already up to date", 27, system_charset_info);
2073 2074
      break;

2075
    case HA_ADMIN_CORRUPT:
2076
      protocol->store("error", 5, system_charset_info);
2077
      protocol->store("Corrupt", 7, system_charset_info);
2078
      fatal_error=1;
unknown's avatar
unknown committed
2079 2080
      break;

unknown's avatar
unknown committed
2081
    case HA_ADMIN_INVALID:
2082 2083
      protocol->store("error", 5, system_charset_info);
      protocol->store("Invalid argument",16, system_charset_info);
unknown's avatar
unknown committed
2084 2085
      break;

2086 2087 2088 2089 2090 2091 2092 2093 2094 2095
    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);
      TABLE_LIST *save_next= table->next;
      table->next= 0;
2096
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
2097
      result_code= mysql_recreate_table(thd, table, 0);
2098
      reenable_binlog(thd);
unknown's avatar
unknown committed
2099
      close_thread_tables(thd);
2100 2101 2102 2103 2104 2105
      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
      }
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127
      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. */
            protocol->store("error", 5, system_charset_info);
            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);
          }
        }
      }
2128 2129 2130 2131 2132
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
      table->next= save_next;
      goto send_result_message;
    }

2133
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
2134 2135
      protocol->store("error", 5, system_charset_info);
      protocol->store("Unknown - internal error during operation", 41
2136
		      , system_charset_info);
2137
      fatal_error=1;
unknown's avatar
unknown committed
2138 2139
      break;
    }
2140
    if (fatal_error)
2141
      table->table->version=0;			// Force close of table
unknown's avatar
unknown committed
2142
    else if (open_for_modify)
2143
    {
2144
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2145
      remove_table_from_cache(thd, table->table->table_cache_key,
unknown's avatar
unknown committed
2146
			      table->table->real_name, RTFC_NO_FLAG);
2147
      pthread_mutex_unlock(&LOCK_open);
2148 2149 2150
      /* May be something modified consequently we have to invalidate cache */
      query_cache_invalidate3(thd, table->table, 0);
    }
unknown's avatar
unknown committed
2151
    close_thread_tables(thd);
2152
    table->table=0;				// For query cache
2153
    if (protocol->write())
unknown's avatar
unknown committed
2154 2155 2156
      goto err;
  }

2157
  send_eof(thd);
unknown's avatar
unknown committed
2158 2159
  DBUG_RETURN(0);
 err:
2160
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
2161 2162
  if (table)
    table->table=0;
unknown's avatar
unknown committed
2163 2164 2165
  DBUG_RETURN(-1);
}

unknown's avatar
unknown committed
2166

unknown's avatar
unknown committed
2167 2168 2169 2170
int mysql_backup_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2171 2172
				"backup", TL_READ, 0, 0, 0,
				&handler::backup));
unknown's avatar
unknown committed
2173
}
unknown's avatar
unknown committed
2174

2175

unknown's avatar
unknown committed
2176 2177 2178 2179
int mysql_restore_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2180 2181 2182
				"restore", TL_WRITE, 1, 0,
				&prepare_for_restore,
				&handler::restore));
unknown's avatar
unknown committed
2183
}
unknown's avatar
unknown committed
2184

2185

2186 2187 2188 2189
int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2190 2191 2192
				"repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR,
				&prepare_for_repair,
				&handler::repair));
2193 2194
}

2195

2196 2197 2198 2199
int mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2200 2201
				"optimize", TL_WRITE, 1,0,0,
				&handler::optimize));
2202 2203 2204
}


unknown's avatar
unknown committed
2205 2206 2207 2208 2209
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
2210 2211
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2212 2213

  RETURN VALUES
2214 2215
    0	  ok
   -1	  error
unknown's avatar
unknown committed
2216 2217
*/

2218
int mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
2219
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
2220
{
2221
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
2222
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
2223
  DBUG_ENTER("mysql_assign_to_keycache");
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235

  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);
    DBUG_RETURN(-1);
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
2236 2237
				"assign_to_keycache", TL_READ_NO_INSERT, 0,
				0, 0, &handler::assign_to_keycache));
unknown's avatar
unknown committed
2238 2239
}

unknown's avatar
unknown committed
2240 2241 2242 2243 2244 2245

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

  SYNOPSIS
    reassign_keycache_tables()
2246 2247 2248
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
2249

2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262
  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
2263
    0	  ok
unknown's avatar
unknown committed
2264 2265
*/

2266 2267
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
2268 2269 2270
{
  DBUG_ENTER("reassign_keycache_tables");

2271 2272
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
2273
  src_cache->param_buff_size= 0;		// Free key cache
2274 2275
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2276
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2277 2278 2279
}


unknown's avatar
unknown committed
2280 2281 2282 2283 2284
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2285 2286
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2287 2288

  RETURN VALUES
2289 2290
    0	  ok
   -1	  error
unknown's avatar
unknown committed
2291 2292 2293 2294 2295 2296
*/

int mysql_preload_keys(THD* thd, TABLE_LIST* tables)
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2297 2298
				"preload_keys", TL_READ, 0, 0, 0,
				&handler::preload_keys));
unknown's avatar
unknown committed
2299 2300 2301
}


unknown's avatar
unknown committed
2302 2303 2304 2305 2306
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2307 2308
    thd		Thread object
    table	Table list (one table only)
unknown's avatar
unknown committed
2309 2310 2311 2312
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
2313 2314
    0	  ok
    -1	error
unknown's avatar
unknown committed
2315 2316
*/

2317 2318 2319
int mysql_create_like_table(THD* thd, TABLE_LIST* table,
			    HA_CREATE_INFO *create_info,
			    Table_ident *table_ident)
unknown's avatar
unknown committed
2320 2321 2322 2323 2324
{
  TABLE **tmp_table;
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char *db= table->db;
  char *table_name= table->real_name;
2325
  char *src_db;
unknown's avatar
unknown committed
2326
  char *src_table= table_ident->table.str;
2327 2328
  int  err, res= -1;
  TABLE_LIST src_tables_list;
unknown's avatar
unknown committed
2329
  DBUG_ENTER("mysql_create_like_table");
2330
  src_db= table_ident->db.str ? table_ident->db.str : thd->db;
unknown's avatar
unknown committed
2331 2332 2333 2334 2335 2336

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
2337
       check_table_name(src_table,table_ident->table.length)))
unknown's avatar
unknown committed
2338
  {
2339
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
unknown's avatar
unknown committed
2340
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
2341
  }
2342 2343 2344 2345 2346
  if (!src_db || check_db_name(src_db))
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
    DBUG_RETURN(-1);
  }
2347

2348 2349
  src_tables_list.db= src_db;
  src_tables_list.real_name= src_table;
2350
  src_tables_list.next= 0;
2351

2352 2353
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
unknown's avatar
unknown committed
2354 2355 2356 2357 2358

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
    strxmov(src_path, (*tmp_table)->path, reg_ext, NullS);
  else
  {
2359 2360 2361 2362
    strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table,
	    reg_ext, NullS);
    /* Resolve symlinks (for windows) */
    fn_format(src_path, src_path, "", "", MYF(MY_UNPACK_FILENAME));
2363 2364
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
unknown's avatar
unknown committed
2365 2366 2367
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2368
      goto err;
unknown's avatar
unknown committed
2369 2370 2371 2372 2373 2374
    }
  }

  /*
    Validate the destination table

2375
    skip the destination table name checking as this is already
unknown's avatar
unknown committed
2376 2377 2378 2379 2380 2381
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2382 2383 2384
    my_snprintf(dst_path, sizeof(dst_path), "%s%s%lx_%lx_%x%s",
		mysql_tmpdir, tmp_file_prefix, current_pid,
		thd->thread_id, thd->tmp_table++, reg_ext);
2385 2386
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
unknown's avatar
unknown committed
2387 2388 2389 2390
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2391 2392 2393
    strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
	    reg_ext, NullS);
    fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME));
unknown's avatar
unknown committed
2394 2395 2396 2397
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2398
  /*
unknown's avatar
unknown committed
2399
    Create a new table by copying from source table
2400
  */
2401 2402 2403 2404 2405 2406
  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);
2407
    goto err;
2408
  }
unknown's avatar
unknown committed
2409 2410

  /*
2411 2412
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
unknown's avatar
unknown committed
2413 2414
    and temporary tables).
  */
2415
  *fn_ext(dst_path)= 0;
unknown's avatar
unknown committed
2416
  err= ha_create_table(dst_path, create_info, 1);
2417

unknown's avatar
unknown committed
2418 2419 2420 2421
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2422 2423
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2424
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
2425 2426 2427 2428
    }
  }
  else if (err)
  {
2429 2430 2431
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
2432
  }
2433 2434 2435 2436

  // Must be written before unlock
  mysql_update_log.write(thd,thd->query, thd->query_length);
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
2437
  {
2438 2439
    thd->clear_error();
    Query_log_event qinfo(thd, thd->query, thd->query_length,
2440
			  test(create_info->options &
2441 2442
			       HA_LEX_CREATE_TMP_TABLE), 
			  FALSE);
2443
    mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
2444
  }
2445
  res= 0;
2446
  goto err;
2447

unknown's avatar
unknown committed
2448 2449 2450 2451
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2452 2453 2454 2455
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
		 ER_TABLE_EXISTS_ERROR,warn_buff);
2456
    res= 0;
unknown's avatar
unknown committed
2457
  }
2458 2459 2460 2461 2462 2463 2464 2465
  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);
unknown's avatar
unknown committed
2466 2467 2468
}


2469 2470
int mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
unknown's avatar
unknown committed
2471 2472 2473 2474 2475 2476
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2477 2478
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2479 2480
				"analyze", lock_type, 1,0,0,
				&handler::analyze));
2481 2482 2483 2484 2485
}


int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
{
unknown's avatar
unknown committed
2486 2487 2488 2489 2490 2491
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2492 2493
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2494 2495 2496
				"check", lock_type,
				0, HA_OPEN_FOR_REPAIR, 0,
				&handler::check));
2497 2498
}

unknown's avatar
unknown committed
2499

unknown's avatar
unknown committed
2500
/* table_list should contain just one table */
unknown's avatar
unknown committed
2501 2502 2503 2504
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
2505 2506 2507 2508 2509 2510
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
2511 2512 2513 2514
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
2515 2516 2517

  thd->proc_info="discard_or_import_tablespace";

unknown's avatar
unknown committed
2518
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
2519

unknown's avatar
unknown committed
2520 2521 2522 2523 2524
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
unknown's avatar
unknown committed
2525 2526 2527 2528 2529
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2530

unknown's avatar
unknown committed
2531 2532 2533 2534 2535 2536 2537
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

unknown's avatar
unknown committed
2538 2539 2540 2541
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
unknown's avatar
unknown committed
2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552
  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;
  mysql_update_log.write(thd, thd->query,thd->query_length);
  if (mysql_bin_log.is_open())
  {
2553
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
unknown's avatar
unknown committed
2554 2555 2556 2557
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
unknown's avatar
unknown committed
2558
  thd->tablespace_op=FALSE;
unknown's avatar
unknown committed
2559 2560
  if (error == 0)
  {
unknown's avatar
unknown committed
2561
    send_ok(thd);
unknown's avatar
unknown committed
2562
    DBUG_RETURN(0);
unknown's avatar
unknown committed
2563
  }
unknown's avatar
unknown committed
2564 2565 2566 2567 2568

  if (error == HA_ERR_ROW_IS_REFERENCED)
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
  
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
2569
}
unknown's avatar
unknown committed
2570

2571 2572

#ifdef NOT_USED
2573 2574 2575 2576 2577 2578 2579
/*
  CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
  the proper arguments.  This isn't very fast but it should work for most
  cases.
  One should normally create all indexes with CREATE TABLE or ALTER TABLE.
*/

2580
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2581 2582 2583 2584 2585
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2586 2587 2588 2589 2590 2591 2592 2593
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2594 2595 2596
  DBUG_ENTER("mysql_create_index");

  /*
2597 2598 2599
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2600

2601 2602
    Open the table to have access to the correct table handler.
  */
2603 2604 2605 2606
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2607 2608 2609 2610 2611
    The add_index method takes an array of KEY structs for the new indexes.
    Preparing a new table structure generates this array.
    It needs a list with all fields of the table, which does not need to
    be correct in every respect. The field names are important.
  */
2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622
  for (f_ptr= table->field; *f_ptr; f_ptr++)
  {
    create_field *c_fld= new create_field(*f_ptr, *f_ptr);
    c_fld->unireg_check= Field::NONE; /*avoid multiple auto_increments*/
    fields.push_back(c_fld);
  }
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.default_table_charset= thd->variables.collation_database;
  db_options= 0;
  if (mysql_prepare_table(thd, &create_info, fields,
2623 2624 2625
			  keys, /*tmp_table*/ 0, db_options, table->file,
			  key_info_buffer, key_count,
			  /*select_field_count*/ 0))
2626 2627 2628
    DBUG_RETURN(-1);

  /*
2629 2630 2631
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2632 2633 2634 2635
  for (idx=0; idx< key_count; idx++)
  {
    DBUG_PRINT("info", ("creating index %s", key_info_buffer[idx].name));
    if (!(table->file->index_ddl_flags(key_info_buffer+idx)&
2636
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2637 2638
      break ;
  }
2639
  if ((idx < key_count)|| !key_count)
2640 2641 2642 2643 2644 2645 2646 2647
  {
    /* Re-initialize the create_info, which was changed by prepare table. */
    bzero((char*) &create_info,sizeof(create_info));
    create_info.db_type=DB_TYPE_DEFAULT;
    create_info.default_table_charset= thd->variables.collation_database;
    /* Cleanup the fields list. We do not want to create existing fields. */
    fields.delete_elements();
    if (real_alter_table(thd, table_list->db, table_list->real_name,
2648 2649 2650 2651
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_ADD_INDEX, DUP_ERROR))
      /* Don't need to free((gptr) key_info_buffer);*/
2652 2653 2654 2655 2656
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
2657 2658
        build_table_path(path, sizeof(path), table_list->db,
                         (lower_case_table_names == 2) ?
2659
                         table_list->alias : table_list->real_name,
2660
                         reg_ext) == 0 ||
2661 2662 2663
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
      /* don't need to free((gptr) key_info_buffer);*/
2664 2665
      DBUG_RETURN(-1);
  }
2666
  /* don't need to free((gptr) key_info_buffer);*/
2667 2668 2669 2670
  DBUG_RETURN(0);
}


2671 2672
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
2673 2674
{
  List<create_field> fields;
2675
  List<Key>	     keys;
2676 2677
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2678 2679 2680 2681 2682 2683 2684 2685 2686
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  uint		     *key_numbers;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN];
2687 2688 2689
  DBUG_ENTER("mysql_drop_index");

  /*
2690 2691 2692
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2693

2694 2695
    Open the table to have access to the correct table handler.
  */
2696 2697 2698 2699
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2700 2701 2702
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
2703 2704 2705 2706
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
2707 2708
    Get the number of each key and check if it can be created online.
  */
2709 2710 2711 2712 2713 2714 2715 2716 2717
  List_iterator<Alter_drop> drop_it(drop);
  Alter_drop *drop_key;
  while ((drop_key= drop_it++))
  {
    /* Find the key in the table. */
    key_info=table->key_info;
    for (idx=0; idx< table->keys; idx++, key_info++)
    {
      if (!my_strcasecmp(system_charset_info, key_info->name, drop_key->name))
2718
	break;
2719 2720 2721 2722 2723 2724 2725 2726
    }
    if (idx>= table->keys)
    {
      my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop_key->name);
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
    }
    /*
2727 2728 2729
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
2730 2731
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
2732
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743
      break ;
    key_numbers[key_count++]= idx;
  }

  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.default_table_charset= thd->variables.collation_database;

  if ((drop_key)|| (drop.elements<= 0))
  {
    if (real_alter_table(thd, table_list->db, table_list->real_name,
2744 2745 2746
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
2747 2748 2749 2750 2751 2752 2753
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }
  else
  {
    db_options= 0;
    if (table->file->drop_index(table, key_numbers, key_count)||
2754 2755 2756 2757
	mysql_prepare_table(thd, &create_info, fields,
			    keys, /*tmp_table*/ 0, db_options, table->file,
			    key_info_buffer, key_count,
			    /*select_field_count*/ 0)||
2758 2759
        build_table_path(path, sizeof(path), table_list->db,
                         (lower_case_table_names == 2) ?
2760
                         table_list->alias : table_list->real_name,
2761
                         reg_ext) == 0 ||
2762 2763
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
2764 2765 2766 2767 2768 2769 2770
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
2771
#endif /* NOT_USED */
2772 2773


2774 2775 2776
/*
  Alter table
*/
2777

unknown's avatar
unknown committed
2778
int mysql_alter_table(THD *thd,char *new_db, char *new_name,
2779 2780
		      HA_CREATE_INFO *create_info,
		      TABLE_LIST *table_list,
2781 2782
		      List<create_field> &fields, List<Key> &keys,
		      uint order_num, ORDER *order,
2783
		      enum enum_duplicates handle_duplicates, bool ignore,
2784
		      ALTER_INFO *alter_info, bool do_send_ok)
unknown's avatar
unknown committed
2785
{
2786
  TABLE *table,*new_table;
unknown's avatar
unknown committed
2787
  int error;
unknown's avatar
unknown committed
2788 2789
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
2790
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
unknown's avatar
unknown committed
2791 2792
  ha_rows copied,deleted;
  ulonglong next_insert_id;
2793
  uint db_create_options, used_fields;
unknown's avatar
unknown committed
2794
  enum db_type old_db_type,new_db_type;
2795
  DBUG_ENTER("mysql_alter_table");
unknown's avatar
unknown committed
2796 2797 2798

  thd->proc_info="init";
  table_name=table_list->real_name;
unknown's avatar
unknown committed
2799 2800
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;

unknown's avatar
unknown committed
2801
  db=table_list->db;
unknown's avatar
unknown committed
2802
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
2803
    new_db= db;
2804
  used_fields=create_info->used_fields;
unknown's avatar
unknown committed
2805
  
2806
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
unknown's avatar
unknown committed
2807

2808
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
2809
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
2810
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
2811
						   alter_info->tablespace_op));
2812 2813 2814
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
2815 2816 2817 2818
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
2819
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
2820
    if (lower_case_table_names)
unknown's avatar
unknown committed
2821 2822 2823
    {
      if (lower_case_table_names != 2)
      {
2824
	my_casedn_str(files_charset_info, new_name_buff);
2825
	new_alias= new_name;			// Create lower case table name
unknown's avatar
unknown committed
2826
      }
2827
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
2828
    }
2829
    if (new_db == db &&
2830
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
2831 2832
    {
      /*
2833 2834
	Source and destination table names are equal: make later check
	easier.
2835
      */
unknown's avatar
unknown committed
2836
      new_alias= new_name= table_name;
2837
    }
unknown's avatar
unknown committed
2838 2839 2840 2841
    else
    {
      if (table->tmp_table)
      {
2842 2843 2844 2845 2846
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
	  my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
	  DBUG_RETURN(-1);
	}
unknown's avatar
unknown committed
2847 2848 2849
      }
      else
      {
2850 2851 2852
	char dir_buff[FN_REFLEN];
	strxnmov(dir_buff, FN_REFLEN, mysql_real_data_home, new_db, NullS);
	if (!access(fn_format(new_name_buff,new_name_buff,dir_buff,reg_ext,0),
2853 2854 2855 2856 2857 2858
		    F_OK))
	{
	  /* Table will be closed in do_command() */
	  my_error(ER_TABLE_EXISTS_ERROR,MYF(0), new_alias);
	  DBUG_RETURN(-1);
	}
unknown's avatar
unknown committed
2859 2860 2861 2862
      }
    }
  }
  else
2863 2864 2865 2866
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
2867 2868 2869 2870

  old_db_type=table->db_type;
  if (create_info->db_type == DB_TYPE_DEFAULT)
    create_info->db_type=old_db_type;
2871 2872 2873 2874 2875
  if ((new_db_type= ha_checktype(create_info->db_type)) !=
      create_info->db_type)
  {
    create_info->db_type= new_db_type;
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
2876 2877 2878 2879
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			new_name);
2880
  }
2881
  if (create_info->row_type == ROW_TYPE_NOT_USED)
unknown's avatar
unknown committed
2882 2883 2884
    create_info->row_type=table->row_type;

  thd->proc_info="setup";
2885
  if (alter_info->is_simple && !table->tmp_table)
unknown's avatar
unknown committed
2886 2887
  {
    error=0;
2888
    if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
2889
    {
2890 2891 2892 2893 2894 2895
      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))
      {
2896 2897
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
	error= -1;
2898 2899 2900
      }
      else
      {
2901 2902 2903 2904
	*fn_ext(new_name)=0;
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
	  error= -1;
2905 2906
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
2907
    }
unknown's avatar
unknown committed
2908

2909
    if (!error)
2910
    {
2911
      switch (alter_info->keys_onoff) {
2912
      case LEAVE_AS_IS:
2913
	break;
2914
      case ENABLE:
2915
	VOID(pthread_mutex_lock(&LOCK_open));
2916
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
2917
	VOID(pthread_mutex_unlock(&LOCK_open));
2918
	error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2919
	/* COND_refresh will be signaled in close_thread_tables() */
2920 2921
	break;
      case DISABLE:
2922 2923 2924
	VOID(pthread_mutex_lock(&LOCK_open));
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
	VOID(pthread_mutex_unlock(&LOCK_open));
2925
	error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2926
	/* COND_refresh will be signaled in close_thread_tables() */
2927
	break;
2928
      }
2929
    }
2930
    if (error == HA_ERR_WRONG_COMMAND)
unknown's avatar
unknown committed
2931 2932
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2933 2934
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->table_name);
unknown's avatar
unknown committed
2935 2936
      error=0;
    }
unknown's avatar
unknown committed
2937 2938
    if (!error)
    {
2939 2940 2941
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2942
	thd->clear_error();
2943
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2944
	mysql_bin_log.write(&qinfo);
2945
      }
2946 2947
      if (do_send_ok)
        send_ok(thd);
unknown's avatar
unknown committed
2948
    }
2949
    else if (error > 0)
2950 2951
    {
      table->file->print_error(error, MYF(0));
2952
      error= -1;
2953
    }
2954
    table_list->table=0;				// For query cache
2955
    query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
2956 2957 2958 2959
    DBUG_RETURN(error);
  }

  /* Full alter table */
2960 2961 2962 2963 2964 2965 2966 2967

  /* let new create options override the old ones */
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
    create_info->min_rows=table->min_rows;
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
    create_info->max_rows=table->max_rows;
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
    create_info->avg_row_length=table->avg_row_length;
2968 2969
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
    create_info->default_table_charset= table->table_charset;
2970

2971
  restore_record(table,default_values);		// Empty record for DEFAULT
2972
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
2973
  List_iterator<create_field> def_it(fields);
2974
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
2975 2976
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
2977 2978
  create_field *def;

unknown's avatar
unknown committed
2979
  /*
2980
    First collect all fields from table which isn't in drop_list
unknown's avatar
unknown committed
2981 2982 2983 2984 2985
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
2986
    /* Check if field should be dropped */
unknown's avatar
unknown committed
2987 2988 2989 2990 2991
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
2992
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
2993
      {
2994 2995 2996 2997 2998 2999 3000 3001
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
	    !(used_fields & HA_CREATE_USED_AUTO))
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
	break;
3002
      }
unknown's avatar
unknown committed
3003 3004 3005 3006 3007 3008 3009 3010 3011 3012
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
3013
      if (def->change &&
3014 3015
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
unknown's avatar
unknown committed
3016 3017
    }
    if (def)
3018
    {						// Field is changed
unknown's avatar
unknown committed
3019
      def->field=field;
3020 3021
      if (!def->after)
      {
3022 3023
	create_list.push_back(def);
	def_it.remove();
3024
      }
unknown's avatar
unknown committed
3025 3026
    }
    else
3027
    {						// Use old field value
3028
      create_list.push_back(def=new create_field(field,field));
3029
      alter_it.rewind();			// Change default if ALTER
unknown's avatar
unknown committed
3030 3031 3032
      Alter_column *alter;
      while ((alter=alter_it++))
      {
3033 3034
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
3035 3036 3037
      }
      if (alter)
      {
3038 3039 3040 3041 3042 3043 3044
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT,MYF(0),def->change);
	  DBUG_RETURN(-1);
	}
	def->def=alter->def;			// Use new default
	alter_it.remove();
unknown's avatar
unknown committed
3045 3046 3047 3048 3049
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
3050
  while ((def=def_it++))			// Add new columns
unknown's avatar
unknown committed
3051
  {
3052
    if (def->change && ! def->field)
unknown's avatar
unknown committed
3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064
    {
      my_error(ER_BAD_FIELD_ERROR,MYF(0),def->change,table_name);
      DBUG_RETURN(-1);
    }
    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();
3065
      while ((find=find_it++))			// Add new columns
unknown's avatar
unknown committed
3066
      {
3067 3068
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
unknown's avatar
unknown committed
3069 3070 3071
      }
      if (!find)
      {
3072 3073
	my_error(ER_BAD_FIELD_ERROR,MYF(0),def->after,table_name);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
3074
      }
3075
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
3076 3077
    }
  }
3078
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3079
  {
3080 3081
    my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_info->alter_list.head()->name,
	     table_name);
unknown's avatar
unknown committed
3082 3083 3084 3085 3086 3087 3088 3089 3090
    DBUG_RETURN(-1);
  }
  if (!create_list.elements)
  {
    my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
    DBUG_RETURN(-1);
  }

  /*
3091 3092
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
3093 3094 3095 3096 3097 3098 3099 3100 3101
  */

  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;
  for (uint i=0 ; i < table->keys ; i++,key_info++)
  {
3102
    char *key_name= key_info->name;
unknown's avatar
unknown committed
3103 3104 3105 3106 3107
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
3108 3109
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
	break;
unknown's avatar
unknown committed
3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121
    }
    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)
3122
	continue;				// Wrong field (from UNIREG)
unknown's avatar
unknown committed
3123 3124 3125 3126 3127
      const char *key_part_name=key_part->field->field_name;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
3128 3129 3130 3131 3132 3133 3134 3135 3136
	if (cfield->change)
	{
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
	    break;
	}
	else if (!my_strcasecmp(system_charset_info,
				key_part_name, cfield->field_name))
	  break;
unknown's avatar
unknown committed
3137 3138
      }
      if (!cfield)
3139
	continue;				// Field is removed
unknown's avatar
unknown committed
3140
      uint key_part_length=key_part->length;
3141 3142 3143 3144 3145 3146 3147
      if (cfield->field)			// Not new field
      {						// Check if sub key
	if (cfield->field->type() != FIELD_TYPE_BLOB &&
	    (cfield->field->pack_length() == key_part_length ||
	     cfield->length <= key_part_length /
			       key_part->field->charset()->mbmaxlen))
	  key_part_length=0;			// Use whole field
unknown's avatar
unknown committed
3148
      }
3149
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
3150
      key_parts.push_back(new key_part_spec(cfield->field_name,
3151
					    key_part_length));
unknown's avatar
unknown committed
3152 3153
    }
    if (key_parts.elements)
unknown's avatar
unknown committed
3154
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
3155 3156 3157 3158 3159 3160 3161 3162
				 (key_info->flags & HA_NOSAME ?
				 (!my_strcasecmp(system_charset_info,
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
				 key_info->algorithm,
3163
                                 test(key_info->flags & HA_GENERATED_KEY),
3164
				 key_parts));
unknown's avatar
unknown committed
3165 3166 3167
  }
  {
    Key *key;
3168
    while ((key=key_it++))			// Add new keys
3169 3170
    {
      if (key->type != Key::FOREIGN_KEY)
3171
	key_list.push_back(key);
3172
      if (key->name &&
3173
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
3174
      {
3175 3176
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
	DBUG_RETURN(-1);
3177
      }
3178
    }
unknown's avatar
unknown committed
3179 3180
  }

3181
  if (alter_info->drop_list.elements)
unknown's avatar
unknown committed
3182
  {
3183 3184
    my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),
	     alter_info->drop_list.head()->name);
unknown's avatar
unknown committed
3185 3186
    goto err;
  }
3187
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3188
  {
3189 3190
    my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),
	     alter_info->alter_list.head()->name);
unknown's avatar
unknown committed
3191 3192 3193
    goto err;
  }

3194
  db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD);
3195 3196
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3197 3198
  /* Safety fix for innodb */
  if (lower_case_table_names)
3199
    my_casedn_str(files_charset_info, tmp_name);
3200 3201 3202 3203
  if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
    goto err;
  }
unknown's avatar
unknown committed
3204 3205 3206
  create_info->db_type=new_db_type;
  if (!create_info->comment)
    create_info->comment=table->comment;
3207 3208 3209 3210 3211

  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))
unknown's avatar
unknown committed
3212 3213 3214 3215 3216 3217 3218
    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 |
3219
			  HA_OPTION_NO_DELAY_KEY_WRITE);
unknown's avatar
unknown committed
3220 3221 3222 3223 3224
  create_info->table_options|= db_create_options;

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

3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249
  /*
    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.
  */

3250
  if (!strcmp(db, new_db))		// Ignore symlink if db changed
3251 3252 3253 3254 3255 3256
  {
    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,
3257 3258
					   create_info->index_file_name,
					   1);
3259 3260 3261 3262 3263 3264
    }
    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,
3265 3266
					  create_info->data_file_name,
					  1);
3267 3268
    }
  }
3269 3270
  else
    create_info->data_file_name=create_info->index_file_name=0;
3271
  {
3272 3273
    /* We don't log the statement, it will be logged later. */
    tmp_disable_binlog(thd);
unknown's avatar
unknown committed
3274
    error= mysql_create_table(thd, new_db, tmp_name,
unknown's avatar
unknown committed
3275
                              create_info,create_list,key_list,1,0);
3276 3277
    reenable_binlog(thd);
    if (error)
3278 3279
      DBUG_RETURN(error);
  }
unknown's avatar
unknown committed
3280 3281 3282
  if (table->tmp_table)
    new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
  else
unknown's avatar
unknown committed
3283
  {
unknown's avatar
unknown committed
3284
    char path[FN_REFLEN];
3285
    build_table_path(path, sizeof(path), new_db, tmp_name, "");
unknown's avatar
unknown committed
3286 3287 3288 3289 3290 3291
    new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
  }
  if (!new_table)
  {
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
    goto err;
unknown's avatar
unknown committed
3292 3293
  }

3294

3295 3296
  /* We don't want update TIMESTAMP fields during ALTER TABLE. */
  new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
unknown's avatar
unknown committed
3297
  new_table->next_number_field=new_table->found_next_number_field;
3298
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
3299 3300
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3301
  next_insert_id=thd->next_insert_id;		// Remember for loggin
unknown's avatar
unknown committed
3302 3303 3304
  copied=deleted=0;
  if (!new_table->is_view)
    error=copy_data_between_tables(table,new_table,create_list,
3305
				   handle_duplicates, ignore,
3306 3307
				   order_num, order, &copied, &deleted);
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3308
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
3309 3310 3311 3312 3313 3314

  if (table->tmp_table)
  {
    /* We changed a temporary table */
    if (error)
    {
unknown's avatar
unknown committed
3315
      /*
3316 3317
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3318
      */
unknown's avatar
unknown committed
3319 3320 3321
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3322 3323 3324 3325 3326 3327
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
unknown's avatar
unknown committed
3328 3329
    /* Remove link to old table and rename the new one */
    close_temporary_table(thd,table->table_cache_key,table_name);
3330 3331
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
3332
    {						// Fatal error
unknown's avatar
unknown committed
3333 3334 3335 3336
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3337 3338 3339
    mysql_update_log.write(thd, thd->query,thd->query_length);
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
3340
      thd->clear_error();
3341
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3342 3343
      mysql_bin_log.write(&qinfo);
    }
unknown's avatar
unknown committed
3344 3345 3346
    goto end_temporary;
  }

3347
  intern_close_table(new_table);		/* close temporary table */
unknown's avatar
unknown committed
3348 3349 3350 3351 3352 3353 3354 3355
  my_free((gptr) new_table,MYF(0));
  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;
  }
3356

unknown's avatar
unknown committed
3357
  /*
3358 3359 3360
    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
    from the cash, free all locks, close the old table and remove it.
unknown's avatar
unknown committed
3361 3362 3363
  */

  thd->proc_info="rename result table";
3364 3365
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3366 3367
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3368
  if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

unknown's avatar
unknown committed
3380 3381 3382 3383 3384
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3385
      Win32 and InnoDB can't drop a table that is in use, so we must
3386
      close the original table at before doing the rename
unknown's avatar
unknown committed
3387
    */
3388
    table_name=thd->strdup(table_name);		// must be saved
3389
    if (close_cached_table(thd, table))
3390
    {						// Aborted
unknown's avatar
unknown committed
3391 3392 3393 3394
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
3395
    table=0;					// Marker that table is closed
unknown's avatar
unknown committed
3396
  }
unknown's avatar
unknown committed
3397 3398
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  else
3399
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
unknown's avatar
unknown committed
3400 3401
#endif

unknown's avatar
unknown committed
3402

unknown's avatar
unknown committed
3403 3404 3405 3406 3407 3408 3409
  error=0;
  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,
3410 3411
			      new_alias))
  {						// Try to get everything back
unknown's avatar
unknown committed
3412
    error=1;
unknown's avatar
unknown committed
3413
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
unknown's avatar
unknown committed
3414
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
unknown's avatar
unknown committed
3415
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
unknown's avatar
unknown committed
3416 3417 3418
  }
  if (error)
  {
unknown's avatar
unknown committed
3419 3420 3421 3422
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
3423 3424
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3425 3426 3427
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
3428
  if (thd->lock || new_name != table_name)	// True if WIN32
unknown's avatar
unknown committed
3429
  {
unknown's avatar
unknown committed
3430 3431 3432 3433
    /*
      Not table locking or alter table with rename
      free locks and remove old table
    */
3434 3435
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3436 3437 3438 3439
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
unknown's avatar
unknown committed
3440 3441 3442 3443 3444
    /*
      Using LOCK TABLES without rename.
      This code is never executed on WIN32!
      Remove old renamed table, reopen table and get new locks
    */
unknown's avatar
unknown committed
3445 3446 3447
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
unknown's avatar
unknown committed
3448
      /* Mark in-use copies old */
unknown's avatar
unknown committed
3449
      remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
unknown's avatar
unknown committed
3450 3451
      /* end threads waiting on lock */
      mysql_lock_abort(thd,table);
unknown's avatar
unknown committed
3452 3453 3454
    }
    VOID(quick_rm_table(old_db_type,db,old_name));
    if (close_data_tables(thd,db,table_name) ||
3455 3456
	reopen_tables(thd,1,0))
    {						// This shouldn't happen
3457
      if (table)
3458
	close_cached_table(thd,table);		// Remove lock for table
unknown's avatar
unknown committed
3459 3460 3461 3462
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
unknown's avatar
unknown committed
3463
  /* The ALTER TABLE is always in its own transaction */
3464 3465 3466 3467
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
unknown's avatar
unknown committed
3468 3469
  {
    VOID(pthread_mutex_unlock(&LOCK_open));
3470
    VOID(pthread_cond_broadcast(&COND_refresh));
unknown's avatar
unknown committed
3471 3472 3473
    goto err;
  }
  thd->proc_info="end";
3474 3475
  mysql_update_log.write(thd, thd->query,thd->query_length);
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
3476
  {
unknown's avatar
unknown committed
3477
    thd->clear_error();
3478
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
unknown's avatar
unknown committed
3479 3480 3481 3482
    mysql_bin_log.write(&qinfo);
  }
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_mutex_unlock(&LOCK_open));
3483 3484 3485
#ifdef HAVE_BERKELEY_DB
  if (old_db_type == DB_TYPE_BERKELEY_DB)
  {
unknown's avatar
unknown committed
3486 3487 3488 3489 3490
    /*
      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.
    */
3491
    char path[FN_REFLEN];
3492
    build_table_path(path, sizeof(path), new_db, table_name, "");
3493 3494
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
unknown's avatar
unknown committed
3495
    {
3496 3497
      intern_close_table(table);
      my_free((char*) table, MYF(0));
unknown's avatar
unknown committed
3498
    }
3499
    else
unknown's avatar
unknown committed
3500 3501
      sql_print_warning("Could not open BDB table %s.%s after rename\n",
                        new_db,table_name);
3502
    (void) berkeley_flush_logs();
3503 3504
  }
#endif
3505
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
3506
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3507 3508

end_temporary:
3509 3510 3511
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3512 3513
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
unknown's avatar
unknown committed
3514 3515 3516 3517 3518 3519 3520 3521 3522
  thd->some_tables_deleted=0;
  DBUG_RETURN(0);

 err:
  DBUG_RETURN(-1);
}


static int
unknown's avatar
unknown committed
3523
copy_data_between_tables(TABLE *from,TABLE *to,
3524 3525
			 List<create_field> &create,
			 enum enum_duplicates handle_duplicates,
3526
                         bool ignore,
3527 3528 3529
			 uint order_num, ORDER *order,
			 ha_rows *copied,
			 ha_rows *deleted)
unknown's avatar
unknown committed
3530 3531
{
  int error;
3532
  Copy_field *copy,*copy_end;
unknown's avatar
unknown committed
3533 3534
  ulong found_count,delete_count;
  THD *thd= current_thd;
unknown's avatar
unknown committed
3535 3536 3537 3538 3539 3540
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3541
  ha_rows examined_rows;
3542
  bool auto_increment_field_copied= 0;
3543
  ulong save_sql_mode;
unknown's avatar
unknown committed
3544 3545
  DBUG_ENTER("copy_data_between_tables");

3546 3547 3548 3549 3550 3551
  /*
    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
  */
3552
  error= ha_enable_transaction(thd, FALSE);
3553 3554
  if (error)
    DBUG_RETURN(-1);
3555
  
unknown's avatar
unknown committed
3556
  if (!(copy= new Copy_field[to->fields]))
3557
    DBUG_RETURN(-1);				/* purecov: inspected */
unknown's avatar
unknown committed
3558

3559 3560
  if (to->file->external_lock(thd, F_WRLCK))
    DBUG_RETURN(-1);
3561
  from->file->info(HA_STATUS_VARIABLE);
3562
  to->file->start_bulk_insert(from->file->records);
unknown's avatar
unknown committed
3563

3564 3565
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
3566 3567 3568 3569 3570 3571 3572
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
3573 3574
    {
      if (*ptr == to->next_number_field)
3575
      {
3576
        auto_increment_field_copied= TRUE;
3577 3578 3579 3580 3581 3582 3583 3584 3585
        /*
          If we are going to copy contents of one auto_increment column to
          another auto_increment column it is sensible to preserve zeroes.
          This condition also covers case when we are don't actually alter
          auto_increment column.
        */
        if (def->field == from->found_next_number_field)
          thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
      }
unknown's avatar
unknown committed
3586
      (copy_end++)->set(*ptr,def->field,0);
3587 3588
    }

unknown's avatar
unknown committed
3589 3590
  }

3591 3592
  found_count=delete_count=0;

unknown's avatar
unknown committed
3593 3594
  if (order)
  {
unknown's avatar
unknown committed
3595
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
3596
					      MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
3597 3598
    bzero((char*) &tables,sizeof(tables));
    tables.table = from;
3599
    tables.alias = tables.real_name= from->real_name;
3600
    tables.db	 = from->table_cache_key;
unknown's avatar
unknown committed
3601 3602
    error=1;

3603
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
3604 3605 3606 3607 3608 3609 3610
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
		    &tables, fields, all_fields, order) ||
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
					     (SQL_SELECT *) 0, HA_POS_ERROR,
					     &examined_rows))
	== HA_POS_ERROR)
unknown's avatar
unknown committed
3611 3612 3613
      goto err;
  };

3614 3615 3616 3617
  /* 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 */
  from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
3618
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
3619
  if (ignore ||
3620
      handle_duplicates == DUP_REPLACE)
unknown's avatar
unknown committed
3621
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
3622
  thd->row_count= 0;
unknown's avatar
unknown committed
3623 3624 3625 3626 3627 3628 3629 3630
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
      my_error(ER_SERVER_SHUTDOWN,MYF(0));
      error= 1;
      break;
    }
3631
    thd->row_count++;
3632 3633
    if (to->next_number_field)
    {
3634
      if (auto_increment_field_copied)
3635
        to->auto_increment_field_not_null= TRUE;
3636 3637 3638 3639 3640
      else
        to->next_number_field->reset();
    }
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
    {
unknown's avatar
unknown committed
3641
      copy_ptr->do_copy(copy_ptr);
3642
    }
unknown's avatar
unknown committed
3643 3644
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
3645
      if ((!ignore &&
3646 3647 3648
	   handle_duplicates != DUP_REPLACE) ||
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
unknown's avatar
unknown committed
3649
      {
3650 3651
	to->file->print_error(error,MYF(0));
	break;
unknown's avatar
unknown committed
3652 3653 3654 3655
      }
      delete_count++;
    }
    else
3656
      found_count++;
unknown's avatar
unknown committed
3657 3658
  }
  end_read_record(&info);
3659
  free_io_cache(from);
3660
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
3661 3662

  if (to->file->end_bulk_insert() && !error)
unknown's avatar
unknown committed
3663
  {
unknown's avatar
unknown committed
3664
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
3665 3666
    error=1;
  }
unknown's avatar
unknown committed
3667
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
3668

3669
  ha_enable_transaction(thd,TRUE);
3670

3671 3672 3673 3674 3675 3676 3677 3678
  /*
    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;
3679

unknown's avatar
unknown committed
3680
 err:
3681
  thd->variables.sql_mode= save_sql_mode;
unknown's avatar
unknown committed
3682
  free_io_cache(from);
unknown's avatar
unknown committed
3683 3684
  *copied= found_count;
  *deleted=delete_count;
3685 3686
  if (to->file->external_lock(thd,F_UNLCK))
    error=1;
unknown's avatar
unknown committed
3687 3688
  DBUG_RETURN(error > 0 ? -1 : 0);
}
3689

unknown's avatar
unknown committed
3690

3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712
/*
  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().
*/
int mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                         bool do_send_ok)
{
  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();
3713
  lex->alter_info.is_simple= 0;                 // Force full recreate
3714 3715 3716 3717 3718 3719 3720
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.row_type=ROW_TYPE_DEFAULT;
  create_info.default_table_charset=default_charset_info;
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
3721
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
3722 3723 3724
}


unknown's avatar
unknown committed
3725
int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
3726 3727 3728 3729 3730
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
3731
  DBUG_ENTER("mysql_checksum_table");
3732 3733 3734 3735 3736 3737 3738 3739

  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;
  if (protocol->send_fields(&field_list, 1))
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
3740
  for (table= tables; table; table= table->next)
3741 3742
  {
    char table_name[NAME_LEN*2+2];
3743
    TABLE *t;
3744

unknown's avatar
unknown committed
3745 3746 3747
    strxmov(table_name, table->db ,".", table->real_name, NullS);

    t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT);
3748
    thd->clear_error();			// these errors shouldn't get client
3749 3750 3751 3752

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

3753
    if (!t)
3754
    {
unknown's avatar
unknown committed
3755
      /* Table didn't exist */
3756 3757 3758 3759 3760
      protocol->store_null();
      thd->net.last_error[0]=0;
    }
    else
    {
3761
      t->pos_in_table_list= table;
3762

3763
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
3764 3765
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
3766
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
3767 3768
	       (check_opt->flags & T_QUICK))
	protocol->store_null();
3769 3770
      else
      {
3771 3772 3773 3774 3775 3776 3777 3778
	/* calculating table's checksum */
	ha_checksum crc= 0;

	/* InnoDB 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 */
	t->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);

unknown's avatar
unknown committed
3779
	if (t->file->ha_rnd_init(1))
3780 3781 3782
	  protocol->store_null();
	else
	{
3783
	  for (;;)
3784 3785
	  {
	    ha_checksum row_crc= 0;
3786 3787 3788 3789 3790 3791 3792
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813
	    if (t->record[0] != (byte*) t->field[0]->ptr)
	      row_crc= my_checksum(row_crc, t->record[0],
				   ((byte*) t->field[0]->ptr) - t->record[0]);

	    for (uint i= 0; i < t->fields; i++ )
	    {
	      Field *f= t->field[i];
	      if (f->type() == FIELD_TYPE_BLOB)
	      {
		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,
				     f->pack_length());
	    }

	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
3814
          t->file->ha_rnd_end();
3815
	}
3816
      }
unknown's avatar
unknown committed
3817
      thd->clear_error();
3818
      close_thread_tables(thd);
3819
      table->table=0;				// For query cache
3820 3821 3822 3823 3824 3825 3826
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3827

3828
 err:
3829
  close_thread_tables(thd);			// Shouldn't be needed
3830 3831 3832 3833
  if (table)
    table->table=0;
  DBUG_RETURN(-1);
}