sql_table.cc 105 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

unknown's avatar
unknown committed
43 44 45 46 47
/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table()
48 49 50
   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
51 52 53 54 55 56 57 58 59 60

  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
61 62
    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
63 64

*/
unknown's avatar
unknown committed
65

66
int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
67
		   my_bool drop_temporary)
unknown's avatar
unknown committed
68
{
unknown's avatar
merge  
unknown committed
69
  int error= 0;
unknown's avatar
unknown committed
70 71 72 73 74 75 76 77
  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));

78
  if (!drop_temporary && global_read_lock)
unknown's avatar
unknown committed
79
  {
unknown's avatar
unknown committed
80
    if (thd->global_read_lock)
81
    {
unknown's avatar
unknown committed
82
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
83
	       tables->real_name);
84
      error= 1;
unknown's avatar
unknown committed
85 86 87 88 89
      goto err;
    }
    while (global_read_lock && ! thd->killed)
    {
      (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
90
    }
unknown's avatar
unknown committed
91 92

  }
93
  error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary, 0);
94

95
 err:
96 97 98 99 100 101 102 103 104
  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);
105
  send_ok(thd);
106 107 108
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
109 110 111 112 113 114

/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table_part2_with_lock()
115 116 117 118
   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
119 120 121 122 123 124

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

 RETURN
125 126
  0	ok
  1	error
unknown's avatar
unknown committed
127 128
*/

unknown's avatar
unknown committed
129
int mysql_rm_table_part2_with_lock(THD *thd,
130 131
				   TABLE_LIST *tables, bool if_exists,
				   bool drop_temporary, bool dont_log_query)
unknown's avatar
unknown committed
132 133 134 135 136 137
{
  int error;
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

138
  error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary,
139
			     dont_log_query);
unknown's avatar
unknown committed
140 141 142 143 144 145 146 147 148 149

  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
150

151
/*
152 153 154 155
  Execute the drop of a normal or temporary table

  SYNOPSIS
    mysql_rm_table_part2()
156 157 158 159 160 161
    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
162

163 164 165 166 167 168 169 170 171
  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.
172 173

 RETURN
174 175 176
   0	ok
   1	Error
   -1	Thread was killed
177
*/
178 179

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
180
			 bool drop_temporary, bool dont_log_query)
181 182
{
  TABLE_LIST *table;
183
  char	path[FN_REFLEN], *alias;
184 185
  String wrong_tables;
  int error;
unknown's avatar
unknown committed
186
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
187 188
  DBUG_ENTER("mysql_rm_table_part2");

189
  if (lock_table_names(thd, tables))
190
    DBUG_RETURN(1);
191

unknown's avatar
unknown committed
192
  for (table=tables ; table ; table=table->next)
unknown's avatar
unknown committed
193
  {
194
    char *db=table->db;
195
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL);
unknown's avatar
unknown committed
196
    if (!close_temporary_table(thd, db, table->real_name))
unknown's avatar
unknown committed
197
    {
198
      tmp_table_deleted=1;
199
      continue;					// removed temporary table
unknown's avatar
unknown committed
200
    }
unknown's avatar
unknown committed
201 202

    error=0;
203
    if (!drop_temporary)
unknown's avatar
unknown committed
204
    {
205 206 207
      abort_locked_tables(thd,db,table->real_name);
      while (remove_table_from_cache(thd,db,table->real_name) && !thd->killed)
      {
208 209 210
	dropping_tables++;
	(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
	dropping_tables--;
211 212 213
      }
      drop_locked_tables(thd,db,table->real_name);
      if (thd->killed)
214
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
215
      alias= (lower_case_table_names == 2) ? table->alias : table->real_name;
216
      /* remove form file and isam files */
unknown's avatar
unknown committed
217
      strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS);
218
      (void) unpack_filename(path,path);
unknown's avatar
unknown committed
219
    }
220
    if (drop_temporary || 
unknown's avatar
unknown committed
221
	(access(path,F_OK) && ha_create_table_from_engine(thd,db,alias,TRUE)))
unknown's avatar
unknown committed
222
    {
223
      if (if_exists)
224 225 226
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
			    table->real_name);
227
      else
228
	error= 1;
unknown's avatar
unknown committed
229 230 231
    }
    else
    {
unknown's avatar
unknown committed
232
      char *end;
unknown's avatar
unknown committed
233
      db_type table_type= get_table_type(path);
234
      *(end=fn_ext(path))=0;			// Remove extension for delete
unknown's avatar
unknown committed
235 236
      error=ha_delete_table(table_type, path);
      if (error == ENOENT && if_exists)
237
	error = 0;
unknown's avatar
unknown committed
238
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
239
      {
240 241
	/* the table is referenced by a foreign key constraint */
	foreign_key_error=1;
unknown's avatar
unknown committed
242
      }
unknown's avatar
unknown committed
243 244
      if (!error || error == ENOENT)
      {
245 246 247 248
	/* 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
249
      }
unknown's avatar
unknown committed
250 251 252 253
    }
    if (error)
    {
      if (wrong_tables.length())
254
	wrong_tables.append(',');
255
      wrong_tables.append(String(table->real_name,system_charset_info));
unknown's avatar
unknown committed
256 257
    }
  }
unknown's avatar
unknown committed
258
  thd->tmp_table_used= tmp_table_deleted;
259 260 261 262 263 264 265 266 267 268 269
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
      my_error(ER_BAD_TABLE_ERROR,MYF(0), wrong_tables.c_ptr());
    else
      my_error(ER_ROW_IS_REFERENCED, MYF(0));
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
270
  {
unknown's avatar
unknown committed
271
    query_cache_invalidate3(thd, tables, 0);
unknown's avatar
unknown committed
272
    if (!dont_log_query)
273
    {
unknown's avatar
unknown committed
274 275 276
      mysql_update_log.write(thd, thd->query,thd->query_length);
      if (mysql_bin_log.is_open())
      {
277 278
        if (!error)
          thd->clear_error();
279
	Query_log_event qinfo(thd, thd->query, thd->query_length,
280 281
			      tmp_table_deleted && !some_tables_deleted, 
			      FALSE);
282
	mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
283
      }
284
    }
unknown's avatar
unknown committed
285
  }
unknown's avatar
unknown committed
286

287
  unlock_table_names(thd, tables);
288
  DBUG_RETURN(error);
unknown's avatar
unknown committed
289 290 291 292
}


int quick_rm_table(enum db_type base,const char *db,
293
		   const char *table_name)
unknown's avatar
unknown committed
294 295 296
{
  char path[FN_REFLEN];
  int error=0;
297 298
  my_snprintf(path, sizeof(path), "%s/%s/%s%s",
	      mysql_data_home, db, table_name, reg_ext);
unknown's avatar
unknown committed
299 300 301
  unpack_filename(path,path);
  if (my_delete(path,MYF(0)))
    error=1; /* purecov: inspected */
302
  my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home, db, table_name);
unknown's avatar
unknown committed
303
  unpack_filename(path,path);
unknown's avatar
unknown committed
304 305 306
  return ha_delete_table(base,path) || error;
}

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
/*
  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;
325
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
326 327
    {
      /* Sort NOT NULL keys before other keys */
328
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
329 330 331 332 333 334 335
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
  }
  else if (b->flags & HA_NOSAME)
336
    return 1;					// Prefer b
337 338 339 340 341

  if ((a->flags ^ b->flags) & HA_FULLTEXT)
  {
    return (a->flags & HA_FULLTEXT) ? 1 : -1;
  }
unknown's avatar
unknown committed
342
  /*
343
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
344 345 346
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
347 348
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
349 350
}

351 352
/*
  Check TYPELIB (set or enum) for duplicates
353

354 355 356
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
357 358
    name	  name of the checked column
    typelib	  list of values for the column
359 360

  DESCRIPTION
361
    This function prints an warning for each value in list
362 363 364 365 366 367 368
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
369 370
                                  const char *name, TYPELIB *typelib,
                                  CHARSET_INFO *cs)
371
{
372
  TYPELIB tmp= *typelib;
373
  const char **cur_value= typelib->type_names;
374 375 376
  unsigned int *cur_length= typelib->type_lengths;
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
377
  {
378 379 380 381
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
382
    {
unknown's avatar
unknown committed
383
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
384 385 386
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
387 388 389
    }
  }
}
390

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

/*
  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
426
/*
427
  Preparation for table creation
unknown's avatar
unknown committed
428 429

  SYNOPSIS
430
    mysql_prepare_table()
431 432 433 434
    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
435

436
  DESCRIPTION
437
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
438 439

  RETURN VALUES
440 441
    0	ok
    -1	error
unknown's avatar
unknown committed
442
*/
unknown's avatar
unknown committed
443

444
int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
445 446 447 448
			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
449
{
450 451 452 453 454
  const char	*key_name;
  create_field	*sql_field,*dup_field;
  uint		field,null_fields,blob_columns;
  ulong		pos;
  KEY		*key_info;
unknown's avatar
unknown committed
455
  KEY_PART_INFO *key_part_info;
456 457 458
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
459
  DBUG_ENTER("mysql_prepare_table");
unknown's avatar
unknown committed
460 461

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

465
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
466
  {
467
    if (!sql_field->charset)
468 469 470
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
471 472 473
      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.
474
    */
475
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
476
      sql_field->charset= create_info->table_charset;
477 478 479 480 481 482 483 484 485 486 487

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

489 490
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
491 492 493
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
494
      TYPELIB *interval= sql_field->interval;
495 496 497 498 499 500

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
501
      if (!interval)
502
      {
503 504 505 506
        interval= sql_field->interval= typelib(sql_field->interval_list);
        List_iterator<String> it(sql_field->interval_list);
        String conv, *tmp;
        for (uint i= 0; (tmp= it++); i++)
507
        {
508 509 510 511 512 513 514 515 516 517 518
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
            char *buf= (char*) sql_alloc(conv.length()+1);
            memcpy(buf, conv.ptr(), conv.length());
            buf[conv.length()]= '\0';
            interval->type_names[i]= buf;
            interval->type_lengths[i]= conv.length();
          }
519

520 521 522 523 524 525 526
          // 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
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
      }

      /*
        Convert the default value from client character
        set into the column character set if necessary.
      */
      if (sql_field->def)
      {
        sql_field->def= 
          sql_field->def->safe_charset_converter(cs);
      }

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

576
    sql_field->create_length_to_internal_length();
577

578
    /* Don't pack keys in old tables if the user has requested this */
unknown's avatar
unknown committed
579
    if ((sql_field->flags & BLOB_FLAG) ||
580 581
	sql_field->sql_type == FIELD_TYPE_VAR_STRING &&
	create_info->row_type != ROW_TYPE_FIXED)
unknown's avatar
unknown committed
582 583 584 585 586
    {
      db_options|=HA_OPTION_PACK_RECORD;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
587

unknown's avatar
unknown committed
588 589
    if (check_column_name(sql_field->field_name))
    {
590
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
unknown's avatar
unknown committed
591 592
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
593

594 595
    /* 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
596
    {
597
      if (my_strcasecmp(system_charset_info,
598 599
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
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 626
	/*
	  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 */
	  sql_field->sql_type=		dup_field->sql_type;
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
	  sql_field->length=		dup_field->length;
	  sql_field->pack_length=	dup_field->pack_length;
	  sql_field->create_length_to_internal_length();
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->flags=		dup_field->flags;
	  sql_field->unireg_check=	dup_field->unireg_check;
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
	}
unknown's avatar
unknown committed
627 628 629 630
      }
    }
    it2.rewind();
  }
631
  /* If fixed row records, we need one bit to check for deleted rows */
unknown's avatar
unknown committed
632 633 634 635 636 637 638
  if (!(db_options & HA_OPTION_PACK_RECORD))
    null_fields++;
  pos=(null_fields+7)/8;

  it.rewind();
  while ((sql_field=it++))
  {
639 640
    DBUG_ASSERT(sql_field->charset);

unknown's avatar
unknown committed
641 642 643 644 645 646
    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 |
647 648
	pack_length_to_packflag(sql_field->pack_length -
				portable_sizeof_char_ptr);
649
      if (sql_field->charset->state & MY_CS_BINSORT)
650 651
	sql_field->pack_flag|=FIELDFLAG_BINARY;
      sql_field->length=8;			// Unireg field length
unknown's avatar
unknown committed
652 653 654
      sql_field->unireg_check=Field::BLOB_FIELD;
      blob_columns++;
      break;
unknown's avatar
unknown committed
655
    case FIELD_TYPE_GEOMETRY:
unknown's avatar
unknown committed
656
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
657
      if (!(file->table_flags() & HA_CAN_GEOMETRY))
658
      {
659 660 661
	my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
			MYF(0), "GEOMETRY");
	DBUG_RETURN(-1);
662
      }
unknown's avatar
unknown committed
663
      sql_field->pack_flag=FIELDFLAG_GEOM |
664 665
	pack_length_to_packflag(sql_field->pack_length -
				portable_sizeof_char_ptr);
unknown's avatar
unknown committed
666
      if (sql_field->charset->state & MY_CS_BINSORT)
667 668
	sql_field->pack_flag|=FIELDFLAG_BINARY;
      sql_field->length=8;			// Unireg field length
unknown's avatar
unknown committed
669 670 671
      sql_field->unireg_check=Field::BLOB_FIELD;
      blob_columns++;
      break;
unknown's avatar
unknown committed
672 673
#else
      my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
674
		      sym_group_geom.name, sym_group_geom.needed_define);
unknown's avatar
unknown committed
675 676
      DBUG_RETURN(-1);
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
677 678 679
    case FIELD_TYPE_VAR_STRING:
    case FIELD_TYPE_STRING:
      sql_field->pack_flag=0;
680
      if (sql_field->charset->state & MY_CS_BINSORT)
681
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
682 683 684
      break;
    case FIELD_TYPE_ENUM:
      sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
685
	FIELDFLAG_INTERVAL;
686
      if (sql_field->charset->state & MY_CS_BINSORT)
687
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
688
      sql_field->unireg_check=Field::INTERVAL_FIELD;
689
      check_duplicates_in_interval("ENUM",sql_field->field_name,
690 691
                                   sql_field->interval,
                                   sql_field->charset);
unknown's avatar
unknown committed
692 693 694
      break;
    case FIELD_TYPE_SET:
      sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
695
	FIELDFLAG_BITFIELD;
696
      if (sql_field->charset->state & MY_CS_BINSORT)
697
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
698
      sql_field->unireg_check=Field::BIT_FIELD;
699
      check_duplicates_in_interval("SET",sql_field->field_name,
700 701
                                   sql_field->interval,
                                   sql_field->charset);
unknown's avatar
unknown committed
702
      break;
703
    case FIELD_TYPE_DATE:			// Rest of string types
unknown's avatar
unknown committed
704 705 706 707 708 709 710
    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:
711 712 713
      /* We should replace old TIMESTAMP fields with their newer analogs */
      if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
      {
714 715 716 717 718 719 720
	if (!timestamps)
	{
	  sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
	  timestamps_with_niladic++;
	}
	else
	  sql_field->unireg_check= Field::NONE;
721
      }
722
      else if (sql_field->unireg_check != Field::NONE)
723 724
	timestamps_with_niladic++;

725
      timestamps++;
726
      /* fall-through */
unknown's avatar
unknown committed
727 728
    default:
      sql_field->pack_flag=(FIELDFLAG_NUMBER |
729 730 731 732 733 734
			    (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
735 736 737 738 739 740 741 742 743
      break;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      sql_field->pack_flag|=FIELDFLAG_MAYBE_NULL;
    sql_field->offset= pos;
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
    pos+=sql_field->pack_length;
  }
744 745 746 747 748
  if (timestamps_with_niladic > 1)
  {
    my_error(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,MYF(0));
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
749 750 751 752 753 754
  if (auto_increment > 1)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
755
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
756 757 758 759 760
  {
    my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0));
    DBUG_RETURN(-1);
  }

761
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
762 763 764 765 766 767
  {
    my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0));
    DBUG_RETURN(-1);
  }

  /* Create keys */
768

769
  List_iterator<Key> key_iterator(keys), key_iterator2(keys);
770
  uint key_parts=0, fk_key_count=0;
771
  bool primary_key=0,unique_key=0;
772
  Key *key, *key2;
unknown's avatar
unknown committed
773
  uint tmp, key_number;
774 775
  /* special marker for keys to be ignored */
  static char ignore_key[1];
776

777
  /* Calculate number of key segements */
778
  *key_count= 0;
779

unknown's avatar
unknown committed
780 781
  while ((key=key_iterator++))
  {
782 783 784 785 786
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
787
	  fk_key->ref_columns.elements != fk_key->columns.elements)
788
      {
789 790 791 792
	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);
793 794 795
      }
      continue;
    }
796
    (*key_count)++;
unknown's avatar
unknown committed
797
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
798 799 800 801 802
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
803
    if (key->name && strlen(key->name) > NAME_LEN)
unknown's avatar
unknown committed
804
    {
805
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
unknown's avatar
unknown committed
806 807
      DBUG_RETURN(-1);
    }
808
    key_iterator2.rewind ();
809
    if (key->type != Key::FOREIGN_KEY)
810
    {
811
      while ((key2 = key_iterator2++) != key)
812
      {
unknown's avatar
unknown committed
813
	/*
814 815 816
          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
817
        */
818 819 820
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
821
        {
822
          /* TODO: issue warning message */
823 824 825 826 827 828 829 830 831 832 833 834 835
          /* 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;
        }
836 837 838 839 840 841
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
842
    if (key->name && !tmp_table &&
843
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
844 845 846 847
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
848
  }
unknown's avatar
unknown committed
849
  tmp=file->max_keys();
850
  if (*key_count > tmp)
851 852 853 854
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
855

856
  key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)* *key_count);
857 858
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
  if (!key_info_buffer || ! key_part_info)
859
    DBUG_RETURN(-1);				// Out of memory
860

861
  key_iterator.rewind();
unknown's avatar
unknown committed
862
  key_number=0;
unknown's avatar
unknown committed
863
  for (; (key=key_iterator++) ; key_number++)
864 865 866 867
  {
    uint key_length=0;
    key_part_spec *column;

868 869 870 871 872 873 874 875 876 877
    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
878
    switch(key->type){
unknown's avatar
unknown committed
879
    case Key::MULTIPLE:
880
	key_info->flags= 0;
881
	break;
unknown's avatar
unknown committed
882
    case Key::FULLTEXT:
883
	key_info->flags= HA_FULLTEXT;
884
	break;
unknown's avatar
unknown committed
885
    case Key::SPATIAL:
unknown's avatar
unknown committed
886
#ifdef HAVE_SPATIAL
887
	key_info->flags= HA_SPATIAL;
888
	break;
unknown's avatar
unknown committed
889
#else
890 891 892
	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
893
#endif
894
    case Key::FOREIGN_KEY:
895
      key_number--;				// Skip this key
896 897
      continue;
    default:
898 899
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
900
    }
901 902
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
903

unknown's avatar
unknown committed
904 905
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
906
    key_info->usable_key_parts= key_number;
907
    key_info->algorithm=key->algorithm;
unknown's avatar
unknown committed
908

909 910
    if (key->type == Key::FULLTEXT)
    {
911
      if (!(file->table_flags() & HA_CAN_FULLTEXT))
912
      {
913 914
	my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0));
	DBUG_RETURN(-1);
915 916
      }
    }
unknown's avatar
unknown committed
917 918 919
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
920
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
921 922 923
       in near future when new frm file is ready
       checking for proper key parts number:
    */
924

925
    /* TODO: Add proper checks if handler supports key_type and algorithm */
926
    if (key_info->flags & HA_SPATIAL)
927 928 929
    {
      if (key_info->key_parts != 1)
      {
930 931 932
	my_printf_error(ER_WRONG_ARGUMENTS,
			ER(ER_WRONG_ARGUMENTS),MYF(0),"SPATIAL INDEX");
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
933
      }
934
    }
unknown's avatar
unknown committed
935
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
936
    {
unknown's avatar
unknown committed
937
#ifdef HAVE_RTREE_KEYS
938 939
      if ((key_info->key_parts & 1) == 1)
      {
940 941 942
	my_printf_error(ER_WRONG_ARGUMENTS,
			ER(ER_WRONG_ARGUMENTS),MYF(0),"RTREE INDEX");
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
943
      }
944 945
      /* TODO: To be deleted */
      my_printf_error(ER_NOT_SUPPORTED_YET, ER(ER_NOT_SUPPORTED_YET),
946
		      MYF(0), "RTREE INDEX");
947
      DBUG_RETURN(-1);
unknown's avatar
unknown committed
948 949
#else
      my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0),
950
		      sym_group_rtree.name, sym_group_rtree.needed_define);
unknown's avatar
unknown committed
951 952
      DBUG_RETURN(-1);
#endif
unknown's avatar
unknown committed
953
    }
954

955
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
956
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
957 958
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
unknown's avatar
unknown committed
959 960
      key_part_spec *dup_column;

unknown's avatar
unknown committed
961 962 963
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
964 965 966 967
	     my_strcasecmp(system_charset_info,
			   column->field_name,
			   sql_field->field_name))
	field++;
unknown's avatar
unknown committed
968 969
      if (!sql_field)
      {
970 971 972 973
	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
974
      }
unknown's avatar
unknown committed
975
      while ((dup_column= cols2++) != column)
976 977 978 979 980 981 982 983 984 985 986
      {
        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();
987
      if (key->type == Key::FULLTEXT)
988
      {
989 990 991 992
	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 ||
993
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
	    (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));
1009
      }
1010
      else
1011
      {
1012 1013 1014 1015
	column->length*= sql_field->charset->mbmaxlen;

	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
1016
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
	  {
	    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
1030
#ifdef HAVE_SPATIAL
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
	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
1042
#endif
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
	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;
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
unknown's avatar
unknown committed
1053
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
	  {
	    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
1070
      }
1071

unknown's avatar
unknown committed
1072 1073 1074 1075 1076 1077
      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)
      {
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
	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
1112 1113 1114
      }
      else if (length == 0)
      {
1115 1116 1117
	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
1118
      }
1119 1120
      if (length > file->max_key_part_length())
      {
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
	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);
	}
1136 1137
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
1138 1139
      /* Use packed keys for long strings on the first column */
      if (!(db_options & HA_OPTION_NO_PACK_KEYS) &&
1140 1141 1142 1143
	  (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
1144
      {
1145 1146 1147 1148
	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
1149 1150 1151 1152 1153 1154 1155
      }
      key_length+=length;
      key_part_info++;

      /* Create the key name based on the first column (if not given) */
      if (column_nr == 0)
      {
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
	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
1175 1176
      }
    }
1177 1178
    if (!key_info->name || check_column_name(key_info->name))
    {
1179
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
1180 1181
      DBUG_RETURN(-1);
    }
1182 1183
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
1184
    key_info->key_length=(uint16) key_length;
unknown's avatar
unknown committed
1185
    uint max_key_length= file->max_key_length();
1186
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
1187
    {
1188
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
unknown's avatar
unknown committed
1189 1190
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
1191
    key_info++;
unknown's avatar
unknown committed
1192
  }
1193
  if (!unique_key && !primary_key &&
1194
      (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
1195 1196 1197 1198
  {
    my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1199 1200 1201 1202 1203
  if (auto_increment > 0)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
1204
  /* Sort keys in optimized order */
1205 1206
  qsort((gptr) key_info_buffer, *key_count, sizeof(KEY),
	(qsort_cmp) sort_keys);
unknown's avatar
unknown committed
1207

1208 1209 1210
  DBUG_RETURN(0);
}

1211

1212 1213 1214 1215 1216
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1217 1218 1219 1220 1221 1222 1223 1224
    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)
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234

  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
1235 1236
    0	ok
    -1	error
1237 1238 1239
*/

int mysql_create_table(THD *thd,const char *db, const char *table_name,
1240 1241
		       HA_CREATE_INFO *create_info,
		       List<create_field> &fields,
1242
		       List<Key> &keys,bool tmp_table,
1243
		       uint select_field_count)
1244
{
1245 1246 1247 1248 1249 1250 1251
  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;
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
  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,
1265 1266 1267 1268
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			table_name);
1269 1270 1271 1272 1273 1274 1275
  }
  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
1276 1277 1278 1279 1280 1281 1282 1283
#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
  */
1284 1285 1286 1287 1288 1289
  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
1290
#endif
1291

1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
  /*
    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;
    uint length;
    char  path[FN_REFLEN];
unknown's avatar
unknown committed
1302
    strxmov(path, mysql_data_home, "/", db, NullS);
1303 1304 1305 1306 1307 1308
    length= unpack_dirname(path,path);             // Convert if not unix
    strmov(path+length, MY_DB_OPT_FILE);
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }

1309
  if (mysql_prepare_table(thd, create_info, fields,
1310 1311 1312
			  keys, tmp_table, db_options, file,
			  key_info_buffer, &key_count,
			  select_field_count))
1313 1314
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
1315 1316 1317
      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1318 1319 1320
    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);
1321 1322
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, path);
unknown's avatar
unknown committed
1323 1324 1325
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
1326 1327
    my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home, db,
		alias, reg_ext);
unknown's avatar
unknown committed
1328 1329 1330 1331 1332
  unpack_filename(path,path);
  /* Check if table already exists */
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
      && find_temporary_table(thd,db,table_name))
  {
1333
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1334
    {
1335
      create_info->table_existed= 1;		// Mark that table existed
1336
      DBUG_RETURN(0);
1337
    }
unknown's avatar
unknown committed
1338
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
unknown's avatar
unknown committed
1339 1340
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1341
  if (wait_if_global_read_lock(thd, 0, 1))
1342
    DBUG_RETURN(error);
unknown's avatar
unknown committed
1343 1344 1345 1346 1347 1348
  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)
1349
      {
1350 1351 1352
	create_info->table_existed= 1;		// Mark that table existed
	error= 0;
      }
1353
      else
1354
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1355
      goto end;
unknown's avatar
unknown committed
1356 1357 1358
    }
  }

unknown's avatar
unknown committed
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
  /*
    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;
1372 1373
    if (!ha_create_table_from_engine(thd, db, table_name,
				     create_if_not_exists))
unknown's avatar
unknown committed
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387
    {
      DBUG_PRINT("info", ("Table already existed in handler"));

      if (create_if_not_exists)
      {
       create_info->table_existed= 1;   // Mark that table existed
       error= 0;
      }
      else
       my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
      goto end;
    }
  }

unknown's avatar
unknown committed
1388
  thd->proc_info="creating table";
1389
  create_info->table_existed= 0;		// Mark that table is created
unknown's avatar
unknown committed
1390

unknown's avatar
unknown committed
1391
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1392
    create_info->data_file_name= create_info->index_file_name= 0;
unknown's avatar
unknown committed
1393
  create_info->table_options=db_options;
1394

1395
  if (rea_create_table(thd, path, create_info, fields, key_count,
1396
		       key_info_buffer))
unknown's avatar
unknown committed
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
  {
    /* my_error(ER_CANT_CREATE_TABLE,MYF(0),table_name,my_errno); */
    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
1409
    thd->tmp_table_used= 1;
unknown's avatar
unknown committed
1410
  }
1411
  if (!tmp_table)
1412 1413 1414 1415 1416
  {
    // 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
1417
      thd->clear_error();
1418
      Query_log_event qinfo(thd, thd->query, thd->query_length,
1419
			    test(create_info->options &
1420 1421
				 HA_LEX_CREATE_TMP_TABLE),
			    FALSE);
1422 1423 1424
      mysql_bin_log.write(&qinfo);
    }
  }
unknown's avatar
unknown committed
1425 1426 1427
  error=0;
end:
  VOID(pthread_mutex_unlock(&LOCK_open));
1428
  start_waiting_global_read_lock(thd);
unknown's avatar
unknown committed
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
  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++)
1441
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451
      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;

1452 1453
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
1454 1455 1456 1457 1458 1459 1460
    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
  */
1461
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
1462
  {
1463 1464
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
1465 1466 1467
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1468
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
1469 1470
}

1471

unknown's avatar
unknown committed
1472 1473 1474 1475 1476
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
1477 1478 1479 1480 1481
			       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
1482
{
1483
  TABLE tmp_table;		// Used during 'create_field()'
1484
  TABLE *table= 0;
unknown's avatar
unknown committed
1485
  tmp_table.table_name=0;
1486
  uint select_field_count= items->elements;
unknown's avatar
unknown committed
1487 1488 1489
  DBUG_ENTER("create_table_from_items");

  /* Add selected items to field list */
unknown's avatar
unknown committed
1490
  List_iterator_fast<Item> it(*items);
unknown's avatar
unknown committed
1491 1492 1493 1494 1495 1496
  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 ||
1497
				    create_info->db_type == DB_TYPE_HEAP);
unknown's avatar
unknown committed
1498 1499 1500 1501

  while ((item=it++))
  {
    create_field *cr_field;
1502 1503 1504 1505 1506
    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(),
1507
				  (Item ***) 0, &tmp_field, 0, 0, 0);
1508
    if (!field ||
1509 1510 1511
	!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
					   ((Item_field *)item)->field :
					   (Field*) 0))))
unknown's avatar
unknown committed
1512 1513 1514 1515
      DBUG_RETURN(0);
    extra_fields->push_back(cr_field);
  }
  /* create and lock table */
1516
  /* QQ: create and open should be done atomic ! */
1517
  /*
1518
    We don't log the statement, it will be logged later.
1519 1520 1521 1522
    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
1523 1524
    TABLE, which is a wrong order. So we keep binary logging disabled when we
    open_table().
1525
  */
1526
  tmp_disable_binlog(thd);
unknown's avatar
unknown committed
1527
  if (!mysql_create_table(thd,db,name,create_info,*extra_fields,
unknown's avatar
unknown committed
1528
			 *keys,0,select_field_count))
unknown's avatar
unknown committed
1529
  {
1530 1531
    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
1532
  }
1533 1534 1535
  reenable_binlog(thd);
  if (!table)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
1536 1537 1538
  table->reginfo.lock_type=TL_WRITE;
  if (!((*lock)=mysql_lock_tables(thd,&table,1)))
  {
1539
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1540
    hash_delete(&open_cache,(byte*) table);
1541
    VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
1542
    quick_rm_table(create_info->db_type,db,table_case_name(create_info, name));
unknown's avatar
unknown committed
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553
    DBUG_RETURN(0);
  }
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  DBUG_RETURN(table);
}


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

1554
bool
unknown's avatar
unknown committed
1555
mysql_rename_table(enum db_type base,
1556 1557 1558 1559
		   const char *old_db,
		   const char *old_name,
		   const char *new_db,
		   const char *new_name)
unknown's avatar
unknown committed
1560
{
unknown's avatar
unknown committed
1561 1562
  char from[FN_REFLEN], to[FN_REFLEN];
  char tmp_from[NAME_LEN+1], tmp_to[NAME_LEN+1];
unknown's avatar
unknown committed
1563
  handler *file=get_new_handler((TABLE*) 0, base);
unknown's avatar
unknown committed
1564
  int error=0;
unknown's avatar
unknown committed
1565
  DBUG_ENTER("mysql_rename_table");
unknown's avatar
unknown committed
1566 1567 1568 1569 1570

  if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
  {
    /* Table handler expects to get all file names as lower case */
    strmov(tmp_from, old_name);
1571
    my_casedn_str(files_charset_info, tmp_from);
unknown's avatar
unknown committed
1572 1573 1574
    old_name= tmp_from;

    strmov(tmp_to, new_name);
1575
    my_casedn_str(files_charset_info, tmp_to);
unknown's avatar
unknown committed
1576 1577
    new_name= tmp_to;
  }
1578 1579 1580 1581
  my_snprintf(from, sizeof(from), "%s/%s/%s",
	      mysql_data_home, old_db, old_name);
  my_snprintf(to, sizeof(to), "%s/%s/%s",
	      mysql_data_home, new_db, new_name);
unknown's avatar
unknown committed
1582 1583
  fn_format(from,from,"","",4);
  fn_format(to,to,    "","",4);
unknown's avatar
unknown committed
1584

unknown's avatar
unknown committed
1585
  if (!(error=file->rename_table((const char*) from,(const char *) to)))
1586 1587 1588
  {
    if (rename_file_ext(from,to,reg_ext))
    {
unknown's avatar
unknown committed
1589
      error=my_errno;
1590 1591 1592 1593
      /* Restore old file name */
      file->rename_table((const char*) to,(const char *) from);
    }
  }
unknown's avatar
unknown committed
1594
  delete file;
unknown's avatar
unknown committed
1595 1596 1597
  if (error)
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
1598 1599
}

unknown's avatar
unknown committed
1600

unknown's avatar
unknown committed
1601
/*
1602 1603 1604 1605
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
1606 1607 1608 1609
    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
1610 1611 1612 1613 1614 1615 1616
  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
1617 1618
*/

1619
static void wait_while_table_is_used(THD *thd,TABLE *table,
1620
				     enum ha_extra_function function)
unknown's avatar
unknown committed
1621
{
1622 1623 1624
  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
1625

1626
  VOID(table->file->extra(function));
1627
  /* Mark all tables that are in use as 'old' */
1628
  mysql_lock_abort(thd, table);			// end threads waiting on lock
1629 1630 1631

  /* Wait until all there are no other threads that has this table open */
  while (remove_table_from_cache(thd,table->table_cache_key,
1632
				 table->real_name))
unknown's avatar
unknown committed
1633
  {
1634 1635 1636
    dropping_tables++;
    (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
    dropping_tables--;
1637 1638 1639
  }
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1640

1641 1642
/*
  Close a cached table
unknown's avatar
unknown committed
1643

1644
  SYNOPSIS
unknown's avatar
unknown committed
1645
    close_cached_table()
1646 1647
    thd			Thread handler
    table		Table to remove from cache
1648 1649 1650 1651

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

1653 1654 1655 1656
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
1657

1658
static bool close_cached_table(THD *thd, TABLE *table)
1659 1660
{
  DBUG_ENTER("close_cached_table");
1661

1662
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
1663 1664
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
1665
  {
1666
    mysql_unlock_tables(thd, thd->lock);
1667
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
1668
  }
1669 1670 1671 1672 1673
  /* 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);
1674
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1675 1676
}

1677
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
1678
			     const char* operator_name, const char* errmsg)
1679

unknown's avatar
unknown committed
1680
{
1681 1682
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
1683 1684 1685 1686
  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
1687
  thd->net.last_error[0]=0;
1688
  if (protocol->write())
unknown's avatar
unknown committed
1689 1690 1691 1692
    return -1;
  return 1;
}

1693

unknown's avatar
unknown committed
1694
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1695
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1696
{
unknown's avatar
unknown committed
1697
  DBUG_ENTER("prepare_for_restore");
1698

unknown's avatar
unknown committed
1699 1700 1701
  if (table->table) // do not overwrite existing tables on restore
  {
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1702 1703
				  "table exists, will not overwrite on restore"
				  ));
unknown's avatar
unknown committed
1704
  }
unknown's avatar
unknown committed
1705
  else
unknown's avatar
unknown committed
1706
  {
1707
    char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
1708
    char src_path[FN_REFLEN], dst_path[FN_REFLEN];
1709
    char* table_name = table->real_name;
unknown's avatar
unknown committed
1710
    char* db = thd->db ? thd->db : table->db;
1711

1712
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
1713
					reg_ext))
unknown's avatar
unknown committed
1714
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
1715

1716
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1717
		mysql_real_data_home, db, table_name);
1718

1719
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
1720
      DBUG_RETURN(-1);
1721

1722
    if (my_copy(src_path,
1723 1724
		fn_format(dst_path, dst_path,"", reg_ext, 4),
		MYF(MY_WME)))
unknown's avatar
unknown committed
1725
    {
1726
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1727
      unlock_table_name(thd, table);
1728
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1729
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1730
				    "Failed copying .frm file"));
unknown's avatar
unknown committed
1731
    }
1732
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
1733
    {
1734
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1735
      unlock_table_name(thd, table);
1736
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1737
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1738
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
1739
    }
unknown's avatar
unknown committed
1740
  }
unknown's avatar
unknown committed
1741

1742 1743 1744 1745
  /*
    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
1746
  if (!(table->table = reopen_name_locked_table(thd, table)))
1747 1748
  {
    pthread_mutex_lock(&LOCK_open);
1749
    unlock_table_name(thd, table);
1750 1751
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1752
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1753
}
1754

1755

unknown's avatar
unknown committed
1756
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
1757
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1758
{
unknown's avatar
unknown committed
1759 1760
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
1761 1762 1763 1764
  DBUG_ENTER("prepare_for_repair");

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

1766
  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
1767
  {
unknown's avatar
unknown committed
1768 1769
    char name[FN_REFLEN];
    strxmov(name, mysql_data_home, "/", table_list->db, "/",
1770
	    table_list->real_name, NullS);
unknown's avatar
unknown committed
1771
    if (openfrm(name, "", 0, 0, 0, &tmp_table))
1772
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
1773
    table= &tmp_table;
1774
  }
unknown's avatar
unknown committed
1775

unknown's avatar
unknown committed
1776 1777 1778 1779 1780 1781 1782 1783 1784
  /*
    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
1785

unknown's avatar
unknown committed
1786 1787 1788
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext= table->file->bas_ext();
  MY_STAT stat_info;
unknown's avatar
unknown committed
1789

unknown's avatar
unknown committed
1790 1791 1792 1793 1794
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
  if (!ext[0] || !ext[1])
1795
    goto end;					// No data file
unknown's avatar
unknown committed
1796

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

1801 1802
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
1803

1804 1805 1806 1807 1808 1809 1810
  /* 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
1811
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
1812
  {
unknown's avatar
unknown committed
1813 1814
    error= -1;
    goto end;
unknown's avatar
unknown committed
1815
  }
unknown's avatar
unknown committed
1816
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
1817
  {
1818
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1819
    unlock_table_name(thd, table_list);
1820
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1821
    error= send_check_errmsg(thd, table_list, "repair",
1822
			     "Failed renaming data file");
unknown's avatar
unknown committed
1823 1824 1825 1826 1827 1828 1829 1830
    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",
1831
			     "Failed generating table from .frm file");
unknown's avatar
unknown committed
1832 1833 1834 1835 1836 1837 1838 1839
    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",
1840
			     "Failed restoring .MYD file");
unknown's avatar
unknown committed
1841
    goto end;
unknown's avatar
unknown committed
1842 1843
  }

1844 1845 1846 1847
  /*
    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
1848
  if (!(table_list->table = reopen_name_locked_table(thd, table_list)))
unknown's avatar
unknown committed
1849 1850
  {
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1851
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
1852 1853
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1854 1855 1856

end:
  if (table == &tmp_table)
1857
    closefrm(table);				// Free allocated memory
unknown's avatar
unknown committed
1858
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1859
}
1860

1861

unknown's avatar
unknown committed
1862 1863 1864 1865 1866 1867
/*
  RETURN VALUES
    0   Message sent to net (admin operation went ok)
   -1   Message should be sent by caller 
        (admin operation or network communication failed)
*/
1868
static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
1869 1870 1871 1872 1873 1874 1875 1876 1877
			     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
1878 1879 1880
{
  TABLE_LIST *table;
  List<Item> field_list;
1881 1882
  Item *item;
  Protocol *protocol= thd->protocol;
1883
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
1884 1885 1886 1887 1888 1889 1890 1891 1892

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

1896
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL);
unknown's avatar
unknown committed
1897 1898 1899
  for (table = tables; table; table = table->next)
  {
    char table_name[NAME_LEN*2+2];
1900
    char* db = table->db;
1901
    bool fatal_error=0;
1902
    strxmov(table_name, db, ".", table->real_name, NullS);
unknown's avatar
unknown committed
1903

unknown's avatar
unknown committed
1904
    thd->open_options|= extra_open_options;
1905
    table->table = open_ltable(thd, table, lock_type);
1906 1907 1908
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
unknown's avatar
unknown committed
1909
    thd->open_options&= ~extra_open_options;
1910

unknown's avatar
unknown committed
1911
    if (prepare_func)
1912
    {
unknown's avatar
unknown committed
1913
      switch ((*prepare_func)(thd, table, check_opt)) {
1914 1915 1916 1917 1918 1919 1920
      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
1921
      }
1922
    }
1923

unknown's avatar
unknown committed
1924 1925 1926
    if (!table->table)
    {
      const char *err_msg;
1927
      protocol->prepare_for_resend();
1928 1929 1930
      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
1931
      if (!(err_msg=thd->net.last_error))
1932
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
1933
      protocol->store(err_msg, system_charset_info);
unknown's avatar
unknown committed
1934
      thd->net.last_error[0]=0;
1935
      if (protocol->write())
1936
	goto err;
unknown's avatar
unknown committed
1937 1938
      continue;
    }
unknown's avatar
unknown committed
1939
    table->table->pos_in_table_list= table;
1940
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
1941
    {
unknown's avatar
unknown committed
1942
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
1943
      protocol->prepare_for_resend();
1944 1945 1946
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error", 5, system_charset_info);
1947
      my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name);
1948
      protocol->store(buff, system_charset_info);
1949
      close_thread_tables(thd);
1950
      table->table=0;				// For query cache
1951
      if (protocol->write())
1952
	goto err;
unknown's avatar
unknown committed
1953 1954 1955
      continue;
    }

1956
    /* Close all instances of the table to allow repair to rename files */
1957
    if (lock_type == TL_WRITE && table->table->version)
1958 1959
    {
      pthread_mutex_lock(&LOCK_open);
1960
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
1961
					      "Waiting to get writelock");
1962 1963
      mysql_lock_abort(thd,table->table);
      while (remove_table_from_cache(thd, table->table->table_cache_key,
1964 1965
				     table->table->real_name) &&
	     ! thd->killed)
1966
      {
1967 1968 1969
	dropping_tables++;
	(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
	dropping_tables--;
1970
      }
1971
      thd->exit_cond(old_message);
1972
      if (thd->killed)
1973
	goto err;
unknown's avatar
unknown committed
1974
      open_for_modify=0;
1975 1976
    }

1977
    int result_code = (table->table->file->*operator_func)(thd, check_opt);
unknown's avatar
unknown committed
1978 1979 1980
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
1981
    protocol->prepare_for_resend();
1982 1983
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
1984

1985 1986 1987
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
1988 1989
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
1990
      {
1991 1992 1993
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
1994
	protocol->store("note", 4, system_charset_info);
1995
	protocol->store(buf, length, system_charset_info);
1996
      }
unknown's avatar
unknown committed
1997 1998
      break;

1999
    case HA_ADMIN_OK:
2000 2001
      protocol->store("status", 6, system_charset_info);
      protocol->store("OK",2, system_charset_info);
unknown's avatar
unknown committed
2002 2003
      break;

2004
    case HA_ADMIN_FAILED:
2005 2006
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation failed",16, system_charset_info);
unknown's avatar
unknown committed
2007 2008
      break;

unknown's avatar
unknown committed
2009 2010 2011
    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
2012
      open_for_modify= FALSE;
unknown's avatar
unknown committed
2013 2014
      break;

2015
    case HA_ADMIN_ALREADY_DONE:
2016 2017
      protocol->store("status", 6, system_charset_info);
      protocol->store("Table is already up to date", 27, system_charset_info);
2018 2019
      break;

2020
    case HA_ADMIN_CORRUPT:
2021
      protocol->store("error", 5, system_charset_info);
2022
      protocol->store("Corrupt", 7, system_charset_info);
2023
      fatal_error=1;
unknown's avatar
unknown committed
2024 2025
      break;

unknown's avatar
unknown committed
2026
    case HA_ADMIN_INVALID:
2027 2028
      protocol->store("error", 5, system_charset_info);
      protocol->store("Invalid argument",16, system_charset_info);
unknown's avatar
unknown committed
2029 2030
      break;

2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041
    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;
      result_code= mysql_recreate_table(thd, table, 0);
unknown's avatar
unknown committed
2042
      close_thread_tables(thd);
2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053
      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
      }
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
      table->next= save_next;
      goto send_result_message;
    }

2054
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
2055 2056
      protocol->store("error", 5, system_charset_info);
      protocol->store("Unknown - internal error during operation", 41
2057
		      , system_charset_info);
2058
      fatal_error=1;
unknown's avatar
unknown committed
2059 2060
      break;
    }
2061
    if (fatal_error)
2062
      table->table->version=0;			// Force close of table
unknown's avatar
unknown committed
2063
    else if (open_for_modify)
2064
    {
2065
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2066
      remove_table_from_cache(thd, table->table->table_cache_key,
2067
			      table->table->real_name);
2068
      pthread_mutex_unlock(&LOCK_open);
2069 2070 2071
      /* May be something modified consequently we have to invalidate cache */
      query_cache_invalidate3(thd, table->table, 0);
    }
unknown's avatar
unknown committed
2072
    close_thread_tables(thd);
2073
    table->table=0;				// For query cache
2074
    if (protocol->write())
unknown's avatar
unknown committed
2075 2076 2077
      goto err;
  }

2078
  send_eof(thd);
unknown's avatar
unknown committed
2079 2080
  DBUG_RETURN(0);
 err:
2081
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
2082 2083
  if (table)
    table->table=0;
unknown's avatar
unknown committed
2084 2085 2086
  DBUG_RETURN(-1);
}

unknown's avatar
unknown committed
2087

unknown's avatar
unknown committed
2088 2089 2090 2091
int mysql_backup_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2092 2093
				"backup", TL_READ, 0, 0, 0,
				&handler::backup));
unknown's avatar
unknown committed
2094
}
unknown's avatar
unknown committed
2095

2096

unknown's avatar
unknown committed
2097 2098 2099 2100
int mysql_restore_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2101 2102 2103
				"restore", TL_WRITE, 1, 0,
				&prepare_for_restore,
				&handler::restore));
unknown's avatar
unknown committed
2104
}
unknown's avatar
unknown committed
2105

2106

2107 2108 2109 2110
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,
2111 2112 2113
				"repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR,
				&prepare_for_repair,
				&handler::repair));
2114 2115
}

2116

2117 2118 2119 2120
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,
2121 2122
				"optimize", TL_WRITE, 1,0,0,
				&handler::optimize));
2123 2124 2125
}


unknown's avatar
unknown committed
2126 2127 2128 2129 2130
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
2131 2132
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2133 2134

  RETURN VALUES
2135 2136
    0	  ok
   -1	  error
unknown's avatar
unknown committed
2137 2138
*/

2139
int mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
2140
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
2141
{
2142
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
2143
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
2144
  DBUG_ENTER("mysql_assign_to_keycache");
2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156

  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,
2157 2158
				"assign_to_keycache", TL_READ_NO_INSERT, 0,
				0, 0, &handler::assign_to_keycache));
unknown's avatar
unknown committed
2159 2160
}

unknown's avatar
unknown committed
2161 2162 2163 2164 2165 2166

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

  SYNOPSIS
    reassign_keycache_tables()
2167 2168 2169
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
2170

2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183
  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
2184
    0	  ok
unknown's avatar
unknown committed
2185 2186
*/

2187 2188
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
2189 2190 2191
{
  DBUG_ENTER("reassign_keycache_tables");

2192 2193
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
2194
  src_cache->param_buff_size= 0;		// Free key cache
2195 2196
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2197
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2198 2199 2200
}


unknown's avatar
unknown committed
2201 2202 2203 2204 2205
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2206 2207
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2208 2209

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

int mysql_preload_keys(THD* thd, TABLE_LIST* tables)
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2218 2219
				"preload_keys", TL_READ, 0, 0, 0,
				&handler::preload_keys));
unknown's avatar
unknown committed
2220 2221 2222
}


unknown's avatar
unknown committed
2223 2224 2225 2226 2227
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2228 2229
    thd		Thread object
    table	Table list (one table only)
unknown's avatar
unknown committed
2230 2231 2232 2233
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
2234 2235
    0	  ok
    -1	error
unknown's avatar
unknown committed
2236 2237
*/

2238 2239 2240
int mysql_create_like_table(THD* thd, TABLE_LIST* table,
			    HA_CREATE_INFO *create_info,
			    Table_ident *table_ident)
unknown's avatar
unknown committed
2241 2242 2243 2244 2245 2246 2247
{
  TABLE **tmp_table;
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char *db= table->db;
  char *table_name= table->real_name;
  char *src_db= thd->db;
  char *src_table= table_ident->table.str;
2248 2249
  int  err, res= -1;
  TABLE_LIST src_tables_list;
unknown's avatar
unknown committed
2250 2251 2252 2253 2254 2255 2256 2257 2258 2259
  DBUG_ENTER("mysql_create_like_table");

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
       check_table_name(src_table,table_ident->table.length)) ||
      table_ident->db.str && check_db_name((src_db= table_ident->db.str)))
  {
2260
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
unknown's avatar
unknown committed
2261
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
2262
  }
2263

2264 2265 2266
  src_tables_list.db= table_ident->db.str ? table_ident->db.str : thd->db;
  src_tables_list.real_name= table_ident->table.str;
  src_tables_list.next= 0;
2267

2268 2269
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
unknown's avatar
unknown committed
2270 2271 2272 2273 2274

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
    strxmov(src_path, (*tmp_table)->path, reg_ext, NullS);
  else
  {
2275 2276
    strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table,
	    reg_ext, NullS);
unknown's avatar
unknown committed
2277 2278 2279
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2280
      goto err;
unknown's avatar
unknown committed
2281 2282 2283 2284 2285 2286
    }
  }

  /*
    Validate the destination table

2287
    skip the destination table name checking as this is already
unknown's avatar
unknown committed
2288 2289 2290 2291 2292 2293
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2294 2295 2296
    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);
2297 2298
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
unknown's avatar
unknown committed
2299 2300 2301 2302
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2303 2304
    strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
	    reg_ext, NullS);
unknown's avatar
unknown committed
2305 2306 2307 2308
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2309
  /*
unknown's avatar
unknown committed
2310
    Create a new table by copying from source table
2311
  */
2312 2313
  if (my_copy(src_path, dst_path, MYF(MY_WME|MY_DONT_OVERWRITE_FILE)))
    goto err;
unknown's avatar
unknown committed
2314 2315

  /*
2316 2317
    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
2318 2319
    and temporary tables).
  */
2320
  *fn_ext(dst_path)= 0;
unknown's avatar
unknown committed
2321
  err= ha_create_table(dst_path, create_info, 1);
2322

unknown's avatar
unknown committed
2323 2324 2325 2326
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2327 2328
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2329
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
2330 2331 2332 2333
    }
  }
  else if (err)
  {
2334 2335 2336
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
2337
  }
2338 2339 2340 2341

  // 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
2342
  {
2343 2344
    thd->clear_error();
    Query_log_event qinfo(thd, thd->query, thd->query_length,
2345
			  test(create_info->options &
2346 2347
			       HA_LEX_CREATE_TMP_TABLE), 
			  FALSE);
2348
    mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
2349
  }
2350
  res= 0;
2351
  goto err;
2352

unknown's avatar
unknown committed
2353 2354 2355 2356
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2357 2358 2359 2360
    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);
2361
    res= 0;
unknown's avatar
unknown committed
2362
  }
2363 2364 2365 2366 2367 2368 2369 2370
  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
2371 2372 2373
}


2374 2375
int mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
unknown's avatar
unknown committed
2376 2377 2378 2379 2380 2381
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2382 2383
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2384 2385
				"analyze", lock_type, 1,0,0,
				&handler::analyze));
2386 2387 2388 2389 2390
}


int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
{
unknown's avatar
unknown committed
2391 2392 2393 2394 2395 2396
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2397 2398
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2399 2400 2401
				"check", lock_type,
				0, HA_OPEN_FOR_REPAIR, 0,
				&handler::check));
2402 2403
}

unknown's avatar
unknown committed
2404

unknown's avatar
unknown committed
2405
/* table_list should contain just one table */
unknown's avatar
unknown committed
2406 2407 2408 2409
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
2410 2411 2412 2413 2414 2415
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
2416 2417 2418 2419
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
2420 2421 2422

  thd->proc_info="discard_or_import_tablespace";

unknown's avatar
unknown committed
2423
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
2424

unknown's avatar
unknown committed
2425 2426 2427 2428 2429
 /*
   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
2430 2431 2432 2433 2434
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2435

unknown's avatar
unknown committed
2436 2437 2438 2439 2440 2441 2442
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

unknown's avatar
unknown committed
2443 2444 2445 2446
  /*
    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
2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457
  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())
  {
2458
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
unknown's avatar
unknown committed
2459 2460 2461 2462
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
unknown's avatar
unknown committed
2463
  thd->tablespace_op=FALSE;
unknown's avatar
unknown committed
2464 2465
  if (error == 0)
  {
unknown's avatar
unknown committed
2466
    send_ok(thd);
unknown's avatar
unknown committed
2467
    DBUG_RETURN(0);
unknown's avatar
unknown committed
2468
  }
unknown's avatar
unknown committed
2469 2470 2471 2472 2473

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

2476 2477

#ifdef NOT_USED
2478 2479 2480 2481 2482 2483 2484
/*
  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.
*/

2485
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2486 2487 2488 2489 2490
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2491 2492 2493 2494 2495 2496 2497 2498
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2499 2500 2501
  DBUG_ENTER("mysql_create_index");

  /*
2502 2503 2504
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2505

2506 2507
    Open the table to have access to the correct table handler.
  */
2508 2509 2510 2511
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2512 2513 2514 2515 2516
    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.
  */
2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
  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,
2528 2529 2530
			  keys, /*tmp_table*/ 0, db_options, table->file,
			  key_info_buffer, key_count,
			  /*select_field_count*/ 0))
2531 2532 2533
    DBUG_RETURN(-1);

  /*
2534 2535 2536
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2537 2538 2539 2540
  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)&
2541
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2542 2543
      break ;
  }
2544
  if ((idx < key_count)|| !key_count)
2545 2546 2547 2548 2549 2550 2551 2552
  {
    /* 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,
2553 2554 2555 2556
			 &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);*/
2557 2558 2559 2560 2561
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
2562 2563 2564 2565 2566 2567 2568 2569
	(my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
		     table_list->db, (lower_case_table_names == 2) ?
		     table_list->alias: table_list->real_name, reg_ext) >=
	 (int) sizeof(path)) ||
	! unpack_filename(path, path) ||
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
      /* don't need to free((gptr) key_info_buffer);*/
2570 2571
      DBUG_RETURN(-1);
  }
2572
  /* don't need to free((gptr) key_info_buffer);*/
2573 2574 2575 2576
  DBUG_RETURN(0);
}


2577 2578
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
2579 2580
{
  List<create_field> fields;
2581
  List<Key>	     keys;
2582 2583
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2584 2585 2586 2587 2588 2589 2590 2591 2592
  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];
2593 2594 2595
  DBUG_ENTER("mysql_drop_index");

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

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

  /*
2606 2607 2608
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
2609 2610 2611 2612
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
2613 2614
    Get the number of each key and check if it can be created online.
  */
2615 2616 2617 2618 2619 2620 2621 2622 2623
  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))
2624
	break;
2625 2626 2627 2628 2629 2630 2631 2632
    }
    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);
    }
    /*
2633 2634 2635
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
2636 2637
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
2638
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649
      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,
2650 2651 2652
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
2653 2654 2655 2656 2657 2658 2659
      /*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)||
2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670
	mysql_prepare_table(thd, &create_info, fields,
			    keys, /*tmp_table*/ 0, db_options, table->file,
			    key_info_buffer, key_count,
			    /*select_field_count*/ 0)||
	(snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
		  table_list->db, (lower_case_table_names == 2)?
		  table_list->alias: table_list->real_name, reg_ext)>=
	 (int)sizeof(path))||
	! unpack_filename(path, path)||
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
2671 2672 2673 2674 2675 2676 2677
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
2678
#endif /* NOT_USED */
2679 2680


2681 2682 2683
/*
  Alter table
*/
2684

unknown's avatar
unknown committed
2685
int mysql_alter_table(THD *thd,char *new_db, char *new_name,
2686 2687
		      HA_CREATE_INFO *create_info,
		      TABLE_LIST *table_list,
2688 2689
		      List<create_field> &fields, List<Key> &keys,
		      uint order_num, ORDER *order,
2690
		      enum enum_duplicates handle_duplicates, bool ignore,
2691
		      ALTER_INFO *alter_info, bool do_send_ok)
unknown's avatar
unknown committed
2692
{
2693
  TABLE *table,*new_table;
unknown's avatar
unknown committed
2694
  int error;
unknown's avatar
unknown committed
2695 2696
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
2697
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
unknown's avatar
unknown committed
2698 2699
  ha_rows copied,deleted;
  ulonglong next_insert_id;
2700
  uint db_create_options, used_fields;
unknown's avatar
unknown committed
2701
  enum db_type old_db_type,new_db_type;
2702
  DBUG_ENTER("mysql_alter_table");
unknown's avatar
unknown committed
2703 2704 2705

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

unknown's avatar
unknown committed
2708
  db=table_list->db;
unknown's avatar
unknown committed
2709
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
2710
    new_db= db;
2711
  used_fields=create_info->used_fields;
unknown's avatar
unknown committed
2712

2713
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL);
2714
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
2715
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
2716
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
2717
						   alter_info->tablespace_op));
2718 2719 2720
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
2721 2722 2723 2724
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
2725
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
2726
    if (lower_case_table_names)
unknown's avatar
unknown committed
2727 2728 2729
    {
      if (lower_case_table_names != 2)
      {
2730
	my_casedn_str(files_charset_info, new_name_buff);
2731
	new_alias= new_name;			// Create lower case table name
unknown's avatar
unknown committed
2732
      }
2733
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
2734
    }
2735
    if (new_db == db &&
2736
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
2737 2738
    {
      /*
2739 2740
	Source and destination table names are equal: make later check
	easier.
2741
      */
unknown's avatar
unknown committed
2742
      new_alias= new_name= table_name;
2743
    }
unknown's avatar
unknown committed
2744 2745 2746 2747
    else
    {
      if (table->tmp_table)
      {
2748 2749 2750 2751 2752
	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
2753 2754 2755
      }
      else
      {
2756 2757 2758
	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),
2759 2760 2761 2762 2763 2764
		    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
2765 2766 2767 2768
      }
    }
  }
  else
2769 2770 2771 2772
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
2773 2774 2775 2776

  old_db_type=table->db_type;
  if (create_info->db_type == DB_TYPE_DEFAULT)
    create_info->db_type=old_db_type;
2777 2778 2779 2780 2781
  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,
2782 2783 2784 2785
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			new_name);
2786
  }
2787
  if (create_info->row_type == ROW_TYPE_NOT_USED)
unknown's avatar
unknown committed
2788 2789 2790
    create_info->row_type=table->row_type;

  thd->proc_info="setup";
2791
  if (alter_info->is_simple && !table->tmp_table)
unknown's avatar
unknown committed
2792 2793
  {
    error=0;
2794
    if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
2795
    {
2796 2797 2798 2799 2800 2801
      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))
      {
2802 2803
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
	error= -1;
2804 2805 2806
      }
      else
      {
2807 2808 2809 2810
	*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;
2811 2812
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
2813
    }
unknown's avatar
unknown committed
2814

2815
    if (!error)
2816
    {
2817
      switch (alter_info->keys_onoff) {
2818
      case LEAVE_AS_IS:
2819
	break;
2820
      case ENABLE:
2821
	VOID(pthread_mutex_lock(&LOCK_open));
2822
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
2823
	VOID(pthread_mutex_unlock(&LOCK_open));
2824
	error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2825
	/* COND_refresh will be signaled in close_thread_tables() */
2826 2827
	break;
      case DISABLE:
2828 2829 2830
	VOID(pthread_mutex_lock(&LOCK_open));
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
	VOID(pthread_mutex_unlock(&LOCK_open));
2831
	error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2832
	/* COND_refresh will be signaled in close_thread_tables() */
2833
	break;
2834
      }
2835
    }
2836
    if (error == HA_ERR_WRONG_COMMAND)
unknown's avatar
unknown committed
2837 2838
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2839 2840
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->table_name);
unknown's avatar
unknown committed
2841 2842
      error=0;
    }
unknown's avatar
unknown committed
2843 2844
    if (!error)
    {
2845 2846 2847
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2848
	thd->clear_error();
2849
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2850
	mysql_bin_log.write(&qinfo);
2851
      }
2852 2853
      if (do_send_ok)
        send_ok(thd);
unknown's avatar
unknown committed
2854
    }
2855
    else if (error > 0)
2856 2857
    {
      table->file->print_error(error, MYF(0));
2858
      error= -1;
2859
    }
2860
    table_list->table=0;				// For query cache
2861
    query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
2862 2863 2864 2865
    DBUG_RETURN(error);
  }

  /* Full alter table */
2866 2867 2868 2869 2870 2871 2872 2873

  /* 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;
2874 2875
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
    create_info->default_table_charset= table->table_charset;
2876

2877
  restore_record(table,default_values);		// Empty record for DEFAULT
2878
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
2879
  List_iterator<create_field> def_it(fields);
2880
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
2881 2882
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
2883 2884
  create_field *def;

unknown's avatar
unknown committed
2885
  /*
2886
    First collect all fields from table which isn't in drop_list
unknown's avatar
unknown committed
2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
    /* Check if field should be droped */
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
2898
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
2899
      {
2900 2901 2902 2903 2904 2905 2906 2907
	/* 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;
2908
      }
unknown's avatar
unknown committed
2909 2910 2911 2912 2913 2914 2915 2916 2917 2918
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
2919
      if (def->change &&
2920 2921
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
unknown's avatar
unknown committed
2922 2923
    }
    if (def)
2924
    {						// Field is changed
unknown's avatar
unknown committed
2925
      def->field=field;
2926 2927
      if (!def->after)
      {
2928 2929
	create_list.push_back(def);
	def_it.remove();
2930
      }
unknown's avatar
unknown committed
2931 2932
    }
    else
2933
    {						// Use old field value
2934
      create_list.push_back(def=new create_field(field,field));
2935
      alter_it.rewind();			// Change default if ALTER
unknown's avatar
unknown committed
2936 2937 2938
      Alter_column *alter;
      while ((alter=alter_it++))
      {
2939 2940
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
2941 2942 2943
      }
      if (alter)
      {
2944 2945 2946 2947 2948 2949 2950
	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
2951 2952 2953 2954 2955
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
2956
  while ((def=def_it++))			// Add new columns
unknown's avatar
unknown committed
2957
  {
2958
    if (def->change && ! def->field)
unknown's avatar
unknown committed
2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970
    {
      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();
2971
      while ((find=find_it++))			// Add new columns
unknown's avatar
unknown committed
2972
      {
2973 2974
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
unknown's avatar
unknown committed
2975 2976 2977
      }
      if (!find)
      {
2978 2979
	my_error(ER_BAD_FIELD_ERROR,MYF(0),def->after,table_name);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
2980
      }
2981
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
2982 2983
    }
  }
2984
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
2985
  {
2986 2987
    my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_info->alter_list.head()->name,
	     table_name);
unknown's avatar
unknown committed
2988 2989 2990 2991 2992 2993 2994 2995 2996
    DBUG_RETURN(-1);
  }
  if (!create_list.elements)
  {
    my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
    DBUG_RETURN(-1);
  }

  /*
2997 2998
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
2999 3000 3001 3002 3003 3004 3005 3006 3007
  */

  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++)
  {
3008
    char *key_name= key_info->name;
unknown's avatar
unknown committed
3009 3010 3011 3012 3013
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
3014 3015
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
	break;
unknown's avatar
unknown committed
3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027
    }
    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)
3028
	continue;				// Wrong field (from UNIREG)
unknown's avatar
unknown committed
3029 3030 3031 3032 3033
      const char *key_part_name=key_part->field->field_name;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
3034 3035 3036 3037 3038 3039 3040 3041 3042
	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
3043 3044
      }
      if (!cfield)
3045
	continue;				// Field is removed
unknown's avatar
unknown committed
3046
      uint key_part_length=key_part->length;
3047 3048 3049 3050 3051 3052 3053
      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
3054
      }
3055
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
3056
      key_parts.push_back(new key_part_spec(cfield->field_name,
3057
					    key_part_length));
unknown's avatar
unknown committed
3058 3059
    }
    if (key_parts.elements)
unknown's avatar
unknown committed
3060
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
3061 3062 3063 3064 3065 3066 3067 3068
				 (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,
3069
                                 test(key_info->flags & HA_GENERATED_KEY),
3070
				 key_parts));
unknown's avatar
unknown committed
3071 3072 3073
  }
  {
    Key *key;
3074
    while ((key=key_it++))			// Add new keys
3075 3076
    {
      if (key->type != Key::FOREIGN_KEY)
3077
	key_list.push_back(key);
3078
      if (key->name &&
3079
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
3080
      {
3081 3082
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
	DBUG_RETURN(-1);
3083
      }
3084
    }
unknown's avatar
unknown committed
3085 3086
  }

3087
  if (alter_info->drop_list.elements)
unknown's avatar
unknown committed
3088
  {
3089 3090
    my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),
	     alter_info->drop_list.head()->name);
unknown's avatar
unknown committed
3091 3092
    goto err;
  }
3093
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3094
  {
3095 3096
    my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),
	     alter_info->alter_list.head()->name);
unknown's avatar
unknown committed
3097 3098 3099
    goto err;
  }

3100
  db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD);
3101 3102
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3103 3104
  /* Safety fix for innodb */
  if (lower_case_table_names)
3105
    my_casedn_str(files_charset_info, tmp_name);
unknown's avatar
unknown committed
3106 3107 3108
  create_info->db_type=new_db_type;
  if (!create_info->comment)
    create_info->comment=table->comment;
3109 3110 3111 3112 3113

  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
3114 3115 3116 3117 3118 3119 3120
    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 |
3121
			  HA_OPTION_NO_DELAY_KEY_WRITE);
unknown's avatar
unknown committed
3122 3123 3124 3125 3126
  create_info->table_options|= db_create_options;

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

3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151
  /*
    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.
  */

3152
  if (!strcmp(db, new_db))		// Ignore symlink if db changed
3153 3154 3155 3156 3157 3158
  {
    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,
3159 3160
					   create_info->index_file_name,
					   1);
3161 3162 3163 3164 3165 3166
    }
    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,
3167 3168
					  create_info->data_file_name,
					  1);
3169 3170
    }
  }
3171 3172
  else
    create_info->data_file_name=create_info->index_file_name=0;
3173
  {
3174 3175
    /* We don't log the statement, it will be logged later. */
    tmp_disable_binlog(thd);
unknown's avatar
unknown committed
3176
    error= mysql_create_table(thd, new_db, tmp_name,
unknown's avatar
unknown committed
3177
                              create_info,create_list,key_list,1,0);
3178 3179
    reenable_binlog(thd);
    if (error)
3180 3181
      DBUG_RETURN(error);
  }
unknown's avatar
unknown committed
3182 3183 3184
  if (table->tmp_table)
    new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
  else
unknown's avatar
unknown committed
3185
  {
unknown's avatar
unknown committed
3186
    char path[FN_REFLEN];
3187 3188
    my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
		new_db, tmp_name);
unknown's avatar
unknown committed
3189 3190 3191 3192 3193 3194 3195
    fn_format(path,path,"","",4);
    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
3196 3197
  }

3198

3199 3200
  /* We don't want update TIMESTAMP fields during ALTER TABLE. */
  new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
unknown's avatar
unknown committed
3201
  new_table->next_number_field=new_table->found_next_number_field;
3202
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
3203 3204
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3205
  next_insert_id=thd->next_insert_id;		// Remember for loggin
unknown's avatar
unknown committed
3206 3207 3208
  copied=deleted=0;
  if (!new_table->is_view)
    error=copy_data_between_tables(table,new_table,create_list,
3209
				   handle_duplicates, ignore,
3210 3211
				   order_num, order, &copied, &deleted);
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3212
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
3213 3214 3215 3216 3217 3218

  if (table->tmp_table)
  {
    /* We changed a temporary table */
    if (error)
    {
unknown's avatar
unknown committed
3219
      /*
3220 3221
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3222
      */
unknown's avatar
unknown committed
3223 3224 3225
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3226 3227 3228 3229 3230 3231
    /* 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
3232 3233
    /* Remove link to old table and rename the new one */
    close_temporary_table(thd,table->table_cache_key,table_name);
unknown's avatar
unknown committed
3234
    if (rename_temporary_table(thd, new_table, new_db, new_alias))
3235
    {						// Fatal error
unknown's avatar
unknown committed
3236 3237 3238 3239
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3240 3241 3242
    mysql_update_log.write(thd, thd->query,thd->query_length);
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
3243
      thd->clear_error();
3244
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3245 3246
      mysql_bin_log.write(&qinfo);
    }
unknown's avatar
unknown committed
3247 3248 3249
    goto end_temporary;
  }

3250
  intern_close_table(new_table);		/* close temporary table */
unknown's avatar
unknown committed
3251 3252 3253 3254 3255 3256 3257 3258
  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;
  }
3259

unknown's avatar
unknown committed
3260
  /*
3261 3262 3263
    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
3264 3265 3266
  */

  thd->proc_info="rename result table";
3267 3268
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3269 3270
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3271
  if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282
  {
    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
3283 3284 3285 3286 3287
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3288
      Win32 and InnoDB can't drop a table that is in use, so we must
3289
      close the original table at before doing the rename
unknown's avatar
unknown committed
3290
    */
3291
    table_name=thd->strdup(table_name);		// must be saved
3292
    if (close_cached_table(thd, table))
3293
    {						// Aborted
unknown's avatar
unknown committed
3294 3295 3296 3297
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
3298
    table=0;					// Marker that table is closed
unknown's avatar
unknown committed
3299
  }
unknown's avatar
unknown committed
3300 3301
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  else
3302
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
unknown's avatar
unknown committed
3303 3304
#endif

unknown's avatar
unknown committed
3305

unknown's avatar
unknown committed
3306 3307 3308 3309 3310 3311 3312
  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,
3313 3314
			      new_alias))
  {						// Try to get everything back
unknown's avatar
unknown committed
3315
    error=1;
unknown's avatar
unknown committed
3316
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
unknown's avatar
unknown committed
3317
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
unknown's avatar
unknown committed
3318
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
unknown's avatar
unknown committed
3319 3320 3321
  }
  if (error)
  {
unknown's avatar
unknown committed
3322 3323 3324 3325
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
3326 3327
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3328 3329 3330
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
3331
  if (thd->lock || new_name != table_name)	// True if WIN32
unknown's avatar
unknown committed
3332
  {
unknown's avatar
unknown committed
3333 3334 3335 3336
    /*
      Not table locking or alter table with rename
      free locks and remove old table
    */
3337 3338
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3339 3340 3341 3342
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
unknown's avatar
unknown committed
3343 3344 3345 3346 3347
    /*
      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
3348 3349 3350 3351
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
      remove_table_from_cache(thd,db,table_name); // Mark all in-use copies old
3352
      mysql_lock_abort(thd,table);		 // end threads waiting on lock
unknown's avatar
unknown committed
3353 3354 3355
    }
    VOID(quick_rm_table(old_db_type,db,old_name));
    if (close_data_tables(thd,db,table_name) ||
3356 3357
	reopen_tables(thd,1,0))
    {						// This shouldn't happen
3358
      if (table)
3359
	close_cached_table(thd,table);		// Remove lock for table
unknown's avatar
unknown committed
3360 3361 3362 3363
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
unknown's avatar
unknown committed
3364
  /* The ALTER TABLE is always in its own transaction */
3365 3366 3367 3368
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
unknown's avatar
unknown committed
3369 3370
  {
    VOID(pthread_mutex_unlock(&LOCK_open));
3371
    VOID(pthread_cond_broadcast(&COND_refresh));
unknown's avatar
unknown committed
3372 3373 3374
    goto err;
  }
  thd->proc_info="end";
3375 3376
  mysql_update_log.write(thd, thd->query,thd->query_length);
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
3377
  {
unknown's avatar
unknown committed
3378
    thd->clear_error();
3379
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
unknown's avatar
unknown committed
3380 3381 3382 3383
    mysql_bin_log.write(&qinfo);
  }
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_mutex_unlock(&LOCK_open));
3384 3385 3386
#ifdef HAVE_BERKELEY_DB
  if (old_db_type == DB_TYPE_BERKELEY_DB)
  {
unknown's avatar
unknown committed
3387 3388 3389 3390 3391
    /*
      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.
    */
3392
    char path[FN_REFLEN];
3393 3394
    my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
		new_db, table_name);
3395 3396 3397
    fn_format(path,path,"","",4);
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
unknown's avatar
unknown committed
3398
    {
3399 3400
      intern_close_table(table);
      my_free((char*) table, MYF(0));
unknown's avatar
unknown committed
3401
    }
3402
    else
unknown's avatar
unknown committed
3403 3404
      sql_print_warning("Could not open BDB table %s.%s after rename\n",
                        new_db,table_name);
3405
    (void) berkeley_flush_logs();
3406 3407
  }
#endif
3408
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
3409
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3410 3411

end_temporary:
3412 3413 3414
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3415 3416
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
unknown's avatar
unknown committed
3417 3418 3419 3420 3421 3422 3423 3424 3425
  thd->some_tables_deleted=0;
  DBUG_RETURN(0);

 err:
  DBUG_RETURN(-1);
}


static int
unknown's avatar
unknown committed
3426
copy_data_between_tables(TABLE *from,TABLE *to,
3427 3428
			 List<create_field> &create,
			 enum enum_duplicates handle_duplicates,
3429
                         bool ignore,
3430 3431 3432
			 uint order_num, ORDER *order,
			 ha_rows *copied,
			 ha_rows *deleted)
unknown's avatar
unknown committed
3433 3434
{
  int error;
3435
  Copy_field *copy,*copy_end;
unknown's avatar
unknown committed
3436 3437
  ulong found_count,delete_count;
  THD *thd= current_thd;
unknown's avatar
unknown committed
3438 3439 3440 3441 3442 3443
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3444
  ha_rows examined_rows;
3445
  bool auto_increment_field_copied= 0;
3446
  ulong save_sql_mode;
unknown's avatar
unknown committed
3447 3448
  DBUG_ENTER("copy_data_between_tables");

3449 3450 3451 3452 3453 3454
  /*
    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
  */
3455
  error= ha_enable_transaction(thd, FALSE);
3456 3457
  if (error)
    DBUG_RETURN(-1);
3458
  
unknown's avatar
unknown committed
3459
  if (!(copy= new Copy_field[to->fields]))
3460
    DBUG_RETURN(-1);				/* purecov: inspected */
unknown's avatar
unknown committed
3461

3462 3463
  if (to->file->external_lock(thd, F_WRLCK))
    DBUG_RETURN(-1);
3464
  from->file->info(HA_STATUS_VARIABLE);
3465
  to->file->start_bulk_insert(from->file->records);
unknown's avatar
unknown committed
3466

3467 3468
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
3469 3470 3471 3472 3473 3474 3475
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
3476 3477
    {
      if (*ptr == to->next_number_field)
3478
      {
3479
        auto_increment_field_copied= TRUE;
3480 3481 3482 3483 3484 3485 3486 3487 3488
        /*
          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
3489
      (copy_end++)->set(*ptr,def->field,0);
3490 3491
    }

unknown's avatar
unknown committed
3492 3493
  }

3494 3495
  found_count=delete_count=0;

unknown's avatar
unknown committed
3496 3497
  if (order)
  {
unknown's avatar
unknown committed
3498
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
3499
					      MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
3500 3501
    bzero((char*) &tables,sizeof(tables));
    tables.table = from;
3502
    tables.alias = tables.real_name= from->real_name;
3503
    tables.db	 = from->table_cache_key;
unknown's avatar
unknown committed
3504 3505
    error=1;

3506
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
3507 3508 3509 3510 3511 3512 3513
	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
3514 3515 3516
      goto err;
  };

3517 3518 3519 3520
  /* 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
3521
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
3522
  if (ignore ||
3523
      handle_duplicates == DUP_REPLACE)
unknown's avatar
unknown committed
3524
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
3525
  thd->row_count= 0;
unknown's avatar
unknown committed
3526 3527 3528 3529 3530 3531 3532 3533
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
      my_error(ER_SERVER_SHUTDOWN,MYF(0));
      error= 1;
      break;
    }
3534
    thd->row_count++;
3535 3536
    if (to->next_number_field)
    {
3537
      if (auto_increment_field_copied)
3538
        to->auto_increment_field_not_null= TRUE;
3539 3540 3541 3542 3543
      else
        to->next_number_field->reset();
    }
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
    {
unknown's avatar
unknown committed
3544
      copy_ptr->do_copy(copy_ptr);
3545
    }
unknown's avatar
unknown committed
3546 3547
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
3548
      if ((!ignore &&
3549 3550 3551
	   handle_duplicates != DUP_REPLACE) ||
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
unknown's avatar
unknown committed
3552
      {
3553 3554
	to->file->print_error(error,MYF(0));
	break;
unknown's avatar
unknown committed
3555 3556 3557 3558
      }
      delete_count++;
    }
    else
3559
      found_count++;
unknown's avatar
unknown committed
3560 3561
  }
  end_read_record(&info);
3562
  free_io_cache(from);
3563
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
3564 3565

  if (to->file->end_bulk_insert() && !error)
unknown's avatar
unknown committed
3566
  {
unknown's avatar
unknown committed
3567
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
3568 3569
    error=1;
  }
unknown's avatar
unknown committed
3570
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
3571

3572
  ha_enable_transaction(thd,TRUE);
3573

3574 3575 3576 3577 3578 3579 3580 3581
  /*
    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;
3582

unknown's avatar
unknown committed
3583
 err:
3584
  thd->variables.sql_mode= save_sql_mode;
unknown's avatar
unknown committed
3585
  free_io_cache(from);
unknown's avatar
unknown committed
3586 3587
  *copied= found_count;
  *deleted=delete_count;
3588 3589
  if (to->file->external_lock(thd,F_UNLCK))
    error=1;
unknown's avatar
unknown committed
3590 3591
  DBUG_RETURN(error > 0 ? -1 : 0);
}
3592

unknown's avatar
unknown committed
3593

3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615
/*
  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();
3616
  lex->alter_info.is_simple= 0;                 // Force full recreate
3617 3618 3619 3620 3621 3622 3623
  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,
3624
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
3625 3626 3627
}


unknown's avatar
unknown committed
3628
int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
3629 3630 3631 3632 3633
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
3634
  DBUG_ENTER("mysql_checksum_table");
3635 3636 3637 3638 3639 3640 3641 3642

  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
3643
  for (table= tables; table; table= table->next)
3644 3645
  {
    char table_name[NAME_LEN*2+2];
3646
    TABLE *t;
3647

unknown's avatar
unknown committed
3648 3649 3650
    strxmov(table_name, table->db ,".", table->real_name, NullS);

    t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT);
3651
    thd->clear_error();			// these errors shouldn't get client
3652 3653 3654 3655

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

3656
    if (!t)
3657
    {
unknown's avatar
unknown committed
3658
      /* Table didn't exist */
3659 3660 3661 3662 3663
      protocol->store_null();
      thd->net.last_error[0]=0;
    }
    else
    {
3664
      t->pos_in_table_list= table;
3665

3666
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
3667 3668
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
3669
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
3670 3671
	       (check_opt->flags & T_QUICK))
	protocol->store_null();
3672 3673
      else
      {
3674 3675 3676 3677 3678 3679 3680 3681
	/* 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
3682
	if (t->file->ha_rnd_init(1))
3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709
	  protocol->store_null();
	else
	{
	  while (!t->file->rnd_next(t->record[0]))
	  {
	    ha_checksum row_crc= 0;
	    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
3710
          t->file->ha_rnd_end();
3711
	}
3712
      }
unknown's avatar
unknown committed
3713
      thd->clear_error();
3714
      close_thread_tables(thd);
3715
      table->table=0;				// For query cache
3716 3717 3718 3719 3720 3721 3722
    }
    if (protocol->write())
      goto err;
  }

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

3724
 err:
3725
  close_thread_tables(thd);			// Shouldn't be needed
3726 3727 3728 3729
  if (table)
    table->table=0;
  DBUG_RETURN(-1);
}