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

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

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

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

/* drop and alter of tables */

#include "mysql_priv.h"
20
#ifdef HAVE_BERKELEY_DB
21
#include "ha_berkeley.h"
22
#endif
23
#include <hash.h>
unknown's avatar
unknown committed
24
#include <myisam.h>
25
#include <my_dir.h>
26 27
#include "sp_head.h"
#include "sql_trigger.h"
unknown's avatar
unknown committed
28 29 30 31 32

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

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

static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
static int copy_data_between_tables(TABLE *from,TABLE *to,
				    List<create_field> &create,
				    enum enum_duplicates handle_duplicates,
40
                                    bool ignore,
41
				    uint order_num, ORDER *order,
unknown's avatar
unknown committed
42
				    ha_rows *copied,ha_rows *deleted);
unknown's avatar
unknown committed
43
static bool prepare_blob_field(THD *thd, create_field *sql_field);
44 45
static bool check_engine(THD *thd, const char *table_name,
                         enum db_type *new_engine);                             
unknown's avatar
unknown committed
46

47 48 49 50 51 52 53 54 55 56 57 58 59 60

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

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

  RETURN
61 62
    0                   Error
    #                   Size of path
63 64
 */

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



unknown's avatar
unknown committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
/*
 delete (drop) tables.

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

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

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

  RETURN
unknown's avatar
unknown committed
93 94
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
95 96

*/
unknown's avatar
unknown committed
97

unknown's avatar
unknown committed
98 99
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
100
{
101
  bool error= FALSE, need_start_waiters= FALSE;
unknown's avatar
unknown committed
102 103 104 105 106 107 108 109
  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));

110
  if (!drop_temporary)
unknown's avatar
unknown committed
111
  {
112
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
unknown's avatar
unknown committed
113
    {
114
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
unknown's avatar
unknown committed
115 116
      goto err;
    }
117 118
    else
      need_start_waiters= TRUE;
unknown's avatar
unknown committed
119
  }
unknown's avatar
VIEW  
unknown committed
120
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
121

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

130 131 132
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

133
  if (error)
unknown's avatar
unknown committed
134
    DBUG_RETURN(TRUE);
135
  send_ok(thd);
unknown's avatar
unknown committed
136
  DBUG_RETURN(FALSE);
137 138
}

unknown's avatar
unknown committed
139 140 141 142 143

/*
 delete (drop) tables.

  SYNOPSIS
144 145 146 147 148
    mysql_rm_table_part2_with_lock()
    thd			Thread handle
    tables		List of tables to delete
    if_exists		If 1, don't give error if one table doesn't exists
    dont_log_query	Don't write query to log files. This will also not
149
                        generate warnings if the handler files doesn't exists
unknown's avatar
unknown committed
150 151 152 153 154 155 156 157 158 159

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

 RETURN
  0	ok
  1	error
*/

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

unknown's avatar
VIEW  
unknown committed
169 170
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
			      dont_log_query);
unknown's avatar
unknown committed
171 172 173 174 175 176 177 178 179 180

  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
181

182
/*
183 184 185 186 187 188 189 190 191
  Execute the drop of a normal or temporary table

  SYNOPSIS
    mysql_rm_table_part2()
    thd			Thread handler
    tables		Tables to drop
    if_exists		If set, don't give an error if table doesn't exists.
			In this case we give an warning of level 'NOTE'
    drop_temporary	Only drop temporary tables
unknown's avatar
VIEW  
unknown committed
192
    drop_view		Allow to delete VIEW .frm
193 194
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
195

196 197 198 199 200 201 202 203 204
  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.
205 206 207 208 209

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
210
*/
211 212

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
unknown's avatar
VIEW  
unknown committed
213 214
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
215 216
{
  TABLE_LIST *table;
unknown's avatar
unknown committed
217
  char	path[FN_REFLEN], *alias;
218 219
  String wrong_tables;
  int error;
unknown's avatar
unknown committed
220
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
221 222
  DBUG_ENTER("mysql_rm_table_part2");

223
  if (lock_table_names(thd, tables))
224
    DBUG_RETURN(1);
225

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

unknown's avatar
VIEW  
unknown committed
229
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
230
  {
231
    char *db=table->db;
232
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL);
233
    if (!close_temporary_table(thd, db, table->table_name))
unknown's avatar
unknown committed
234
    {
235
      tmp_table_deleted=1;
unknown's avatar
unknown committed
236
      continue;					// removed temporary table
unknown's avatar
unknown committed
237
    }
unknown's avatar
unknown committed
238 239

    error=0;
240
    if (!drop_temporary)
unknown's avatar
unknown committed
241
    {
unknown's avatar
unknown committed
242 243 244 245 246
      abort_locked_tables(thd, db, table->table_name);
      remove_table_from_cache(thd, db, table->table_name,
	                      RTFC_WAIT_OTHER_THREAD_FLAG |
			      RTFC_CHECK_KILLED_FLAG);
      drop_locked_tables(thd, db, table->table_name);
247
      if (thd->killed)
248 249
      {
        thd->no_warnings_for_error= 0;
250
	DBUG_RETURN(-1);
251
      }
252
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
253
      /* remove form file and isam files */
254
      build_table_path(path, sizeof(path), db, alias, reg_ext);
unknown's avatar
unknown committed
255
    }
unknown's avatar
unknown committed
256
    if (drop_temporary ||
unknown's avatar
unknown committed
257 258
       (access(path,F_OK) &&
         ha_create_table_from_engine(thd,db,alias)) ||
unknown's avatar
unknown committed
259
        (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE))
unknown's avatar
unknown committed
260
    {
261
      // Table was not found on disk and table can't be created from engine
262
      if (if_exists)
263 264
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
265
			    table->table_name);
266
      else
267
        error= 1;
unknown's avatar
unknown committed
268 269 270
    }
    else
    {
unknown's avatar
unknown committed
271
      char *end;
272
      db_type table_type= get_table_type(thd, path);
unknown's avatar
unknown committed
273
      *(end=fn_ext(path))=0;			// Remove extension for delete
274 275
      error= ha_delete_table(thd, table_type, path, table->table_name,
                             !dont_log_query);
276 277
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && 
	  (if_exists || table_type == DB_TYPE_UNKNOWN))
278
	error= 0;
unknown's avatar
unknown committed
279
      if (error == HA_ERR_ROW_IS_REFERENCED)
unknown's avatar
unknown committed
280 281
      {
	/* the table is referenced by a foreign key constraint */
282
	foreign_key_error=1;
unknown's avatar
unknown committed
283
      }
284
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
285
      {
286
        int new_error;
unknown's avatar
unknown committed
287 288
	/* Delete the table definition file */
	strmov(end,reg_ext);
289
	if (!(new_error=my_delete(path,MYF(MY_WME))))
290
        {
unknown's avatar
unknown committed
291
	  some_tables_deleted=1;
292 293
          new_error= Table_triggers_list::drop_all_triggers(thd, db,
                                                            table->table_name);
294
        }
295
        error|= new_error;
296
      }
unknown's avatar
unknown committed
297 298 299 300 301
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
302
      wrong_tables.append(String(table->table_name,system_charset_info));
unknown's avatar
unknown committed
303 304
    }
  }
unknown's avatar
unknown committed
305
  thd->tmp_table_used= tmp_table_deleted;
306 307 308 309
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
310
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
311
                      wrong_tables.c_ptr());
312
    else
unknown's avatar
unknown committed
313
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
314 315 316 317
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
318
  {
unknown's avatar
unknown committed
319
    query_cache_invalidate3(thd, tables, 0);
320
    if (!dont_log_query && mysql_bin_log.is_open())
321
    {
unknown's avatar
unknown committed
322 323
      if (!error)
        thd->clear_error();
unknown's avatar
unknown committed
324
      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
325
      mysql_bin_log.write(&qinfo);
326
    }
unknown's avatar
unknown committed
327
  }
unknown's avatar
unknown committed
328

unknown's avatar
unknown committed
329
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
330
  thd->no_warnings_for_error= 0;
331
  DBUG_RETURN(error);
unknown's avatar
unknown committed
332 333 334 335 336 337 338 339
}


int quick_rm_table(enum db_type base,const char *db,
		   const char *table_name)
{
  char path[FN_REFLEN];
  int error=0;
340
  build_table_path(path, sizeof(path), db, table_name, reg_ext);
unknown's avatar
unknown committed
341 342
  if (my_delete(path,MYF(0)))
    error=1; /* purecov: inspected */
343
  *fn_ext(path)= 0;                             // Remove reg_ext
344
  return ha_delete_table(current_thd, base, path, table_name, 0) || error;
unknown's avatar
unknown committed
345 346
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
/*
  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;
365
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
366 367
    {
      /* Sort NOT NULL keys before other keys */
368
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
369 370 371 372 373 374 375 376 377 378 379 380 381
    }
    if (a->name == primary_key_name)
      return -1;
    if (b->name == primary_key_name)
      return 1;
  }
  else if (b->flags & HA_NOSAME)
    return 1;					// Prefer b

  if ((a->flags ^ b->flags) & HA_FULLTEXT)
  {
    return (a->flags & HA_FULLTEXT) ? 1 : -1;
  }
unknown's avatar
unknown committed
382
  /*
383
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
384 385 386 387 388
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
389 390
}

391 392
/*
  Check TYPELIB (set or enum) for duplicates
393

394 395 396
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
397 398
    name	  name of the checked column
    typelib	  list of values for the column
399 400

  DESCRIPTION
401
    This function prints an warning for each value in list
402 403 404 405 406 407 408
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
409 410
                                  const char *name, TYPELIB *typelib,
                                  CHARSET_INFO *cs)
411
{
412
  TYPELIB tmp= *typelib;
413
  const char **cur_value= typelib->type_names;
414 415 416
  unsigned int *cur_length= typelib->type_lengths;
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
417
  {
418 419 420 421
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
422
    {
unknown's avatar
unknown committed
423
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
424 425 426 427 428 429
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
    }
  }
}
430

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465

/*
  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
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
/*
  Prepare a create_table instance for packing

  SYNOPSIS
    prepare_create_field()
    sql_field     field to prepare for packing
    blob_columns  count for BLOBs
    timestamps    count for timestamps
    table_flags   table flags

  DESCRIPTION
    This function prepares a create_field instance.
    Fields such as pack_flag are valid after this call.

  RETURN VALUES
   0	ok
   1	Error
*/

int prepare_create_field(create_field *sql_field, 
unknown's avatar
unknown committed
486 487
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
unknown's avatar
unknown committed
488 489 490
			 uint table_flags)
{
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509

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

  switch (sql_field->sql_type) {
  case FIELD_TYPE_BLOB:
  case FIELD_TYPE_MEDIUM_BLOB:
  case FIELD_TYPE_TINY_BLOB:
  case FIELD_TYPE_LONG_BLOB:
    sql_field->pack_flag=FIELDFLAG_BLOB |
      pack_length_to_packflag(sql_field->pack_length -
                              portable_sizeof_char_ptr);
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->length=8;			// Unireg field length
    sql_field->unireg_check=Field::BLOB_FIELD;
510
    (*blob_columns)++;
unknown's avatar
unknown committed
511 512
    break;
  case FIELD_TYPE_GEOMETRY:
unknown's avatar
unknown committed
513
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
514 515 516 517
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
unknown's avatar
unknown committed
518
      DBUG_RETURN(1);
unknown's avatar
unknown committed
519 520 521 522 523 524 525 526
    }
    sql_field->pack_flag=FIELDFLAG_GEOM |
      pack_length_to_packflag(sql_field->pack_length -
                              portable_sizeof_char_ptr);
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->length=8;			// Unireg field length
    sql_field->unireg_check=Field::BLOB_FIELD;
527
    (*blob_columns)++;
unknown's avatar
unknown committed
528 529 530 531 532
    break;
#else
    my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
                    sym_group_geom.name, sym_group_geom.needed_define);
    DBUG_RETURN(1);
unknown's avatar
unknown committed
533
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
534
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
535
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
536 537 538 539 540 541 542 543
    if (table_flags & HA_NO_VARCHAR)
    {
      /* convert VARCHAR to CHAR because handler is not yet up to date */
      sql_field->sql_type=    MYSQL_TYPE_VAR_STRING;
      sql_field->pack_length= calc_pack_length(sql_field->sql_type,
                                               (uint) sql_field->length);
      if ((sql_field->length / sql_field->charset->mbmaxlen) >
          MAX_FIELD_CHARLENGTH)
unknown's avatar
unknown committed
544
      {
unknown's avatar
unknown committed
545 546
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
unknown's avatar
unknown committed
547 548
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
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 576 577 578 579 580 581 582 583 584
    }
#endif
    /* fall through */
  case FIELD_TYPE_STRING:
    sql_field->pack_flag=0;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    break;
  case FIELD_TYPE_ENUM:
    sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
      FIELDFLAG_INTERVAL;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->unireg_check=Field::INTERVAL_FIELD;
    check_duplicates_in_interval("ENUM",sql_field->field_name,
                                 sql_field->interval,
                                 sql_field->charset);
    break;
  case FIELD_TYPE_SET:
    sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
      FIELDFLAG_BITFIELD;
    if (sql_field->charset->state & MY_CS_BINSORT)
      sql_field->pack_flag|=FIELDFLAG_BINARY;
    sql_field->unireg_check=Field::BIT_FIELD;
    check_duplicates_in_interval("SET",sql_field->field_name,
                                 sql_field->interval,
                                 sql_field->charset);
    break;
  case FIELD_TYPE_DATE:			// Rest of string types
  case FIELD_TYPE_NEWDATE:
  case FIELD_TYPE_TIME:
  case FIELD_TYPE_DATETIME:
  case FIELD_TYPE_NULL:
    sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
    break;
  case FIELD_TYPE_BIT:
unknown's avatar
unknown committed
585 586 587
    /* 
      We have sql_field->pack_flag already set here, see mysql_prepare_table().
    */
unknown's avatar
unknown committed
588 589 590 591 592 593 594 595 596 597 598 599 600
    break;
  case FIELD_TYPE_NEWDECIMAL:
    sql_field->pack_flag=(FIELDFLAG_NUMBER |
                          (sql_field->flags & UNSIGNED_FLAG ? 0 :
                           FIELDFLAG_DECIMAL) |
                          (sql_field->flags & ZEROFILL_FLAG ?
                           FIELDFLAG_ZEROFILL : 0) |
                          (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
    break;
  case FIELD_TYPE_TIMESTAMP:
    /* We should replace old TIMESTAMP fields with their newer analogs */
    if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
    {
601
      if (!*timestamps)
unknown's avatar
unknown committed
602
      {
unknown's avatar
unknown committed
603
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
604
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
605
      }
unknown's avatar
unknown committed
606 607 608 609
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
610
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
611

612
    (*timestamps)++;
unknown's avatar
unknown committed
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
    /* fall-through */
  default:
    sql_field->pack_flag=(FIELDFLAG_NUMBER |
                          (sql_field->flags & UNSIGNED_FLAG ? 0 :
                           FIELDFLAG_DECIMAL) |
                          (sql_field->flags & ZEROFILL_FLAG ?
                           FIELDFLAG_ZEROFILL : 0) |
                          f_settype((uint) sql_field->sql_type) |
                          (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
    break;
  }
  if (!(sql_field->flags & NOT_NULL_FLAG))
    sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
  if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
    sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
unknown's avatar
unknown committed
628 629 630
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
631
/*
632
  Preparation for table creation
unknown's avatar
unknown committed
633 634

  SYNOPSIS
635
    mysql_prepare_table()
unknown's avatar
unknown committed
636 637 638 639 640
    thd			Thread object
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create

641
  DESCRIPTION
642
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
643

644
  NOTES
645
    sets create_info->varchar if the table has a varchar
646

unknown's avatar
unknown committed
647 648 649 650
  RETURN VALUES
    0	ok
    -1	error
*/
unknown's avatar
unknown committed
651

652 653 654 655 656 657
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
                               List<create_field> *fields,
                               List<Key> *keys, bool tmp_table,
                               uint *db_options,
                               handler *file, KEY **key_info_buffer,
                               uint *key_count, int select_field_count)
unknown's avatar
unknown committed
658
{
659
  const char	*key_name;
unknown's avatar
unknown committed
660
  create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
661
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
662
  ulong		record_offset= 0;
663
  KEY		*key_info;
unknown's avatar
unknown committed
664
  KEY_PART_INFO *key_part_info;
665 666 667
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
668
  List_iterator<create_field> it(*fields),it2(*fields);
unknown's avatar
unknown committed
669
  uint total_uneven_bit_length= 0;
670
  DBUG_ENTER("mysql_prepare_table");
unknown's avatar
unknown committed
671

672
  select_field_pos= fields->elements - select_field_count;
unknown's avatar
unknown committed
673
  null_fields=blob_columns=0;
674
  create_info->varchar= 0;
unknown's avatar
unknown committed
675
  max_key_length= file->max_key_length();
676

677
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
678
  {
679 680
    CHARSET_INFO *save_cs;

681
    if (!sql_field->charset)
682 683 684
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
685 686 687
      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.
688
    */
689
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
690
      sql_field->charset= create_info->table_charset;
691

692
    save_cs= sql_field->charset;
693 694 695 696 697
    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];
698
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4), "_bin", 4);
699 700 701
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
702

703 704
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
705 706 707
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
708
      TYPELIB *interval= sql_field->interval;
709 710 711 712 713 714

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
715
      if (!interval)
716
      {
717 718 719 720
        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++)
721
        {
unknown's avatar
unknown committed
722
          uint lengthsp;
723 724 725 726 727
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
unknown's avatar
unknown committed
728 729
            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
                                                  conv.length());
730 731
            interval->type_lengths[i]= conv.length();
          }
732

733
          // Strip trailing spaces.
unknown's avatar
unknown committed
734 735
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
736 737
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
738
        }
739
        sql_field->interval_list.empty(); // Don't need interval_list anymore
740 741 742 743 744 745
      }

      /*
        Convert the default value from client character
        set into the column character set if necessary.
      */
unknown's avatar
unknown committed
746
      if (sql_field->def && cs != sql_field->def->collation.collation)
747
      {
unknown's avatar
unknown committed
748 749 750 751 752 753 754
        if (!(sql_field->def= 
              sql_field->def->safe_charset_converter(cs)))
        {
          /* Could not convert */
          my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
          DBUG_RETURN(-1);
        }
755 756 757 758
      }

      if (sql_field->sql_type == FIELD_TYPE_SET)
      {
759
        uint32 field_length;
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
        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);
          }
        }
775 776
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
777 778 779
      }
      else  /* FIELD_TYPE_ENUM */
      {
780
        uint32 field_length;
781 782 783 784 785 786 787 788 789 790
        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);
          }
        }
791 792
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
793 794 795 796
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

797 798
    if (sql_field->sql_type == FIELD_TYPE_BIT)
    { 
unknown's avatar
unknown committed
799
      sql_field->pack_flag= FIELDFLAG_NUMBER;
800 801 802 803 804 805
      if (file->table_flags() & HA_CAN_BIT_FIELD)
        total_uneven_bit_length+= sql_field->length & 7;
      else
        sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
    }

806
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
807 808
    if (prepare_blob_field(thd, sql_field))
      DBUG_RETURN(-1);
809

unknown's avatar
unknown committed
810 811
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
812

unknown's avatar
unknown committed
813 814
    if (check_column_name(sql_field->field_name))
    {
unknown's avatar
unknown committed
815
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
unknown's avatar
unknown committed
816 817
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
818

819 820
    /* 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
821
    {
unknown's avatar
unknown committed
822
      if (my_strcasecmp(system_charset_info,
823 824
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
825
      {
826 827 828 829
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
830 831
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
832
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
833 834 835 836
	  DBUG_RETURN(-1);
	}
	else
	{
837
	  /* Field redefined */
838
	  sql_field->sql_type=		dup_field->sql_type;
839 840 841
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
842 843
	  sql_field->length=		dup_field->chars_length;
          sql_field->pack_length=	dup_field->pack_length;
844
          sql_field->key_length=	dup_field->key_length;
845
	  sql_field->create_length_to_internal_length();
846 847 848 849 850 851
	  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;
852
	}
unknown's avatar
unknown committed
853 854
      }
    }
855 856 857 858
    /* Don't pack rows in old tables if the user has requested this */
    if ((sql_field->flags & BLOB_FLAG) ||
	sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
	create_info->row_type != ROW_TYPE_FIXED)
859
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
860 861
    it2.rewind();
  }
862 863 864

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
865
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
866 867 868 869

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

unknown's avatar
unknown committed
872 873
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
unknown's avatar
unknown committed
874
			     file->table_flags()))
unknown's avatar
SCRUM:  
unknown committed
875
      DBUG_RETURN(-1);
876
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
unknown's avatar
unknown committed
877
      create_info->varchar= 1;
878
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
879 880
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
881
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
882
  }
883 884
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
885 886
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
887 888
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
889 890
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
891
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
unknown's avatar
unknown committed
892 893 894
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
unknown's avatar
unknown committed
895
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
896
  {
unknown's avatar
unknown committed
897 898
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
unknown's avatar
unknown committed
899 900 901
    DBUG_RETURN(-1);
  }

unknown's avatar
unknown committed
902
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
903
  {
unknown's avatar
unknown committed
904 905
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
unknown's avatar
unknown committed
906 907 908 909
    DBUG_RETURN(-1);
  }

  /* Create keys */
910

911
  List_iterator<Key> key_iterator(*keys), key_iterator2(*keys);
912
  uint key_parts=0, fk_key_count=0;
913
  bool primary_key=0,unique_key=0;
914
  Key *key, *key2;
unknown's avatar
unknown committed
915
  uint tmp, key_number;
916 917
  /* special marker for keys to be ignored */
  static char ignore_key[1];
918

919
  /* Calculate number of key segements */
920
  *key_count= 0;
921

unknown's avatar
unknown committed
922 923
  while ((key=key_iterator++))
  {
924 925 926 927 928 929 930
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
	  fk_key->ref_columns.elements != fk_key->columns.elements)
      {
931 932 933
        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));
934 935 936 937
	DBUG_RETURN(-1);
      }
      continue;
    }
938
    (*key_count)++;
unknown's avatar
unknown committed
939
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
940 941 942 943 944
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
945
    if (key->name && strlen(key->name) > NAME_LEN)
unknown's avatar
unknown committed
946
    {
947
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
unknown's avatar
unknown committed
948 949
      DBUG_RETURN(-1);
    }
950
    key_iterator2.rewind ();
951
    if (key->type != Key::FOREIGN_KEY)
952
    {
953
      while ((key2 = key_iterator2++) != key)
954
      {
unknown's avatar
unknown committed
955
	/*
956 957 958
          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
959
        */
960 961 962
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
963
        {
964
          /* TODO: issue warning message */
965 966 967 968 969 970 971
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
972 973 974
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
975 976 977
          }
          break;
        }
978 979 980 981 982 983
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
984 985 986 987 988 989
    if (key->name && !tmp_table &&
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
990
  }
unknown's avatar
unknown committed
991
  tmp=file->max_keys();
992
  if (*key_count > tmp)
993 994 995 996
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
997

998
  (*key_info_buffer) = key_info= (KEY*) sql_calloc(sizeof(KEY)* *key_count);
999
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
1000
  if (!*key_info_buffer || ! key_part_info)
1001 1002
    DBUG_RETURN(-1);				// Out of memory

1003
  key_iterator.rewind();
unknown's avatar
unknown committed
1004
  key_number=0;
unknown's avatar
unknown committed
1005
  for (; (key=key_iterator++) ; key_number++)
1006 1007 1008 1009
  {
    uint key_length=0;
    key_part_spec *column;

1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
    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
1020
    switch(key->type){
unknown's avatar
unknown committed
1021
    case Key::MULTIPLE:
1022
	key_info->flags= 0;
1023
	break;
unknown's avatar
unknown committed
1024
    case Key::FULLTEXT:
1025
	key_info->flags= HA_FULLTEXT;
1026
	break;
unknown's avatar
unknown committed
1027
    case Key::SPATIAL:
unknown's avatar
SCRUM:  
unknown committed
1028
#ifdef HAVE_SPATIAL
1029
	key_info->flags= HA_SPATIAL;
1030
	break;
unknown's avatar
SCRUM:  
unknown committed
1031
#else
1032 1033
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
unknown's avatar
SCRUM:  
unknown committed
1034 1035
	DBUG_RETURN(-1);
#endif
1036 1037 1038 1039
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
1040 1041
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
1042
    }
1043 1044
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
1045

unknown's avatar
unknown committed
1046 1047
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
1048
    key_info->usable_key_parts= key_number;
unknown's avatar
unknown committed
1049
    key_info->algorithm=key->algorithm;
unknown's avatar
unknown committed
1050

1051 1052
    if (key->type == Key::FULLTEXT)
    {
unknown's avatar
unknown committed
1053
      if (!(file->table_flags() & HA_CAN_FULLTEXT))
1054
      {
unknown's avatar
unknown committed
1055 1056
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
1057
	DBUG_RETURN(-1);
1058 1059
      }
    }
unknown's avatar
unknown committed
1060 1061 1062
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
1063
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
1064 1065 1066
       in near future when new frm file is ready
       checking for proper key parts number:
    */
1067

1068
    /* TODO: Add proper checks if handler supports key_type and algorithm */
1069
    if (key_info->flags & HA_SPATIAL)
unknown's avatar
unknown committed
1070 1071 1072
    {
      if (key_info->key_parts != 1)
      {
1073
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
1074
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
1075
      }
unknown's avatar
unknown committed
1076
    }
unknown's avatar
unknown committed
1077
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
1078
    {
unknown's avatar
SCRUM:  
unknown committed
1079
#ifdef HAVE_RTREE_KEYS
unknown's avatar
unknown committed
1080 1081
      if ((key_info->key_parts & 1) == 1)
      {
1082
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
unknown's avatar
unknown committed
1083
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
1084
      }
1085
      /* TODO: To be deleted */
1086
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
1087
      DBUG_RETURN(-1);
unknown's avatar
SCRUM:  
unknown committed
1088
#else
1089 1090
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
unknown's avatar
SCRUM:  
unknown committed
1091 1092
      DBUG_RETURN(-1);
#endif
unknown's avatar
unknown committed
1093
    }
1094

1095
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
1096
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
1097 1098
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
1099
      uint length;
unknown's avatar
unknown committed
1100 1101
      key_part_spec *dup_column;

unknown's avatar
unknown committed
1102 1103 1104
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
1105
	     my_strcasecmp(system_charset_info,
1106 1107
			   column->field_name,
			   sql_field->field_name))
unknown's avatar
unknown committed
1108 1109 1110
	field++;
      if (!sql_field)
      {
1111
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
unknown's avatar
unknown committed
1112 1113
	DBUG_RETURN(-1);
      }
unknown's avatar
unknown committed
1114
      while ((dup_column= cols2++) != column)
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
      {
        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();
1126
      if (key->type == Key::FULLTEXT)
1127
      {
1128 1129
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
1130 1131
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
1132
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
1133 1134
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
1135
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
	    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));
1147
      }
1148
      else
1149
      {
1150 1151
	column->length*= sql_field->charset->mbmaxlen;

1152 1153
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
1154
	{
unknown's avatar
unknown committed
1155
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
1156
	  {
1157
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
1158 1159
	    DBUG_RETURN(-1);
	  }
1160 1161 1162
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
            column->length= 21;
1163 1164
	  if (!column->length)
	  {
1165
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
1166 1167 1168
	    DBUG_RETURN(-1);
	  }
	}
unknown's avatar
SCRUM:  
unknown committed
1169
#ifdef HAVE_SPATIAL
1170
	if (key->type == Key::SPATIAL)
1171
	{
1172
	  if (!column->length)
1173 1174
	  {
	    /*
1175 1176
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
1177
	    */
1178
	    column->length= 4*sizeof(double);
1179 1180
	  }
	}
unknown's avatar
SCRUM:  
unknown committed
1181
#endif
1182 1183 1184 1185 1186 1187 1188
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
unknown's avatar
unknown committed
1189
            null_fields--;
1190 1191 1192
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
unknown's avatar
unknown committed
1193
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
1194
	  {
1195
	    my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
1196 1197 1198 1199
	    DBUG_RETURN(-1);
	  }
	  if (key->type == Key::SPATIAL)
	  {
unknown's avatar
unknown committed
1200 1201
	    my_message(ER_SPATIAL_CANT_HAVE_NULL,
                       ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
1202 1203 1204 1205 1206 1207 1208 1209
	    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
1210
      }
1211

unknown's avatar
unknown committed
1212 1213 1214
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
1215 1216
      length= sql_field->key_length;

unknown's avatar
unknown committed
1217 1218 1219 1220
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
1221
	  if ((length=column->length) > max_key_length ||
1222
	      length > file->max_key_part_length())
1223
	  {
unknown's avatar
unknown committed
1224
	    length=min(max_key_length, file->max_key_part_length());
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
	    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);
	    }
	  }
unknown's avatar
unknown committed
1240
	}
1241
	else if (!f_is_geom(sql_field->pack_flag) &&
1242 1243 1244 1245 1246 1247
		  (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)))
	{
unknown's avatar
unknown committed
1248
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
1249 1250 1251 1252
	  DBUG_RETURN(-1);
	}
	else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
	  length=column->length;
unknown's avatar
unknown committed
1253 1254 1255
      }
      else if (length == 0)
      {
1256
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
unknown's avatar
unknown committed
1257 1258
	  DBUG_RETURN(-1);
      }
1259 1260
      if (length > file->max_key_part_length())
      {
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
	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);
	}
1276 1277
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
1278
      /* Use packed keys for long strings on the first column */
1279
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
1280
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
1281 1282
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
1283 1284
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
1285 1286 1287
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB) ||
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
unknown's avatar
unknown committed
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
	else
	  key_info->flags|= HA_PACK_KEY;
      }
      key_length+=length;
      key_part_info++;

      /* Create the key name based on the first column (if not given) */
      if (column_nr == 0)
      {
	if (key->type == Key::PRIMARY)
1298 1299 1300
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
1301 1302
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
1303 1304 1305 1306 1307
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
1308
	else if (!(key_name = key->name))
unknown's avatar
unknown committed
1309
	  key_name=make_unique_key_name(sql_field->field_name,
1310 1311
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
1312
	{
1313
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
unknown's avatar
unknown committed
1314 1315 1316 1317 1318
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
      }
    }
1319 1320
    if (!key_info->name || check_column_name(key_info->name))
    {
1321
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
1322 1323
      DBUG_RETURN(-1);
    }
1324 1325
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
1326
    key_info->key_length=(uint16) key_length;
1327
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
1328
    {
1329
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
unknown's avatar
unknown committed
1330 1331
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
1332
    key_info++;
unknown's avatar
unknown committed
1333
  }
1334
  if (!unique_key && !primary_key &&
unknown's avatar
unknown committed
1335
      (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
1336
  {
unknown's avatar
unknown committed
1337
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
1338 1339
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1340 1341
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
1342
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
unknown's avatar
unknown committed
1343 1344
    DBUG_RETURN(-1);
  }
1345
  /* Sort keys in optimized order */
1346
  qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
1347
	(qsort_cmp) sort_keys);
unknown's avatar
unknown committed
1348
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
1349

1350 1351 1352
  DBUG_RETURN(0);
}

1353

unknown's avatar
unknown committed
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
/*
  Extend long VARCHAR fields to blob & prepare field if it's a blob

  SYNOPSIS
    prepare_blob_field()
    sql_field		Field to check

  RETURN
    0	ok
    1	Error (sql_field can't be converted to blob)
        In this case the error is given
*/

static bool prepare_blob_field(THD *thd, create_field *sql_field)
{
  DBUG_ENTER("prepare_blob_field");

  if (sql_field->length > MAX_FIELD_VARCHARLENGTH &&
      !(sql_field->flags & BLOB_FLAG))
  {
    /* Convert long VARCHAR columns to TEXT or BLOB */
    char warn_buff[MYSQL_ERRMSG_SIZE];

1377 1378
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
1379 1380 1381 1382 1383 1384 1385 1386
    {
      my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
               MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
      DBUG_RETURN(1);
    }
    sql_field->sql_type= FIELD_TYPE_BLOB;
    sql_field->flags|= BLOB_FLAG;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
1387
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
            (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
                 warn_buff);
  }
    
  if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
  {
    if (sql_field->sql_type == FIELD_TYPE_BLOB)
    {
      /* The user has given a length to the blob column */
      sql_field->sql_type= get_blob_type_from_length(sql_field->length);
      sql_field->pack_length= calc_pack_length(sql_field->sql_type, 0);
    }
    sql_field->length= 0;
  }
  DBUG_RETURN(0);
}


1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
/*
  Preparation of create_field for SP function return values.
  Based on code used in the inner loop of mysql_prepare_table() above

  SYNOPSIS
    sp_prepare_create_field()
    thd			Thread object
    sql_field		Field to prepare

  DESCRIPTION
    Prepares the field structures for field creation.

*/

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

  if (sql_field->sql_type == FIELD_TYPE_BIT)
  {
    sql_field->pack_flag= FIELDFLAG_NUMBER |
                          FIELDFLAG_TREAT_BIT_AS_CHAR;
  }
  sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
1451 1452 1453 1454
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
1455 1456


1457 1458 1459 1460 1461
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1462 1463 1464 1465 1466 1467
    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
unknown's avatar
unknown committed
1468
    internal_tmp_table  Set to 1 if this is an internal temporary table
1469
			(From ALTER TABLE)
1470 1471

  DESCRIPTION
1472
    If one creates a temporary table, this is automatically opened
1473 1474 1475 1476 1477 1478 1479

    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
unknown's avatar
unknown committed
1480 1481
    FALSE OK
    TRUE  error
1482 1483
*/

unknown's avatar
unknown committed
1484 1485 1486
bool mysql_create_table(THD *thd,const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
                        List<create_field> &fields,
unknown's avatar
unknown committed
1487
                        List<Key> &keys,bool internal_tmp_table,
unknown's avatar
unknown committed
1488
                        uint select_field_count)
1489
{
1490 1491 1492 1493 1494
  char		path[FN_REFLEN];
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
1495
  bool		error= TRUE;
1496 1497 1498 1499 1500
  DBUG_ENTER("mysql_create_table");

  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
unknown's avatar
unknown committed
1501 1502
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
1503
    DBUG_RETURN(TRUE);
1504
  }
1505 1506
  if (check_engine(thd, table_name, &create_info->db_type))
    DBUG_RETURN(TRUE);
1507
  db_options= create_info->table_options;
1508 1509 1510 1511 1512
  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
1513 1514 1515 1516 1517 1518 1519 1520
#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
  */
1521 1522 1523
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      (file->table_flags() & HA_NO_TEMP_TABLES))
  {
1524
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
1525
    DBUG_RETURN(TRUE);
1526
  }
unknown's avatar
unknown committed
1527
#endif
1528

1529 1530 1531 1532 1533 1534 1535 1536 1537
  /*
    If the table character set was not given explicitely,
    let's fetch the database default character set and
    apply it to the table.
  */
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
    char  path[FN_REFLEN];
1538 1539
    /* Abuse build_table_path() to build the path to the db.opt file */
    build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, "");
1540 1541 1542 1543
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }

1544 1545 1546
  if (mysql_prepare_table(thd, create_info, &fields,
			  &keys, internal_tmp_table, &db_options, file,
			  &key_info_buffer, &key_count,
1547
			  select_field_count))
unknown's avatar
unknown committed
1548
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1549 1550 1551 1552

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1553 1554 1555
    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);
1556
    if (lower_case_table_names)
1557
      my_casedn_str(files_charset_info, path);
unknown's avatar
unknown committed
1558 1559 1560
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
1561 1562
    build_table_path(path, sizeof(path), db, alias, reg_ext);

unknown's avatar
unknown committed
1563 1564 1565 1566
  /* Check if table already exists */
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
      && find_temporary_table(thd,db,table_name))
  {
1567
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1568 1569
    {
      create_info->table_existed= 1;		// Mark that table existed
1570 1571 1572
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
unknown's avatar
unknown committed
1573
      DBUG_RETURN(FALSE);
1574
    }
unknown's avatar
unknown committed
1575
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
unknown's avatar
unknown committed
1576
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1577
  }
unknown's avatar
unknown committed
1578
  if (wait_if_global_read_lock(thd, 0, 1))
unknown's avatar
unknown committed
1579
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1580
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1581
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
1582 1583 1584 1585
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1586 1587
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1588
      goto end;
unknown's avatar
unknown committed
1589 1590 1591
    }
  }

unknown's avatar
unknown committed
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604
  /*
    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;
1605
    if (ha_table_exists_in_engine(thd, db, table_name))
unknown's avatar
unknown committed
1606
    {
1607
      DBUG_PRINT("info", ("Table with same name already existed in handler"));
unknown's avatar
unknown committed
1608 1609

      if (create_if_not_exists)
1610 1611
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1612
      goto end;
unknown's avatar
unknown committed
1613 1614 1615 1616
    }
  }

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

unknown's avatar
unknown committed
1619
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1620
    create_info->data_file_name= create_info->index_file_name= 0;
unknown's avatar
unknown committed
1621
  create_info->table_options=db_options;
1622

unknown's avatar
unknown committed
1623
  if (rea_create_table(thd, path, db, table_name,
1624
                       create_info, fields, key_count,
unknown's avatar
unknown committed
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
		       key_info_buffer))
    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
1635
    thd->tmp_table_used= 1;
unknown's avatar
unknown committed
1636
  }
unknown's avatar
unknown committed
1637
  if (!internal_tmp_table && mysql_bin_log.is_open())
1638
  {
unknown's avatar
unknown committed
1639
    thd->clear_error();
unknown's avatar
unknown committed
1640
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
1641
    mysql_bin_log.write(&qinfo);
1642
  }
unknown's avatar
unknown committed
1643
  error= FALSE;
unknown's avatar
unknown committed
1644

unknown's avatar
unknown committed
1645 1646
end:
  VOID(pthread_mutex_unlock(&LOCK_open));
1647
  start_waiting_global_read_lock(thd);
unknown's avatar
unknown committed
1648 1649
  thd->proc_info="After create";
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1650 1651 1652 1653 1654 1655 1656 1657

warn:
  error= FALSE;
  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                      ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                      alias);
  create_info->table_existed= 1;		// Mark that table existed
  goto end;
unknown's avatar
unknown committed
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667
}

/*
** 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++)
1668
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
1669 1670 1671 1672 1673 1674 1675 1676 1677 1678
      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;

1679 1680
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
1681
    return (char*) field_name;			// Use fieldname
1682 1683 1684 1685 1686 1687
  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
  */
1688
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
1689
  {
1690 1691
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
1692 1693 1694
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1695
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
1696 1697
}

1698

unknown's avatar
unknown committed
1699 1700 1701 1702 1703
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
unknown's avatar
VIEW  
unknown committed
1704
			       TABLE_LIST *create_table,
unknown's avatar
unknown committed
1705 1706 1707 1708 1709 1710
			       List<create_field> *extra_fields,
			       List<Key> *keys,
			       List<Item> *items,
			       MYSQL_LOCK **lock)
{
  TABLE tmp_table;		// Used during 'create_field()'
1711
  TABLE *table= 0;
1712
  uint select_field_count= items->elements;
unknown's avatar
unknown committed
1713
  /* Add selected items to field list */
unknown's avatar
unknown committed
1714
  List_iterator_fast<Item> it(*items);
unknown's avatar
unknown committed
1715 1716
  Item *item;
  Field *tmp_field;
1717
  bool not_used;
1718 1719 1720
  DBUG_ENTER("create_table_from_items");

  tmp_table.alias= 0;
1721
  tmp_table.timestamp_field= 0;
1722 1723 1724 1725 1726
  tmp_table.s= &tmp_table.share_not_to_be_used;
  tmp_table.s->db_create_options=0;
  tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
  tmp_table.s->db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
                                       create_info->db_type == DB_TYPE_HEAP);
unknown's avatar
unknown committed
1727 1728 1729 1730 1731
  tmp_table.null_row=tmp_table.maybe_null=0;

  while ((item=it++))
  {
    create_field *cr_field;
1732 1733 1734 1735 1736
    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(),
unknown's avatar
a fix.  
unknown committed
1737
                             (Item ***) 0, &tmp_field, 0, 0, 0, 0);
1738 1739
    if (!field ||
	!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
1740 1741
					   ((Item_field *)item)->field :
					   (Field*) 0))))
unknown's avatar
unknown committed
1742
      DBUG_RETURN(0);
unknown's avatar
unknown committed
1743 1744
    if (item->maybe_null)
      cr_field->flags &= ~NOT_NULL_FLAG;
unknown's avatar
unknown committed
1745 1746
    extra_fields->push_back(cr_field);
  }
unknown's avatar
unknown committed
1747
  /*
unknown's avatar
unknown committed
1748 1749
    create and lock table

1750
    We don't log the statement, it will be logged later.
unknown's avatar
unknown committed
1751

unknown's avatar
unknown committed
1752 1753 1754 1755
    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
1756 1757
    TABLE, which is a wrong order. So we keep binary logging disabled when we
    open_table().
unknown's avatar
unknown committed
1758
    TODO: create and open should be done atomic !
unknown's avatar
unknown committed
1759
  */
unknown's avatar
unknown committed
1760
  {
unknown's avatar
unknown committed
1761
    tmp_disable_binlog(thd);
1762
    if (!mysql_create_table(thd, create_table->db, create_table->table_name,
unknown's avatar
unknown committed
1763 1764 1765
                            create_info, *extra_fields, *keys, 0,
                            select_field_count))
    {
1766 1767
      if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
                               MYSQL_LOCK_IGNORE_FLUSH)))
unknown's avatar
unknown committed
1768
        quick_rm_table(create_info->db_type, create_table->db,
1769
                       table_case_name(create_info, create_table->table_name));
unknown's avatar
unknown committed
1770 1771 1772 1773
    }
    reenable_binlog(thd);
    if (!table)                                   // open failed
      DBUG_RETURN(0);
unknown's avatar
unknown committed
1774
  }
unknown's avatar
unknown committed
1775

1776 1777 1778 1779 1780 1781
  /*
    FIXME: What happens if trigger manages to be created while we are
           obtaining this lock ? May be it is sensible just to disable
           trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
           save us from that ?
  */
unknown's avatar
unknown committed
1782
  table->reginfo.lock_type=TL_WRITE;
1783 1784
  if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
                                    MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
unknown's avatar
unknown committed
1785
  {
1786
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1787
    hash_delete(&open_cache,(byte*) table);
1788
    VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
VIEW  
unknown committed
1789
    quick_rm_table(create_info->db_type, create_table->db,
1790
		   table_case_name(create_info, create_table->table_name));
unknown's avatar
unknown committed
1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801
    DBUG_RETURN(0);
  }
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  DBUG_RETURN(table);
}


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

1802
bool
unknown's avatar
unknown committed
1803 1804
mysql_rename_table(enum db_type base,
		   const char *old_db,
unknown's avatar
unknown committed
1805
		   const char *old_name,
unknown's avatar
unknown committed
1806
		   const char *new_db,
unknown's avatar
unknown committed
1807
		   const char *new_name)
unknown's avatar
unknown committed
1808
{
1809 1810 1811
  char from[FN_REFLEN], to[FN_REFLEN], lc_from[FN_REFLEN], lc_to[FN_REFLEN];
  char *from_base= from, *to_base= to;
  char tmp_name[NAME_LEN+1];
1812
  handler *file=(base == DB_TYPE_UNKNOWN ? 0 : get_new_handler((TABLE*) 0, base));
unknown's avatar
unknown committed
1813
  int error=0;
unknown's avatar
unknown committed
1814
  DBUG_ENTER("mysql_rename_table");
unknown's avatar
unknown committed
1815

1816 1817 1818 1819 1820 1821 1822 1823
  build_table_path(from, sizeof(from), old_db, old_name, "");
  build_table_path(to, sizeof(to), new_db, new_name, "");

  /*
    If lower_case_table_names == 2 (case-preserving but case-insensitive
    file system) and the storage is not HA_FILE_BASED, we need to provide
    a lowercase file name, but we leave the .frm in mixed case.
   */
1824 1825
  if (lower_case_table_names == 2 && file &&
      !(file->table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
1826
  {
1827 1828 1829 1830
    strmov(tmp_name, old_name);
    my_casedn_str(files_charset_info, tmp_name);
    build_table_path(lc_from, sizeof(lc_from), old_db, tmp_name, "");
    from_base= lc_from;
unknown's avatar
unknown committed
1831

1832 1833 1834 1835
    strmov(tmp_name, new_name);
    my_casedn_str(files_charset_info, tmp_name);
    build_table_path(lc_to, sizeof(lc_to), new_db, tmp_name, "");
    to_base= lc_to;
unknown's avatar
unknown committed
1836 1837
  }

1838
  if (!file || !(error=file->rename_table(from_base, to_base)))
1839 1840 1841
  {
    if (rename_file_ext(from,to,reg_ext))
    {
unknown's avatar
unknown committed
1842
      error=my_errno;
1843
      /* Restore old file name */
1844
      if (file)
1845
        file->rename_table(to_base, from_base);
1846 1847
    }
  }
unknown's avatar
unknown committed
1848
  delete file;
unknown's avatar
unknown committed
1849 1850 1851
  if (error)
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
1852 1853
}

unknown's avatar
unknown committed
1854

unknown's avatar
unknown committed
1855
/*
1856 1857 1858 1859 1860 1861
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
1862
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
1863
			HA_EXTRA_FORCE_REOPEN if table is not be used
1864 1865 1866 1867 1868 1869 1870
  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
1871 1872
*/

1873 1874
static void wait_while_table_is_used(THD *thd,TABLE *table,
				     enum ha_extra_function function)
unknown's avatar
unknown committed
1875
{
1876
  DBUG_PRINT("enter",("table: %s", table->s->table_name));
1877 1878
  DBUG_ENTER("wait_while_table_is_used");
  safe_mutex_assert_owner(&LOCK_open);
unknown's avatar
unknown committed
1879

1880
  VOID(table->file->extra(function));
1881 1882 1883 1884
  /* Mark all tables that are in use as 'old' */
  mysql_lock_abort(thd, table);			// end threads waiting on lock

  /* Wait until all there are no other threads that has this table open */
1885 1886
  remove_table_from_cache(thd, table->s->db,
                          table->s->table_name, RTFC_WAIT_OTHER_THREAD_FLAG);
1887 1888
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1889

1890 1891
/*
  Close a cached table
unknown's avatar
unknown committed
1892

1893
  SYNOPSIS
unknown's avatar
unknown committed
1894
    close_cached_table()
1895 1896 1897 1898 1899 1900
    thd			Thread handler
    table		Table to remove from cache

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

1902 1903 1904 1905
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
1906

1907
void close_cached_table(THD *thd, TABLE *table)
1908 1909
{
  DBUG_ENTER("close_cached_table");
1910

1911
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
1912 1913
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
1914
  {
1915 1916
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
1917
  }
1918 1919 1920 1921 1922
  /* 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);
unknown's avatar
unknown committed
1923
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1924 1925
}

unknown's avatar
unknown committed
1926
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
1927
			     const char* operator_name, const char* errmsg)
1928

unknown's avatar
unknown committed
1929
{
unknown's avatar
unknown committed
1930 1931
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
1932 1933 1934 1935
  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
1936
  thd->clear_error();
unknown's avatar
unknown committed
1937
  if (protocol->write())
unknown's avatar
unknown committed
1938 1939 1940 1941
    return -1;
  return 1;
}

1942

unknown's avatar
unknown committed
1943
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1944
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1945
{
unknown's avatar
unknown committed
1946
  DBUG_ENTER("prepare_for_restore");
1947

unknown's avatar
unknown committed
1948 1949 1950 1951 1952 1953
  if (table->table) // do not overwrite existing tables on restore
  {
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				  "table exists, will not overwrite on restore"
				  ));
  }
unknown's avatar
unknown committed
1954
  else
unknown's avatar
unknown committed
1955
  {
1956
    char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
1957
    char src_path[FN_REFLEN], dst_path[FN_REFLEN];
1958 1959
    char* table_name= table->table_name;
    char* db= table->db;
1960

1961 1962
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
					reg_ext))
unknown's avatar
unknown committed
1963
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
1964

1965
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1966
		mysql_real_data_home, db, table_name);
1967

1968
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
1969
      DBUG_RETURN(-1);
1970

1971
    if (my_copy(src_path,
1972 1973
		fn_format(dst_path, dst_path,"", reg_ext, 4),
		MYF(MY_WME)))
unknown's avatar
unknown committed
1974
    {
1975
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1976
      unlock_table_name(thd, table);
1977
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1978 1979 1980
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
1981
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
1982
    {
1983
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1984
      unlock_table_name(thd, table);
1985
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1986 1987
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
1988
    }
unknown's avatar
unknown committed
1989
  }
unknown's avatar
unknown committed
1990

1991 1992 1993 1994
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
1995 1996
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table))
1997
  {
1998
    unlock_table_name(thd, table);
1999
    pthread_mutex_unlock(&LOCK_open);
2000 2001
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
2002
  }
2003
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2004
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2005
}
2006

2007

unknown's avatar
unknown committed
2008
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
2009
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
2010
{
unknown's avatar
unknown committed
2011 2012
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
2013 2014 2015 2016
  DBUG_ENTER("prepare_for_repair");

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

  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
2019
  {
unknown's avatar
unknown committed
2020
    char name[FN_REFLEN];
2021
    build_table_path(name, sizeof(name), table_list->db,
2022
                     table_list->table_name, "");
2023
    if (openfrm(thd, name, "", 0, 0, 0, &tmp_table))
unknown's avatar
unknown committed
2024 2025
      DBUG_RETURN(0);				// Can't open frm file
    table= &tmp_table;
2026
  }
unknown's avatar
unknown committed
2027

unknown's avatar
unknown committed
2028 2029 2030 2031 2032 2033 2034 2035 2036
  /*
    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
2037

unknown's avatar
unknown committed
2038 2039 2040
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext= table->file->bas_ext();
  MY_STAT stat_info;
unknown's avatar
unknown committed
2041

unknown's avatar
unknown committed
2042 2043 2044 2045 2046 2047
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
  if (!ext[0] || !ext[1])
    goto end;					// No data file
unknown's avatar
unknown committed
2048

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

2053 2054
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
2055

2056 2057 2058 2059 2060 2061 2062
  /* 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
2063
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
2064
  {
unknown's avatar
unknown committed
2065 2066
    error= -1;
    goto end;
unknown's avatar
unknown committed
2067
  }
unknown's avatar
unknown committed
2068
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
2069
  {
2070
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2071
    unlock_table_name(thd, table_list);
2072
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
unknown's avatar
unknown committed
2094 2095
  }

2096 2097 2098 2099
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
2100 2101
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table_list))
unknown's avatar
unknown committed
2102
  {
unknown's avatar
unknown committed
2103
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
2104
    pthread_mutex_unlock(&LOCK_open);
2105 2106 2107
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
2108
  }
2109
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2110 2111 2112 2113 2114

end:
  if (table == &tmp_table)
    closefrm(table);				// Free allocated memory
  DBUG_RETURN(error);
unknown's avatar
unknown committed
2115
}
2116

2117

2118

unknown's avatar
unknown committed
2119 2120
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
2121 2122 2123
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
unknown's avatar
unknown committed
2124
*/
unknown's avatar
unknown committed
2125 2126 2127 2128 2129
static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
                              HA_CHECK_OPT* check_opt,
                              const char *operator_name,
                              thr_lock_type lock_type,
                              bool open_for_modify,
2130
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
2131 2132 2133
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
2134 2135 2136
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
2137
{
2138 2139
  TABLE_LIST *table, *save_next_global, *save_next_local;
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
2140
  List<Item> field_list;
unknown's avatar
unknown committed
2141 2142
  Item *item;
  Protocol *protocol= thd->protocol;
2143
  LEX *lex= thd->lex;
2144
  int result_code;
2145
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
2146 2147 2148 2149 2150 2151 2152 2153 2154

  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;
2155 2156
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
2157
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2158

2159
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL);
unknown's avatar
VIEW  
unknown committed
2160
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
2161 2162
  {
    char table_name[NAME_LEN*2+2];
2163
    char* db = table->db;
2164
    bool fatal_error=0;
unknown's avatar
unknown committed
2165

unknown's avatar
merged  
unknown committed
2166
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
2167
    thd->open_options|= extra_open_options;
2168 2169
    table->lock_type= lock_type;
    /* open only one table from local list of command */
2170
    save_next_global= table->next_global;
2171
    table->next_global= 0;
2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183
    save_next_local= table->next_local;
    table->next_local= 0;
    select->table_list.first= (byte*)table;
    /*
      Time zone tables and SP tables can be add to lex->query_tables list,
      so it have to be prepared.
      TODO: Investigate if we can put extra tables into argument instead of
      using lex->query_tables
    */
    lex->query_tables= table;
    lex->query_tables_last= &table->next_global;
    lex->query_tables_own_last= 0;;
2184
    thd->no_warnings_for_error= no_warnings_for_error;
2185
    open_and_lock_tables(thd, table);
2186
    thd->no_warnings_for_error= 0;
2187 2188
    table->next_global= save_next_global;
    table->next_local= save_next_local;
2189
    /* if view are unsupported */
2190
    if (table->view && view_operator_func == NULL)
2191 2192 2193 2194
    {
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
    }
unknown's avatar
unknown committed
2195
    thd->open_options&= ~extra_open_options;
unknown's avatar
unknown committed
2196

unknown's avatar
unknown committed
2197
    if (prepare_func)
2198
    {
unknown's avatar
unknown committed
2199
      switch ((*prepare_func)(thd, table, check_opt)) {
2200 2201 2202 2203 2204 2205 2206
      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
2207
      }
2208
    }
2209

2210
    /*
unknown's avatar
unknown committed
2211 2212 2213 2214 2215 2216
      CHECK TABLE command is only command where VIEW allowed here and this
      command use only temporary teble method for VIEWs resolving => there
      can't be VIEW tree substitition of join view => if opening table
      succeed then table->table will have real TABLE pointer as value (in
      case of join view substitution table->table can be 0, but here it is
      impossible)
2217
    */
unknown's avatar
unknown committed
2218 2219
    if (!table->table)
    {
unknown's avatar
unknown committed
2220
      char buf[ERRMSGSIZE+ERRMSGSIZE+2];
unknown's avatar
unknown committed
2221
      const char *err_msg;
unknown's avatar
unknown committed
2222
      protocol->prepare_for_resend();
2223 2224 2225
      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
2226 2227
      if (!(err_msg=thd->net.last_error))
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
2228 2229 2230 2231
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
      {
unknown's avatar
unknown committed
2232
        strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
2233 2234
        err_msg= (const char *)buf;
      }
2235
      protocol->store(err_msg, system_charset_info);
2236
      lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
2237
      thd->clear_error();
2238 2239 2240 2241 2242
      /*
        View opening can be interrupted in the middle of process so some
        tables can be left opening
      */
      close_thread_tables(thd);
unknown's avatar
unknown committed
2243
      if (protocol->write())
unknown's avatar
unknown committed
2244 2245 2246
	goto err;
      continue;
    }
2247 2248 2249 2250 2251 2252 2253

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

unknown's avatar
unknown committed
2254
    table->table->pos_in_table_list= table;
2255
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
2256
    {
unknown's avatar
unknown committed
2257
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
2258
      uint length;
unknown's avatar
unknown committed
2259
      protocol->prepare_for_resend();
2260 2261 2262
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error", 5, system_charset_info);
2263 2264 2265
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
2266
      close_thread_tables(thd);
unknown's avatar
unknown committed
2267
      table->table=0;				// For query cache
unknown's avatar
unknown committed
2268
      if (protocol->write())
unknown's avatar
unknown committed
2269 2270 2271 2272
	goto err;
      continue;
    }

2273
    /* Close all instances of the table to allow repair to rename files */
2274
    if (lock_type == TL_WRITE && table->table->s->version)
2275 2276
    {
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2277 2278
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
2279
      mysql_lock_abort(thd,table->table);
2280
      remove_table_from_cache(thd, table->table->s->db,
unknown's avatar
unknown committed
2281
                              table->table->s->table_name,
unknown's avatar
unknown committed
2282 2283
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
unknown's avatar
unknown committed
2284
      thd->exit_cond(old_message);
2285 2286
      if (thd->killed)
	goto err;
2287 2288 2289
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
2290 2291
    }

2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302
    if (table->table->s->crashed && operator_func == &handler::check)
    {
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("warning", 7, system_charset_info);
      protocol->store("Table is marked as crashed", 26, system_charset_info);
      if (protocol->write())
        goto err;
    }

2303 2304 2305 2306
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

2307
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
2308
    thd->clear_error();  // these errors shouldn't get client
unknown's avatar
unknown committed
2309
    protocol->prepare_for_resend();
2310 2311
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
2312

2313 2314 2315
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
2316 2317
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
2318
      {
2319 2320
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
unknown's avatar
unknown committed
2321
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
2322
	protocol->store("note", 4, system_charset_info);
2323
	protocol->store(buf, length, system_charset_info);
2324
      }
unknown's avatar
unknown committed
2325 2326
      break;

2327
    case HA_ADMIN_OK:
2328 2329
      protocol->store("status", 6, system_charset_info);
      protocol->store("OK",2, system_charset_info);
unknown's avatar
unknown committed
2330 2331
      break;

2332
    case HA_ADMIN_FAILED:
2333 2334
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation failed",16, system_charset_info);
unknown's avatar
unknown committed
2335 2336
      break;

unknown's avatar
unknown committed
2337 2338 2339
    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
2340
      open_for_modify= FALSE;
unknown's avatar
unknown committed
2341 2342
      break;

2343
    case HA_ADMIN_ALREADY_DONE:
2344 2345
      protocol->store("status", 6, system_charset_info);
      protocol->store("Table is already up to date", 27, system_charset_info);
2346 2347
      break;

2348
    case HA_ADMIN_CORRUPT:
2349
      protocol->store("error", 5, system_charset_info);
2350
      protocol->store("Corrupt", 7, system_charset_info);
2351
      fatal_error=1;
unknown's avatar
unknown committed
2352 2353
      break;

unknown's avatar
unknown committed
2354
    case HA_ADMIN_INVALID:
2355 2356
      protocol->store("error", 5, system_charset_info);
      protocol->store("Invalid argument",16, system_charset_info);
unknown's avatar
unknown committed
2357 2358
      break;

2359 2360 2361 2362 2363 2364 2365 2366
    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);
unknown's avatar
VIEW  
unknown committed
2367 2368 2369
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
2370
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
2371
      result_code= mysql_recreate_table(thd, table, 0);
2372
      reenable_binlog(thd);
unknown's avatar
unknown committed
2373
      close_thread_tables(thd);
2374 2375 2376 2377 2378 2379
      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
      }
2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401
      if (result_code) // either mysql_recreate_table or analyze failed
      {
        const char *err_msg;
        if ((err_msg= thd->net.last_error))
        {
          if (!thd->vio_ok())
          {
            sql_print_error(err_msg);
          }
          else
          {
            /* Hijack the row already in-progress. */
            protocol->store("error", 5, system_charset_info);
            protocol->store(err_msg, system_charset_info);
            (void)protocol->write();
            /* Start off another row for HA_ADMIN_FAILED */
            protocol->prepare_for_resend();
            protocol->store(table_name, system_charset_info);
            protocol->store(operator_name, system_charset_info);
          }
        }
      }
2402
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
2403 2404
      table->next_local= save_next_local;
      table->next_global= save_next_global;
2405 2406
      goto send_result_message;
    }
2407 2408 2409
    case HA_ADMIN_WRONG_CHECKSUM:
    {
      protocol->store("note", 4, system_charset_info);
unknown's avatar
unknown committed
2410 2411
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
2412 2413
      break;
    }
2414

2415
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
        protocol->store("error", 5, system_charset_info);
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
2426
    }
2427
    if (fatal_error)
2428
      table->table->s->version=0;               // Force close of table
unknown's avatar
unknown committed
2429
    else if (open_for_modify)
2430
    {
2431
      pthread_mutex_lock(&LOCK_open);
2432
      remove_table_from_cache(thd, table->table->s->db,
2433
			      table->table->s->table_name, RTFC_NO_FLAG);
2434
      pthread_mutex_unlock(&LOCK_open);
2435 2436 2437
      /* May be something modified consequently we have to invalidate cache */
      query_cache_invalidate3(thd, table->table, 0);
    }
unknown's avatar
unknown committed
2438
    close_thread_tables(thd);
unknown's avatar
unknown committed
2439
    table->table=0;				// For query cache
unknown's avatar
unknown committed
2440
    if (protocol->write())
unknown's avatar
unknown committed
2441 2442 2443
      goto err;
  }

2444
  send_eof(thd);
unknown's avatar
unknown committed
2445
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
2446
 err:
2447
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
2448 2449
  if (table)
    table->table=0;
unknown's avatar
unknown committed
2450
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2451 2452
}

unknown's avatar
unknown committed
2453

unknown's avatar
unknown committed
2454
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
2455 2456 2457
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2458
				"backup", TL_READ, 0, 0, 0, 0,
2459
				&handler::backup, 0));
unknown's avatar
unknown committed
2460
}
unknown's avatar
unknown committed
2461

2462

unknown's avatar
unknown committed
2463
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
2464 2465 2466
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2467
				"restore", TL_WRITE, 1, 1, 0,
2468
				&prepare_for_restore,
2469
				&handler::restore, 0));
unknown's avatar
unknown committed
2470
}
unknown's avatar
unknown committed
2471

2472

unknown's avatar
unknown committed
2473
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2474 2475 2476
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2477 2478 2479
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
2480
				&prepare_for_repair,
2481
				&handler::repair, 0));
2482 2483
}

2484

unknown's avatar
unknown committed
2485
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2486 2487 2488
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2489
				"optimize", TL_WRITE, 1,0,0,0,
2490
				&handler::optimize, 0));
2491 2492 2493
}


unknown's avatar
unknown committed
2494 2495 2496 2497 2498
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
2499 2500
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2501 2502

  RETURN VALUES
unknown's avatar
unknown committed
2503 2504
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
2505 2506
*/

unknown's avatar
unknown committed
2507
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
2508
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
2509
{
2510
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
2511
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
2512
  DBUG_ENTER("mysql_assign_to_keycache");
2513 2514 2515 2516 2517 2518 2519

  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);
unknown's avatar
unknown committed
2520
    DBUG_RETURN(TRUE);
2521 2522 2523 2524
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
2525
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
2526
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
2527 2528
}

unknown's avatar
unknown committed
2529 2530 2531 2532 2533 2534

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

  SYNOPSIS
    reassign_keycache_tables()
2535 2536 2537
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
2538

2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551
  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
unknown's avatar
unknown committed
2552 2553 2554
    0	  ok
*/

2555 2556
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
2557 2558 2559
{
  DBUG_ENTER("reassign_keycache_tables");

2560 2561
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
unknown's avatar
unknown committed
2562
  src_cache->param_buff_size= 0;		// Free key cache
2563 2564
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2565
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2566 2567 2568
}


unknown's avatar
unknown committed
2569 2570 2571 2572 2573
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2574 2575
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2576 2577

  RETURN VALUES
unknown's avatar
unknown committed
2578 2579
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
2580 2581
*/

unknown's avatar
unknown committed
2582
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
2583 2584 2585
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2586
				"preload_keys", TL_READ, 0, 0, 0, 0,
2587
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
2588 2589 2590
}


unknown's avatar
unknown committed
2591 2592 2593 2594 2595
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2596 2597
    thd		Thread object
    table	Table list (one table only)
unknown's avatar
unknown committed
2598 2599 2600 2601
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
unknown's avatar
unknown committed
2602 2603
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
2604 2605
*/

unknown's avatar
unknown committed
2606 2607 2608
bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
                             HA_CREATE_INFO *create_info,
                             Table_ident *table_ident)
unknown's avatar
unknown committed
2609 2610 2611 2612
{
  TABLE **tmp_table;
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char *db= table->db;
2613
  char *table_name= table->table_name;
unknown's avatar
unknown committed
2614
  char *src_db;
unknown's avatar
unknown committed
2615
  char *src_table= table_ident->table.str;
unknown's avatar
unknown committed
2616 2617
  int  err;
  bool res= TRUE;
2618
  TABLE_LIST src_tables_list;
unknown's avatar
unknown committed
2619
  DBUG_ENTER("mysql_create_like_table");
unknown's avatar
unknown committed
2620
  src_db= table_ident->db.str ? table_ident->db.str : thd->db;
unknown's avatar
unknown committed
2621 2622 2623 2624 2625 2626

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
unknown's avatar
unknown committed
2627
       check_table_name(src_table,table_ident->table.length)))
unknown's avatar
unknown committed
2628
  {
unknown's avatar
unknown committed
2629
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
unknown's avatar
unknown committed
2630
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2631
  }
unknown's avatar
unknown committed
2632 2633 2634 2635 2636
  if (!src_db || check_db_name(src_db))
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
    DBUG_RETURN(-1);
  }
2637

unknown's avatar
VIEW  
unknown committed
2638
  bzero((gptr)&src_tables_list, sizeof(src_tables_list));
unknown's avatar
unknown committed
2639
  src_tables_list.db= src_db;
unknown's avatar
Merge  
unknown committed
2640
  src_tables_list.table_name= src_table;
2641

2642 2643
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
unknown's avatar
unknown committed
2644 2645

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
2646
    strxmov(src_path, (*tmp_table)->s->path, reg_ext, NullS);
unknown's avatar
unknown committed
2647 2648
  else
  {
2649 2650 2651 2652
    strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table,
	    reg_ext, NullS);
    /* Resolve symlinks (for windows) */
    fn_format(src_path, src_path, "", "", MYF(MY_UNPACK_FILENAME));
2653 2654
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
unknown's avatar
unknown committed
2655 2656 2657
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2658
      goto err;
unknown's avatar
unknown committed
2659 2660 2661
    }
  }

2662 2663 2664 2665 2666
  /* 
     create like should be not allowed for Views, Triggers, ... 
  */
  if (mysql_frm_type(src_path) != FRMTYPE_TABLE)
  {
2667
    my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE");
2668 2669 2670
    goto err;
  }

unknown's avatar
unknown committed
2671 2672 2673
  /*
    Validate the destination table

2674
    skip the destination table name checking as this is already
unknown's avatar
unknown committed
2675 2676 2677 2678 2679 2680
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2681 2682 2683
    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);
2684 2685
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
unknown's avatar
unknown committed
2686 2687 2688 2689
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2690 2691 2692
    strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
	    reg_ext, NullS);
    fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME));
unknown's avatar
unknown committed
2693 2694 2695 2696
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2697
  /*
unknown's avatar
unknown committed
2698
    Create a new table by copying from source table
2699
  */
2700 2701 2702 2703 2704 2705
  if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
  {
    if (my_errno == ENOENT)
      my_error(ER_BAD_DB_ERROR,MYF(0),db);
    else
      my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
2706
    goto err;
2707
  }
unknown's avatar
unknown committed
2708 2709

  /*
2710 2711
    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
2712 2713
    and temporary tables).
  */
2714
  *fn_ext(dst_path)= 0;
unknown's avatar
unknown committed
2715
  err= ha_create_table(dst_path, create_info, 1);
2716

unknown's avatar
unknown committed
2717 2718 2719 2720
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2721 2722
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2723
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
2724 2725 2726 2727
    }
  }
  else if (err)
  {
2728 2729 2730
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
2731
  }
2732 2733 2734

  // Must be written before unlock
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
2735
  {
2736
    thd->clear_error();
unknown's avatar
unknown committed
2737
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
2738
    mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
2739
  }
unknown's avatar
unknown committed
2740
  res= FALSE;
2741
  goto err;
2742

unknown's avatar
unknown committed
2743 2744 2745 2746
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2747 2748
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
2749
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2750
		 ER_TABLE_EXISTS_ERROR,warn_buff);
unknown's avatar
unknown committed
2751
    res= FALSE;
unknown's avatar
unknown committed
2752
  }
2753 2754 2755 2756 2757 2758 2759 2760
  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
2761 2762 2763
}


unknown's avatar
unknown committed
2764
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2765
{
unknown's avatar
unknown committed
2766 2767 2768 2769 2770 2771
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2772 2773
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2774
				"analyze", lock_type, 1, 0, 0, 0,
2775
				&handler::analyze, 0));
2776 2777 2778
}


unknown's avatar
unknown committed
2779
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
2780
{
unknown's avatar
unknown committed
2781 2782 2783 2784 2785 2786
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2787 2788
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
2789
				"check", lock_type,
2790
				0, HA_OPEN_FOR_REPAIR, 0, 0,
2791
				&handler::check, &view_checksum));
2792 2793
}

unknown's avatar
unknown committed
2794

unknown's avatar
unknown committed
2795
/* table_list should contain just one table */
unknown's avatar
unknown committed
2796 2797 2798 2799
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
2800 2801 2802 2803 2804 2805
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
2806 2807 2808 2809
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
2810 2811 2812

  thd->proc_info="discard_or_import_tablespace";

unknown's avatar
unknown committed
2813
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
2814

unknown's avatar
unknown committed
2815 2816 2817 2818 2819
 /*
   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
2820 2821 2822 2823 2824
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2825

unknown's avatar
unknown committed
2826 2827 2828 2829 2830 2831 2832
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

unknown's avatar
unknown committed
2833 2834 2835 2836
  /*
    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
2837 2838 2839 2840 2841 2842 2843 2844 2845 2846
  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;
  if (mysql_bin_log.is_open())
  {
unknown's avatar
unknown committed
2847
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
unknown's avatar
unknown committed
2848 2849 2850 2851
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
unknown's avatar
unknown committed
2852
  thd->tablespace_op=FALSE;
2853
  
unknown's avatar
unknown committed
2854 2855
  if (error == 0)
  {
unknown's avatar
unknown committed
2856
    send_ok(thd);
unknown's avatar
unknown committed
2857
    DBUG_RETURN(0);
unknown's avatar
unknown committed
2858
  }
unknown's avatar
unknown committed
2859

2860 2861
  table->file->print_error(error, MYF(0));
    
unknown's avatar
unknown committed
2862
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
2863
}
unknown's avatar
unknown committed
2864

2865 2866

#ifdef NOT_USED
2867 2868 2869 2870 2871 2872 2873
/*
  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.
*/

2874
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2875 2876 2877 2878 2879
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2880 2881 2882 2883 2884 2885 2886 2887
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2888 2889 2890
  DBUG_ENTER("mysql_create_index");

  /*
2891 2892 2893
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2894

2895 2896
    Open the table to have access to the correct table handler.
  */
2897 2898 2899 2900
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2901 2902 2903 2904 2905
    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.
  */
2906 2907 2908 2909 2910 2911 2912 2913 2914 2915
  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;
2916 2917 2918
  if (mysql_prepare_table(thd, &create_info, &fields,
			  &keys, /*tmp_table*/ 0, &db_options, table->file,
			  &key_info_buffer, key_count,
2919
			  /*select_field_count*/ 0))
2920 2921 2922
    DBUG_RETURN(-1);

  /*
2923 2924 2925
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2926 2927 2928 2929
  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)&
2930
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2931 2932
      break ;
  }
2933
  if ((idx < key_count)|| !key_count)
2934 2935 2936 2937 2938 2939 2940
  {
    /* 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();
2941
    if (real_alter_table(thd, table_list->db, table_list->table_name,
2942 2943 2944 2945
			 &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);*/
2946 2947 2948 2949 2950
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
2951 2952
        build_table_path(path, sizeof(path), table_list->db,
                         (lower_case_table_names == 2) ?
2953
                         table_list->alias : table_list->table_name,
2954
                         reg_ext) == 0 ||
2955 2956 2957
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
      /* don't need to free((gptr) key_info_buffer);*/
2958 2959
      DBUG_RETURN(-1);
  }
2960
  /* don't need to free((gptr) key_info_buffer);*/
2961 2962 2963 2964
  DBUG_RETURN(0);
}


2965 2966
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
2967 2968
{
  List<create_field> fields;
2969
  List<Key>	     keys;
2970 2971
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2972 2973 2974 2975 2976 2977 2978 2979 2980
  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];
2981 2982 2983
  DBUG_ENTER("mysql_drop_index");

  /*
2984 2985 2986
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2987

2988 2989
    Open the table to have access to the correct table handler.
  */
2990 2991 2992 2993
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2994 2995 2996
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
2997 2998 2999 3000
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
3001 3002
    Get the number of each key and check if it can be created online.
  */
3003 3004 3005 3006 3007 3008 3009 3010 3011
  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))
3012
	break;
3013 3014 3015 3016 3017 3018 3019 3020
    }
    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);
    }
    /*
3021 3022 3023
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
3024 3025
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
3026
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
3027 3028 3029 3030 3031 3032 3033 3034 3035 3036
      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))
  {
3037
    if (real_alter_table(thd, table_list->db, table_list->table_name,
3038 3039 3040
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
3041 3042 3043 3044 3045 3046 3047
      /*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)||
3048 3049 3050
	mysql_prepare_table(thd, &create_info, &fields,
			    &keys, /*tmp_table*/ 0, &db_options, table->file,
			    &key_info_buffer, key_count,
3051
			    /*select_field_count*/ 0)||
3052 3053
        build_table_path(path, sizeof(path), table_list->db,
                         (lower_case_table_names == 2) ?
3054
                         table_list->alias : table_list->table_name,
3055
                         reg_ext) == 0 ||
3056 3057
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
3058 3059 3060 3061 3062 3063 3064
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
3065
#endif /* NOT_USED */
3066 3067


3068 3069 3070
/*
  Alter table
*/
3071

unknown's avatar
unknown committed
3072 3073 3074 3075 3076
bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
                       HA_CREATE_INFO *create_info,
                       TABLE_LIST *table_list,
                       List<create_field> &fields, List<Key> &keys,
                       uint order_num, ORDER *order,
3077
                       enum enum_duplicates handle_duplicates, bool ignore,
unknown's avatar
unknown committed
3078
                       ALTER_INFO *alter_info, bool do_send_ok)
unknown's avatar
unknown committed
3079
{
3080
  TABLE *table,*new_table=0;
unknown's avatar
unknown committed
3081
  int error;
unknown's avatar
unknown committed
3082 3083
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
unknown's avatar
unknown committed
3084
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
unknown's avatar
unknown committed
3085 3086
  ha_rows copied,deleted;
  ulonglong next_insert_id;
3087
  uint db_create_options, used_fields;
unknown's avatar
unknown committed
3088
  enum db_type old_db_type,new_db_type;
3089
  bool need_copy_table;
unknown's avatar
unknown committed
3090 3091 3092
  DBUG_ENTER("mysql_alter_table");

  thd->proc_info="init";
3093
  table_name=table_list->table_name;
unknown's avatar
unknown committed
3094 3095
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;

unknown's avatar
unknown committed
3096
  db=table_list->db;
unknown's avatar
unknown committed
3097
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
3098
    new_db= db;
3099
  used_fields=create_info->used_fields;
unknown's avatar
unknown committed
3100

3101
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL);
unknown's avatar
unknown committed
3102
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
3103
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
unknown's avatar
unknown committed
3104
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
3105
						   alter_info->tablespace_op));
unknown's avatar
unknown committed
3106
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
unknown's avatar
unknown committed
3107
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3108 3109 3110 3111 3112

  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
unknown's avatar
unknown committed
3113
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
3114
    if (lower_case_table_names)
unknown's avatar
unknown committed
3115 3116 3117
    {
      if (lower_case_table_names != 2)
      {
3118
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
3119 3120
	new_alias= new_name;			// Create lower case table name
      }
3121
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
3122
    }
3123
    if (new_db == db &&
unknown's avatar
unknown committed
3124
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
3125 3126
    {
      /*
3127 3128
	Source and destination table names are equal: make later check
	easier.
3129
      */
unknown's avatar
unknown committed
3130
      new_alias= new_name= table_name;
3131
    }
unknown's avatar
unknown committed
3132 3133
    else
    {
3134
      if (table->s->tmp_table)
unknown's avatar
unknown committed
3135 3136 3137
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
3138
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
3139
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3140 3141 3142 3143
	}
      }
      else
      {
3144 3145 3146
	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),
unknown's avatar
unknown committed
3147 3148 3149
		    F_OK))
	{
	  /* Table will be closed in do_command() */
3150
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
3151
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3152 3153 3154 3155 3156
	}
      }
    }
  }
  else
3157 3158 3159 3160
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
3161

3162
  old_db_type= table->s->db_type;
unknown's avatar
unknown committed
3163
  if (create_info->db_type == DB_TYPE_DEFAULT)
3164
    create_info->db_type= old_db_type;
3165 3166 3167
  if (check_engine(thd, new_name, &create_info->db_type))
    DBUG_RETURN(TRUE);
  new_db_type= create_info->db_type;
3168
  if (create_info->row_type == ROW_TYPE_NOT_USED)
3169
    create_info->row_type= table->s->row_type;
unknown's avatar
unknown committed
3170

unknown's avatar
unknown committed
3171 3172 3173
  DBUG_PRINT("info", ("old type: %d  new type: %d", old_db_type, new_db_type));
  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
3174 3175 3176 3177 3178 3179
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
    DBUG_RETURN(TRUE);
  }
  
unknown's avatar
unknown committed
3180
  thd->proc_info="setup";
3181
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
3182
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
3183 3184
  {
    error=0;
3185
    if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3186
    {
3187 3188 3189 3190 3191 3192
      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))
      {
3193
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
3194
	error= -1;
3195 3196 3197
      }
      else
      {
3198 3199 3200
	*fn_ext(new_name)=0;
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
3201 3202 3203
	  error= -1;
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
3204
    }
unknown's avatar
unknown committed
3205

3206
    if (!error)
3207
    {
3208
      switch (alter_info->keys_onoff) {
3209
      case LEAVE_AS_IS:
3210
        break;
3211
      case ENABLE:
3212 3213 3214 3215 3216 3217
        VOID(pthread_mutex_lock(&LOCK_open));
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
        VOID(pthread_mutex_unlock(&LOCK_open));
        error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
        /* COND_refresh will be signaled in close_thread_tables() */
        break;
3218
      case DISABLE:
3219 3220 3221 3222 3223 3224
        VOID(pthread_mutex_lock(&LOCK_open));
        wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
        VOID(pthread_mutex_unlock(&LOCK_open));
        error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
        /* COND_refresh will be signaled in close_thread_tables() */
        break;
3225
      }
3226
    }
3227

3228
    if (error == HA_ERR_WRONG_COMMAND)
unknown's avatar
unknown committed
3229 3230
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
3231
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
3232
			  table->alias);
unknown's avatar
unknown committed
3233 3234
      error=0;
    }
unknown's avatar
unknown committed
3235 3236
    if (!error)
    {
3237 3238
      if (mysql_bin_log.is_open())
      {
3239
	thd->clear_error();
unknown's avatar
unknown committed
3240
	Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
3241 3242
	mysql_bin_log.write(&qinfo);
      }
3243 3244
      if (do_send_ok)
        send_ok(thd);
unknown's avatar
unknown committed
3245
    }
3246
    else if (error > 0)
3247 3248
    {
      table->file->print_error(error, MYF(0));
3249
      error= -1;
3250
    }
3251 3252
    table_list->table=0;				// For query cache
    query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3253 3254 3255 3256
    DBUG_RETURN(error);
  }

  /* Full alter table */
3257

3258
  /* Let new create options override the old ones */
3259
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
3260
    create_info->min_rows= table->s->min_rows;
3261
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
3262
    create_info->max_rows= table->s->max_rows;
3263
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
3264
    create_info->avg_row_length= table->s->avg_row_length;
3265
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
3266
    create_info->default_table_charset= table->s->table_charset;
3267

3268
  restore_record(table, s->default_values);     // Empty record for DEFAULT
3269
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
3270
  List_iterator<create_field> def_it(fields);
3271
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
unknown's avatar
unknown committed
3272 3273
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
3274 3275
  create_field *def;

unknown's avatar
unknown committed
3276
  /*
3277
    First collect all fields from table which isn't in drop_list
unknown's avatar
unknown committed
3278 3279 3280 3281 3282
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
3283
    /* Check if field should be dropped */
unknown's avatar
unknown committed
3284 3285 3286 3287 3288
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
3289
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
3290 3291 3292
      {
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
3293
	    !(used_fields & HA_CREATE_USED_AUTO))
3294 3295 3296 3297
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
unknown's avatar
unknown committed
3298
	break;
3299
      }
unknown's avatar
unknown committed
3300 3301 3302 3303 3304 3305 3306 3307 3308 3309
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
3310
      if (def->change &&
3311
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
unknown's avatar
unknown committed
3312 3313 3314 3315 3316
	break;
    }
    if (def)
    {						// Field is changed
      def->field=field;
3317 3318 3319 3320 3321
      if (!def->after)
      {
	create_list.push_back(def);
	def_it.remove();
      }
unknown's avatar
unknown committed
3322 3323 3324
    }
    else
    {						// Use old field value
3325
      create_list.push_back(def=new create_field(field,field));
unknown's avatar
unknown committed
3326 3327 3328 3329
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
      {
3330
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
unknown's avatar
unknown committed
3331 3332 3333 3334
	  break;
      }
      if (alter)
      {
3335 3336
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
3337
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
3338
	  DBUG_RETURN(TRUE);
3339
	}
unknown's avatar
unknown committed
3340 3341 3342 3343 3344 3345 3346 3347 3348
	def->def=alter->def;			// Use new default
	alter_it.remove();
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
  while ((def=def_it++))			// Add new columns
  {
3349
    if (def->change && ! def->field)
unknown's avatar
unknown committed
3350
    {
3351
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
unknown's avatar
unknown committed
3352
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363
    }
    if (!def->after)
      create_list.push_back(def);
    else if (def->after == first_keyword)
      create_list.push_front(def);
    else
    {
      create_field *find;
      find_it.rewind();
      while ((find=find_it++))			// Add new columns
      {
3364
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
unknown's avatar
unknown committed
3365 3366 3367 3368
	  break;
      }
      if (!find)
      {
3369
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
unknown's avatar
unknown committed
3370
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3371 3372 3373 3374
      }
      find_it.after(def);			// Put element after this
    }
  }
3375
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3376
  {
3377 3378
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
             alter_info->alter_list.head()->name, table_name);
unknown's avatar
unknown committed
3379
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3380 3381 3382
  }
  if (!create_list.elements)
  {
unknown's avatar
unknown committed
3383 3384
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
3385
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3386 3387 3388
  }

  /*
3389 3390
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
3391 3392 3393 3394 3395 3396 3397
  */

  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;
3398
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
3399
  {
3400
    char *key_name= key_info->name;
unknown's avatar
unknown committed
3401 3402 3403 3404 3405
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
3406
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427
	break;
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }

    KEY_PART_INFO *key_part= key_info->key_part;
    key_parts.empty();
    for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
    {
      if (!key_part->field)
	continue;				// Wrong field (from UNIREG)
      const char *key_part_name=key_part->field->field_name;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
	if (cfield->change)
	{
unknown's avatar
unknown committed
3428 3429
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
3430 3431
	    break;
	}
3432
	else if (!my_strcasecmp(system_charset_info,
3433
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
3434
	  break;
unknown's avatar
unknown committed
3435 3436 3437 3438 3439
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
3440 3441 3442 3443 3444 3445 3446
      {
        /*
          If the field can't have only a part used in a key according to its
          new type, or should not be used partially according to its
          previous type, or the field length is less than the key part
          length, unset the key part length.

3447 3448 3449
          We also unset the key part length if it is the same as the
          old field's length, so the whole new field will be used.

3450 3451 3452 3453 3454
          BLOBs may have cfield->length == 0, which is why we test it before
          checking whether cfield->length < key_part_length (in chars).
         */
        if (!Field::type_can_have_key_part(cfield->field->type()) ||
            !Field::type_can_have_key_part(cfield->sql_type) ||
unknown's avatar
unknown committed
3455 3456
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
3457 3458 3459
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
3460
      }
3461
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
3462 3463 3464 3465
      key_parts.push_back(new key_part_spec(cfield->field_name,
					    key_part_length));
    }
    if (key_parts.elements)
unknown's avatar
unknown committed
3466
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
3467
				 (key_info->flags & HA_NOSAME ?
3468
				 (!my_strcasecmp(system_charset_info,
3469 3470
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
3471 3472 3473
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
3474
				 key_info->algorithm,
3475
                                 test(key_info->flags & HA_GENERATED_KEY),
3476
				 key_parts));
unknown's avatar
unknown committed
3477 3478 3479 3480
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
3481 3482 3483
    {
      if (key->type != Key::FOREIGN_KEY)
	key_list.push_back(key);
3484 3485 3486 3487
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
unknown's avatar
unknown committed
3488
	DBUG_RETURN(TRUE);
3489
      }
3490
    }
unknown's avatar
unknown committed
3491 3492
  }

3493
  if (alter_info->drop_list.elements)
unknown's avatar
unknown committed
3494
  {
3495 3496
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
unknown's avatar
unknown committed
3497 3498
    goto err;
  }
3499
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3500
  {
3501 3502
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
unknown's avatar
unknown committed
3503 3504 3505
    goto err;
  }

3506
  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
3507 3508
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3509 3510
  /* Safety fix for innodb */
  if (lower_case_table_names)
3511
    my_casedn_str(files_charset_info, tmp_name);
3512 3513 3514 3515
  if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
    goto err;
  }
unknown's avatar
unknown committed
3516 3517
  create_info->db_type=new_db_type;
  if (!create_info->comment)
3518
    create_info->comment= table->s->comment;
3519 3520 3521 3522 3523

  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
3524 3525 3526 3527 3528 3529 3530 3531 3532 3533
    db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
  if (create_info->table_options &
      (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
    db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
  if (create_info->table_options &
      (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
    db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
			  HA_OPTION_NO_DELAY_KEY_WRITE);
  create_info->table_options|= db_create_options;

3534
  if (table->s->tmp_table)
unknown's avatar
unknown committed
3535 3536
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

3537 3538
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
3539
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
3540 3541
    so that ALTER TABLE won't break when somebody will add new flag
  */
unknown's avatar
unknown committed
3542 3543 3544 3545 3546
  need_copy_table= (alter_info->flags &
                    ~(ALTER_CHANGE_COLUMN_DEFAULT|ALTER_OPTIONS) ||
                    (create_info->used_fields &
                     ~(HA_CREATE_USED_COMMENT|HA_CREATE_USED_PASSWORD)) ||
                    table->s->tmp_table);
3547 3548
  create_info->frm_only= !need_copy_table;

unknown's avatar
unknown committed
3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592
  /*
    Handling of symlinked tables:
    If no rename:
      Create new data file and index file on the same disk as the
      old data and index files.
      Copy data.
      Rename new data file over old data file and new index file over
      old index file.
      Symlinks are not changed.

   If rename:
      Create new data file and index file on the same disk as the
      old data and index files.  Create also symlinks to point at
      the new tables.
      Copy data.
      At end, rename temporary tables and symlinks to temporary table
      to final table name.
      Remove old table and old symlinks

    If rename is made to another database:
      Create new tables in new database.
      Copy data.
      Remove old table and symlinks.
  */

  if (!strcmp(db, new_db))		// Ignore symlink if db changed
  {
    if (create_info->index_file_name)
    {
      /* Fix index_file_name to have 'tmp_name' as basename */
      strmov(index_file, tmp_name);
      create_info->index_file_name=fn_same(index_file,
					   create_info->index_file_name,
					   1);
    }
    if (create_info->data_file_name)
    {
      /* Fix data_file_name to have 'tmp_name' as basename */
      strmov(data_file, tmp_name);
      create_info->data_file_name=fn_same(data_file,
					  create_info->data_file_name,
					  1);
    }
  }
3593 3594
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
3595 3596

  /* We don't log the statement, it will be logged later. */
unknown's avatar
unknown committed
3597
  {
unknown's avatar
unknown committed
3598 3599 3600 3601 3602
    tmp_disable_binlog(thd);
    error= mysql_create_table(thd, new_db, tmp_name,
                              create_info,create_list,key_list,1,0);
    reenable_binlog(thd);
    if (error)
unknown's avatar
unknown committed
3603 3604
      DBUG_RETURN(error);
  }
3605
  if (need_copy_table)
unknown's avatar
unknown committed
3606
  {
3607
    if (table->s->tmp_table)
3608 3609 3610 3611
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
3612
      tbl.table_name= tbl.alias= tmp_name;
3613 3614
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628
    }
    else
    {
      char path[FN_REFLEN];
      my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
                  new_db, tmp_name);
      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
3629 3630
  }

3631
  /* We don't want update TIMESTAMP fields during ALTER TABLE. */
3632
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
3633 3634
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3635
  next_insert_id=thd->next_insert_id;		// Remember for logging
unknown's avatar
unknown committed
3636
  copied=deleted=0;
3637
  if (new_table && !new_table->s->is_view)
3638
  {
unknown's avatar
unknown committed
3639
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
3640
    new_table->next_number_field=new_table->found_next_number_field;
unknown's avatar
unknown committed
3641
    error=copy_data_between_tables(table,new_table,create_list,
3642
				   handle_duplicates, ignore,
3643
				   order_num, order, &copied, &deleted);
3644
  }
unknown's avatar
unknown committed
3645
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3646
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
3647

3648
  if (table->s->tmp_table)
unknown's avatar
unknown committed
3649 3650 3651 3652
  {
    /* We changed a temporary table */
    if (error)
    {
unknown's avatar
unknown committed
3653 3654 3655
      /*
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3656
      */
unknown's avatar
unknown committed
3657 3658 3659
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3660 3661 3662 3663 3664 3665
    /* 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
3666
    /* Remove link to old table and rename the new one */
3667
    close_temporary_table(thd, table->s->db, table_name);
3668 3669
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
unknown's avatar
unknown committed
3670 3671 3672 3673 3674
    {						// Fatal error
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3675 3676
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
3677
      thd->clear_error();
unknown's avatar
unknown committed
3678
      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
3679 3680
      mysql_bin_log.write(&qinfo);
    }
unknown's avatar
unknown committed
3681 3682 3683
    goto end_temporary;
  }

3684 3685 3686 3687 3688
  if (new_table)
  {
    intern_close_table(new_table);              /* close temporary table */
    my_free((gptr) new_table,MYF(0));
  }
unknown's avatar
unknown committed
3689 3690 3691 3692 3693 3694 3695
  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;
  }
3696

unknown's avatar
unknown committed
3697
  /*
3698 3699
    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
3700
    from the cache, free all locks, close the old table and remove it.
unknown's avatar
unknown committed
3701 3702 3703
  */

  thd->proc_info="rename result table";
3704 3705
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3706 3707
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3708
  if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3709 3710 3711 3712
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
3713
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
3714 3715 3716 3717 3718 3719
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

unknown's avatar
unknown committed
3720 3721 3722 3723 3724
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3725
      Win32 and InnoDB can't drop a table that is in use, so we must
3726
      close the original table at before doing the rename
unknown's avatar
unknown committed
3727 3728
    */
    table_name=thd->strdup(table_name);		// must be saved
3729
    close_cached_table(thd, table);
unknown's avatar
unknown committed
3730
    table=0;					// Marker that table is closed
unknown's avatar
unknown committed
3731
  }
unknown's avatar
unknown committed
3732 3733 3734
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  else
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
unknown's avatar
unknown committed
3735 3736
#endif

unknown's avatar
unknown committed
3737

unknown's avatar
unknown committed
3738
  error=0;
3739 3740
  if (!need_copy_table)
    new_db_type=old_db_type=DB_TYPE_UNKNOWN; // this type cannot happen in regular ALTER
unknown's avatar
unknown committed
3741 3742 3743 3744 3745 3746
  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,
unknown's avatar
unknown committed
3747
			      new_alias))
unknown's avatar
unknown committed
3748 3749
  {						// Try to get everything back
    error=1;
unknown's avatar
unknown committed
3750
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
unknown's avatar
unknown committed
3751
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
unknown's avatar
unknown committed
3752
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
unknown's avatar
unknown committed
3753 3754 3755
  }
  if (error)
  {
unknown's avatar
unknown committed
3756 3757 3758 3759
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
3760 3761
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3762 3763 3764 3765 3766
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
  if (thd->lock || new_name != table_name)	// True if WIN32
  {
unknown's avatar
unknown committed
3767 3768 3769 3770
    /*
      Not table locking or alter table with rename
      free locks and remove old table
    */
3771 3772
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3773 3774 3775 3776
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
unknown's avatar
unknown committed
3777 3778 3779 3780 3781
    /*
      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
3782 3783 3784
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
unknown's avatar
unknown committed
3785
      /* Mark in-use copies old */
unknown's avatar
unknown committed
3786
      remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
unknown's avatar
unknown committed
3787 3788
      /* end threads waiting on lock */
      mysql_lock_abort(thd,table);
unknown's avatar
unknown committed
3789 3790 3791 3792 3793
    }
    VOID(quick_rm_table(old_db_type,db,old_name));
    if (close_data_tables(thd,db,table_name) ||
	reopen_tables(thd,1,0))
    {						// This shouldn't happen
3794 3795
      if (table)
	close_cached_table(thd,table);		// Remove lock for table
unknown's avatar
unknown committed
3796 3797 3798 3799
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
unknown's avatar
unknown committed
3800
  /* The ALTER TABLE is always in its own transaction */
3801 3802 3803 3804
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
unknown's avatar
unknown committed
3805 3806
  {
    VOID(pthread_mutex_unlock(&LOCK_open));
3807
    VOID(pthread_cond_broadcast(&COND_refresh));
unknown's avatar
unknown committed
3808 3809 3810
    goto err;
  }
  thd->proc_info="end";
3811
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
3812
  {
unknown's avatar
unknown committed
3813
    thd->clear_error();
unknown's avatar
unknown committed
3814
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
unknown's avatar
unknown committed
3815 3816 3817 3818
    mysql_bin_log.write(&qinfo);
  }
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_mutex_unlock(&LOCK_open));
3819 3820 3821
#ifdef HAVE_BERKELEY_DB
  if (old_db_type == DB_TYPE_BERKELEY_DB)
  {
unknown's avatar
unknown committed
3822 3823 3824 3825 3826
    /*
      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.
    */
3827
    char path[FN_REFLEN];
3828
    build_table_path(path, sizeof(path), new_db, table_name, "");
3829 3830
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
unknown's avatar
unknown committed
3831
    {
3832 3833
      intern_close_table(table);
      my_free((char*) table, MYF(0));
unknown's avatar
unknown committed
3834
    }
3835
    else
unknown's avatar
unknown committed
3836 3837
      sql_print_warning("Could not open BDB table %s.%s after rename\n",
                        new_db,table_name);
3838
    (void) berkeley_flush_logs();
3839 3840
  }
#endif
unknown's avatar
unknown committed
3841
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
3842
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3843 3844

end_temporary:
3845 3846 3847
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3848 3849
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
unknown's avatar
unknown committed
3850
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
3851
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
3852 3853

 err:
unknown's avatar
unknown committed
3854
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3855 3856 3857 3858
}


static int
unknown's avatar
unknown committed
3859
copy_data_between_tables(TABLE *from,TABLE *to,
3860
			 List<create_field> &create,
unknown's avatar
unknown committed
3861
			 enum enum_duplicates handle_duplicates,
3862
                         bool ignore,
3863
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
3864
			 ha_rows *copied,
3865
			 ha_rows *deleted)
unknown's avatar
unknown committed
3866 3867 3868 3869 3870
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
unknown's avatar
unknown committed
3871 3872 3873 3874 3875 3876
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3877
  ha_rows examined_rows;
3878
  bool auto_increment_field_copied= 0;
3879
  ulong save_sql_mode;
unknown's avatar
unknown committed
3880 3881
  DBUG_ENTER("copy_data_between_tables");

3882 3883 3884 3885 3886 3887
  /*
    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
  */
3888
  error= ha_enable_transaction(thd, FALSE);
3889 3890
  if (error)
    DBUG_RETURN(-1);
3891
  
3892
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
3893 3894
    DBUG_RETURN(-1);				/* purecov: inspected */

3895 3896
  if (to->file->external_lock(thd, F_WRLCK))
    DBUG_RETURN(-1);
3897 3898 3899 3900 3901 3902 3903

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

3904
  from->file->info(HA_STATUS_VARIABLE);
3905
  to->file->start_bulk_insert(from->file->records);
unknown's avatar
unknown committed
3906

3907 3908
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
3909 3910 3911 3912 3913 3914 3915
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
3916 3917
    {
      if (*ptr == to->next_number_field)
3918
      {
3919
        auto_increment_field_copied= TRUE;
3920 3921 3922 3923 3924 3925 3926 3927 3928
        /*
          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
3929
      (copy_end++)->set(*ptr,def->field,0);
3930 3931
    }

unknown's avatar
unknown committed
3932 3933
  }

3934 3935
  found_count=delete_count=0;

unknown's avatar
unknown committed
3936 3937
  if (order)
  {
unknown's avatar
unknown committed
3938
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
3939
					      MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
3940
    bzero((char*) &tables,sizeof(tables));
3941 3942 3943
    tables.table= from;
    tables.alias= tables.table_name= (char*) from->s->table_name;
    tables.db=    (char*) from->s->db;
unknown's avatar
unknown committed
3944 3945
    error=1;

unknown's avatar
unknown committed
3946
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
unknown's avatar
unknown committed
3947
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
3948
		    &tables, fields, all_fields, order) ||
3949 3950 3951
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
					     (SQL_SELECT *) 0, HA_POS_ERROR,
unknown's avatar
unknown committed
3952 3953
					     &examined_rows)) ==
	HA_POS_ERROR)
unknown's avatar
unknown committed
3954 3955 3956
      goto err;
  };

3957 3958 3959 3960 3961
  /*
    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
  */
unknown's avatar
unknown committed
3962
  from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
3963
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
3964
  if (ignore ||
3965
      handle_duplicates == DUP_REPLACE)
unknown's avatar
unknown committed
3966
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
3967
  thd->row_count= 0;
3968
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
3969 3970 3971 3972
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
3973
      thd->send_kill_message();
unknown's avatar
unknown committed
3974 3975 3976
      error= 1;
      break;
    }
3977
    thd->row_count++;
3978 3979
    if (to->next_number_field)
    {
3980
      if (auto_increment_field_copied)
3981
        to->auto_increment_field_not_null= TRUE;
3982 3983 3984
      else
        to->next_number_field->reset();
    }
3985
    
unknown's avatar
unknown committed
3986
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
3987
    {
unknown's avatar
unknown committed
3988
      copy_ptr->do_copy(copy_ptr);
3989
    }
unknown's avatar
unknown committed
3990 3991
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
3992
      if ((!ignore &&
3993
	   handle_duplicates != DUP_REPLACE) ||
unknown's avatar
unknown committed
3994 3995 3996 3997 3998 3999
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
      {
	to->file->print_error(error,MYF(0));
	break;
      }
4000
      to->file->restore_auto_increment();
unknown's avatar
unknown committed
4001 4002 4003
      delete_count++;
    }
    else
4004
      found_count++;
unknown's avatar
unknown committed
4005 4006
  }
  end_read_record(&info);
unknown's avatar
unknown committed
4007
  free_io_cache(from);
4008
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
4009

4010
  if (to->file->end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
4011
  {
unknown's avatar
unknown committed
4012
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
4013 4014
    error=1;
  }
unknown's avatar
unknown committed
4015
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
4016

4017
  ha_enable_transaction(thd,TRUE);
4018

4019 4020 4021 4022 4023 4024 4025 4026
  /*
    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;
4027

unknown's avatar
unknown committed
4028
 err:
4029
  thd->variables.sql_mode= save_sql_mode;
4030
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
4031
  free_io_cache(from);
unknown's avatar
unknown committed
4032 4033
  *copied= found_count;
  *deleted=delete_count;
4034 4035
  if (to->file->external_lock(thd,F_UNLCK))
    error=1;
unknown's avatar
unknown committed
4036 4037
  DBUG_RETURN(error > 0 ? -1 : 0);
}
4038

unknown's avatar
unknown committed
4039

4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051
/*
  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().
*/
unknown's avatar
unknown committed
4052 4053
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                          bool do_send_ok)
4054 4055 4056 4057 4058 4059 4060 4061 4062 4063
{
  DBUG_ENTER("mysql_recreate_table");
  LEX *lex= thd->lex;
  HA_CREATE_INFO create_info;
  lex->create_list.empty();
  lex->key_list.empty();
  lex->col_list.empty();
  lex->alter_info.reset();
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
unknown's avatar
unknown committed
4064
  create_info.row_type=ROW_TYPE_NOT_USED;
4065
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
4066 4067
  /* Force alter table to recreate table */
  lex->alter_info.flags= ALTER_CHANGE_COLUMN;
4068 4069 4070
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
4071
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
4072 4073 4074
}


unknown's avatar
unknown committed
4075
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
4076 4077 4078 4079 4080
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
4081
  DBUG_ENTER("mysql_checksum_table");
4082 4083 4084 4085 4086

  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;
4087 4088
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4089
    DBUG_RETURN(TRUE);
4090

unknown's avatar
VIEW  
unknown committed
4091
  for (table= tables; table; table= table->next_local)
4092 4093
  {
    char table_name[NAME_LEN*2+2];
4094
    TABLE *t;
4095

4096
    strxmov(table_name, table->db ,".", table->table_name, NullS);
unknown's avatar
unknown committed
4097

4098
    t= table->table= open_ltable(thd, table, TL_READ);
unknown's avatar
unknown committed
4099
    thd->clear_error();			// these errors shouldn't get client
4100 4101 4102 4103

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

4104
    if (!t)
4105
    {
unknown's avatar
unknown committed
4106
      /* Table didn't exist */
4107
      protocol->store_null();
unknown's avatar
unknown committed
4108
      thd->clear_error();
4109 4110 4111
    }
    else
    {
4112
      t->pos_in_table_list= table;
4113

4114
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
4115 4116
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
4117
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
4118
	       (check_opt->flags & T_QUICK))
4119
	protocol->store_null();
4120 4121
      else
      {
4122 4123
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
4124
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
4125 4126 4127 4128 4129 4130

	/* 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
4131
	if (t->file->ha_rnd_init(1))
4132 4133 4134
	  protocol->store_null();
	else
	{
4135
	  for (;;)
4136 4137
	  {
	    ha_checksum row_crc= 0;
4138 4139 4140 4141 4142 4143 4144
            int error= t->file->rnd_next(t->record[0]);
            if (unlikely(error))
            {
              if (error == HA_ERR_RECORD_DELETED)
                continue;
              break;
            }
unknown's avatar
unknown committed
4145 4146 4147 4148
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
4149 4150 4151
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
4152 4153
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
4154

4155
	    for (uint i= 0; i < t->s->fields; i++ )
4156 4157 4158 4159 4160 4161 4162 4163 4164 4165
	    {
	      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,
unknown's avatar
unknown committed
4166
				     f->pack_length());
4167
	    }
4168

4169 4170 4171
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
4172
          t->file->ha_rnd_end();
4173
	}
4174
      }
unknown's avatar
unknown committed
4175
      thd->clear_error();
4176 4177 4178 4179 4180 4181 4182 4183
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
unknown's avatar
unknown committed
4184
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
4185

4186 4187 4188 4189
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
4190
  DBUG_RETURN(TRUE);
4191
}
4192 4193 4194 4195 4196

static bool check_engine(THD *thd, const char *table_name,
                         enum db_type *new_engine)
{
  enum db_type req_engine= *new_engine;
unknown's avatar
unknown committed
4197
  bool no_substitution=
4198
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
4199
  if ((*new_engine=
4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212
       ha_checktype(thd, req_engine, no_substitution, 1)) == DB_TYPE_UNKNOWN)
    return TRUE;

  if (req_engine != *new_engine)
  {
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_WARN_USING_OTHER_HANDLER,
                       ER(ER_WARN_USING_OTHER_HANDLER),
                       ha_get_storage_engine(*new_engine),
                       table_name);
  }
  return FALSE;
}