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

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,
38
                                    List<create_field> &create, bool ignore,
39
				    uint order_num, ORDER *order,
unknown's avatar
unknown committed
40
				    ha_rows *copied,ha_rows *deleted);
unknown's avatar
unknown committed
41
static bool prepare_blob_field(THD *thd, create_field *sql_field);
42 43
static bool check_engine(THD *thd, const char *table_name,
                         enum db_type *new_engine);                             
unknown's avatar
unknown committed
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58

/*
 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
59 60
    0                   Error
    #                   Size of path
61 62
 */

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



unknown's avatar
unknown committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
/*
 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
91 92
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
unknown's avatar
unknown committed
93 94

*/
unknown's avatar
unknown committed
95

unknown's avatar
unknown committed
96 97
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
unknown's avatar
unknown committed
98
{
99
  bool error= FALSE, need_start_waiters= FALSE;
unknown's avatar
unknown committed
100 101 102 103
  DBUG_ENTER("mysql_rm_table");

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

104
  if (!drop_temporary)
unknown's avatar
unknown committed
105
  {
106
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
107
    {
108
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
109
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
110
    }
111 112
    else
      need_start_waiters= TRUE;
unknown's avatar
unknown committed
113
  }
114 115 116 117 118 119 120 121 122 123

  /*
    Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
    LOCK_open during wait_if_global_read_lock(), other threads could not
    close their tables. This would make a pretty deadlock.
  */
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

unknown's avatar
VIEW  
unknown committed
124
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
125 126 127 128 129 130 131 132

  pthread_mutex_unlock(&LOCK_open);

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

133 134 135
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

136
  if (error)
unknown's avatar
unknown committed
137
    DBUG_RETURN(TRUE);
138
  send_ok(thd);
unknown's avatar
unknown committed
139
  DBUG_RETURN(FALSE);
140 141
}

unknown's avatar
unknown committed
142 143 144 145 146

/*
 delete (drop) tables.

  SYNOPSIS
147 148 149 150 151
    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
152
                        generate warnings if the handler files doesn't exists
unknown's avatar
unknown committed
153 154 155 156 157 158 159 160 161 162

 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
163 164
int mysql_rm_table_part2_with_lock(THD *thd,
				   TABLE_LIST *tables, bool if_exists,
165
				   bool drop_temporary, bool dont_log_query)
unknown's avatar
unknown committed
166 167 168 169 170 171
{
  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
172 173
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
			      dont_log_query);
unknown's avatar
unknown committed
174 175 176 177 178 179 180 181 182 183

  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
184

185
/*
186 187 188 189 190 191 192 193 194
  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
195
    drop_view		Allow to delete VIEW .frm
196 197
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
198

199 200 201 202 203 204 205 206 207
  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.
208 209 210 211 212

 RETURN
   0	ok
   1	Error
   -1	Thread was killed
213
*/
214 215

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

225 226
  DBUG_ENTER("mysql_rm_table_part2");

227
  if (lock_table_names(thd, tables))
228
    DBUG_RETURN(1);
229

230 231 232
  /* 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
233
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
234
  {
235
    char *db=table->db;
236 237
    db_type table_type= DB_TYPE_UNKNOWN;

238
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
239
    if (!close_temporary_table(thd, db, table->table_name))
unknown's avatar
unknown committed
240
    {
241
      tmp_table_deleted=1;
unknown's avatar
unknown committed
242
      continue;					// removed temporary table
unknown's avatar
unknown committed
243
    }
unknown's avatar
unknown committed
244 245

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

  if (some_tables_deleted || tmp_table_deleted || !error)
unknown's avatar
unknown committed
326
  {
unknown's avatar
unknown committed
327
    query_cache_invalidate3(thd, tables, 0);
328
    if (!dont_log_query && mysql_bin_log.is_open())
329
    {
unknown's avatar
unknown committed
330 331
      if (!error)
        thd->clear_error();
unknown's avatar
unknown committed
332
      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
333
      mysql_bin_log.write(&qinfo);
334
    }
unknown's avatar
unknown committed
335
  }
unknown's avatar
unknown committed
336

unknown's avatar
unknown committed
337
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
338
  thd->no_warnings_for_error= 0;
339
  DBUG_RETURN(error);
unknown's avatar
unknown committed
340 341 342 343 344 345 346 347
}


int quick_rm_table(enum db_type base,const char *db,
		   const char *table_name)
{
  char path[FN_REFLEN];
  int error=0;
348
  build_table_path(path, sizeof(path), db, table_name, reg_ext);
unknown's avatar
unknown committed
349 350
  if (my_delete(path,MYF(0)))
    error=1; /* purecov: inspected */
351
  *fn_ext(path)= 0;                             // Remove reg_ext
352
  return ha_delete_table(current_thd, base, path, table_name, 0) || error;
unknown's avatar
unknown committed
353 354
}

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
/*
  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;
373
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
374 375
    {
      /* Sort NOT NULL keys before other keys */
376
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
377 378 379 380 381 382 383 384 385 386 387 388 389
    }
    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
390
  /*
391
    Prefer original key order.	usable_key_parts contains here
unknown's avatar
unknown committed
392 393 394 395 396
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
397 398
}

399 400
/*
  Check TYPELIB (set or enum) for duplicates
401

402 403 404
  SYNOPSIS
    check_duplicates_in_interval()
    set_or_name   "SET" or "ENUM" string for warning message
405 406
    name	  name of the checked column
    typelib	  list of values for the column
407 408

  DESCRIPTION
409
    This function prints an warning for each value in list
410 411 412 413 414 415 416
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
417 418
                                  const char *name, TYPELIB *typelib,
                                  CHARSET_INFO *cs)
419
{
420
  TYPELIB tmp= *typelib;
421
  const char **cur_value= typelib->type_names;
422 423 424
  unsigned int *cur_length= typelib->type_lengths;
  
  for ( ; tmp.count > 1; cur_value++, cur_length++)
425
  {
426 427 428 429
    tmp.type_names++;
    tmp.type_lengths++;
    tmp.count--;
    if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
430
    {
unknown's avatar
unknown committed
431
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
432 433 434 435 436 437
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
    }
  }
}
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 466 467 468 469 470 471 472 473

/*
  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
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
/*
  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
494 495
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
unknown's avatar
unknown committed
496 497 498
			 uint table_flags)
{
  DBUG_ENTER("prepare_field");
unknown's avatar
unknown committed
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

  /*
    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;
518
    (*blob_columns)++;
unknown's avatar
unknown committed
519 520
    break;
  case FIELD_TYPE_GEOMETRY:
unknown's avatar
unknown committed
521
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
522 523 524 525
    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
526
      DBUG_RETURN(1);
unknown's avatar
unknown committed
527 528 529 530 531 532 533 534
    }
    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;
535
    (*blob_columns)++;
unknown's avatar
unknown committed
536 537 538 539 540
    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
541
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
542
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
543
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
unknown's avatar
unknown committed
544 545 546 547 548 549 550 551
    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
552
      {
unknown's avatar
unknown committed
553 554
        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
555 556
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
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 585 586 587 588 589 590 591 592
    }
#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
593 594 595
    /* 
      We have sql_field->pack_flag already set here, see mysql_prepare_table().
    */
unknown's avatar
unknown committed
596 597 598 599 600 601 602 603 604 605 606 607 608
    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)
    {
609
      if (!*timestamps)
unknown's avatar
unknown committed
610
      {
unknown's avatar
unknown committed
611
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
612
        (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
613
      }
unknown's avatar
unknown committed
614 615 616 617
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
618
      (*timestamps_with_niladic)++;
unknown's avatar
unknown committed
619

620
    (*timestamps)++;
unknown's avatar
unknown committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    /* 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
636 637 638
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
639
/*
640
  Preparation for table creation
unknown's avatar
unknown committed
641 642

  SYNOPSIS
643
    mysql_prepare_table()
unknown's avatar
unknown committed
644 645 646 647 648
    thd			Thread object
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create

649
  DESCRIPTION
650
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
651

652
  NOTES
653
    sets create_info->varchar if the table has a varchar
654

unknown's avatar
unknown committed
655 656 657 658
  RETURN VALUES
    0	ok
    -1	error
*/
unknown's avatar
unknown committed
659

660 661 662 663 664 665
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
666
{
667
  const char	*key_name;
unknown's avatar
unknown committed
668
  create_field	*sql_field,*dup_field;
unknown's avatar
unknown committed
669
  uint		field,null_fields,blob_columns,max_key_length;
unknown's avatar
unknown committed
670
  ulong		record_offset= 0;
671
  KEY		*key_info;
unknown's avatar
unknown committed
672
  KEY_PART_INFO *key_part_info;
673 674 675
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
676
  List_iterator<create_field> it(*fields),it2(*fields);
unknown's avatar
unknown committed
677
  uint total_uneven_bit_length= 0;
678
  DBUG_ENTER("mysql_prepare_table");
unknown's avatar
unknown committed
679

680
  select_field_pos= fields->elements - select_field_count;
unknown's avatar
unknown committed
681
  null_fields=blob_columns=0;
682
  create_info->varchar= 0;
unknown's avatar
unknown committed
683
  max_key_length= file->max_key_length();
684

685
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
686
  {
687 688
    CHARSET_INFO *save_cs;

689 690 691 692 693 694
    /*
      Initialize length from its original value (number of characters),
      which was set in the parser. This is necessary if we're
      executing a prepared statement for the second time.
    */
    sql_field->length= sql_field->char_length;
695
    if (!sql_field->charset)
696 697 698
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
699 700 701
      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.
702
    */
703
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
704
      sql_field->charset= create_info->table_charset;
705

706
    save_cs= sql_field->charset;
707 708 709 710 711
    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];
712 713
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
              STRING_WITH_LEN("_bin"));
714 715 716
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
717

718 719
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
720 721 722
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
723
      TYPELIB *interval= sql_field->interval;
724 725 726 727 728 729

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
730
      if (!interval)
731
      {
732 733 734 735
        /*
          Create the typelib in prepared statement memory if we're
          executing one.
        */
unknown's avatar
unknown committed
736
        MEM_ROOT *stmt_root= thd->stmt_arena->mem_root;
737 738 739

        interval= sql_field->interval= typelib(stmt_root,
                                               sql_field->interval_list);
740 741
        List_iterator<String> it(sql_field->interval_list);
        String conv, *tmp;
742 743 744 745 746
        char comma_buf[2];
        int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf,
                                          (uchar*) comma_buf + 
                                          sizeof(comma_buf));
        DBUG_ASSERT(comma_length > 0);
747
        for (uint i= 0; (tmp= it++); i++)
748
        {
unknown's avatar
unknown committed
749
          uint lengthsp;
750 751 752 753 754
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
755
            interval->type_names[i]= strmake_root(stmt_root, conv.ptr(),
unknown's avatar
unknown committed
756
                                                  conv.length());
757 758
            interval->type_lengths[i]= conv.length();
          }
759

760
          // Strip trailing spaces.
unknown's avatar
unknown committed
761 762
          lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                       interval->type_lengths[i]);
763 764
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
765 766 767 768 769 770
          if (sql_field->sql_type == FIELD_TYPE_SET)
          {
            if (cs->coll->instr(cs, interval->type_names[i], 
                                interval->type_lengths[i], 
                                comma_buf, comma_length, NULL, 0))
            {
unknown's avatar
unknown committed
771
              my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
772 773 774
              DBUG_RETURN(-1);
            }
          }
775
        }
776
        sql_field->interval_list.empty(); // Don't need interval_list anymore
777 778 779 780 781 782
      }

      /*
        Convert the default value from client character
        set into the column character set if necessary.
      */
unknown's avatar
unknown committed
783
      if (sql_field->def && cs != sql_field->def->collation.collation)
784
      {
unknown's avatar
unknown committed
785 786
        Query_arena backup_arena;
        bool need_to_change_arena= !thd->stmt_arena->is_conventional();
787 788 789
        if (need_to_change_arena)
        {
          /* Asser that we don't do that at every PS execute */
unknown's avatar
unknown committed
790 791 792
          DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() ||
                      thd->stmt_arena->is_first_sp_execute());
          thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena);
793 794 795 796 797
        }

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

        if (need_to_change_arena)
unknown's avatar
unknown committed
798
          thd->restore_active_arena(thd->stmt_arena, &backup_arena);
799

800
        if (sql_field->def == NULL)
unknown's avatar
unknown committed
801 802 803 804 805
        {
          /* Could not convert */
          my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
          DBUG_RETURN(-1);
        }
806 807 808 809
      }

      if (sql_field->sql_type == FIELD_TYPE_SET)
      {
810
        uint32 field_length;
811
        if (sql_field->def != NULL)
812 813 814 815 816
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
          if (def == NULL) /* SQL "NULL" maps to NULL */
          {
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }

            /* else, NULL is an allowed value */
            (void) find_set(interval, NULL, 0,
                            cs, &not_used, &not_used2, &not_found);
          }
          else /* not NULL */
          {
            (void) find_set(interval, def->ptr(), def->length(),
                            cs, &not_used, &not_used2, &not_found);
          }

835 836 837 838 839 840
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
841 842
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
843 844 845
      }
      else  /* FIELD_TYPE_ENUM */
      {
846
        uint32 field_length;
847 848
        DBUG_ASSERT(sql_field->sql_type == FIELD_TYPE_ENUM);
        if (sql_field->def != NULL)
849 850
        {
          String str, *def= sql_field->def->val_str(&str);
851
          if (def == NULL) /* SQL "NULL" maps to NULL */
852
          {
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
            if ((sql_field->flags & NOT_NULL_FLAG) != 0)
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }

            /* else, the defaults yield the correct length for NULLs. */
          } 
          else /* not NULL */
          {
            def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
            if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
            {
              my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
              DBUG_RETURN(-1);
            }
869 870
          }
        }
871 872
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
873 874 875 876
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

877 878
    if (sql_field->sql_type == FIELD_TYPE_BIT)
    { 
unknown's avatar
unknown committed
879
      sql_field->pack_flag= FIELDFLAG_NUMBER;
880 881 882 883 884 885
      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;
    }

886
    sql_field->create_length_to_internal_length();
unknown's avatar
unknown committed
887 888
    if (prepare_blob_field(thd, sql_field))
      DBUG_RETURN(-1);
889

unknown's avatar
unknown committed
890 891
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
unknown's avatar
unknown committed
892

unknown's avatar
unknown committed
893 894
    if (check_column_name(sql_field->field_name))
    {
895
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
unknown's avatar
unknown committed
896 897
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
898

899 900
    /* 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
901
    {
902
      if (my_strcasecmp(system_charset_info,
903 904
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
905
      {
906 907 908 909
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
910 911
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
912
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
913 914 915 916
	  DBUG_RETURN(-1);
	}
	else
	{
917
	  /* Field redefined */
918
	  sql_field->def=		dup_field->def;
919
	  sql_field->sql_type=		dup_field->sql_type;
920 921 922
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
923
	  sql_field->length=		dup_field->char_length;
924
          sql_field->pack_length=	dup_field->pack_length;
925
          sql_field->key_length=	dup_field->key_length;
926
	  sql_field->create_length_to_internal_length();
927 928
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->unireg_check=	dup_field->unireg_check;
929 930 931 932 933 934 935 936
          /* 
            We're making one field from two, the result field will have
            dup_field->flags as flags. If we've incremented null_fields
            because of sql_field->flags, decrement it back.
          */
          if (!(sql_field->flags & NOT_NULL_FLAG))
            null_fields--;
	  sql_field->flags=		dup_field->flags;
unknown's avatar
unknown committed
937
          sql_field->interval=          dup_field->interval;
938 939 940
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
941
	}
unknown's avatar
unknown committed
942 943
      }
    }
944 945 946 947
    /* 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)
948
      (*db_options)|= HA_OPTION_PACK_RECORD;
unknown's avatar
unknown committed
949 950
    it2.rewind();
  }
951 952 953

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
unknown's avatar
unknown committed
954
  null_fields+= total_uneven_bit_length;
unknown's avatar
unknown committed
955 956 957 958

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

unknown's avatar
unknown committed
961 962
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
unknown's avatar
unknown committed
963
			     file->table_flags()))
unknown's avatar
unknown committed
964
      DBUG_RETURN(-1);
965
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
unknown's avatar
unknown committed
966
      create_info->varchar= 1;
967
    sql_field->offset= record_offset;
unknown's avatar
unknown committed
968 969
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
970
    record_offset+= sql_field->pack_length;
unknown's avatar
unknown committed
971
  }
972 973
  if (timestamps_with_niladic > 1)
  {
unknown's avatar
unknown committed
974 975
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
976 977
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
978 979
  if (auto_increment > 1)
  {
unknown's avatar
unknown committed
980
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
unknown's avatar
unknown committed
981 982 983
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
984
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
985
  {
unknown's avatar
unknown committed
986 987
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
unknown's avatar
unknown committed
988 989 990
    DBUG_RETURN(-1);
  }

991
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
992
  {
unknown's avatar
unknown committed
993 994
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
unknown's avatar
unknown committed
995 996 997 998
    DBUG_RETURN(-1);
  }

  /* Create keys */
999

1000
  List_iterator<Key> key_iterator(*keys), key_iterator2(*keys);
1001
  uint key_parts=0, fk_key_count=0;
1002
  bool primary_key=0,unique_key=0;
1003
  Key *key, *key2;
unknown's avatar
unknown committed
1004
  uint tmp, key_number;
1005 1006
  /* special marker for keys to be ignored */
  static char ignore_key[1];
1007

1008
  /* Calculate number of key segements */
1009
  *key_count= 0;
1010

unknown's avatar
unknown committed
1011 1012
  while ((key=key_iterator++))
  {
1013 1014 1015 1016 1017 1018 1019
    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)
      {
1020 1021 1022
        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));
1023 1024 1025 1026
	DBUG_RETURN(-1);
      }
      continue;
    }
1027
    (*key_count)++;
unknown's avatar
unknown committed
1028
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
1029 1030 1031 1032 1033
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
1034
    if (key->name && strlen(key->name) > NAME_LEN)
unknown's avatar
unknown committed
1035
    {
1036
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
unknown's avatar
unknown committed
1037 1038
      DBUG_RETURN(-1);
    }
1039
    key_iterator2.rewind ();
1040
    if (key->type != Key::FOREIGN_KEY)
1041
    {
1042
      while ((key2 = key_iterator2++) != key)
1043
      {
unknown's avatar
unknown committed
1044
	/*
1045 1046 1047
          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
1048
        */
1049 1050 1051
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
1052
        {
1053
          /* TODO: issue warning message */
1054 1055 1056 1057 1058 1059 1060
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
1061 1062 1063
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
1064 1065 1066
          }
          break;
        }
1067 1068 1069 1070 1071 1072
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
1073 1074 1075 1076 1077 1078
    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);
    }
1079
  }
unknown's avatar
unknown committed
1080
  tmp=file->max_keys();
1081
  if (*key_count > tmp)
1082 1083 1084 1085
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
1086

1087
  (*key_info_buffer) = key_info= (KEY*) sql_calloc(sizeof(KEY)* *key_count);
1088
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
1089
  if (!*key_info_buffer || ! key_part_info)
1090 1091
    DBUG_RETURN(-1);				// Out of memory

1092
  key_iterator.rewind();
unknown's avatar
unknown committed
1093
  key_number=0;
unknown's avatar
unknown committed
1094
  for (; (key=key_iterator++) ; key_number++)
1095 1096 1097 1098
  {
    uint key_length=0;
    key_part_spec *column;

1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
    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
1109
    switch(key->type){
unknown's avatar
unknown committed
1110
    case Key::MULTIPLE:
1111
	key_info->flags= 0;
1112
	break;
unknown's avatar
unknown committed
1113
    case Key::FULLTEXT:
1114
	key_info->flags= HA_FULLTEXT;
1115
	break;
unknown's avatar
unknown committed
1116
    case Key::SPATIAL:
unknown's avatar
unknown committed
1117
#ifdef HAVE_SPATIAL
1118
	key_info->flags= HA_SPATIAL;
1119
	break;
unknown's avatar
unknown committed
1120
#else
1121 1122
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
unknown's avatar
unknown committed
1123 1124
	DBUG_RETURN(-1);
#endif
1125 1126 1127 1128
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
1129 1130
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
1131
    }
1132 1133
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
1134

unknown's avatar
unknown committed
1135 1136
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
1137
    key_info->usable_key_parts= key_number;
1138
    key_info->algorithm=key->algorithm;
unknown's avatar
unknown committed
1139

1140 1141
    if (key->type == Key::FULLTEXT)
    {
1142
      if (!(file->table_flags() & HA_CAN_FULLTEXT))
1143
      {
unknown's avatar
unknown committed
1144 1145
	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                   MYF(0));
1146
	DBUG_RETURN(-1);
1147 1148
      }
    }
unknown's avatar
unknown committed
1149 1150 1151
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
1152
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
1153 1154 1155
       in near future when new frm file is ready
       checking for proper key parts number:
    */
1156

1157
    /* TODO: Add proper checks if handler supports key_type and algorithm */
1158
    if (key_info->flags & HA_SPATIAL)
1159
    {
1160 1161 1162 1163 1164 1165
      if (!(file->table_flags() & HA_CAN_RTREEKEYS))
      {
        my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                   MYF(0));
        DBUG_RETURN(-1);
      }
1166 1167
      if (key_info->key_parts != 1)
      {
1168
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
1169
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
1170
      }
1171
    }
unknown's avatar
unknown committed
1172
    else if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
1173
    {
unknown's avatar
unknown committed
1174
#ifdef HAVE_RTREE_KEYS
1175 1176
      if ((key_info->key_parts & 1) == 1)
      {
1177
	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
1178
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
1179
      }
1180
      /* TODO: To be deleted */
1181
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
1182
      DBUG_RETURN(-1);
unknown's avatar
unknown committed
1183
#else
1184 1185
      my_error(ER_FEATURE_DISABLED, MYF(0),
               sym_group_rtree.name, sym_group_rtree.needed_define);
unknown's avatar
unknown committed
1186 1187
      DBUG_RETURN(-1);
#endif
unknown's avatar
unknown committed
1188
    }
1189

1190
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
1191
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
1192 1193
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
1194
      uint length;
unknown's avatar
unknown committed
1195 1196
      key_part_spec *dup_column;

unknown's avatar
unknown committed
1197 1198 1199
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
1200
	     my_strcasecmp(system_charset_info,
1201 1202
			   column->field_name,
			   sql_field->field_name))
unknown's avatar
unknown committed
1203 1204 1205
	field++;
      if (!sql_field)
      {
1206
	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
unknown's avatar
unknown committed
1207 1208
	DBUG_RETURN(-1);
      }
unknown's avatar
unknown committed
1209
      while ((dup_column= cols2++) != column)
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
      {
        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();
1221
      if (key->type == Key::FULLTEXT)
1222
      {
1223 1224
	if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
	     sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
1225 1226
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
1227
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
1228 1229
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
1230
	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
	    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));
1242
      }
1243
      else
1244
      {
1245 1246
	column->length*= sql_field->charset->mbmaxlen;

1247 1248
	if (f_is_blob(sql_field->pack_flag) ||
            (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
1249
	{
unknown's avatar
unknown committed
1250
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
1251
	  {
1252
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
1253 1254
	    DBUG_RETURN(-1);
	  }
1255 1256 1257
          if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
              Field::GEOM_POINT)
            column->length= 21;
1258 1259
	  if (!column->length)
	  {
1260
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
1261 1262 1263
	    DBUG_RETURN(-1);
	  }
	}
unknown's avatar
unknown committed
1264
#ifdef HAVE_SPATIAL
1265
	if (key->type == Key::SPATIAL)
1266
	{
1267
	  if (!column->length)
1268 1269
	  {
	    /*
1270 1271
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
1272
	    */
1273
	    column->length= 4*sizeof(double);
1274 1275
	  }
	}
unknown's avatar
unknown committed
1276
#endif
1277 1278 1279 1280 1281 1282 1283
	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
1284
            null_fields--;
1285 1286 1287
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
unknown's avatar
unknown committed
1288
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
1289
	  {
1290
	    my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
1291 1292 1293 1294
	    DBUG_RETURN(-1);
	  }
	  if (key->type == Key::SPATIAL)
	  {
unknown's avatar
unknown committed
1295 1296
	    my_message(ER_SPATIAL_CANT_HAVE_NULL,
                       ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
1297 1298 1299 1300 1301 1302 1303 1304
	    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
1305
      }
1306

unknown's avatar
unknown committed
1307 1308 1309
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
1310 1311
      length= sql_field->key_length;

unknown's avatar
unknown committed
1312 1313 1314 1315
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
1316
	  if ((length=column->length) > max_key_length ||
1317
	      length > file->max_key_part_length())
1318
	  {
unknown's avatar
unknown committed
1319
	    length=min(max_key_length, file->max_key_part_length());
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
	    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
1335
	}
1336
	else if (!f_is_geom(sql_field->pack_flag) &&
1337 1338 1339 1340 1341 1342
		  (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
1343
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
1344 1345 1346 1347
	  DBUG_RETURN(-1);
	}
	else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
	  length=column->length;
unknown's avatar
unknown committed
1348 1349 1350
      }
      else if (length == 0)
      {
1351
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
unknown's avatar
unknown committed
1352 1353
	  DBUG_RETURN(-1);
      }
1354
      if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
1355
      {
1356 1357 1358
        length= file->max_key_part_length();
        /* Align key length to multibyte char boundary */
        length-= length % sql_field->charset->mbmaxlen;
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
	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);
	}
1373 1374
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
1375
      /* Use packed keys for long strings on the first column */
1376
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
unknown's avatar
unknown committed
1377
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
1378 1379
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
unknown's avatar
unknown committed
1380 1381
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
1382 1383 1384
	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
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394
	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)
1395 1396 1397
	{
	  if (primary_key)
	  {
unknown's avatar
unknown committed
1398 1399
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
1400 1401 1402 1403 1404
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
1405
	else if (!(key_name = key->name))
unknown's avatar
unknown committed
1406
	  key_name=make_unique_key_name(sql_field->field_name,
1407 1408
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
unknown's avatar
unknown committed
1409
	{
1410
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
unknown's avatar
unknown committed
1411 1412 1413 1414 1415
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
      }
    }
1416 1417
    if (!key_info->name || check_column_name(key_info->name))
    {
1418
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
1419 1420
      DBUG_RETURN(-1);
    }
1421 1422
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
1423
    key_info->key_length=(uint16) key_length;
1424
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
1425
    {
1426
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
unknown's avatar
unknown committed
1427 1428
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
1429
    key_info++;
unknown's avatar
unknown committed
1430
  }
1431
  if (!unique_key && !primary_key &&
1432
      (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
1433
  {
unknown's avatar
unknown committed
1434
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
1435 1436
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1437 1438
  if (auto_increment > 0)
  {
unknown's avatar
unknown committed
1439
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
unknown's avatar
unknown committed
1440 1441
    DBUG_RETURN(-1);
  }
1442
  /* Sort keys in optimized order */
1443
  qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
1444
	(qsort_cmp) sort_keys);
unknown's avatar
unknown committed
1445
  create_info->null_bits= null_fields;
unknown's avatar
unknown committed
1446

1447 1448 1449
  DBUG_RETURN(0);
}

1450

unknown's avatar
unknown committed
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
/*
  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];

1474 1475
    if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
                                                      MODE_STRICT_ALL_TABLES)))
unknown's avatar
unknown committed
1476 1477 1478 1479 1480 1481 1482 1483
    {
      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,
1484
            (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
unknown's avatar
unknown committed
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
            (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);
}


1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
/*
  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
1548 1549 1550 1551
  DBUG_ASSERT(sql_field->def == 0);
  /* Can't go wrong as sql_field->def is not defined */
  (void) prepare_blob_field(thd, sql_field);
}
1552 1553


1554 1555 1556 1557 1558
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1559 1560 1561 1562 1563 1564
    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
1565
    internal_tmp_table  Set to 1 if this is an internal temporary table
1566
			(From ALTER TABLE)
1567 1568

  DESCRIPTION
1569
    If one creates a temporary table, this is automatically opened
1570 1571 1572 1573 1574 1575 1576

    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
1577 1578
    FALSE OK
    TRUE  error
1579 1580
*/

unknown's avatar
unknown committed
1581 1582 1583
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
1584
                        List<Key> &keys,bool internal_tmp_table,
unknown's avatar
unknown committed
1585
                        uint select_field_count)
1586
{
1587 1588 1589 1590 1591
  char		path[FN_REFLEN];
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
unknown's avatar
unknown committed
1592
  bool		error= TRUE;
1593 1594 1595 1596 1597
  DBUG_ENTER("mysql_create_table");

  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
unknown's avatar
unknown committed
1598 1599
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
unknown's avatar
unknown committed
1600
    DBUG_RETURN(TRUE);
1601
  }
1602 1603
  if (check_engine(thd, table_name, &create_info->db_type))
    DBUG_RETURN(TRUE);
1604
  db_options= create_info->table_options;
1605 1606 1607
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
1608
  file= get_new_handler((TABLE*) 0, thd->mem_root, create_info->db_type);
1609

unknown's avatar
unknown committed
1610 1611 1612 1613 1614 1615 1616 1617
#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
  */
1618 1619 1620
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      (file->table_flags() & HA_NO_TEMP_TABLES))
  {
1621
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
unknown's avatar
unknown committed
1622
    DBUG_RETURN(TRUE);
1623
  }
unknown's avatar
unknown committed
1624
#endif
1625

1626 1627 1628 1629 1630 1631 1632 1633 1634
  /*
    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];
1635 1636
    /* Abuse build_table_path() to build the path to the db.opt file */
    build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, "");
1637 1638 1639 1640
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }

1641 1642 1643
  if (mysql_prepare_table(thd, create_info, &fields,
			  &keys, internal_tmp_table, &db_options, file,
			  &key_info_buffer, &key_count,
1644
			  select_field_count))
unknown's avatar
unknown committed
1645
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1646 1647 1648 1649

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1650 1651 1652
    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);
1653
    if (lower_case_table_names)
1654
      my_casedn_str(files_charset_info, path);
unknown's avatar
unknown committed
1655 1656
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671
  else  
  {
	#ifdef FN_DEVCHAR
	  /* check if the table name contains FN_DEVCHAR when defined */
	  const char *start= alias;
	  while (*start != '\0')
	  {
		  if (*start == FN_DEVCHAR)
		  {
			  my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
			  DBUG_RETURN(TRUE);
		  }
		  start++;
	  }	  
	#endif
1672
    build_table_path(path, sizeof(path), db, alias, reg_ext);
1673
  }
1674

unknown's avatar
unknown committed
1675 1676 1677 1678
  /* Check if table already exists */
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
      && find_temporary_table(thd,db,table_name))
  {
1679
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1680 1681
    {
      create_info->table_existed= 1;		// Mark that table existed
1682 1683 1684
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
                          alias);
unknown's avatar
unknown committed
1685
      DBUG_RETURN(FALSE);
1686
    }
unknown's avatar
unknown committed
1687
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
unknown's avatar
unknown committed
1688
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1689 1690
  }
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1691
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
1692 1693 1694 1695
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1696 1697
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1698
      goto end;
unknown's avatar
unknown committed
1699 1700 1701
    }
  }

unknown's avatar
unknown committed
1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714
  /*
    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;
1715
    if (ha_table_exists_in_engine(thd, db, table_name))
unknown's avatar
unknown committed
1716
    {
1717
      DBUG_PRINT("info", ("Table with same name already existed in handler"));
unknown's avatar
unknown committed
1718 1719

      if (create_if_not_exists)
1720 1721
        goto warn;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1722
      goto end;
unknown's avatar
unknown committed
1723 1724 1725 1726
    }
  }

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

unknown's avatar
unknown committed
1729
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1730
    create_info->data_file_name= create_info->index_file_name= 0;
unknown's avatar
unknown committed
1731
  create_info->table_options=db_options;
1732

unknown's avatar
unknown committed
1733
  if (rea_create_table(thd, path, db, table_name,
1734
                       create_info, fields, key_count,
unknown's avatar
unknown committed
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
		       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
1745
    thd->tmp_table_used= 1;
unknown's avatar
unknown committed
1746
  }
unknown's avatar
unknown committed
1747
  if (!internal_tmp_table && mysql_bin_log.is_open())
1748
  {
unknown's avatar
unknown committed
1749
    thd->clear_error();
unknown's avatar
unknown committed
1750
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
1751
    mysql_bin_log.write(&qinfo);
1752
  }
unknown's avatar
unknown committed
1753
  error= FALSE;
unknown's avatar
unknown committed
1754

unknown's avatar
unknown committed
1755 1756 1757 1758
end:
  VOID(pthread_mutex_unlock(&LOCK_open));
  thd->proc_info="After create";
  DBUG_RETURN(error);
1759 1760 1761 1762 1763 1764 1765 1766

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
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
}

/*
** 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++)
1777
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
1778 1779 1780 1781 1782 1783 1784 1785 1786 1787
      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;

1788 1789
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
unknown's avatar
unknown committed
1790
    return (char*) field_name;			// Use fieldname
1791 1792 1793 1794 1795 1796
  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
  */
1797
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
1798
  {
1799 1800
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
1801 1802 1803
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1804
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
1805 1806
}

1807

unknown's avatar
unknown committed
1808 1809 1810 1811
/****************************************************************************
** Alter a table definition
****************************************************************************/

1812
bool
unknown's avatar
unknown committed
1813 1814
mysql_rename_table(enum db_type base,
		   const char *old_db,
unknown's avatar
unknown committed
1815
		   const char *old_name,
unknown's avatar
unknown committed
1816
		   const char *new_db,
unknown's avatar
unknown committed
1817
		   const char *new_name)
unknown's avatar
unknown committed
1818
{
1819
  THD *thd= current_thd;
1820 1821 1822
  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];
1823 1824
  handler *file= (base == DB_TYPE_UNKNOWN ? 0 :
                  get_new_handler((TABLE*) 0, thd->mem_root, base));
unknown's avatar
unknown committed
1825
  int error=0;
unknown's avatar
unknown committed
1826
  DBUG_ENTER("mysql_rename_table");
unknown's avatar
unknown committed
1827

1828 1829 1830 1831 1832 1833 1834 1835
  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.
   */
1836 1837
  if (lower_case_table_names == 2 && file &&
      !(file->table_flags() & HA_FILE_BASED))
unknown's avatar
unknown committed
1838
  {
1839 1840 1841 1842
    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
1843

1844 1845 1846 1847
    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
1848 1849
  }

1850
  if (!file || !(error=file->rename_table(from_base, to_base)))
1851 1852 1853
  {
    if (rename_file_ext(from,to,reg_ext))
    {
unknown's avatar
unknown committed
1854
      error=my_errno;
1855
      /* Restore old file name */
1856
      if (file)
1857
        file->rename_table(to_base, from_base);
1858 1859
    }
  }
unknown's avatar
unknown committed
1860
  delete file;
1861 1862 1863
  if (error == HA_ERR_WRONG_COMMAND)
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
  else if (error)
unknown's avatar
unknown committed
1864 1865
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
1866 1867
}

unknown's avatar
unknown committed
1868

unknown's avatar
unknown committed
1869
/*
1870 1871 1872 1873 1874 1875
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
1876
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
1877
			HA_EXTRA_FORCE_REOPEN if table is not be used
1878 1879 1880 1881 1882 1883 1884
  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
1885 1886
*/

1887 1888
static void wait_while_table_is_used(THD *thd,TABLE *table,
				     enum ha_extra_function function)
unknown's avatar
unknown committed
1889
{
1890
  DBUG_PRINT("enter",("table: %s", table->s->table_name));
1891 1892
  DBUG_ENTER("wait_while_table_is_used");
  safe_mutex_assert_owner(&LOCK_open);
unknown's avatar
unknown committed
1893

1894
  VOID(table->file->extra(function));
1895 1896 1897 1898
  /* 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 */
1899 1900
  remove_table_from_cache(thd, table->s->db,
                          table->s->table_name, RTFC_WAIT_OTHER_THREAD_FLAG);
1901 1902
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1903

1904 1905
/*
  Close a cached table
unknown's avatar
unknown committed
1906

1907
  SYNOPSIS
unknown's avatar
unknown committed
1908
    close_cached_table()
1909 1910 1911 1912 1913 1914
    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
1915

1916 1917 1918 1919
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
1920

1921
void close_cached_table(THD *thd, TABLE *table)
1922 1923
{
  DBUG_ENTER("close_cached_table");
1924

1925
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
1926 1927
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
1928
  {
1929 1930
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
1931
  }
1932 1933 1934 1935
  /* 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 */
1936
  broadcast_refresh();
unknown's avatar
unknown committed
1937
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1938 1939
}

1940
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
unknown's avatar
unknown committed
1941
			     const char* operator_name, const char* errmsg)
1942

unknown's avatar
unknown committed
1943
{
1944 1945
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
1946 1947
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
1948
  protocol->store(STRING_WITH_LEN("error"), system_charset_info);
1949
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
1950
  thd->clear_error();
1951
  if (protocol->write())
unknown's avatar
unknown committed
1952 1953 1954 1955
    return -1;
  return 1;
}

1956

unknown's avatar
unknown committed
1957
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1958
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1959
{
unknown's avatar
unknown committed
1960
  DBUG_ENTER("prepare_for_restore");
1961

unknown's avatar
unknown committed
1962 1963 1964 1965 1966 1967
  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
1968
  else
unknown's avatar
unknown committed
1969
  {
1970
    char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
1971
    char src_path[FN_REFLEN], dst_path[FN_REFLEN];
1972 1973
    char* table_name= table->table_name;
    char* db= table->db;
1974

1975 1976
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
					reg_ext))
unknown's avatar
unknown committed
1977
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
1978

1979
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1980
		mysql_real_data_home, db, table_name);
1981

1982
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
1983
      DBUG_RETURN(-1);
1984

1985
    if (my_copy(src_path,
1986 1987
		fn_format(dst_path, dst_path,"", reg_ext, 4),
		MYF(MY_WME)))
unknown's avatar
unknown committed
1988
    {
1989
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1990
      unlock_table_name(thd, table);
1991
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1992 1993 1994
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
1995
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
1996
    {
1997
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1998
      unlock_table_name(thd, table);
1999
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2000 2001
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
2002
    }
unknown's avatar
unknown committed
2003
  }
unknown's avatar
unknown committed
2004

2005 2006 2007 2008
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
2009 2010
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table))
2011
  {
2012
    unlock_table_name(thd, table);
2013
    pthread_mutex_unlock(&LOCK_open);
2014 2015
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                  "Failed to open partially restored table"));
2016
  }
2017
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2018
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2019
}
2020

2021

unknown's avatar
unknown committed
2022
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
2023
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
2024
{
unknown's avatar
unknown committed
2025 2026
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
2027 2028 2029 2030
  DBUG_ENTER("prepare_for_repair");

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

  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
2033
  {
unknown's avatar
unknown committed
2034
    char name[FN_REFLEN];
2035
    build_table_path(name, sizeof(name), table_list->db,
2036
                     table_list->table_name, "");
2037
    if (openfrm(thd, name, "", 0, 0, 0, &tmp_table))
unknown's avatar
unknown committed
2038 2039
      DBUG_RETURN(0);				// Can't open frm file
    table= &tmp_table;
2040
  }
unknown's avatar
unknown committed
2041

unknown's avatar
unknown committed
2042 2043 2044 2045 2046 2047 2048 2049 2050
  /*
    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
2051

unknown's avatar
unknown committed
2052 2053 2054
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext= table->file->bas_ext();
  MY_STAT stat_info;
unknown's avatar
unknown committed
2055

unknown's avatar
unknown committed
2056 2057 2058 2059 2060 2061
  /*
    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
2062

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

2067 2068
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
2069

2070 2071 2072 2073 2074 2075 2076
  /* 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
2077
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
2078
  {
unknown's avatar
unknown committed
2079 2080
    error= -1;
    goto end;
unknown's avatar
unknown committed
2081
  }
unknown's avatar
unknown committed
2082
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
2083
  {
2084
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
2085
    unlock_table_name(thd, table_list);
2086
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107
    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
2108 2109
  }

2110 2111 2112 2113
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
2114 2115
  pthread_mutex_lock(&LOCK_open);
  if (reopen_name_locked_table(thd, table_list))
unknown's avatar
unknown committed
2116
  {
unknown's avatar
unknown committed
2117
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
2118
    pthread_mutex_unlock(&LOCK_open);
2119 2120 2121
    error= send_check_errmsg(thd, table_list, "repair",
                             "Failed to open partially repaired table");
    goto end;
unknown's avatar
unknown committed
2122
  }
2123
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
2124 2125 2126 2127 2128

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

2131

2132

unknown's avatar
unknown committed
2133 2134
/*
  RETURN VALUES
unknown's avatar
merge  
unknown committed
2135 2136 2137
    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
2138
*/
unknown's avatar
unknown committed
2139 2140 2141 2142 2143
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,
2144
                              bool no_warnings_for_error,
unknown's avatar
unknown committed
2145 2146 2147
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
unknown's avatar
unknown committed
2148 2149 2150
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
unknown's avatar
unknown committed
2151
{
2152 2153
  TABLE_LIST *table, *save_next_global, *save_next_local;
  SELECT_LEX *select= &thd->lex->select_lex;
unknown's avatar
unknown committed
2154
  List<Item> field_list;
2155 2156
  Item *item;
  Protocol *protocol= thd->protocol;
2157
  LEX *lex= thd->lex;
2158
  int result_code;
2159
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
2160 2161 2162 2163 2164 2165 2166 2167 2168

  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;
2169 2170
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
2171
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2172

2173
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
unknown's avatar
VIEW  
unknown committed
2174
  for (table= tables; table; table= table->next_local)
unknown's avatar
unknown committed
2175 2176
  {
    char table_name[NAME_LEN*2+2];
2177
    char* db = table->db;
2178
    bool fatal_error=0;
unknown's avatar
unknown committed
2179

unknown's avatar
unknown committed
2180
    strxmov(table_name, db, ".", table->table_name, NullS);
unknown's avatar
unknown committed
2181
    thd->open_options|= extra_open_options;
2182 2183
    table->lock_type= lock_type;
    /* open only one table from local list of command */
2184
    save_next_global= table->next_global;
2185
    table->next_global= 0;
2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196
    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;
2197
    lex->query_tables_own_last= 0;
2198
    thd->no_warnings_for_error= no_warnings_for_error;
2199
    if (view_operator_func == NULL)
2200 2201
      table->required_type=FRMTYPE_TABLE;
    open_and_lock_tables(thd, table);
2202
    thd->no_warnings_for_error= 0;
2203 2204
    table->next_global= save_next_global;
    table->next_local= save_next_local;
unknown's avatar
unknown committed
2205
    thd->open_options&= ~extra_open_options;
2206

unknown's avatar
unknown committed
2207
    if (prepare_func)
2208
    {
unknown's avatar
unknown committed
2209
      switch ((*prepare_func)(thd, table, check_opt)) {
2210 2211 2212 2213 2214 2215 2216
      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
2217
      }
2218
    }
2219

2220
    /*
unknown's avatar
unknown committed
2221 2222 2223 2224 2225 2226
      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)
2227
    */
unknown's avatar
unknown committed
2228 2229
    if (!table->table)
    {
unknown's avatar
unknown committed
2230
      char buf[ERRMSGSIZE+ERRMSGSIZE+2];
unknown's avatar
unknown committed
2231
      const char *err_msg;
2232
      protocol->prepare_for_resend();
2233 2234
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
2235
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
unknown's avatar
unknown committed
2236 2237
      if (!(err_msg=thd->net.last_error))
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
2238 2239 2240 2241
      /* 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
2242
        strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
2243 2244
        err_msg= (const char *)buf;
      }
2245
      protocol->store(err_msg, system_charset_info);
2246
      lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
2247
      thd->clear_error();
2248 2249 2250 2251 2252
      /*
        View opening can be interrupted in the middle of process so some
        tables can be left opening
      */
      close_thread_tables(thd);
2253
      lex->reset_query_tables_list(FALSE);
2254
      if (protocol->write())
unknown's avatar
unknown committed
2255 2256 2257
	goto err;
      continue;
    }
2258 2259 2260 2261 2262 2263 2264

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

unknown's avatar
unknown committed
2265
    table->table->pos_in_table_list= table;
2266
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
2267
    {
unknown's avatar
unknown committed
2268
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
2269
      uint length;
2270
      protocol->prepare_for_resend();
2271 2272
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
2273
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
2274 2275 2276
      length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
                          table_name);
      protocol->store(buff, length, system_charset_info);
2277
      close_thread_tables(thd);
unknown's avatar
unknown committed
2278
      table->table=0;				// For query cache
2279
      if (protocol->write())
unknown's avatar
unknown committed
2280 2281 2282 2283
	goto err;
      continue;
    }

2284
    /* Close all instances of the table to allow repair to rename files */
2285
    if (lock_type == TL_WRITE && table->table->s->version)
2286 2287
    {
      pthread_mutex_lock(&LOCK_open);
2288 2289
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
2290
      mysql_lock_abort(thd,table->table);
2291
      remove_table_from_cache(thd, table->table->s->db,
unknown's avatar
unknown committed
2292
                              table->table->s->table_name,
unknown's avatar
unknown committed
2293 2294
                              RTFC_WAIT_OTHER_THREAD_FLAG |
                              RTFC_CHECK_KILLED_FLAG);
2295
      thd->exit_cond(old_message);
2296 2297
      if (thd->killed)
	goto err;
2298 2299 2300
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
2301 2302
    }

unknown's avatar
unknown committed
2303
    if (table->table->s->crashed && operator_func == &handler::ha_check)
2304 2305 2306 2307
    {
      protocol->prepare_for_resend();
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
2308 2309 2310
      protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
                      system_charset_info);
2311 2312 2313 2314
      if (protocol->write())
        goto err;
    }

unknown's avatar
unknown committed
2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329
    if (operator_func == &handler::ha_repair)
    {
      if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
          (table->table->file->ha_check_for_upgrade(check_opt) ==
           HA_ADMIN_NEEDS_ALTER))
      {
        close_thread_tables(thd);
        tmp_disable_binlog(thd); // binlogging is done by caller if wanted
        result_code= mysql_recreate_table(thd, table, 0);
        reenable_binlog(thd);
        goto send_result;
      }

    }

2330 2331 2332 2333
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

2334
    lex->cleanup_after_one_table_open();
unknown's avatar
unknown committed
2335
    thd->clear_error();  // these errors shouldn't get client
2336
    protocol->prepare_for_resend();
2337 2338
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
2339

2340 2341 2342
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
2343 2344
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
2345
      {
2346 2347
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
2348
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
2349
	protocol->store(STRING_WITH_LEN("note"), system_charset_info);
2350
	protocol->store(buf, length, system_charset_info);
2351
      }
unknown's avatar
unknown committed
2352 2353
      break;

unknown's avatar
unknown committed
2354 2355
    case HA_ADMIN_NOT_BASE_TABLE:
      {
unknown's avatar
unknown committed
2356 2357
        char buf[ERRMSGSIZE+20];
        uint length= my_snprintf(buf, ERRMSGSIZE,
unknown's avatar
unknown committed
2358
                                 ER(ER_BAD_TABLE_ERROR), table_name);
2359
        protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
2360
        protocol->store(buf, length, system_charset_info);
unknown's avatar
unknown committed
2361 2362 2363
      }
      break;

2364
    case HA_ADMIN_OK:
2365 2366
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
unknown's avatar
unknown committed
2367 2368
      break;

2369
    case HA_ADMIN_FAILED:
2370 2371 2372
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation failed"),
                      system_charset_info);
unknown's avatar
unknown committed
2373 2374
      break;

unknown's avatar
unknown committed
2375
    case HA_ADMIN_REJECT:
2376 2377 2378
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Operation need committed state"),
                      system_charset_info);
unknown's avatar
unknown committed
2379
      open_for_modify= FALSE;
unknown's avatar
unknown committed
2380 2381
      break;

2382
    case HA_ADMIN_ALREADY_DONE:
2383 2384 2385
      protocol->store(STRING_WITH_LEN("status"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Table is already up to date"),
                      system_charset_info);
2386 2387
      break;

2388
    case HA_ADMIN_CORRUPT:
2389 2390
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
2391
      fatal_error=1;
unknown's avatar
unknown committed
2392 2393
      break;

unknown's avatar
unknown committed
2394
    case HA_ADMIN_INVALID:
2395 2396 2397
      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      protocol->store(STRING_WITH_LEN("Invalid argument"),
                      system_charset_info);
unknown's avatar
unknown committed
2398 2399
      break;

2400 2401 2402 2403 2404 2405 2406 2407
    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
2408 2409 2410
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
2411
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
2412
      result_code= mysql_recreate_table(thd, table, 0);
2413
      reenable_binlog(thd);
unknown's avatar
unknown committed
2414
      close_thread_tables(thd);
2415 2416 2417 2418 2419 2420
      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
      }
2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432
      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. */
2433
            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
2434 2435 2436 2437 2438 2439 2440 2441 2442
            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);
          }
        }
      }
2443
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
unknown's avatar
VIEW  
unknown committed
2444 2445
      table->next_local= save_next_local;
      table->next_global= save_next_global;
2446 2447
      goto send_result_message;
    }
2448 2449
    case HA_ADMIN_WRONG_CHECKSUM:
    {
2450
      protocol->store(STRING_WITH_LEN("note"), system_charset_info);
unknown's avatar
unknown committed
2451 2452
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
2453 2454
      break;
    }
2455

unknown's avatar
unknown committed
2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
    case HA_ADMIN_NEEDS_UPGRADE:
    case HA_ADMIN_NEEDS_ALTER:
    {
      char buf[ERRMSGSIZE];
      uint length;

      protocol->store(STRING_WITH_LEN("error"), system_charset_info);
      length=my_snprintf(buf, ERRMSGSIZE, ER(ER_TABLE_NEEDS_UPGRADE), table->table_name);
      protocol->store(buf, length, system_charset_info);
      fatal_error=1;
      break;
    }

2469
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
2470 2471 2472 2473 2474
      {
        char buf[ERRMSGSIZE+20];
        uint length=my_snprintf(buf, ERRMSGSIZE,
                                "Unknown - internal error %d during operation",
                                result_code);
2475
        protocol->store(STRING_WITH_LEN("error"), system_charset_info);
2476 2477 2478 2479
        protocol->store(buf, length, system_charset_info);
        fatal_error=1;
        break;
      }
unknown's avatar
unknown committed
2480
    }
unknown's avatar
unknown committed
2481
    if (table->table)
2482
    {
unknown's avatar
unknown committed
2483 2484 2485 2486
      if (fatal_error)
        table->table->s->version=0;               // Force close of table
      else if (open_for_modify)
      {
unknown's avatar
unknown committed
2487
        if (table->table->s->tmp_table)
unknown's avatar
unknown committed
2488 2489 2490 2491 2492 2493 2494 2495 2496
          table->table->file->info(HA_STATUS_CONST);
        else
        {
          pthread_mutex_lock(&LOCK_open);
          remove_table_from_cache(thd, table->table->s->db,
                                  table->table->s->table_name, RTFC_NO_FLAG);
          pthread_mutex_unlock(&LOCK_open);
        }
        /* May be something modified consequently we have to invalidate cache */
unknown's avatar
unknown committed
2497 2498
        query_cache_invalidate3(thd, table->table, 0);
      }
2499
    }
unknown's avatar
unknown committed
2500
    close_thread_tables(thd);
2501
    lex->reset_query_tables_list(FALSE);
unknown's avatar
unknown committed
2502
    table->table=0;				// For query cache
2503
    if (protocol->write())
unknown's avatar
unknown committed
2504 2505 2506
      goto err;
  }

2507
  send_eof(thd);
unknown's avatar
unknown committed
2508
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
2509
 err:
2510
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
2511 2512
  if (table)
    table->table=0;
unknown's avatar
unknown committed
2513
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2514 2515
}

unknown's avatar
unknown committed
2516

unknown's avatar
unknown committed
2517
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
2518 2519 2520
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2521
				"backup", TL_READ, 0, 0, 0, 0,
2522
				&handler::backup, 0));
unknown's avatar
unknown committed
2523
}
unknown's avatar
unknown committed
2524

2525

unknown's avatar
unknown committed
2526
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
unknown's avatar
unknown committed
2527 2528 2529
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2530
				"restore", TL_WRITE, 1, 1, 0,
2531
				&prepare_for_restore,
2532
				&handler::restore, 0));
unknown's avatar
unknown committed
2533
}
unknown's avatar
unknown committed
2534

2535

unknown's avatar
unknown committed
2536
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2537 2538 2539
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2540 2541 2542
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
2543
				&prepare_for_repair,
unknown's avatar
unknown committed
2544
				&handler::ha_repair, 0));
2545 2546
}

2547

unknown's avatar
unknown committed
2548
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2549 2550 2551
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2552
				"optimize", TL_WRITE, 1,0,0,0,
2553
				&handler::optimize, 0));
2554 2555 2556
}


unknown's avatar
unknown committed
2557 2558 2559 2560 2561
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
2562 2563
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2564 2565

  RETURN VALUES
unknown's avatar
unknown committed
2566 2567
   FALSE ok
   TRUE  error
unknown's avatar
unknown committed
2568 2569
*/

unknown's avatar
unknown committed
2570
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
2571
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
2572
{
2573
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
2574
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
2575
  DBUG_ENTER("mysql_assign_to_keycache");
2576 2577 2578 2579 2580 2581 2582

  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
2583
    DBUG_RETURN(TRUE);
2584 2585 2586 2587
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
2588
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
2589
				0, 0, &handler::assign_to_keycache, 0));
unknown's avatar
unknown committed
2590 2591
}

unknown's avatar
unknown committed
2592 2593 2594 2595 2596 2597

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

  SYNOPSIS
    reassign_keycache_tables()
2598 2599 2600
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
2601

2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614
  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
2615 2616 2617
    0	  ok
*/

2618 2619
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
2620 2621 2622
{
  DBUG_ENTER("reassign_keycache_tables");

2623 2624
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
unknown's avatar
unknown committed
2625
  src_cache->param_buff_size= 0;		// Free key cache
2626 2627
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2628
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2629 2630 2631
}


unknown's avatar
unknown committed
2632 2633 2634 2635 2636
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2637 2638
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2639 2640

  RETURN VALUES
unknown's avatar
unknown committed
2641 2642
    FALSE ok
    TRUE  error
unknown's avatar
unknown committed
2643 2644
*/

unknown's avatar
unknown committed
2645
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
unknown's avatar
unknown committed
2646 2647 2648
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2649
				"preload_keys", TL_READ, 0, 0, 0, 0,
2650
				&handler::preload_keys, 0));
unknown's avatar
unknown committed
2651 2652 2653
}


unknown's avatar
unknown committed
2654 2655 2656 2657 2658
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2659 2660
    thd		Thread object
    table	Table list (one table only)
unknown's avatar
unknown committed
2661 2662 2663 2664
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
unknown's avatar
unknown committed
2665 2666
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
2667 2668
*/

unknown's avatar
unknown committed
2669 2670 2671
bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
                             HA_CREATE_INFO *create_info,
                             Table_ident *table_ident)
unknown's avatar
unknown committed
2672 2673 2674 2675
{
  TABLE **tmp_table;
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char *db= table->db;
2676
  char *table_name= table->table_name;
2677
  char *src_db;
unknown's avatar
unknown committed
2678
  char *src_table= table_ident->table.str;
unknown's avatar
unknown committed
2679 2680
  int  err;
  bool res= TRUE;
2681 2682
  db_type not_used;

2683
  TABLE_LIST src_tables_list;
unknown's avatar
unknown committed
2684
  DBUG_ENTER("mysql_create_like_table");
unknown's avatar
unknown committed
2685 2686
  DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */
  src_db= table_ident->db.str;
unknown's avatar
unknown committed
2687 2688 2689 2690 2691 2692

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
2693
       check_table_name(src_table,table_ident->table.length)))
unknown's avatar
unknown committed
2694
  {
2695
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
unknown's avatar
unknown committed
2696
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2697
  }
2698 2699 2700 2701 2702
  if (!src_db || check_db_name(src_db))
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
    DBUG_RETURN(-1);
  }
2703

unknown's avatar
VIEW  
unknown committed
2704
  bzero((gptr)&src_tables_list, sizeof(src_tables_list));
2705
  src_tables_list.db= src_db;
unknown's avatar
Merge  
unknown committed
2706
  src_tables_list.table_name= src_table;
2707

2708 2709
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
unknown's avatar
unknown committed
2710 2711

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
2712
    strxmov(src_path, (*tmp_table)->s->path, reg_ext, NullS);
unknown's avatar
unknown committed
2713 2714
  else
  {
2715 2716 2717 2718
    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));
2719 2720
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
unknown's avatar
unknown committed
2721 2722 2723
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2724
      goto err;
unknown's avatar
unknown committed
2725 2726 2727
    }
  }

2728 2729 2730
  /* 
     create like should be not allowed for Views, Triggers, ... 
  */
2731
  if (mysql_frm_type(thd, src_path, &not_used) != FRMTYPE_TABLE)
2732
  {
2733
    my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE");
2734 2735 2736
    goto err;
  }

unknown's avatar
unknown committed
2737 2738 2739
  /*
    Validate the destination table

2740
    skip the destination table name checking as this is already
unknown's avatar
unknown committed
2741 2742 2743 2744 2745 2746
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2747 2748 2749
    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);
2750 2751
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
unknown's avatar
unknown committed
2752 2753 2754 2755
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2756 2757 2758
    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
2759 2760 2761 2762
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2763
  /*
unknown's avatar
unknown committed
2764
    Create a new table by copying from source table
2765
  */
2766 2767 2768 2769 2770 2771
  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);
2772
    goto err;
2773
  }
unknown's avatar
unknown committed
2774 2775

  /*
2776 2777
    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
2778 2779
    and temporary tables).
  */
2780
  *fn_ext(dst_path)= 0;
unknown's avatar
unknown committed
2781
  err= ha_create_table(dst_path, create_info, 1);
2782

unknown's avatar
unknown committed
2783 2784 2785 2786
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2787 2788
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2789
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
2790 2791 2792 2793
    }
  }
  else if (err)
  {
2794 2795 2796
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
2797
  }
2798 2799 2800

  // Must be written before unlock
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
2801
  {
2802
    thd->clear_error();
unknown's avatar
unknown committed
2803
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
2804
    mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
2805
  }
unknown's avatar
unknown committed
2806
  res= FALSE;
2807
  goto err;
2808

unknown's avatar
unknown committed
2809 2810 2811 2812
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2813 2814
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
2815
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2816
		 ER_TABLE_EXISTS_ERROR,warn_buff);
unknown's avatar
unknown committed
2817
    res= FALSE;
unknown's avatar
unknown committed
2818
  }
2819 2820 2821 2822 2823 2824 2825 2826
  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
2827 2828 2829
}


unknown's avatar
unknown committed
2830
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2831
{
unknown's avatar
unknown committed
2832 2833 2834 2835 2836 2837
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2838 2839
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2840
				"analyze", lock_type, 1, 0, 0, 0,
2841
				&handler::analyze, 0));
2842 2843 2844
}


unknown's avatar
unknown committed
2845
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
2846
{
unknown's avatar
unknown committed
2847 2848 2849 2850 2851 2852
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2853 2854
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
unknown's avatar
unknown committed
2855
				"check", lock_type,
2856
				0, HA_OPEN_FOR_REPAIR, 0, 0,
unknown's avatar
unknown committed
2857
				&handler::ha_check, &view_checksum));
2858 2859
}

unknown's avatar
unknown committed
2860

unknown's avatar
unknown committed
2861
/* table_list should contain just one table */
unknown's avatar
unknown committed
2862 2863 2864 2865
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
2866 2867 2868 2869 2870 2871
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

unknown's avatar
unknown committed
2872 2873 2874 2875
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
unknown's avatar
unknown committed
2876 2877 2878

  thd->proc_info="discard_or_import_tablespace";

unknown's avatar
unknown committed
2879
  discard= test(tablespace_op == DISCARD_TABLESPACE);
unknown's avatar
unknown committed
2880

unknown's avatar
unknown committed
2881 2882 2883 2884 2885
 /*
   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
2886 2887 2888 2889 2890
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2891

unknown's avatar
unknown committed
2892 2893 2894 2895 2896 2897 2898
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

unknown's avatar
unknown committed
2899 2900 2901 2902
  /*
    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
2903 2904 2905 2906 2907 2908 2909 2910 2911 2912
  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
2913
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
unknown's avatar
unknown committed
2914 2915 2916 2917
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
unknown's avatar
unknown committed
2918
  thd->tablespace_op=FALSE;
2919
  
unknown's avatar
unknown committed
2920 2921
  if (error == 0)
  {
unknown's avatar
unknown committed
2922
    send_ok(thd);
unknown's avatar
unknown committed
2923
    DBUG_RETURN(0);
unknown's avatar
unknown committed
2924
  }
unknown's avatar
unknown committed
2925

2926 2927
  table->file->print_error(error, MYF(0));
    
unknown's avatar
unknown committed
2928
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
2929
}
unknown's avatar
unknown committed
2930

2931 2932

#ifdef NOT_USED
2933 2934 2935 2936 2937 2938 2939
/*
  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.
*/

2940
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2941 2942 2943 2944 2945
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2946 2947 2948 2949 2950 2951 2952 2953
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2954 2955 2956
  DBUG_ENTER("mysql_create_index");

  /*
2957 2958 2959
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2960

2961 2962
    Open the table to have access to the correct table handler.
  */
2963 2964 2965 2966
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2967 2968 2969 2970 2971
    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.
  */
2972 2973 2974 2975 2976 2977 2978 2979 2980 2981
  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;
2982 2983 2984
  if (mysql_prepare_table(thd, &create_info, &fields,
			  &keys, /*tmp_table*/ 0, &db_options, table->file,
			  &key_info_buffer, key_count,
2985
			  /*select_field_count*/ 0))
2986 2987 2988
    DBUG_RETURN(-1);

  /*
2989 2990 2991
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2992 2993 2994 2995
  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)&
2996
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2997 2998
      break ;
  }
2999
  if ((idx < key_count)|| !key_count)
3000 3001 3002 3003 3004 3005 3006
  {
    /* 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();
3007
    if (real_alter_table(thd, table_list->db, table_list->table_name,
3008 3009 3010 3011
			 &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);*/
3012 3013 3014 3015 3016
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
3017 3018
        build_table_path(path, sizeof(path), table_list->db,
                         (lower_case_table_names == 2) ?
3019
                         table_list->alias : table_list->table_name,
3020
                         reg_ext) == 0 ||
3021 3022 3023
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
      /* don't need to free((gptr) key_info_buffer);*/
3024 3025
      DBUG_RETURN(-1);
  }
3026
  /* don't need to free((gptr) key_info_buffer);*/
3027 3028 3029 3030
  DBUG_RETURN(0);
}


3031 3032
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
3033 3034
{
  List<create_field> fields;
3035
  List<Key>	     keys;
3036 3037
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
3038 3039 3040 3041 3042 3043 3044 3045 3046
  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];
3047 3048 3049
  DBUG_ENTER("mysql_drop_index");

  /*
3050 3051 3052
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
3053

3054 3055
    Open the table to have access to the correct table handler.
  */
3056 3057 3058 3059
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
3060 3061 3062
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
3063 3064 3065 3066
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
3067 3068
    Get the number of each key and check if it can be created online.
  */
3069 3070 3071 3072 3073 3074 3075 3076 3077
  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))
3078
	break;
3079 3080 3081 3082 3083 3084 3085 3086
    }
    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);
    }
    /*
3087 3088 3089
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
3090 3091
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
3092
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
3093 3094 3095 3096 3097 3098 3099 3100 3101 3102
      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))
  {
3103
    if (real_alter_table(thd, table_list->db, table_list->table_name,
3104 3105 3106
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
3107 3108 3109 3110 3111 3112 3113
      /*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)||
3114 3115 3116
	mysql_prepare_table(thd, &create_info, &fields,
			    &keys, /*tmp_table*/ 0, &db_options, table->file,
			    &key_info_buffer, key_count,
3117
			    /*select_field_count*/ 0)||
3118 3119
        build_table_path(path, sizeof(path), table_list->db,
                         (lower_case_table_names == 2) ?
3120
                         table_list->alias : table_list->table_name,
3121
                         reg_ext) == 0 ||
3122 3123
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
3124 3125 3126 3127 3128 3129 3130
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
3131
#endif /* NOT_USED */
3132 3133


3134 3135 3136
/*
  Alter table
*/
3137

unknown's avatar
unknown committed
3138 3139 3140 3141
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,
3142
                       uint order_num, ORDER *order, bool ignore,
unknown's avatar
unknown committed
3143
                       ALTER_INFO *alter_info, bool do_send_ok)
unknown's avatar
unknown committed
3144
{
3145
  TABLE *table,*new_table=0;
unknown's avatar
unknown committed
3146
  int error;
unknown's avatar
unknown committed
3147 3148
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
3149
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
unknown's avatar
unknown committed
3150 3151
  ha_rows copied,deleted;
  ulonglong next_insert_id;
3152
  uint db_create_options, used_fields;
unknown's avatar
unknown committed
3153
  enum db_type old_db_type,new_db_type;
3154
  bool need_copy_table;
3155
  bool no_table_reopen= FALSE, varchar= FALSE;
unknown's avatar
unknown committed
3156 3157 3158
  DBUG_ENTER("mysql_alter_table");

  thd->proc_info="init";
3159
  table_name=table_list->table_name;
unknown's avatar
unknown committed
3160 3161
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;

unknown's avatar
unknown committed
3162
  db=table_list->db;
unknown's avatar
unknown committed
3163
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
3164
    new_db= db;
3165
  used_fields=create_info->used_fields;
unknown's avatar
unknown committed
3166
  
3167
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
unknown's avatar
unknown committed
3168

unknown's avatar
unknown committed
3169
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
3170
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
unknown's avatar
unknown committed
3171
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
3172
						   alter_info->tablespace_op));
unknown's avatar
unknown committed
3173
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
unknown's avatar
unknown committed
3174
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3175 3176 3177 3178 3179

  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
3180
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
3181
    if (lower_case_table_names)
unknown's avatar
unknown committed
3182 3183 3184
    {
      if (lower_case_table_names != 2)
      {
3185
	my_casedn_str(files_charset_info, new_name_buff);
unknown's avatar
unknown committed
3186 3187
	new_alias= new_name;			// Create lower case table name
      }
3188
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
3189
    }
3190
    if (new_db == db &&
unknown's avatar
unknown committed
3191
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
3192 3193
    {
      /*
3194 3195
	Source and destination table names are equal: make later check
	easier.
3196
      */
unknown's avatar
unknown committed
3197
      new_alias= new_name= table_name;
3198
    }
unknown's avatar
unknown committed
3199 3200
    else
    {
3201
      if (table->s->tmp_table)
unknown's avatar
unknown committed
3202 3203 3204
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
3205
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
3206
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3207 3208 3209 3210
	}
      }
      else
      {
3211 3212 3213
	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
3214 3215 3216
		    F_OK))
	{
	  /* Table will be closed in do_command() */
3217
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
unknown's avatar
unknown committed
3218
	  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3219 3220 3221 3222 3223
	}
      }
    }
  }
  else
3224 3225 3226 3227
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
3228

3229
  old_db_type= table->s->db_type;
unknown's avatar
unknown committed
3230
  if (create_info->db_type == DB_TYPE_DEFAULT)
3231
    create_info->db_type= old_db_type;
3232 3233 3234
  if (check_engine(thd, new_name, &create_info->db_type))
    DBUG_RETURN(TRUE);
  new_db_type= create_info->db_type;
3235
  if (create_info->row_type == ROW_TYPE_NOT_USED)
3236
    create_info->row_type= table->s->row_type;
unknown's avatar
unknown committed
3237

unknown's avatar
unknown committed
3238 3239
  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) ||
3240
      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
3241 3242 3243 3244 3245 3246
  {
    DBUG_PRINT("info", ("doesn't support alter"));
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
    DBUG_RETURN(TRUE);
  }
  
unknown's avatar
unknown committed
3247
  thd->proc_info="setup";
3248
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
3249
      !table->s->tmp_table) // no need to touch frm
unknown's avatar
unknown committed
3250 3251
  {
    error=0;
3252
    if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3253
    {
3254 3255 3256 3257 3258 3259
      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))
      {
3260
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
3261
	error= -1;
3262 3263 3264
      }
      else
      {
3265 3266 3267
	*fn_ext(new_name)=0;
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
3268
	  error= -1;
3269 3270 3271 3272 3273 3274 3275
        else if (Table_triggers_list::change_table_name(thd, db, table_name,
                                                        new_db, new_alias))
        {
          VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
                                  table_name));
          error= -1;
        }
3276 3277
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
3278
    }
unknown's avatar
unknown committed
3279

3280
    if (!error)
3281
    {
3282
      switch (alter_info->keys_onoff) {
3283
      case LEAVE_AS_IS:
3284
        break;
3285
      case ENABLE:
3286 3287 3288 3289 3290 3291
        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;
3292
      case DISABLE:
3293 3294 3295 3296 3297 3298
        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;
3299
      }
3300
    }
3301

3302
    if (error == HA_ERR_WRONG_COMMAND)
unknown's avatar
unknown committed
3303 3304
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
3305
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
3306
			  table->alias);
unknown's avatar
unknown committed
3307 3308
      error=0;
    }
unknown's avatar
unknown committed
3309 3310
    if (!error)
    {
3311 3312
      if (mysql_bin_log.is_open())
      {
3313
	thd->clear_error();
unknown's avatar
unknown committed
3314
	Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
3315 3316
	mysql_bin_log.write(&qinfo);
      }
3317 3318
      if (do_send_ok)
        send_ok(thd);
unknown's avatar
unknown committed
3319
    }
3320
    else if (error > 0)
3321 3322
    {
      table->file->print_error(error, MYF(0));
3323
      error= -1;
3324
    }
3325 3326
    table_list->table=0;				// For query cache
    query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3327 3328 3329 3330
    DBUG_RETURN(error);
  }

  /* Full alter table */
3331

3332
  /* Let new create options override the old ones */
3333
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
3334
    create_info->min_rows= table->s->min_rows;
3335
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
3336
    create_info->max_rows= table->s->max_rows;
3337
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
3338
    create_info->avg_row_length= table->s->avg_row_length;
3339
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
3340
    create_info->default_table_charset= table->s->table_charset;
3341

3342
  restore_record(table, s->default_values);     // Empty record for DEFAULT
3343
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
3344
  List_iterator<create_field> def_it(fields);
3345
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
unknown's avatar
unknown committed
3346 3347
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
3348 3349
  create_field *def;

unknown's avatar
unknown committed
3350
  /*
3351
    First collect all fields from table which isn't in drop_list
unknown's avatar
unknown committed
3352 3353 3354 3355 3356
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
3357 3358
    if (field->type() == MYSQL_TYPE_STRING)
      varchar= TRUE;
3359
    /* Check if field should be dropped */
unknown's avatar
unknown committed
3360 3361 3362 3363 3364
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
3365
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
3366 3367 3368
      {
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
3369
	    !(used_fields & HA_CREATE_USED_AUTO))
3370 3371 3372 3373
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
unknown's avatar
unknown committed
3374
	break;
3375
      }
unknown's avatar
unknown committed
3376 3377 3378 3379 3380 3381 3382 3383 3384 3385
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
3386
      if (def->change &&
3387
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
unknown's avatar
unknown committed
3388 3389 3390 3391 3392
	break;
    }
    if (def)
    {						// Field is changed
      def->field=field;
3393 3394 3395 3396 3397
      if (!def->after)
      {
	create_list.push_back(def);
	def_it.remove();
      }
unknown's avatar
unknown committed
3398 3399 3400
    }
    else
    {						// Use old field value
3401
      create_list.push_back(def=new create_field(field,field));
unknown's avatar
unknown committed
3402 3403 3404 3405
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
      {
3406
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
unknown's avatar
unknown committed
3407 3408 3409 3410
	  break;
      }
      if (alter)
      {
3411 3412
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
3413
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
unknown's avatar
unknown committed
3414
	  DBUG_RETURN(TRUE);
3415
	}
3416 3417 3418 3419
	if ((def->def=alter->def))              // Use new default
          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
        else
          def->flags|= NO_DEFAULT_VALUE_FLAG;
unknown's avatar
unknown committed
3420 3421 3422 3423 3424 3425 3426 3427
	alter_it.remove();
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
  while ((def=def_it++))			// Add new columns
  {
3428
    if (def->change && ! def->field)
unknown's avatar
unknown committed
3429
    {
3430
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
unknown's avatar
unknown committed
3431
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442
    }
    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
      {
3443
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
unknown's avatar
unknown committed
3444 3445 3446 3447
	  break;
      }
      if (!find)
      {
3448
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
unknown's avatar
unknown committed
3449
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3450 3451 3452 3453
      }
      find_it.after(def);			// Put element after this
    }
  }
3454
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3455
  {
3456 3457
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
             alter_info->alter_list.head()->name, table_name);
unknown's avatar
unknown committed
3458
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3459 3460 3461
  }
  if (!create_list.elements)
  {
unknown's avatar
unknown committed
3462 3463
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
unknown's avatar
unknown committed
3464
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3465 3466 3467
  }

  /*
3468 3469
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
3470 3471 3472 3473 3474 3475 3476
  */

  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;
3477
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
unknown's avatar
unknown committed
3478
  {
3479
    char *key_name= key_info->name;
unknown's avatar
unknown committed
3480 3481 3482 3483 3484
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
3485
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
unknown's avatar
unknown committed
3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506
	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
3507 3508
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
unknown's avatar
unknown committed
3509 3510
	    break;
	}
3511
	else if (!my_strcasecmp(system_charset_info,
3512
				key_part_name, cfield->field_name))
unknown's avatar
unknown committed
3513
	  break;
unknown's avatar
unknown committed
3514 3515 3516 3517 3518
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
3519 3520 3521 3522 3523 3524 3525
      {
        /*
          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.

3526 3527 3528
          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.

3529 3530 3531 3532 3533
          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
3534 3535
            (cfield->field->field_length == key_part_length &&
             !f_is_blob(key_part->key_type)) ||
3536 3537 3538
	    (cfield->length && (cfield->length < key_part_length /
                                key_part->field->charset()->mbmaxlen)))
	  key_part_length= 0;			// Use whole field
unknown's avatar
unknown committed
3539
      }
3540
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
3541 3542 3543 3544
      key_parts.push_back(new key_part_spec(cfield->field_name,
					    key_part_length));
    }
    if (key_parts.elements)
unknown's avatar
unknown committed
3545
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
3546
				 (key_info->flags & HA_NOSAME ?
3547
				 (!my_strcasecmp(system_charset_info,
3548 3549
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
3550 3551 3552
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
3553
				 key_info->algorithm,
3554
                                 test(key_info->flags & HA_GENERATED_KEY),
3555
				 key_parts));
unknown's avatar
unknown committed
3556 3557 3558 3559
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
3560 3561 3562
    {
      if (key->type != Key::FOREIGN_KEY)
	key_list.push_back(key);
3563 3564 3565 3566
      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
3567
	DBUG_RETURN(TRUE);
3568
      }
3569
    }
unknown's avatar
unknown committed
3570 3571
  }

3572
  if (alter_info->drop_list.elements)
unknown's avatar
unknown committed
3573
  {
3574 3575
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
unknown's avatar
unknown committed
3576 3577
    goto err;
  }
3578
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
3579
  {
3580 3581
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
unknown's avatar
unknown committed
3582 3583 3584
    goto err;
  }

3585
  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
3586 3587
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3588 3589
  /* Safety fix for innodb */
  if (lower_case_table_names)
3590
    my_casedn_str(files_charset_info, tmp_name);
3591 3592 3593 3594
  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
3595
  create_info->db_type=new_db_type;
3596 3597 3598 3599 3600
  if (!create_info->comment.str)
  {
    create_info->comment.str= table->s->comment.str;
    create_info->comment.length= table->s->comment.length;
  }
3601 3602 3603 3604 3605

  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
3606 3607 3608 3609 3610 3611 3612 3613 3614 3615
    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;

3616
  if (table->s->tmp_table)
unknown's avatar
unknown committed
3617 3618
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

3619 3620
  /*
    better have a negative test here, instead of positive, like
unknown's avatar
unknown committed
3621
    alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
3622
    so that ALTER TABLE won't break when somebody will add new flag
3623 3624 3625 3626 3627

    MySQL uses frm version to determine the type of the data fields and
    their layout. See Field_string::type() for details.
    Thus, if the table is too old we may have to rebuild the data to
    update the layout.
3628 3629 3630 3631 3632 3633 3634 3635

    There was a bug prior to mysql-4.0.25. Number of null fields was
    calculated incorrectly. As a result frm and data files gets out of
    sync after fast alter table. There is no way to determine by which
    mysql version (in 4.0 and 4.1 branches) table was created, thus we
    disable fast alter table for all tables created by mysql versions
    prior to 5.0 branch.
    See BUG#6236.
3636
  */
unknown's avatar
unknown committed
3637 3638 3639 3640
  need_copy_table= (alter_info->flags &
                    ~(ALTER_CHANGE_COLUMN_DEFAULT|ALTER_OPTIONS) ||
                    (create_info->used_fields &
                     ~(HA_CREATE_USED_COMMENT|HA_CREATE_USED_PASSWORD)) ||
3641
                    table->s->tmp_table ||
3642
                    !table->s->mysql_version ||
3643
                    (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar));
3644 3645
  create_info->frm_only= !need_copy_table;

3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689
  /*
    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);
    }
  }
3690 3691
  else
    create_info->data_file_name=create_info->index_file_name=0;
unknown's avatar
unknown committed
3692 3693

  /* We don't log the statement, it will be logged later. */
3694
  {
unknown's avatar
unknown committed
3695 3696 3697 3698 3699
    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)
3700 3701
      DBUG_RETURN(error);
  }
3702
  if (need_copy_table)
unknown's avatar
unknown committed
3703
  {
3704
    if (table->s->tmp_table)
3705 3706 3707 3708
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
3709
      tbl.table_name= tbl.alias= tmp_name;
3710 3711
      new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725
    }
    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
3726 3727
  }

3728
  /* We don't want update TIMESTAMP fields during ALTER TABLE. */
3729
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
3730 3731
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3732
  next_insert_id=thd->next_insert_id;		// Remember for logging
unknown's avatar
unknown committed
3733
  copied=deleted=0;
3734
  if (new_table && !new_table->s->is_view)
3735
  {
unknown's avatar
unknown committed
3736
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
3737
    new_table->next_number_field=new_table->found_next_number_field;
3738
    error=copy_data_between_tables(table, new_table, create_list, ignore,
3739
				   order_num, order, &copied, &deleted);
3740
  }
unknown's avatar
unknown committed
3741
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3742
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
3743

3744
  if (table->s->tmp_table)
unknown's avatar
unknown committed
3745 3746 3747 3748
  {
    /* We changed a temporary table */
    if (error)
    {
unknown's avatar
unknown committed
3749 3750 3751
      /*
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3752
      */
unknown's avatar
unknown committed
3753 3754 3755
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3756 3757 3758 3759 3760 3761
    /* 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
3762
    /* Remove link to old table and rename the new one */
3763
    close_temporary_table(thd, table->s->db, table_name);
3764 3765
    /* 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
3766 3767 3768 3769 3770
    {						// Fatal error
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3771 3772
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
3773
      thd->clear_error();
unknown's avatar
unknown committed
3774
      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
3775 3776
      mysql_bin_log.write(&qinfo);
    }
unknown's avatar
unknown committed
3777 3778 3779
    goto end_temporary;
  }

3780 3781 3782 3783 3784
  if (new_table)
  {
    intern_close_table(new_table);              /* close temporary table */
    my_free((gptr) new_table,MYF(0));
  }
unknown's avatar
unknown committed
3785 3786 3787 3788 3789 3790 3791
  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;
  }
3792

unknown's avatar
unknown committed
3793
  /*
3794 3795
    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
3796
    from the cache, free all locks, close the old table and remove it.
unknown's avatar
unknown committed
3797 3798 3799
  */

  thd->proc_info="rename result table";
3800 3801
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3802 3803
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3804
  if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3805 3806 3807 3808
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
3809
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
unknown's avatar
unknown committed
3810 3811 3812 3813 3814 3815
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

unknown's avatar
unknown committed
3816 3817 3818 3819 3820
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3821
      Win32 and InnoDB can't drop a table that is in use, so we must
3822
      close the original table at before doing the rename
unknown's avatar
unknown committed
3823
    */
3824
    close_cached_table(thd, table);
unknown's avatar
unknown committed
3825
    table=0;					// Marker that table is closed
3826
    no_table_reopen= TRUE;
unknown's avatar
unknown committed
3827
  }
unknown's avatar
unknown committed
3828 3829 3830
#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
3831 3832
#endif

unknown's avatar
unknown committed
3833

unknown's avatar
unknown committed
3834
  error=0;
3835 3836
  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
3837 3838 3839 3840 3841 3842
  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,
3843 3844 3845 3846 3847
			      new_alias) ||
           (new_name != table_name || new_db != db) && // we also do rename
           Table_triggers_list::change_table_name(thd, db, table_name,
                                                  new_db, new_alias))
       
unknown's avatar
unknown committed
3848 3849
  {						// Try to get everything back
    error=1;
unknown's avatar
unknown committed
3850
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
unknown's avatar
unknown committed
3851
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
unknown's avatar
unknown committed
3852
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
unknown's avatar
unknown committed
3853 3854 3855
  }
  if (error)
  {
unknown's avatar
unknown committed
3856 3857 3858 3859
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
3860 3861
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3862 3863 3864
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
3865
  if (thd->lock || new_name != table_name || no_table_reopen)  // True if WIN32
unknown's avatar
unknown committed
3866
  {
unknown's avatar
unknown committed
3867 3868 3869 3870
    /*
      Not table locking or alter table with rename
      free locks and remove old table
    */
3871 3872
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3873 3874 3875 3876
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
unknown's avatar
unknown committed
3877 3878 3879 3880 3881
    /*
      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
3882 3883 3884
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
unknown's avatar
unknown committed
3885
      /* Mark in-use copies old */
unknown's avatar
unknown committed
3886
      remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
unknown's avatar
unknown committed
3887 3888
      /* end threads waiting on lock */
      mysql_lock_abort(thd,table);
unknown's avatar
unknown committed
3889 3890 3891 3892 3893
    }
    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
3894 3895
      if (table)
	close_cached_table(thd,table);		// Remove lock for table
unknown's avatar
unknown committed
3896 3897 3898 3899
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
unknown's avatar
unknown committed
3900
  /* The ALTER TABLE is always in its own transaction */
3901 3902 3903 3904
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
unknown's avatar
unknown committed
3905 3906
  {
    VOID(pthread_mutex_unlock(&LOCK_open));
3907
    broadcast_refresh();
unknown's avatar
unknown committed
3908 3909 3910
    goto err;
  }
  thd->proc_info="end";
3911
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
3912
  {
unknown's avatar
unknown committed
3913
    thd->clear_error();
unknown's avatar
unknown committed
3914
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
unknown's avatar
unknown committed
3915 3916
    mysql_bin_log.write(&qinfo);
  }
3917
  broadcast_refresh();
unknown's avatar
unknown committed
3918
  VOID(pthread_mutex_unlock(&LOCK_open));
3919 3920 3921
#ifdef HAVE_BERKELEY_DB
  if (old_db_type == DB_TYPE_BERKELEY_DB)
  {
unknown's avatar
unknown committed
3922 3923 3924 3925 3926
    /*
      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.
    */
3927
    char path[FN_REFLEN];
3928
    build_table_path(path, sizeof(path), new_db, table_name, "");
3929 3930
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
unknown's avatar
unknown committed
3931
    {
3932 3933
      intern_close_table(table);
      my_free((char*) table, MYF(0));
unknown's avatar
unknown committed
3934
    }
3935
    else
unknown's avatar
unknown committed
3936 3937
      sql_print_warning("Could not open BDB table %s.%s after rename\n",
                        new_db,table_name);
3938
    (void) berkeley_flush_logs();
3939 3940
  }
#endif
unknown's avatar
unknown committed
3941
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
3942
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3943 3944

end_temporary:
3945 3946 3947
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3948 3949
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
unknown's avatar
unknown committed
3950
  thd->some_tables_deleted=0;
unknown's avatar
unknown committed
3951
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
3952 3953

 err:
unknown's avatar
unknown committed
3954
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3955 3956 3957 3958
}


static int
unknown's avatar
unknown committed
3959
copy_data_between_tables(TABLE *from,TABLE *to,
3960
			 List<create_field> &create,
3961
                         bool ignore,
3962
			 uint order_num, ORDER *order,
unknown's avatar
unknown committed
3963
			 ha_rows *copied,
3964
			 ha_rows *deleted)
unknown's avatar
unknown committed
3965 3966 3967 3968 3969
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
unknown's avatar
unknown committed
3970 3971 3972 3973 3974 3975
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3976
  ha_rows examined_rows;
3977
  bool auto_increment_field_copied= 0;
3978
  ulong save_sql_mode;
unknown's avatar
unknown committed
3979 3980
  DBUG_ENTER("copy_data_between_tables");

3981 3982 3983 3984 3985 3986
  /*
    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
  */
3987
  error= ha_enable_transaction(thd, FALSE);
3988 3989
  if (error)
    DBUG_RETURN(-1);
3990
  
3991
  if (!(copy= new Copy_field[to->s->fields]))
unknown's avatar
unknown committed
3992 3993
    DBUG_RETURN(-1);				/* purecov: inspected */

3994 3995
  if (to->file->external_lock(thd, F_WRLCK))
    DBUG_RETURN(-1);
3996 3997 3998 3999 4000 4001 4002

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

4003
  from->file->info(HA_STATUS_VARIABLE);
4004
  to->file->start_bulk_insert(from->file->records);
unknown's avatar
unknown committed
4005

4006 4007
  save_sql_mode= thd->variables.sql_mode;

unknown's avatar
unknown committed
4008 4009 4010 4011 4012 4013 4014
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
4015 4016
    {
      if (*ptr == to->next_number_field)
4017
      {
4018
        auto_increment_field_copied= TRUE;
4019 4020 4021 4022 4023 4024 4025 4026 4027
        /*
          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
4028
      (copy_end++)->set(*ptr,def->field,0);
4029 4030
    }

unknown's avatar
unknown committed
4031 4032
  }

4033 4034
  found_count=delete_count=0;

unknown's avatar
unknown committed
4035 4036
  if (order)
  {
unknown's avatar
unknown committed
4037
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
4038
					      MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
4039
    bzero((char*) &tables,sizeof(tables));
4040 4041 4042
    tables.table= from;
    tables.alias= tables.table_name= (char*) from->s->table_name;
    tables.db=    (char*) from->s->db;
unknown's avatar
unknown committed
4043 4044
    error=1;

unknown's avatar
unknown committed
4045
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
unknown's avatar
unknown committed
4046
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
4047
		    &tables, fields, all_fields, order) ||
4048 4049 4050
	!(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
4051 4052
					     &examined_rows)) ==
	HA_POS_ERROR)
unknown's avatar
unknown committed
4053 4054 4055
      goto err;
  };

4056 4057 4058 4059 4060
  /*
    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
  */
4061
  from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
4062
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
4063
  if (ignore)
unknown's avatar
unknown committed
4064
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
4065
  thd->row_count= 0;
4066
  restore_record(to, s->default_values);        // Create empty record
unknown's avatar
unknown committed
4067 4068 4069 4070
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
4071
      thd->send_kill_message();
unknown's avatar
unknown committed
4072 4073 4074
      error= 1;
      break;
    }
4075
    thd->row_count++;
4076 4077
    if (to->next_number_field)
    {
4078
      if (auto_increment_field_copied)
4079
        to->auto_increment_field_not_null= TRUE;
4080 4081 4082
      else
        to->next_number_field->reset();
    }
4083
    
unknown's avatar
unknown committed
4084
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
4085
    {
unknown's avatar
unknown committed
4086
      copy_ptr->do_copy(copy_ptr);
4087
    }
unknown's avatar
unknown committed
4088 4089
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
4090
      if (!ignore ||
unknown's avatar
unknown committed
4091 4092 4093 4094 4095 4096
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
      {
	to->file->print_error(error,MYF(0));
	break;
      }
4097
      to->file->restore_auto_increment();
unknown's avatar
unknown committed
4098 4099 4100
      delete_count++;
    }
    else
4101
      found_count++;
unknown's avatar
unknown committed
4102 4103
  }
  end_read_record(&info);
4104
  free_io_cache(from);
4105
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
4106

4107
  if (to->file->end_bulk_insert() && error <= 0)
unknown's avatar
unknown committed
4108
  {
unknown's avatar
unknown committed
4109
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
4110 4111
    error=1;
  }
unknown's avatar
unknown committed
4112
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
4113

4114
  ha_enable_transaction(thd,TRUE);
4115

4116 4117 4118 4119 4120 4121 4122 4123
  /*
    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;
4124

unknown's avatar
unknown committed
4125
 err:
4126
  thd->variables.sql_mode= save_sql_mode;
4127
  thd->abort_on_warning= 0;
unknown's avatar
unknown committed
4128
  free_io_cache(from);
unknown's avatar
unknown committed
4129 4130
  *copied= found_count;
  *deleted=delete_count;
4131 4132
  if (to->file->external_lock(thd,F_UNLCK))
    error=1;
unknown's avatar
unknown committed
4133 4134
  DBUG_RETURN(error > 0 ? -1 : 0);
}
4135

unknown's avatar
unknown committed
4136

4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148
/*
  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
4149 4150
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                          bool do_send_ok)
4151 4152 4153 4154 4155 4156 4157 4158 4159 4160
{
  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
4161
  create_info.row_type=ROW_TYPE_NOT_USED;
4162
  create_info.default_table_charset=default_charset_info;
unknown's avatar
unknown committed
4163 4164
  /* Force alter table to recreate table */
  lex->alter_info.flags= ALTER_CHANGE_COLUMN;
4165 4166 4167
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
4168
                                0, &lex->alter_info, do_send_ok));
4169 4170 4171
}


unknown's avatar
unknown committed
4172
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
4173 4174 4175 4176 4177
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
4178
  DBUG_ENTER("mysql_checksum_table");
4179 4180 4181 4182 4183

  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;
4184 4185
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
4186
    DBUG_RETURN(TRUE);
4187

unknown's avatar
VIEW  
unknown committed
4188
  for (table= tables; table; table= table->next_local)
4189 4190
  {
    char table_name[NAME_LEN*2+2];
4191
    TABLE *t;
4192

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

4195
    t= table->table= open_ltable(thd, table, TL_READ);
unknown's avatar
unknown committed
4196
    thd->clear_error();			// these errors shouldn't get client
4197 4198 4199 4200

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

4201
    if (!t)
4202
    {
unknown's avatar
unknown committed
4203
      /* Table didn't exist */
4204
      protocol->store_null();
unknown's avatar
unknown committed
4205
      thd->clear_error();
4206 4207 4208
    }
    else
    {
4209
      t->pos_in_table_list= table;
4210

4211
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
4212 4213
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
4214
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
unknown's avatar
unknown committed
4215
	       (check_opt->flags & T_QUICK))
4216
	protocol->store_null();
4217 4218
      else
      {
4219 4220
	/* calculating table's checksum */
	ha_checksum crc= 0;
unknown's avatar
unknown committed
4221
        uchar null_mask=256 -  (1 << t->s->last_null_bit_pos);
4222 4223 4224 4225 4226 4227

	/* 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
4228
	if (t->file->ha_rnd_init(1))
4229 4230 4231
	  protocol->store_null();
	else
	{
4232
	  for (;;)
4233 4234
	  {
	    ha_checksum row_crc= 0;
4235 4236 4237 4238 4239 4240 4241
            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
4242 4243 4244 4245
	    if (t->s->null_bytes)
            {
              /* fix undefined null bits */
              t->record[0][t->s->null_bytes-1] |= null_mask;
unknown's avatar
unknown committed
4246 4247 4248
              if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
                t->record[0][0] |= 1;

unknown's avatar
unknown committed
4249 4250
	      row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
            }
4251

4252
	    for (uint i= 0; i < t->s->fields; i++ )
4253 4254
	    {
	      Field *f= t->field[i];
4255 4256
	      if ((f->type() == FIELD_TYPE_BLOB) ||
                  (f->type() == MYSQL_TYPE_VARCHAR))
4257 4258 4259 4260 4261 4262 4263
	      {
		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
4264
				     f->pack_length());
4265
	    }
4266

4267 4268 4269
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
4270
          t->file->ha_rnd_end();
4271
	}
4272
      }
unknown's avatar
unknown committed
4273
      thd->clear_error();
4274 4275 4276 4277 4278 4279 4280 4281
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
unknown's avatar
unknown committed
4282
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
4283

4284 4285 4286 4287
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
unknown's avatar
unknown committed
4288
  DBUG_RETURN(TRUE);
4289
}
4290 4291 4292 4293 4294

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
4295
  bool no_substitution=
4296
        test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
unknown's avatar
unknown committed
4297
  if ((*new_engine=
4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310
       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;
}