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

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

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

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

/* drop and alter of tables */

#include "mysql_priv.h"
20
#ifdef HAVE_BERKELEY_DB
21
#include "ha_berkeley.h"
22
#endif
23
#include <hash.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
24
#include <myisam.h>
25
#include <my_dir.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
26 27 28 29 30

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

serg@serg.mylan's avatar
serg@serg.mylan committed
31
const char *primary_key_name="PRIMARY";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
32 33 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,
				    List<create_field> &create,
				    enum enum_duplicates handle_duplicates,
38
                                    bool ignore,
39
				    uint order_num, ORDER *order,
40
				    ha_rows *copied,ha_rows *deleted);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
/*
 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
60 61
    FALSE OK.  In this case ok packet is sent to user
    TRUE  Error
62 63

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

65 66
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
                    my_bool drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
67
{
68
  bool error= FALSE, need_start_waiters= FALSE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
69 70 71 72 73 74 75 76
  DBUG_ENTER("mysql_rm_table");

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

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

77
  if (!drop_temporary)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
78
  {
79
    if ((error= wait_if_global_read_lock(thd, 0, 1)))
80
    {
81
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
82 83
      goto err;
    }
84 85
    else
      need_start_waiters= TRUE;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
86
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
87
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
88

89
err:
90 91 92 93 94 95 96
  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);

97 98 99
  if (need_start_waiters)
    start_waiting_global_read_lock(thd);

100
  if (error)
101
    DBUG_RETURN(TRUE);
102
  send_ok(thd);
103
  DBUG_RETURN(FALSE);
104 105
}

106 107 108 109 110

/*
 delete (drop) tables.

  SYNOPSIS
111 112 113 114 115
    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
116
                        generate warnings if the handler files doesn't exists
117 118 119 120 121 122 123 124 125 126

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

127 128
int mysql_rm_table_part2_with_lock(THD *thd,
				   TABLE_LIST *tables, bool if_exists,
129
				   bool drop_temporary, bool dont_log_query)
130 131 132 133 134 135
{
  int error;
  thd->mysys_var->current_mutex= &LOCK_open;
  thd->mysys_var->current_cond= &COND_refresh;
  VOID(pthread_mutex_lock(&LOCK_open));

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
136 137
  error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
			      dont_log_query);
138 139 140 141 142 143 144 145 146 147

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

148

149
/*
150 151 152 153 154 155 156 157 158
  Execute the drop of a normal or temporary table

  SYNOPSIS
    mysql_rm_table_part2()
    thd			Thread handler
    tables		Tables to drop
    if_exists		If set, don't give an error if table doesn't exists.
			In this case we give an warning of level 'NOTE'
    drop_temporary	Only drop temporary tables
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
159
    drop_view		Allow to delete VIEW .frm
160 161
    dont_log_query	Don't write query to log files. This will also not
			generate warnings if the handler files doesn't exists  
162

163 164 165 166 167 168 169 170 171
  TODO:
    When logging to the binary log, we should log
    tmp_tables and transactional tables as separate statements if we
    are in a transaction;  This is needed to get these tables into the
    cached binary log that is only written on COMMIT.

   The current code only writes DROP statements that only uses temporary
   tables to the cache binary log.  This should be ok on most cases, but
   not all.
172 173 174 175 176

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

int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
180 181
			 bool drop_temporary, bool drop_view,
			 bool dont_log_query)
182 183
{
  TABLE_LIST *table;
184
  char	path[FN_REFLEN], *alias;
185 186
  String wrong_tables;
  int error;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
187
  bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
188 189
  DBUG_ENTER("mysql_rm_table_part2");

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
196
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197
  {
198
    char *db=table->db;
199
    mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL);
200
    if (!close_temporary_table(thd, db, table->table_name))
201
    {
202
      tmp_table_deleted=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
203
      continue;					// removed temporary table
204
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
205 206

    error=0;
207
    if (!drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
208
    {
209
      abort_locked_tables(thd,db,table->table_name);
monty@mysql.com's avatar
monty@mysql.com committed
210 211
      while (remove_table_from_cache(thd, db, table->table_name, 0) &&
             !thd->killed)
212 213 214 215 216
      {
	dropping_tables++;
	(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
	dropping_tables--;
      }
217
      drop_locked_tables(thd,db,table->table_name);
218
      if (thd->killed)
219 220
      {
        thd->no_warnings_for_error= 0;
221
	DBUG_RETURN(-1);
222
      }
223
      alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
224
      /* remove form file and isam files */
monty@mysql.com's avatar
monty@mysql.com committed
225
      strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS);
226
      (void) unpack_filename(path,path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
227
    }
monty@mysql.com's avatar
monty@mysql.com committed
228
    if (drop_temporary ||
229 230
        (access(path,F_OK) &&
         ha_create_table_from_engine(thd,db,alias,TRUE)) ||
monty@mysql.com's avatar
monty@mysql.com committed
231
        (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
232
    {
233
      if (if_exists)
234 235
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
236
			    table->table_name);
237
      else
238
	error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
239 240 241
    }
    else
    {
242
      char *end;
monty@mysql.com's avatar
monty@mysql.com committed
243
      db_type table_type= get_table_type(path);
244
      *(end=fn_ext(path))=0;			// Remove extension for delete
245 246
      error= ha_delete_table(thd, table_type, path, table->table_name,
                             !dont_log_query);
247 248
      if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && if_exists)
	error= 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
249
      if (error == HA_ERR_ROW_IS_REFERENCED)
monty@mysql.com's avatar
monty@mysql.com committed
250 251
      {
	/* the table is referenced by a foreign key constraint */
252
	foreign_key_error=1;
monty@mysql.com's avatar
monty@mysql.com committed
253
      }
254
      if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
255
      {
256
        int new_error;
257 258
	/* Delete the table definition file */
	strmov(end,reg_ext);
259
	if (!(new_error=my_delete(path,MYF(MY_WME))))
260
        {
261
	  some_tables_deleted=1;
262 263 264 265 266 267 268 269 270 271 272
          /*
            Destroy triggers for this table if there are any.

            We won't need this as soon as we will have new .FRM format,
            in which we will store trigger definitions in the same .FRM
            files as table descriptions.
          */
          strmov(end, triggers_file_ext);
          if (!access(path, F_OK))
            new_error= my_delete(path, MYF(MY_WME));
        }
273
        error|= new_error;
274
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
275 276 277 278 279
    }
    if (error)
    {
      if (wrong_tables.length())
	wrong_tables.append(',');
280
      wrong_tables.append(String(table->table_name,system_charset_info));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
281 282
    }
  }
283
  thd->tmp_table_used= tmp_table_deleted;
284 285 286 287
  error= 0;
  if (wrong_tables.length())
  {
    if (!foreign_key_error)
288
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
289
                      wrong_tables.c_ptr());
290
    else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
291
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
292 293 294 295
    error= 1;
  }

  if (some_tables_deleted || tmp_table_deleted || !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
296
  {
297
    query_cache_invalidate3(thd, tables, 0);
298
    if (!dont_log_query && mysql_bin_log.is_open())
299
    {
monty@mysql.com's avatar
monty@mysql.com committed
300 301
      if (!error)
        thd->clear_error();
302
      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
303
      mysql_bin_log.write(&qinfo);
304
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
305
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
306

monty@mysql.com's avatar
monty@mysql.com committed
307
  unlock_table_names(thd, tables, (TABLE_LIST*) 0);
308
  thd->no_warnings_for_error= 0;
309
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
310 311 312 313 314 315 316 317
}


int quick_rm_table(enum db_type base,const char *db,
		   const char *table_name)
{
  char path[FN_REFLEN];
  int error=0;
318 319
  my_snprintf(path, sizeof(path), "%s/%s/%s%s",
	      mysql_data_home, db, table_name, reg_ext);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
320 321 322
  unpack_filename(path,path);
  if (my_delete(path,MYF(0)))
    error=1; /* purecov: inspected */
323
  my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home, db, table_name);
324
  unpack_filename(path,path);
325
  return ha_delete_table(current_thd, base, path, table_name, 0) || error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
326 327
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
/*
  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;
346
    if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
347 348
    {
      /* Sort NOT NULL keys before other keys */
349
      return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
350 351 352 353 354 355 356 357 358 359 360 361 362
    }
    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;
  }
363
  /*
364
    Prefer original key order.	usable_key_parts contains here
365 366 367 368 369
    the original key position.
  */
  return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
	  (a->usable_key_parts > b->usable_key_parts) ? 1 :
	  0);
370 371
}

372 373
/*
  Check TYPELIB (set or enum) for duplicates
374

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

  DESCRIPTION
382
    This function prints an warning for each value in list
383 384 385 386 387 388 389
    which has some duplicates on its right

  RETURN VALUES
    void
*/

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

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

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


447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
/*
  Prepare a create_table instance for packing

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

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

  RETURN VALUES
   0	ok
   1	Error
*/

int prepare_create_field(create_field *sql_field, 
monty@mysql.com's avatar
monty@mysql.com committed
467 468
			 uint *blob_columns, 
			 int *timestamps, int *timestamps_with_niladic,
469 470 471
			 uint table_flags)
{
  DBUG_ENTER("prepare_field");
monty@mysql.com's avatar
monty@mysql.com committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490

  /*
    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;
491
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
492 493
    break;
  case FIELD_TYPE_GEOMETRY:
494
#ifdef HAVE_SPATIAL
monty@mysql.com's avatar
monty@mysql.com committed
495 496 497 498
    if (!(table_flags & HA_CAN_GEOMETRY))
    {
      my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
                      MYF(0), "GEOMETRY");
499
      DBUG_RETURN(1);
monty@mysql.com's avatar
monty@mysql.com committed
500 501 502 503 504 505 506 507
    }
    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;
508
    (*blob_columns)++;
monty@mysql.com's avatar
monty@mysql.com committed
509 510 511 512 513
    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);
514
#endif /*HAVE_SPATIAL*/
monty@mysql.com's avatar
monty@mysql.com committed
515
  case MYSQL_TYPE_VARCHAR:
516
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
monty@mysql.com's avatar
monty@mysql.com committed
517 518 519 520 521 522 523 524
    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)
525
      {
monty@mysql.com's avatar
monty@mysql.com committed
526 527
        my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
                        MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
528 529
        DBUG_RETURN(1);
      }
monty@mysql.com's avatar
monty@mysql.com committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
    }
#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:
566
    sql_field->pack_flag|= FIELDFLAG_NUMBER;
monty@mysql.com's avatar
monty@mysql.com committed
567 568 569 570 571 572 573 574 575 576 577 578 579
    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)
    {
580
      if (!*timestamps)
581
      {
monty@mysql.com's avatar
monty@mysql.com committed
582
        sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
583
        (*timestamps_with_niladic)++;
584
      }
monty@mysql.com's avatar
monty@mysql.com committed
585 586 587 588
      else
        sql_field->unireg_check= Field::NONE;
    }
    else if (sql_field->unireg_check != Field::NONE)
589
      (*timestamps_with_niladic)++;
monty@mysql.com's avatar
monty@mysql.com committed
590

591
    (*timestamps)++;
monty@mysql.com's avatar
monty@mysql.com committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
    /* 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;
607 608 609
  DBUG_RETURN(0);
}

610
/*
611
  Preparation for table creation
612 613

  SYNOPSIS
614
    mysql_prepare_table()
615 616 617 618 619
    thd			Thread object
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create

620
  DESCRIPTION
621
    Prepares the table and key structures for table creation.
622

623
  NOTES
624
    sets create_info->varchar if the table has a varchar
625

626 627 628 629
  RETURN VALUES
    0	ok
    -1	error
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
630

631 632 633 634 635 636
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
                               List<create_field> *fields,
                               List<Key> *keys, bool tmp_table,
                               uint *db_options,
                               handler *file, KEY **key_info_buffer,
                               uint *key_count, int select_field_count)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
637
{
638
  const char	*key_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
639
  create_field	*sql_field,*dup_field;
640
  uint		field,null_fields,blob_columns;
641
  uint		max_key_length= file->max_key_length();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
642
  ulong		pos;
643
  KEY		*key_info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
644
  KEY_PART_INFO *key_part_info;
645 646 647
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
648
  List_iterator<create_field> it(*fields),it2(*fields);
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
649
  uint total_uneven_bit_length= 0;
650
  DBUG_ENTER("mysql_prepare_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
651

652
  select_field_pos= fields->elements - select_field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
653
  null_fields=blob_columns=0;
654
  create_info->varchar= 0;
655

656
  for (field_no=0; (sql_field=it++) ; field_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
657
  {
658 659
    CHARSET_INFO *save_cs;

660
    if (!sql_field->charset)
661 662 663
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
664 665 666
      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.
667
    */
668
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
669
      sql_field->charset= create_info->table_charset;
670

671
    save_cs= sql_field->charset;
672 673 674 675 676
    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];
677
      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4), "_bin", 4);
678 679 680
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
681

682 683
    if (sql_field->sql_type == FIELD_TYPE_SET ||
        sql_field->sql_type == FIELD_TYPE_ENUM)
684 685 686
    {
      uint32 dummy;
      CHARSET_INFO *cs= sql_field->charset;
687
      TYPELIB *interval= sql_field->interval;
688 689 690 691 692 693

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
694
      if (!interval)
695
      {
696 697 698 699
        interval= sql_field->interval= typelib(sql_field->interval_list);
        List_iterator<String> it(sql_field->interval_list);
        String conv, *tmp;
        for (uint i= 0; (tmp= it++); i++)
700
        {
701 702 703 704 705 706 707 708 709 710 711
          if (String::needs_conversion(tmp->length(), tmp->charset(),
                                       cs, &dummy))
          {
            uint cnv_errs;
            conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
            char *buf= (char*) sql_alloc(conv.length()+1);
            memcpy(buf, conv.ptr(), conv.length());
            buf[conv.length()]= '\0';
            interval->type_names[i]= buf;
            interval->type_lengths[i]= conv.length();
          }
712

713 714 715 716 717
          // Strip trailing spaces.
          uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                            interval->type_lengths[i]);
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
718
        }
719
        sql_field->interval_list.empty(); // Don't need interval_list anymore
720 721 722 723 724 725 726 727 728 729 730 731 732 733
      }

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

      if (sql_field->sql_type == FIELD_TYPE_SET)
      {
734
        uint32 field_length;
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
        if (sql_field->def)
        {
          char *not_used;
          uint not_used2;
          bool not_found= 0;
          String str, *def= sql_field->def->val_str(&str);
          def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
          (void) find_set(interval, def->ptr(), def->length(),
                          cs, &not_used, &not_used2, &not_found);
          if (not_found)
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
750 751
        calculate_interval_lengths(cs, interval, &dummy, &field_length);
        sql_field->length= field_length + (interval->count - 1);
752 753 754
      }
      else  /* FIELD_TYPE_ENUM */
      {
755
        uint32 field_length;
756 757 758 759 760 761 762 763 764 765
        if (sql_field->def)
        {
          String str, *def= sql_field->def->val_str(&str);
          def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
          if (!find_type2(interval, def->ptr(), def->length(), cs))
          {
            my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
            DBUG_RETURN(-1);
          }
        }
766 767
        calculate_interval_lengths(cs, interval, &field_length, &dummy);
        sql_field->length= field_length;
768 769 770 771
      }
      set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
    }

772 773 774 775 776 777 778 779
    if (sql_field->sql_type == FIELD_TYPE_BIT)
    { 
      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;
    }

780
    sql_field->create_length_to_internal_length();
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
    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];

      if (sql_field->def)
      {
        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,
              "VARCHAR",
              (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;                     // Probably from an item
    }
812

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

816 817
    if (check_column_name(sql_field->field_name))
    {
818
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
819 820
      DBUG_RETURN(-1);
    }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
821

822 823
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
824
    {
825
      if (my_strcasecmp(system_charset_info,
826 827
			sql_field->field_name,
			dup_field->field_name) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
828
      {
829 830 831 832
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
833 834
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
835
	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
836 837 838 839
	  DBUG_RETURN(-1);
	}
	else
	{
840
	  /* Field redefined */
841
	  sql_field->sql_type=		dup_field->sql_type;
842 843 844
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
845
	  sql_field->length=		dup_field->length;
846
	  sql_field->pack_length=	dup_field->pack_length;
847
          sql_field->key_length=	dup_field->key_length;
848
	  sql_field->create_length_to_internal_length();
849 850 851 852 853 854
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->flags=		dup_field->flags;
	  sql_field->unireg_check=	dup_field->unireg_check;
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
855
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
856 857
      }
    }
858 859 860 861
    /* 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)
862
      (*db_options)|= HA_OPTION_PACK_RECORD;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
863 864
    it2.rewind();
  }
865
  /* If fixed row records, we need one bit to check for deleted rows */
866
  if (!((*db_options) & HA_OPTION_PACK_RECORD))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
867
    null_fields++;
ram@gw.mysql.r18.ru's avatar
ram@gw.mysql.r18.ru committed
868
  pos= (null_fields + total_uneven_bit_length + 7) / 8;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
869 870 871 872

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

monty@mysql.com's avatar
monty@mysql.com committed
875 876
    if (prepare_create_field(sql_field, &blob_columns, 
			     &timestamps, &timestamps_with_niladic,
877
			     file->table_flags()))
hf@deer.(none)'s avatar
hf@deer.(none) committed
878
      DBUG_RETURN(-1);
879
    if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
880
      create_info->varchar= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
881 882 883 884 885
    sql_field->offset= pos;
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
    pos+=sql_field->pack_length;
  }
886 887
  if (timestamps_with_niladic > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
888 889
    my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
               ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
890 891
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
892 893
  if (auto_increment > 1)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
894
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
895 896 897
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
898
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
899
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
900 901
    my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
               ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
902 903 904
    DBUG_RETURN(-1);
  }

905
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
906
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
907 908
    my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
               MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
909 910 911 912
    DBUG_RETURN(-1);
  }

  /* Create keys */
913

914
  List_iterator<Key> key_iterator(*keys), key_iterator2(*keys);
915
  uint key_parts=0, fk_key_count=0;
916
  bool primary_key=0,unique_key=0;
917
  Key *key, *key2;
918
  uint tmp, key_number;
919 920
  /* special marker for keys to be ignored */
  static char ignore_key[1];
921

922
  /* Calculate number of key segements */
923
  *key_count= 0;
924

bk@work.mysql.com's avatar
bk@work.mysql.com committed
925 926
  while ((key=key_iterator++))
  {
927 928 929 930 931 932 933
    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)
      {
934 935 936
        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));
937 938 939 940
	DBUG_RETURN(-1);
      }
      continue;
    }
941
    (*key_count)++;
942
    tmp=file->max_key_parts();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
943 944 945 946 947
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
948
    if (key->name && strlen(key->name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
949
    {
950
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
951 952
      DBUG_RETURN(-1);
    }
953
    key_iterator2.rewind ();
954
    if (key->type != Key::FOREIGN_KEY)
955
    {
956
      while ((key2 = key_iterator2++) != key)
957
      {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
958
	/*
959 960 961
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
962
        */
963 964 965
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
966
        {
967
          /* TODO: issue warning message */
968 969 970 971 972 973 974
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
975 976 977
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
978 979 980
          }
          break;
        }
981 982 983 984 985 986
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
987 988 989 990 991 992
    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);
    }
993
  }
994
  tmp=file->max_keys();
995
  if (*key_count > tmp)
996 997 998 999
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
1000

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

1006
  key_iterator.rewind();
1007
  key_number=0;
1008
  for (; (key=key_iterator++) ; key_number++)
1009 1010 1011 1012
  {
    uint key_length=0;
    key_part_spec *column;

1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

1023
    switch(key->type){
1024
    case Key::MULTIPLE:
1025
	key_info->flags= 0;
1026
	break;
1027
    case Key::FULLTEXT:
1028
	key_info->flags= HA_FULLTEXT;
1029
	break;
1030
    case Key::SPATIAL:
hf@deer.(none)'s avatar
hf@deer.(none) committed
1031
#ifdef HAVE_SPATIAL
1032
	key_info->flags= HA_SPATIAL;
1033
	break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
1034
#else
1035 1036
	my_error(ER_FEATURE_DISABLED, MYF(0),
                 sym_group_geom.name, sym_group_geom.needed_define);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1037 1038
	DBUG_RETURN(-1);
#endif
1039 1040 1041 1042
    case Key::FOREIGN_KEY:
      key_number--;				// Skip this key
      continue;
    default:
1043 1044
      key_info->flags = HA_NOSAME;
      break;
1045
    }
1046 1047
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
1048

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1049 1050
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
1051
    key_info->usable_key_parts= key_number;
1052
    key_info->algorithm=key->algorithm;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1053

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

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

1098
    List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
1099
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1100 1101
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
1102
      uint length;
1103 1104
      key_part_spec *dup_column;

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

	if (f_is_blob(sql_field->pack_flag))
	{
1157
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
1158
	  {
1159
	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
1160 1161 1162 1163
	    DBUG_RETURN(-1);
	  }
	  if (!column->length)
	  {
1164
	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
1165 1166 1167
	    DBUG_RETURN(-1);
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
1168
#ifdef HAVE_SPATIAL
1169
	if (key->type == Key::SPATIAL)
1170
	{
1171
	  if (!column->length)
1172 1173
	  {
	    /*
1174 1175
              4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
              Lately we'll extend this code to support more dimensions
1176
	    */
1177
	    column->length= 4*sizeof(double);
1178 1179
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
1180
#endif
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
1191
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
1192
	  {
1193
	    my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
1194 1195 1196 1197
	    DBUG_RETURN(-1);
	  }
	  if (key->type == Key::SPATIAL)
	  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1198 1199
	    my_message(ER_SPATIAL_CANT_HAVE_NULL,
                       ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
1200 1201 1202 1203 1204 1205 1206 1207
	    DBUG_RETURN(-1);
	  }
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
	  if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY))
	    auto_increment--;			// Field is used
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1208
      }
1209

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1210 1211 1212
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
1213 1214
      length= sql_field->key_length;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1215 1216 1217 1218
      if (column->length)
      {
	if (f_is_blob(sql_field->pack_flag))
	{
1219 1220
	  if ((length=column->length) > file->max_key_length() ||
	      length > file->max_key_part_length())
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
	  {
	    length=min(file->max_key_length(), file->max_key_part_length());
	    if (key->type == Key::MULTIPLE)
	    {
	      /* not a critical problem */
	      char warn_buff[MYSQL_ERRMSG_SIZE];
	      my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
			  length);
	      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			   ER_TOO_LONG_KEY, warn_buff);
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
	      DBUG_RETURN(-1);
	    }
	  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1238
	}
1239
	else if (!f_is_geom(sql_field->pack_flag) &&
1240 1241 1242 1243 1244 1245
		  (column->length > length ||
		   ((f_is_packed(sql_field->pack_flag) ||
		     ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1246
	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
1247 1248 1249 1250
	  DBUG_RETURN(-1);
	}
	else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
	  length=column->length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1251 1252 1253
      }
      else if (length == 0)
      {
1254
	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1255 1256
	  DBUG_RETURN(-1);
      }
1257 1258
      if (length > file->max_key_part_length())
      {
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
	length=file->max_key_part_length();
	if (key->type == Key::MULTIPLE)
	{
	  /* not a critical problem */
	  char warn_buff[MYSQL_ERRMSG_SIZE];
	  my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
		      length);
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
		       ER_TOO_LONG_KEY, warn_buff);
	}
	else
	{
	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
	  DBUG_RETURN(-1);
	}
1274 1275
      }
      key_part_info->length=(uint16) length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1276
      /* Use packed keys for long strings on the first column */
1277
      if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1278
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
1279 1280
	   (sql_field->sql_type == MYSQL_TYPE_STRING ||
	    sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1281 1282
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
      {
1283 1284 1285
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB) ||
            sql_field->sql_type == MYSQL_TYPE_VARCHAR)
	  key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
	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)
1296 1297 1298
	{
	  if (primary_key)
	  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1299 1300
	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                       MYF(0));
1301 1302 1303 1304 1305
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
1306
	else if (!(key_name = key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1307
	  key_name=make_unique_key_name(sql_field->field_name,
1308 1309
					*key_info_buffer, key_info);
	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1310
	{
1311
	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1312 1313 1314 1315 1316
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
      }
    }
1317 1318
    if (!key_info->name || check_column_name(key_info->name))
    {
1319
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
1320 1321
      DBUG_RETURN(-1);
    }
1322 1323
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1324
    key_info->key_length=(uint16) key_length;
1325
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1326
    {
1327
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1328 1329
      DBUG_RETURN(-1);
    }
1330
    key_info++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1331
  }
1332
  if (!unique_key && !primary_key &&
1333
      (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
1334
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1335
    my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
1336 1337
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1338 1339
  if (auto_increment > 0)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1340
    my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1341 1342
    DBUG_RETURN(-1);
  }
1343
  /* Sort keys in optimized order */
1344
  qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
1345
	(qsort_cmp) sort_keys);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1346

1347 1348 1349
  DBUG_RETURN(0);
}

1350

1351 1352 1353 1354 1355
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1356 1357 1358 1359 1360 1361
    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
1362
    internal_tmp_table  Set to 1 if this is an internal temporary table
1363
			(From ALTER TABLE)
1364 1365

  DESCRIPTION
1366
    If one creates a temporary table, this is automatically opened
1367 1368 1369 1370 1371 1372 1373

    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
1374 1375
    FALSE OK
    TRUE  error
1376 1377
*/

1378 1379 1380
bool mysql_create_table(THD *thd,const char *db, const char *table_name,
                        HA_CREATE_INFO *create_info,
                        List<create_field> &fields,
1381
                        List<Key> &keys,bool internal_tmp_table,
1382
                        uint select_field_count)
1383
{
1384 1385 1386 1387 1388
  char		path[FN_REFLEN];
  const char	*alias;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
1389
  bool		error= TRUE;
1390
  enum db_type	new_db_type;
1391 1392 1393 1394 1395
  DBUG_ENTER("mysql_create_table");

  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1396 1397
    my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
               MYF(0));
1398
    DBUG_RETURN(TRUE);
1399 1400 1401 1402 1403 1404
  }
  if ((new_db_type= ha_checktype(create_info->db_type)) !=
      create_info->db_type)
  {
    create_info->db_type= new_db_type;
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
1405 1406 1407 1408
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			table_name);
1409
  }
1410
  db_options= create_info->table_options;
1411 1412 1413 1414 1415
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
  file=get_new_handler((TABLE*) 0, create_info->db_type);

1416 1417 1418 1419 1420 1421 1422 1423
#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
  */
1424 1425 1426
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      (file->table_flags() & HA_NO_TEMP_TABLES))
  {
1427
    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
1428
    DBUG_RETURN(TRUE);
1429
  }
1430
#endif
1431

1432 1433 1434 1435 1436 1437 1438 1439 1440 1441
  /*
    If the table character set was not given explicitely,
    let's fetch the database default character set and
    apply it to the table.
  */
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
    uint length;
    char  path[FN_REFLEN];
1442
    strxmov(path, mysql_data_home, "/", db, NullS);
1443 1444 1445 1446 1447 1448
    length= unpack_dirname(path,path);             // Convert if not unix
    strmov(path+length, MY_DB_OPT_FILE);
    load_db_opt(thd, path, &db_info);
    create_info->default_table_charset= db_info.default_table_charset;
  }

1449 1450 1451
  if (mysql_prepare_table(thd, create_info, &fields,
			  &keys, internal_tmp_table, &db_options, file,
			  &key_info_buffer, &key_count,
1452
			  select_field_count))
1453
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1454 1455 1456 1457

      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1458 1459 1460
    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);
1461 1462
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1463 1464 1465
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
1466 1467
    my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home, db,
		alias, reg_ext);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1468 1469 1470 1471 1472
  unpack_filename(path,path);
  /* Check if table already exists */
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
      && find_temporary_table(thd,db,table_name))
  {
1473
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1474 1475
    {
      create_info->table_existed= 1;		// Mark that table existed
1476
      DBUG_RETURN(FALSE);
1477
    }
monty@mysql.com's avatar
monty@mysql.com committed
1478
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
1479
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1480
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
1481
  if (wait_if_global_read_lock(thd, 0, 1))
1482
    DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1483
  VOID(pthread_mutex_lock(&LOCK_open));
1484
  if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1485 1486 1487 1488
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1489 1490
      {
	create_info->table_existed= 1;		// Mark that table existed
1491
	error= FALSE;
1492
      }
1493
      else
1494
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
1495
      goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1496 1497 1498
    }
  }

1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511
  /*
    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;
1512 1513
    if (!ha_create_table_from_engine(thd, db, table_name,
				     create_if_not_exists))
1514 1515 1516 1517 1518 1519
    {
      DBUG_PRINT("info", ("Table already existed in handler"));

      if (create_if_not_exists)
      {
       create_info->table_existed= 1;   // Mark that table existed
1520
       error= FALSE;
1521
      }
1522
      else
1523
       my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
1524
      goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1525 1526 1527 1528
    }
  }

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1531
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1532
    create_info->data_file_name= create_info->index_file_name= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1533
  create_info->table_options=db_options;
1534

1535
  if (rea_create_table(thd, path, create_info, fields, key_count,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
		       key_info_buffer))
  {
    /* my_error(ER_CANT_CREATE_TABLE,MYF(0),table_name,my_errno); */
    goto end;
  }
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    /* Open table and put in temporary table list */
    if (!(open_temporary_table(thd, path, db, table_name, 1)))
    {
      (void) rm_temporary_table(create_info->db_type, path);
      goto end;
    }
1549
    thd->tmp_table_used= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1550
  }
1551
  if (!internal_tmp_table && mysql_bin_log.is_open())
1552
  {
pem@mysql.com's avatar
pem@mysql.com committed
1553
    thd->clear_error();
1554
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
1555
    mysql_bin_log.write(&qinfo);
1556
  }
1557
  error= FALSE;
monty@mysql.com's avatar
monty@mysql.com committed
1558

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1559 1560
end:
  VOID(pthread_mutex_unlock(&LOCK_open));
1561
  start_waiting_global_read_lock(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573
  thd->proc_info="After create";
  DBUG_RETURN(error);
}

/*
** Give the key name after the first field with an optional '_#' after
**/

static bool
check_if_keyname_exists(const char *name, KEY *start, KEY *end)
{
  for (KEY *key=start ; key != end ; key++)
1574
    if (!my_strcasecmp(system_charset_info,name,key->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584
      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;

1585 1586
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1587
    return (char*) field_name;			// Use fieldname
1588 1589 1590 1591 1592 1593
  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
  */
1594
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1595
  {
1596 1597
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1598 1599 1600
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1601
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1602 1603
}

1604

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1605 1606 1607 1608 1609
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1610
			       TABLE_LIST *create_table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1611 1612 1613 1614 1615 1616
			       List<create_field> *extra_fields,
			       List<Key> *keys,
			       List<Item> *items,
			       MYSQL_LOCK **lock)
{
  TABLE tmp_table;		// Used during 'create_field()'
1617
  TABLE *table= 0;
1618
  uint select_field_count= items->elements;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1619
  /* Add selected items to field list */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
1620
  List_iterator_fast<Item> it(*items);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1621 1622
  Item *item;
  Field *tmp_field;
1623 1624 1625 1626 1627 1628 1629 1630
  DBUG_ENTER("create_table_from_items");

  tmp_table.alias= 0;
  tmp_table.s= &tmp_table.share_not_to_be_used;
  tmp_table.s->db_create_options=0;
  tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
  tmp_table.s->db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
                                       create_info->db_type == DB_TYPE_HEAP);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1631 1632 1633 1634 1635
  tmp_table.null_row=tmp_table.maybe_null=0;

  while ((item=it++))
  {
    create_field *cr_field;
1636 1637 1638 1639 1640
    Field *field;
    if (item->type() == Item::FUNC_ITEM)
      field=item->tmp_table_field(&tmp_table);
    else
      field=create_tmp_field(thd, &tmp_table, item, item->type(),
monty@mysql.com's avatar
monty@mysql.com committed
1641
                             (Item ***) 0, &tmp_field,0,0,0);
1642 1643
    if (!field ||
	!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
1644 1645
					   ((Item_field *)item)->field :
					   (Field*) 0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1646
      DBUG_RETURN(0);
1647 1648
    if (item->maybe_null)
      cr_field->flags &= ~NOT_NULL_FLAG;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1649 1650
    extra_fields->push_back(cr_field);
  }
1651
  /*
monty@mysql.com's avatar
monty@mysql.com committed
1652 1653
    create and lock table

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

1656 1657 1658 1659
    If this is a HEAP table, the automatic DELETE FROM which is written to the
    binlog when a HEAP table is opened for the first time since startup, must
    not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we
    don't want to delete from it) 2) it would be written before the CREATE
1660 1661
    TABLE, which is a wrong order. So we keep binary logging disabled when we
    open_table().
monty@mysql.com's avatar
monty@mysql.com committed
1662
    TODO: create and open should be done atomic !
1663
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1664
  {
monty@mysql.com's avatar
monty@mysql.com committed
1665
    tmp_disable_binlog(thd);
1666
    if (!mysql_create_table(thd, create_table->db, create_table->table_name,
monty@mysql.com's avatar
monty@mysql.com committed
1667 1668 1669
                            create_info, *extra_fields, *keys, 0,
                            select_field_count))
    {
monty@mysql.com's avatar
monty@mysql.com committed
1670
      if (!(table= open_table(thd, create_table, thd->mem_root, (bool*) 0)))
monty@mysql.com's avatar
monty@mysql.com committed
1671
        quick_rm_table(create_info->db_type, create_table->db,
1672
                       table_case_name(create_info, create_table->table_name));
monty@mysql.com's avatar
monty@mysql.com committed
1673 1674 1675 1676
    }
    reenable_binlog(thd);
    if (!table)                                   // open failed
      DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1677
  }
monty@mysql.com's avatar
monty@mysql.com committed
1678

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1679
  table->reginfo.lock_type=TL_WRITE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1680
  if (!((*lock)= mysql_lock_tables(thd, &table,1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1681
  {
1682
    VOID(pthread_mutex_lock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1683
    hash_delete(&open_cache,(byte*) table);
1684
    VOID(pthread_mutex_unlock(&LOCK_open));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1685
    quick_rm_table(create_info->db_type, create_table->db,
1686
		   table_case_name(create_info, create_table->table_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
    DBUG_RETURN(0);
  }
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  DBUG_RETURN(table);
}


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

1698
bool
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1699 1700
mysql_rename_table(enum db_type base,
		   const char *old_db,
1701
		   const char *old_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1702
		   const char *new_db,
1703
		   const char *new_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1704
{
1705 1706
  char from[FN_REFLEN], to[FN_REFLEN];
  char tmp_from[NAME_LEN+1], tmp_to[NAME_LEN+1];
1707
  handler *file=(base == DB_TYPE_UNKNOWN ? 0 : get_new_handler((TABLE*) 0, base));
1708
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1709
  DBUG_ENTER("mysql_rename_table");
1710

1711 1712
  if (lower_case_table_names == 2 && file &&
      !(file->table_flags() & HA_FILE_BASED))
1713 1714 1715
  {
    /* Table handler expects to get all file names as lower case */
    strmov(tmp_from, old_name);
1716
    my_casedn_str(files_charset_info, tmp_from);
1717 1718 1719
    old_name= tmp_from;

    strmov(tmp_to, new_name);
1720
    my_casedn_str(files_charset_info, tmp_to);
1721 1722
    new_name= tmp_to;
  }
1723 1724 1725 1726
  my_snprintf(from, sizeof(from), "%s/%s/%s",
	      mysql_data_home, old_db, old_name);
  my_snprintf(to, sizeof(to), "%s/%s/%s",
	      mysql_data_home, new_db, new_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1727 1728
  fn_format(from,from,"","",4);
  fn_format(to,to,    "","",4);
1729

1730 1731
  if (!file ||
      !(error=file->rename_table((const char*) from,(const char *) to)))
1732 1733 1734
  {
    if (rename_file_ext(from,to,reg_ext))
    {
1735
      error=my_errno;
1736
      /* Restore old file name */
1737 1738
      if (file)
        file->rename_table((const char*) to,(const char *) from);
1739 1740
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1741
  delete file;
1742 1743 1744
  if (error)
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1745 1746
}

1747

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1748
/*
1749 1750 1751 1752 1753 1754
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
    thd			Thread handler
    table		Table to remove from cache
1755
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
1756
			HA_EXTRA_FORCE_REOPEN if table is not be used
1757 1758 1759 1760 1761 1762 1763
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1764 1765
*/

1766 1767
static void wait_while_table_is_used(THD *thd,TABLE *table,
				     enum ha_extra_function function)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1768
{
1769
  DBUG_PRINT("enter",("table: %s", table->s->table_name));
1770 1771
  DBUG_ENTER("wait_while_table_is_used");
  safe_mutex_assert_owner(&LOCK_open);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1772

1773
  VOID(table->file->extra(function));
1774 1775 1776 1777
  /* 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 */
monty@mysql.com's avatar
monty@mysql.com committed
1778
  while (remove_table_from_cache(thd, table->s->db, table->s->table_name, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1779
  {
1780 1781 1782
    dropping_tables++;
    (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
    dropping_tables--;
1783 1784 1785
  }
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1786

1787 1788
/*
  Close a cached table
1789

1790
  SYNOPSIS
1791
    close_cached_table()
1792 1793 1794 1795 1796 1797
    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.
1798

1799 1800 1801 1802
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
1803

1804
void close_cached_table(THD *thd, TABLE *table)
1805 1806
{
  DBUG_ENTER("close_cached_table");
1807

1808
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
1809 1810
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
1811
  {
1812 1813
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;			// Start locked threads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1814
  }
1815 1816 1817 1818 1819
  /* Close all copies of 'table'.  This also frees all LOCK TABLES lock */
  thd->open_tables=unlink_open_table(thd,thd->open_tables,table);

  /* When lock on LOCK_open is freed other threads can continue */
  pthread_cond_broadcast(&COND_refresh);
monty@mysql.com's avatar
monty@mysql.com committed
1820
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1821 1822
}

1823
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
1824
			     const char* operator_name, const char* errmsg)
1825

1826
{
1827 1828
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
1829 1830 1831 1832
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
  protocol->store("error", 5, system_charset_info);
  protocol->store(errmsg, system_charset_info);
1833
  thd->clear_error();
1834
  if (protocol->write())
1835 1836 1837 1838
    return -1;
  return 1;
}

1839

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1840
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1841
			       HA_CHECK_OPT *check_opt)
1842
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1843
  DBUG_ENTER("prepare_for_restore");
1844

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1845 1846 1847 1848 1849 1850
  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"
				  ));
  }
1851
  else
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1852
  {
1853
    char* backup_dir= thd->lex->backup_dir;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1854
    char src_path[FN_REFLEN], dst_path[FN_REFLEN];
1855
    char* table_name = table->table_name;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1856
    char* db = thd->db ? thd->db : table->db;
1857

1858 1859
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
					reg_ext))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1860
      DBUG_RETURN(-1); // protect buffer overflow
1861

1862
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1863
		mysql_real_data_home, db, table_name);
1864

1865
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1866
      DBUG_RETURN(-1);
1867

1868
    if (my_copy(src_path,
1869 1870
		fn_format(dst_path, dst_path,"", reg_ext, 4),
		MYF(MY_WME)))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1871
    {
1872
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1873
      unlock_table_name(thd, table);
1874
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1875 1876 1877
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed copying .frm file"));
    }
1878
    if (mysql_truncate(thd, table, 1))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1879
    {
1880
      pthread_mutex_lock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1881
      unlock_table_name(thd, table);
1882
      pthread_mutex_unlock(&LOCK_open);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1883 1884
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
				    "Failed generating table from .frm file"));
1885
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1886
  }
1887

1888 1889 1890 1891
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
1892
  if (!(table->table = reopen_name_locked_table(thd, table)))
1893 1894
  {
    pthread_mutex_lock(&LOCK_open);
1895
    unlock_table_name(thd, table);
1896 1897
    pthread_mutex_unlock(&LOCK_open);
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1898
  DBUG_RETURN(0);
1899
}
1900

1901

1902
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
1903
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1904
{
1905 1906
  int error= 0;
  TABLE tmp_table, *table;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1907 1908 1909 1910
  DBUG_ENTER("prepare_for_repair");

  if (!(check_opt->sql_flags & TT_USEFRM))
    DBUG_RETURN(0);
1911 1912

  if (!(table= table_list->table))		/* if open_ltable failed */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1913
  {
1914 1915
    char name[FN_REFLEN];
    strxmov(name, mysql_data_home, "/", table_list->db, "/",
1916
	    table_list->table_name, NullS);
1917
    if (openfrm(thd, name, "", 0, 0, 0, &tmp_table))
1918 1919
      DBUG_RETURN(0);				// Can't open frm file
    table= &tmp_table;
1920
  }
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1921

1922 1923 1924 1925 1926 1927 1928 1929 1930
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1931

1932 1933 1934
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext= table->file->bas_ext();
  MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1935

1936 1937 1938 1939 1940 1941
  /*
    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
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1942

1943
  strxmov(from, table->s->path, ext[1], NullS);	// Name of data file
1944 1945
  if (!my_stat(from, &stat_info, MYF(0)))
    goto end;				// Can't use USE_FRM flag
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1946

1947 1948
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
1949

1950 1951 1952 1953 1954 1955 1956
  /* 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);
  }
1957
  if (lock_and_wait_for_table_name(thd,table_list))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1958
  {
1959 1960
    error= -1;
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1961
  }
1962
  if (my_rename(from, tmp, MYF(MY_WME)))
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1963
  {
1964
    pthread_mutex_lock(&LOCK_open);
1965
    unlock_table_name(thd, table_list);
1966
    pthread_mutex_unlock(&LOCK_open);
1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed renaming data file");
    goto end;
  }
  if (mysql_truncate(thd, table_list, 1))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed generating table from .frm file");
    goto end;
  }
  if (my_rename(tmp, from, MYF(MY_WME)))
  {
    pthread_mutex_lock(&LOCK_open);
    unlock_table_name(thd, table_list);
    pthread_mutex_unlock(&LOCK_open);
    error= send_check_errmsg(thd, table_list, "repair",
			     "Failed restoring .MYD file");
    goto end;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1988 1989
  }

1990 1991 1992 1993
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
1994
  if (!(table_list->table = reopen_name_locked_table(thd, table_list)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1995 1996
  {
    pthread_mutex_lock(&LOCK_open);
1997
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1998 1999
    pthread_mutex_unlock(&LOCK_open);
  }
2000 2001 2002 2003 2004

end:
  if (table == &tmp_table)
    closefrm(table);				// Free allocated memory
  DBUG_RETURN(error);
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2005
}
2006

2007

2008 2009
/*
  RETURN VALUES
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2010 2011 2012
    FALSE Message sent to net (admin operation went ok)
    TRUE  Message should be sent by caller 
          (admin operation or network communication failed)
2013
*/
2014 2015 2016 2017 2018
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,
2019
                              bool no_warnings_for_error,
2020 2021 2022
                              uint extra_open_options,
                              int (*prepare_func)(THD *, TABLE_LIST *,
                                                  HA_CHECK_OPT *),
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2023 2024 2025
                              int (handler::*operator_func)(THD *,
                                                            HA_CHECK_OPT *),
                              int (view_operator_func)(THD *, TABLE_LIST*))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2026
{
2027
  TABLE_LIST *table, *next_global_table;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2028
  List<Item> field_list;
2029 2030
  Item *item;
  Protocol *protocol= thd->protocol;
2031
  int result_code;
2032
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2033 2034 2035 2036 2037 2038 2039 2040 2041

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

2046
  mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2047
  for (table= tables; table; table= table->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2048 2049
  {
    char table_name[NAME_LEN*2+2];
2050
    char* db = table->db;
2051
    bool fatal_error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2052

serg@serg.mylan's avatar
serg@serg.mylan committed
2053
    strxmov(table_name, db, ".", table->table_name, NullS);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2054
    thd->open_options|= extra_open_options;
2055 2056 2057 2058
    table->lock_type= lock_type;
    /* open only one table from local list of command */
    next_global_table= table->next_global;
    table->next_global= 0;
2059
    thd->no_warnings_for_error= no_warnings_for_error;
2060
    open_and_lock_tables(thd, table);
2061
    thd->no_warnings_for_error= 0;
2062 2063
    table->next_global= next_global_table;
    /* if view are unsupported */
2064
    if (table->view && view_operator_func == NULL)
2065 2066 2067 2068
    {
      result_code= HA_ADMIN_NOT_IMPLEMENTED;
      goto send_result;
    }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2069
    thd->open_options&= ~extra_open_options;
2070

2071
    if (prepare_func)
2072
    {
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2073
      switch ((*prepare_func)(thd, table, check_opt)) {
2074 2075 2076 2077 2078 2079 2080
      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
        ;
2081
      }
2082
    }
2083

2084
    /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2085 2086 2087 2088 2089 2090
      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)
2091
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2092 2093
    if (!table->table)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2094
      char buf[ERRMSGSIZE+ERRMSGSIZE+2];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2095
      const char *err_msg;
2096
      protocol->prepare_for_resend();
2097 2098 2099
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error",5, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2100 2101
      if (!(err_msg=thd->net.last_error))
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
2102 2103 2104 2105
      /* if it was a view will check md5 sum */
      if (table->view &&
          view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2106
        strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
2107 2108
        err_msg= (const char *)buf;
      }
2109
      protocol->store(err_msg, system_charset_info);
2110
      thd->clear_error();
2111
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2112 2113 2114
	goto err;
      continue;
    }
2115 2116 2117 2118 2119 2120 2121

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

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2122
    table->table->pos_in_table_list= table;
2123
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2124
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2125
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
2126
      protocol->prepare_for_resend();
2127 2128 2129
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error", 5, system_charset_info);
2130
      my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name);
2131
      protocol->store(buff, system_charset_info);
2132
      close_thread_tables(thd);
2133
      table->table=0;				// For query cache
2134
      if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2135 2136 2137 2138
	goto err;
      continue;
    }

2139
    /* Close all instances of the table to allow repair to rename files */
2140
    if (lock_type == TL_WRITE && table->table->s->version)
2141 2142
    {
      pthread_mutex_lock(&LOCK_open);
2143 2144
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
					      "Waiting to get writelock");
2145
      mysql_lock_abort(thd,table->table);
2146
      while (remove_table_from_cache(thd, table->table->s->db,
monty@mysql.com's avatar
monty@mysql.com committed
2147
				     table->table->s->table_name, 0) &&
2148 2149 2150 2151 2152 2153
	     ! thd->killed)
      {
	dropping_tables++;
	(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
	dropping_tables--;
      }
2154
      thd->exit_cond(old_message);
2155 2156
      if (thd->killed)
	goto err;
2157 2158 2159
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
2160 2161
    }

2162 2163 2164 2165
    result_code = (table->table->file->*operator_func)(thd, check_opt);

send_result:

2166
    thd->clear_error();  // these errors shouldn't get client
2167
    protocol->prepare_for_resend();
2168 2169
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2170

2171 2172 2173
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
2174 2175
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
2176
      {
2177 2178
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
2179
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
2180
	protocol->store("note", 4, system_charset_info);
2181
	protocol->store(buf, length, system_charset_info);
2182
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2183 2184
      break;

2185
    case HA_ADMIN_OK:
2186 2187
      protocol->store("status", 6, system_charset_info);
      protocol->store("OK",2, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2188 2189
      break;

2190
    case HA_ADMIN_FAILED:
2191 2192
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation failed",16, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2193 2194
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2195 2196 2197
    case HA_ADMIN_REJECT:
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation need committed state",30, system_charset_info);
monty@mysql.com's avatar
monty@mysql.com committed
2198
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2199 2200
      break;

2201
    case HA_ADMIN_ALREADY_DONE:
2202 2203
      protocol->store("status", 6, system_charset_info);
      protocol->store("Table is already up to date", 27, system_charset_info);
2204 2205
      break;

2206
    case HA_ADMIN_CORRUPT:
2207
      protocol->store("error", 5, system_charset_info);
2208
      protocol->store("Corrupt", 7, system_charset_info);
2209
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2210 2211
      break;

2212
    case HA_ADMIN_INVALID:
2213 2214
      protocol->store("error", 5, system_charset_info);
      protocol->store("Invalid argument",16, system_charset_info);
2215 2216
      break;

2217 2218 2219 2220 2221 2222 2223 2224
    case HA_ADMIN_TRY_ALTER:
    {
      /*
        This is currently used only by InnoDB. ha_innobase::optimize() answers
        "try with alter", so here we close the table, do an ALTER TABLE,
        reopen the table and do ha_innobase::analyze() on it.
      */
      close_thread_tables(thd);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2225 2226 2227
      TABLE_LIST *save_next_local= table->next_local,
                 *save_next_global= table->next_global;
      table->next_local= table->next_global= 0;
2228
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
2229
      result_code= mysql_recreate_table(thd, table, 0);
2230
      reenable_binlog(thd);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2231
      close_thread_tables(thd);
2232 2233 2234 2235 2236 2237 2238
      if (!result_code) // recreation went ok
      {
        if ((table->table= open_ltable(thd, table, lock_type)) &&
            ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
          result_code= 0; // analyze went ok
      }
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2239 2240
      table->next_local= save_next_local;
      table->next_global= save_next_global;
2241 2242
      goto send_result_message;
    }
2243 2244 2245
    case HA_ADMIN_WRONG_CHECKSUM:
    {
      protocol->store("note", 4, system_charset_info);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2246 2247
      protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
                      system_charset_info);
2248 2249
      break;
    }
2250

2251
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
2252 2253 2254
      protocol->store("error", 5, system_charset_info);
      protocol->store("Unknown - internal error during operation", 41
		      , system_charset_info);
2255
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2256 2257
      break;
    }
2258
    if (fatal_error)
2259
      table->table->s->version=0;               // Force close of table
2260
    else if (open_for_modify)
2261
    {
2262
      pthread_mutex_lock(&LOCK_open);
2263
      remove_table_from_cache(thd, table->table->s->db,
monty@mysql.com's avatar
monty@mysql.com committed
2264
			      table->table->s->table_name, 0);
2265
      pthread_mutex_unlock(&LOCK_open);
2266 2267 2268
      /* May be something modified consequently we have to invalidate cache */
      query_cache_invalidate3(thd, table->table, 0);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2269
    close_thread_tables(thd);
2270
    table->table=0;				// For query cache
2271
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2272 2273 2274
      goto err;
  }

2275
  send_eof(thd);
2276
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2277
 err:
2278
  close_thread_tables(thd);			// Shouldn't be needed
2279 2280
  if (table)
    table->table=0;
2281
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2282 2283
}

2284

2285
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
2286 2287 2288
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2289
				"backup", TL_READ, 0, 0, 0, 0,
2290
				&handler::backup, 0));
2291
}
2292

2293

2294
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
2295 2296 2297
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2298
				"restore", TL_WRITE, 1, 1, 0,
2299
				&prepare_for_restore,
2300
				&handler::restore, 0));
2301
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2302

2303

2304
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2305 2306 2307
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2308 2309 2310
				"repair", TL_WRITE, 1,
                                test(check_opt->sql_flags & TT_USEFRM),
                                HA_OPEN_FOR_REPAIR,
2311
				&prepare_for_repair,
2312
				&handler::repair, 0));
2313 2314
}

2315

2316
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2317 2318 2319
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2320
				"optimize", TL_WRITE, 1,0,0,0,
2321
				&handler::optimize, 0));
2322 2323 2324
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2325 2326 2327 2328 2329
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
2330 2331
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2332 2333

  RETURN VALUES
2334 2335
   FALSE ok
   TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2336 2337
*/

2338
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
2339
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2340
{
2341
  HA_CHECK_OPT check_opt;
2342
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2343
  DBUG_ENTER("mysql_assign_to_keycache");
2344 2345 2346 2347 2348 2349 2350

  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);
2351
    DBUG_RETURN(TRUE);
2352 2353 2354 2355
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
2356
				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
2357
				0, 0, &handler::assign_to_keycache, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2358 2359
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2360 2361 2362 2363 2364 2365

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

  SYNOPSIS
    reassign_keycache_tables()
2366 2367 2368
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2369

2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
  NOTES
    This is called when one sets a key cache size to zero, in which
    case we have to move the tables associated to this key cache to
    the "default" one.

    One has to ensure that one never calls this function while
    some other thread is changing the key cache. This is assured by
    the caller setting src_cache->in_init before calling this function.

    We don't delete the old key cache as there may still be pointers pointing
    to it for a while after this function returns.

 RETURN VALUES
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2383 2384 2385
    0	  ok
*/

2386 2387
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2388 2389 2390
{
  DBUG_ENTER("reassign_keycache_tables");

2391 2392
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
2393
  src_cache->param_buff_size= 0;		// Free key cache
2394 2395
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2396
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2397 2398 2399
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2400 2401 2402 2403 2404
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2405 2406
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2407 2408

  RETURN VALUES
2409 2410
    FALSE ok
    TRUE  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2411 2412
*/

2413
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2414 2415 2416
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2417
				"preload_keys", TL_READ, 0, 0, 0, 0,
2418
				&handler::preload_keys, 0));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2419 2420 2421
}


venu@myvenu.com's avatar
venu@myvenu.com committed
2422 2423 2424 2425 2426
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2427 2428
    thd		Thread object
    table	Table list (one table only)
venu@myvenu.com's avatar
venu@myvenu.com committed
2429 2430 2431 2432
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
2433 2434
    FALSE OK
    TRUE  error
venu@myvenu.com's avatar
venu@myvenu.com committed
2435 2436
*/

2437 2438 2439
bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
                             HA_CREATE_INFO *create_info,
                             Table_ident *table_ident)
venu@myvenu.com's avatar
venu@myvenu.com committed
2440 2441 2442 2443
{
  TABLE **tmp_table;
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char *db= table->db;
2444
  char *table_name= table->table_name;
venu@myvenu.com's avatar
venu@myvenu.com committed
2445 2446
  char *src_db= thd->db;
  char *src_table= table_ident->table.str;
2447 2448
  int  err;
  bool res= TRUE;
2449
  TABLE_LIST src_tables_list;
venu@myvenu.com's avatar
venu@myvenu.com committed
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459
  DBUG_ENTER("mysql_create_like_table");

  /*
    Validate the source table
  */
  if (table_ident->table.length > NAME_LEN ||
      (table_ident->table.length &&
       check_table_name(src_table,table_ident->table.length)) ||
      table_ident->db.str && check_db_name((src_db= table_ident->db.str)))
  {
2460
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
2461
    DBUG_RETURN(TRUE);
venu@myvenu.com's avatar
venu@myvenu.com committed
2462
  }
2463

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2464
  bzero((gptr)&src_tables_list, sizeof(src_tables_list));
2465
  src_tables_list.db= table_ident->db.str ? table_ident->db.str : thd->db;
2466
  src_tables_list.table_name= table_ident->table.str;
2467

2468 2469
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
2470 2471

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
2472
    strxmov(src_path, (*tmp_table)->s->path, reg_ext, NullS);
venu@myvenu.com's avatar
venu@myvenu.com committed
2473 2474
  else
  {
2475 2476 2477 2478
    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));
venu@myvenu.com's avatar
venu@myvenu.com committed
2479 2480 2481
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2482
      goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
2483 2484 2485 2486 2487 2488
    }
  }

  /*
    Validate the destination table

2489
    skip the destination table name checking as this is already
venu@myvenu.com's avatar
venu@myvenu.com committed
2490 2491 2492 2493 2494 2495
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2496 2497 2498
    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);
2499 2500
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
2501 2502 2503 2504
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2505 2506 2507
    strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
	    reg_ext, NullS);
    fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME));
venu@myvenu.com's avatar
venu@myvenu.com committed
2508 2509 2510 2511
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2512
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
2513
    Create a new table by copying from source table
2514
  */
2515 2516
  if (my_copy(src_path, dst_path, MYF(MY_WME|MY_DONT_OVERWRITE_FILE)))
    goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
2517 2518

  /*
2519 2520
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
venu@myvenu.com's avatar
venu@myvenu.com committed
2521 2522
    and temporary tables).
  */
2523
  *fn_ext(dst_path)= 0;
venu@myvenu.com's avatar
venu@myvenu.com committed
2524
  err= ha_create_table(dst_path, create_info, 1);
2525

venu@myvenu.com's avatar
venu@myvenu.com committed
2526 2527 2528 2529
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2530 2531
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2532
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
2533 2534 2535 2536
    }
  }
  else if (err)
  {
2537 2538 2539
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
2540
  }
2541 2542 2543

  // Must be written before unlock
  if (mysql_bin_log.is_open())
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2544
  {
2545
    thd->clear_error();
2546
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
2547
    mysql_bin_log.write(&qinfo);
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2548
  }
2549
  res= FALSE;
2550
  goto err;
2551

venu@myvenu.com's avatar
venu@myvenu.com committed
2552 2553 2554 2555
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2556 2557
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
2558
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2559
		 ER_TABLE_EXISTS_ERROR,warn_buff);
2560
    res= FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
2561
  }
2562 2563 2564 2565 2566 2567 2568 2569
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
  pthread_mutex_lock(&LOCK_open);
  unlock_table_name(thd, &src_tables_list);
  pthread_mutex_unlock(&LOCK_open);
  DBUG_RETURN(res);
venu@myvenu.com's avatar
venu@myvenu.com committed
2570 2571 2572
}


2573
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
2574
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2575 2576 2577 2578 2579 2580
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2581 2582
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2583
				"analyze", lock_type, 1, 0, 0, 0,
2584
				&handler::analyze, 0));
2585 2586 2587
}


2588
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
2589
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2590 2591 2592 2593 2594 2595
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2596 2597
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2598
				"check", lock_type,
2599
				0, HA_OPEN_FOR_REPAIR, 0, 0,
2600
				&handler::check, &view_checksum));
2601 2602
}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2604
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
2605 2606 2607 2608
static int
mysql_discard_or_import_tablespace(THD *thd,
                                   TABLE_LIST *table_list,
                                   enum tablespace_op_type tablespace_op)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2609 2610 2611 2612 2613 2614
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
2615 2616 2617 2618
  /*
    Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
    ALTER TABLE
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2619 2620 2621

  thd->proc_info="discard_or_import_tablespace";

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

monty@mysql.com's avatar
monty@mysql.com committed
2624 2625 2626 2627 2628
 /*
   We set this flag so that ha_innobase::open and ::external_lock() do
   not complain when we lock the table
 */
  thd->tablespace_op= TRUE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2629 2630 2631 2632 2633
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2634

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2635 2636 2637 2638 2639 2640 2641
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

monty@mysql.com's avatar
monty@mysql.com committed
2642 2643 2644 2645
  /*
    The 0 in the call below means 'not in a transaction', which means
    immediate invalidation; that is probably what we wish here
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2646 2647 2648 2649 2650 2651 2652 2653 2654 2655
  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())
  {
2656
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2657 2658 2659 2660
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2661
  thd->tablespace_op=FALSE;
monty@mysql.com's avatar
monty@mysql.com committed
2662 2663
  if (error == 0)
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2664
    send_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
2665
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2666
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2667 2668 2669 2670 2671

  if (error == HA_ERR_ROW_IS_REFERENCED)
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
  
  DBUG_RETURN(-1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2672
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2673

2674 2675

#ifdef NOT_USED
2676 2677 2678 2679 2680 2681 2682
/*
  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.
*/

2683
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2684 2685 2686 2687 2688
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2689 2690 2691 2692 2693 2694 2695 2696
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2697 2698 2699
  DBUG_ENTER("mysql_create_index");

  /*
2700 2701 2702
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2703

2704 2705
    Open the table to have access to the correct table handler.
  */
2706 2707 2708 2709
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2710 2711 2712 2713 2714
    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.
  */
2715 2716 2717 2718 2719 2720 2721 2722 2723 2724
  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;
2725 2726 2727
  if (mysql_prepare_table(thd, &create_info, &fields,
			  &keys, /*tmp_table*/ 0, &db_options, table->file,
			  &key_info_buffer, key_count,
2728
			  /*select_field_count*/ 0))
2729 2730 2731
    DBUG_RETURN(-1);

  /*
2732 2733 2734
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2735 2736 2737 2738
  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)&
2739
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2740 2741
      break ;
  }
2742
  if ((idx < key_count)|| !key_count)
2743 2744 2745 2746 2747 2748 2749
  {
    /* 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();
2750
    if (real_alter_table(thd, table_list->db, table_list->table_name,
2751 2752 2753 2754
			 &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);*/
2755 2756 2757 2758 2759
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
2760 2761
	(my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
		     table_list->db, (lower_case_table_names == 2) ?
2762
		     table_list->alias: table_list->table_name, reg_ext) >=
2763 2764 2765 2766 2767
	 (int) sizeof(path)) ||
	! unpack_filename(path, path) ||
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
      /* don't need to free((gptr) key_info_buffer);*/
2768 2769
      DBUG_RETURN(-1);
  }
2770
  /* don't need to free((gptr) key_info_buffer);*/
2771 2772 2773 2774
  DBUG_RETURN(0);
}


2775 2776
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
2777 2778
{
  List<create_field> fields;
2779
  List<Key>	     keys;
2780 2781
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2782 2783 2784 2785 2786 2787 2788 2789 2790
  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];
2791 2792 2793
  DBUG_ENTER("mysql_drop_index");

  /*
2794 2795 2796
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2797

2798 2799
    Open the table to have access to the correct table handler.
  */
2800 2801 2802 2803
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2804 2805 2806
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
2807 2808 2809 2810
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
2811 2812
    Get the number of each key and check if it can be created online.
  */
2813 2814 2815 2816 2817 2818 2819 2820 2821
  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))
2822
	break;
2823 2824 2825 2826 2827 2828 2829 2830
    }
    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);
    }
    /*
2831 2832 2833
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
2834 2835
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
2836
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2837 2838 2839 2840 2841 2842 2843 2844 2845 2846
      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))
  {
2847
    if (real_alter_table(thd, table_list->db, table_list->table_name,
2848 2849 2850
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
2851 2852 2853 2854 2855 2856 2857
      /*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)||
2858 2859 2860
	mysql_prepare_table(thd, &create_info, &fields,
			    &keys, /*tmp_table*/ 0, &db_options, table->file,
			    &key_info_buffer, key_count,
2861 2862 2863
			    /*select_field_count*/ 0)||
	(snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
		  table_list->db, (lower_case_table_names == 2)?
2864
		  table_list->alias: table_list->table_name, reg_ext)>=
2865 2866 2867 2868
	 (int)sizeof(path))||
	! unpack_filename(path, path)||
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
2869 2870 2871 2872 2873 2874 2875
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
2876
#endif /* NOT_USED */
2877 2878


2879 2880 2881
/*
  Alter table
*/
2882

2883 2884 2885 2886 2887
bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
                       HA_CREATE_INFO *create_info,
                       TABLE_LIST *table_list,
                       List<create_field> &fields, List<Key> &keys,
                       uint order_num, ORDER *order,
2888
                       enum enum_duplicates handle_duplicates, bool ignore,
2889
                       ALTER_INFO *alter_info, bool do_send_ok)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2890
{
2891
  TABLE *table,*new_table=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2892
  int error;
2893 2894
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
2895
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
2896 2897
  ha_rows copied,deleted;
  ulonglong next_insert_id;
2898
  uint db_create_options, used_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2899
  enum db_type old_db_type,new_db_type;
2900
  bool need_copy_table;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2901 2902 2903
  DBUG_ENTER("mysql_alter_table");

  thd->proc_info="init";
2904
  table_name=table_list->table_name;
2905 2906
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2907
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
2908
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
2909
    new_db= db;
2910
  used_fields=create_info->used_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2911

2912
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2913
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
2914
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2915
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
2916
						   alter_info->tablespace_op));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2917
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
2918
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2919 2920 2921 2922 2923

  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
2924
    strmov(new_alias= new_alias_buff, new_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2925
    if (lower_case_table_names)
2926 2927 2928
    {
      if (lower_case_table_names != 2)
      {
2929
	my_casedn_str(files_charset_info, new_name_buff);
2930 2931
	new_alias= new_name;			// Create lower case table name
      }
2932
      my_casedn_str(files_charset_info, new_name);
2933
    }
2934
    if (new_db == db &&
monty@mysql.com's avatar
monty@mysql.com committed
2935
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
2936 2937
    {
      /*
2938 2939
	Source and destination table names are equal: make later check
	easier.
2940
      */
2941
      new_alias= new_name= table_name;
2942
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2943 2944
    else
    {
2945
      if (table->s->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2946 2947 2948
      {
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
2949
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
2950
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2951 2952 2953 2954
	}
      }
      else
      {
2955 2956 2957
	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),
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2958 2959 2960
		    F_OK))
	{
	  /* Table will be closed in do_command() */
2961
	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
2962
	  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2963 2964 2965 2966 2967
	}
      }
    }
  }
  else
2968 2969 2970 2971
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2972

2973
  old_db_type= table->s->db_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2974
  if (create_info->db_type == DB_TYPE_DEFAULT)
2975
    create_info->db_type= old_db_type;
2976 2977 2978 2979 2980 2981 2982
  if ((new_db_type= ha_checktype(create_info->db_type)) !=
      create_info->db_type)
  {
    create_info->db_type= new_db_type;
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
2983
			ha_get_storage_engine(new_db_type),
2984 2985
			new_name);
  }
2986
  if (create_info->row_type == ROW_TYPE_NOT_USED)
2987
    create_info->row_type= table->s->row_type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2988 2989

  thd->proc_info="setup";
2990
  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
2991
      !table->s->tmp_table) // no need to touch frm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2992 2993
  {
    error=0;
2994
    if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2995
    {
2996 2997 2998 2999 3000 3001
      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))
      {
3002
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
3003
	error= -1;
3004 3005 3006
      }
      else
      {
3007 3008 3009
	*fn_ext(new_name)=0;
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
3010 3011 3012
	  error= -1;
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3013
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3014

3015
    if (!error)
3016
    {
3017
      switch (alter_info->keys_onoff) {
3018
      case LEAVE_AS_IS:
3019
        break;
3020
      case ENABLE:
3021 3022 3023 3024 3025 3026
        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;
3027
      case DISABLE:
3028 3029 3030 3031 3032 3033
        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;
3034
      }
3035
    }
3036

3037
    if (error == HA_ERR_WRONG_COMMAND)
serg@serg.mylan's avatar
serg@serg.mylan committed
3038 3039
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
3040
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
3041
			  table->alias);
serg@serg.mylan's avatar
serg@serg.mylan committed
3042 3043
      error=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3044 3045
    if (!error)
    {
3046 3047
      if (mysql_bin_log.is_open())
      {
3048
	thd->clear_error();
3049
	Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
3050 3051
	mysql_bin_log.write(&qinfo);
      }
3052 3053
      if (do_send_ok)
        send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3054
    }
3055
    else if (error > 0)
3056 3057
    {
      table->file->print_error(error, MYF(0));
3058
      error= -1;
3059
    }
3060 3061
    table_list->table=0;				// For query cache
    query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3062 3063 3064 3065
    DBUG_RETURN(error);
  }

  /* Full alter table */
3066

3067
  /* Let new create options override the old ones */
3068
  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
3069
    create_info->min_rows= table->s->min_rows;
3070
  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
3071
    create_info->max_rows= table->s->max_rows;
3072
  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
3073
    create_info->avg_row_length= table->s->avg_row_length;
3074
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
3075
    create_info->default_table_charset= table->s->table_charset;
3076

3077
  restore_record(table, s->default_values);     // Empty record for DEFAULT
3078
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3079
  List_iterator<create_field> def_it(fields);
3080
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3081 3082
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
3083 3084
  create_field *def;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3085
  /*
3086
    First collect all fields from table which isn't in drop_list
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097
  */

  Field **f_ptr,*field;
  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
  {
    /* Check if field should be droped */
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::COLUMN &&
3098
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
3099 3100 3101
      {
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
3102
	    !(used_fields & HA_CREATE_USED_AUTO))
3103 3104 3105 3106
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3107
	break;
3108
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3109 3110 3111 3112 3113 3114 3115 3116 3117 3118
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
3119
      if (def->change &&
3120
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3121 3122 3123 3124 3125
	break;
    }
    if (def)
    {						// Field is changed
      def->field=field;
3126 3127 3128 3129 3130
      if (!def->after)
      {
	create_list.push_back(def);
	def_it.remove();
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3131 3132 3133
    }
    else
    {						// Use old field value
3134
      create_list.push_back(def=new create_field(field,field));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3135 3136 3137 3138
      alter_it.rewind();			// Change default if ALTER
      Alter_column *alter;
      while ((alter=alter_it++))
      {
3139
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3140 3141 3142 3143
	  break;
      }
      if (alter)
      {
3144 3145
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
3146
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
3147
	  DBUG_RETURN(TRUE);
3148
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3149 3150 3151 3152 3153 3154 3155 3156 3157
	def->def=alter->def;			// Use new default
	alter_it.remove();
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
  while ((def=def_it++))			// Add new columns
  {
3158
    if (def->change && ! def->field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3159
    {
3160
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
3161
      DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172
    }
    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
      {
3173
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3174 3175 3176 3177
	  break;
      }
      if (!find)
      {
3178
	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
3179
	DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3180 3181 3182 3183
      }
      find_it.after(def);			// Put element after this
    }
  }
3184
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3185
  {
3186 3187
    my_error(ER_BAD_FIELD_ERROR, MYF(0),
             alter_info->alter_list.head()->name, table_name);
3188
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3189 3190 3191
  }
  if (!create_list.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3192 3193
    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
               MYF(0));
3194
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3195 3196 3197
  }

  /*
3198 3199
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3200 3201 3202 3203 3204 3205 3206
  */

  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;
3207
  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3208
  {
3209
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3210 3211 3212 3213 3214
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
3215
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236
	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)
	{
3237 3238
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3239 3240
	    break;
	}
3241
	else if (!my_strcasecmp(system_charset_info,
3242
				key_part_name, cfield->field_name))
3243
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3244 3245 3246 3247 3248 3249 3250 3251
      }
      if (!cfield)
	continue;				// Field is removed
      uint key_part_length=key_part->length;
      if (cfield->field)			// Not new field
      {						// Check if sub key
	if (cfield->field->type() != FIELD_TYPE_BLOB &&
	    (cfield->field->pack_length() == key_part_length ||
3252
	     cfield->length <= key_part_length /
3253
			       key_part->field->charset()->mbmaxlen))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3254 3255
	  key_part_length=0;			// Use whole field
      }
3256
      key_part_length /= key_part->field->charset()->mbmaxlen;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3257 3258 3259 3260
      key_parts.push_back(new key_part_spec(cfield->field_name,
					    key_part_length));
    }
    if (key_parts.elements)
3261
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
3262
				 (key_info->flags & HA_NOSAME ?
3263
				 (!my_strcasecmp(system_charset_info,
3264 3265
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
3266 3267 3268
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
3269
				 key_info->algorithm,
3270
                                 test(key_info->flags & HA_GENERATED_KEY),
3271
				 key_parts));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3272 3273 3274 3275
  }
  {
    Key *key;
    while ((key=key_it++))			// Add new keys
3276 3277 3278
    {
      if (key->type != Key::FOREIGN_KEY)
	key_list.push_back(key);
3279 3280 3281 3282
      if (key->name &&
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
      {
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
3283
	DBUG_RETURN(TRUE);
3284
      }
3285
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3286 3287
  }

3288
  if (alter_info->drop_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3289
  {
3290 3291
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->drop_list.head()->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3292 3293
    goto err;
  }
3294
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3295
  {
3296 3297
    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
             alter_info->alter_list.head()->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3298 3299 3300
    goto err;
  }

3301
  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
3302 3303
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3304 3305
  /* Safety fix for innodb */
  if (lower_case_table_names)
3306
    my_casedn_str(files_charset_info, tmp_name);
3307 3308 3309 3310
  if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
    my_error(ER_ROW_IS_REFERENCED, MYF(0));
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3311 3312
  create_info->db_type=new_db_type;
  if (!create_info->comment)
3313
    create_info->comment= table->s->comment;
3314 3315 3316 3317 3318

  table->file->update_create_info(create_info);
  if ((create_info->table_options &
       (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
      (used_fields & HA_CREATE_USED_PACK_KEYS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3319 3320 3321 3322 3323 3324 3325 3326 3327 3328
    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;

3329
  if (table->s->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3330 3331
    create_info->options|=HA_LEX_CREATE_TMP_TABLE;

3332 3333 3334 3335 3336 3337 3338
  /*
    better have a negative test here, instead of positive, like
      alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
    so that ALTER TABLE won't break when somebody will add new flag
  */
  need_copy_table=(alter_info->flags & ~(ALTER_CHANGE_COLUMN_DEFAULT|ALTER_OPTIONS) ||
                   create_info->used_fields & ~(HA_CREATE_USED_COMMENT|HA_CREATE_USED_PASSWORD) ||
3339
                   table->s->tmp_table);
3340 3341
  create_info->frm_only= !need_copy_table;

3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385
  /*
    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);
    }
  }
3386 3387
  else
    create_info->data_file_name=create_info->index_file_name=0;
monty@mysql.com's avatar
monty@mysql.com committed
3388 3389

  /* We don't log the statement, it will be logged later. */
3390
  {
monty@mysql.com's avatar
monty@mysql.com committed
3391 3392 3393 3394 3395
    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)
3396 3397
      DBUG_RETURN(error);
  }
3398
  if (need_copy_table)
3399
  {
3400
    if (table->s->tmp_table)
3401 3402 3403 3404
    {
      TABLE_LIST tbl;
      bzero((void*) &tbl, sizeof(tbl));
      tbl.db= new_db;
3405
      tbl.table_name= tbl.alias= tmp_name;
monty@mysql.com's avatar
monty@mysql.com committed
3406
      new_table= open_table(thd, &tbl, thd->mem_root, 0);
3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420
    }
    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;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3421 3422
  }

3423
  /* We don't want update TIMESTAMP fields during ALTER TABLE. */
3424
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3425 3426
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3427
  next_insert_id=thd->next_insert_id;		// Remember for logging
3428
  copied=deleted=0;
3429
  if (new_table && !new_table->s->is_view)
3430
  {
monty@mysql.com's avatar
monty@mysql.com committed
3431
    new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
3432
    new_table->next_number_field=new_table->found_next_number_field;
3433
    error=copy_data_between_tables(table,new_table,create_list,
3434
				   handle_duplicates, ignore,
3435
				   order_num, order, &copied, &deleted);
3436
  }
3437
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3438
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3439

3440
  if (table->s->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3441 3442 3443 3444
  {
    /* We changed a temporary table */
    if (error)
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3445 3446 3447
      /*
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3448
      */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3449 3450 3451
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3452 3453 3454 3455 3456 3457
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3458
    /* Remove link to old table and rename the new one */
3459
    close_temporary_table(thd, table->s->db, table_name);
3460 3461
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3462 3463 3464 3465 3466
    {						// Fatal error
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3467 3468
    if (mysql_bin_log.is_open())
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3469
      thd->clear_error();
3470
      Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
3471 3472
      mysql_bin_log.write(&qinfo);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3473 3474 3475
    goto end_temporary;
  }

3476 3477 3478 3479 3480
  if (new_table)
  {
    intern_close_table(new_table);              /* close temporary table */
    my_free((gptr) new_table,MYF(0));
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3481 3482 3483 3484 3485 3486 3487
  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;
  }
3488

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3489
  /*
3490 3491
    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
3492
    from the cache, free all locks, close the old table and remove it.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3493 3494 3495
  */

  thd->proc_info="rename result table";
3496 3497
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3498 3499
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3500
  if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3501 3502 3503 3504
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
3505
      my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3506 3507 3508 3509 3510 3511
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

3512 3513 3514 3515 3516
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3517
      Win32 and InnoDB can't drop a table that is in use, so we must
3518
      close the original table at before doing the rename
3519 3520
    */
    table_name=thd->strdup(table_name);		// must be saved
3521
    close_cached_table(thd, table);
3522
    table=0;					// Marker that table is closed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3523
  }
3524 3525 3526
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  else
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3527 3528
#endif

3529

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3530
  error=0;
3531 3532
  if (!need_copy_table)
    new_db_type=old_db_type=DB_TYPE_UNKNOWN; // this type cannot happen in regular ALTER
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3533 3534 3535 3536 3537 3538
  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,
3539
			      new_alias))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3540 3541
  {						// Try to get everything back
    error=1;
3542
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3543
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
3544
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3545 3546 3547
  }
  if (error)
  {
3548 3549 3550 3551
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
3552 3553
    if (table)
      close_cached_table(thd,table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3554 3555 3556 3557 3558
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
  if (thd->lock || new_name != table_name)	// True if WIN32
  {
3559 3560 3561 3562
    /*
      Not table locking or alter table with rename
      free locks and remove old table
    */
3563 3564
    if (table)
      close_cached_table(thd,table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3565 3566 3567 3568
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
3569 3570 3571 3572 3573
    /*
      Using LOCK TABLES without rename.
      This code is never executed on WIN32!
      Remove old renamed table, reopen table and get new locks
    */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3574 3575 3576
    if (table)
    {
      VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
monty@mysql.com's avatar
monty@mysql.com committed
3577
      remove_table_from_cache(thd,db,table_name, 0); // Mark in-use copies old
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3578 3579 3580 3581 3582 3583
      mysql_lock_abort(thd,table);		 // end threads waiting on lock
    }
    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
3584 3585
      if (table)
	close_cached_table(thd,table);		// Remove lock for table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3586 3587 3588 3589
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3590
  /* The ALTER TABLE is always in its own transaction */
3591 3592 3593 3594
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3595 3596
  {
    VOID(pthread_mutex_unlock(&LOCK_open));
3597
    VOID(pthread_cond_broadcast(&COND_refresh));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3598 3599 3600
    goto err;
  }
  thd->proc_info="end";
3601
  if (mysql_bin_log.is_open())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3602
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3603
    thd->clear_error();
3604
    Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3605 3606 3607 3608
    mysql_bin_log.write(&qinfo);
  }
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_mutex_unlock(&LOCK_open));
3609 3610 3611
#ifdef HAVE_BERKELEY_DB
  if (old_db_type == DB_TYPE_BERKELEY_DB)
  {
3612 3613 3614 3615 3616
    /*
      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.
    */
3617
    char path[FN_REFLEN];
3618 3619
    my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
		new_db, table_name);
3620 3621 3622
    fn_format(path,path,"","",4);
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
3623
    {
3624 3625
      intern_close_table(table);
      my_free((char*) table, MYF(0));
3626
    }
3627
    else
serg@serg.mylan's avatar
serg@serg.mylan committed
3628 3629
      sql_print_warning("Could not open BDB table %s.%s after rename\n",
                        new_db,table_name);
3630
    (void) berkeley_flush_logs();
3631 3632
  }
#endif
3633
  table_list->table=0;				// For query cache
3634
  query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3635 3636

end_temporary:
3637 3638 3639
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3640 3641
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3642
  thd->some_tables_deleted=0;
3643
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3644 3645

 err:
3646
  DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3647 3648 3649 3650
}


static int
3651
copy_data_between_tables(TABLE *from,TABLE *to,
3652
			 List<create_field> &create,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3653
			 enum enum_duplicates handle_duplicates,
3654
                         bool ignore,
3655
			 uint order_num, ORDER *order,
3656
			 ha_rows *copied,
3657
			 ha_rows *deleted)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3658 3659 3660 3661 3662
{
  int error;
  Copy_field *copy,*copy_end;
  ulong found_count,delete_count;
  THD *thd= current_thd;
3663 3664 3665 3666 3667 3668
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3669
  ha_rows examined_rows;
3670
  bool auto_increment_field_copied= 0;
3671
  ulong save_sql_mode;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3672 3673
  DBUG_ENTER("copy_data_between_tables");

3674 3675 3676 3677 3678 3679
  /*
    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
  */
3680
  error= ha_enable_transaction(thd, FALSE);
3681 3682
  if (error)
    DBUG_RETURN(-1);
3683
  
3684
  if (!(copy= new Copy_field[to->s->fields]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3685 3686
    DBUG_RETURN(-1);				/* purecov: inspected */

3687 3688
  if (to->file->external_lock(thd, F_WRLCK))
    DBUG_RETURN(-1);
3689 3690 3691 3692 3693 3694 3695

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

3696
  from->file->info(HA_STATUS_VARIABLE);
3697
  to->file->start_bulk_insert(from->file->records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3698

3699 3700
  save_sql_mode= thd->variables.sql_mode;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3701 3702 3703 3704 3705 3706 3707
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
3708 3709
    {
      if (*ptr == to->next_number_field)
3710
      {
3711
        auto_increment_field_copied= TRUE;
3712 3713 3714 3715 3716 3717 3718 3719 3720
        /*
          If we are going to copy contents of one auto_increment column to
          another auto_increment column it is sensible to preserve zeroes.
          This condition also covers case when we are don't actually alter
          auto_increment column.
        */
        if (def->field == from->found_next_number_field)
          thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3721
      (copy_end++)->set(*ptr,def->field,0);
3722 3723
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3724 3725
  }

3726 3727
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
3728 3729
  if (order)
  {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
3730
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
3731
					      MYF(MY_FAE | MY_ZEROFILL));
3732
    bzero((char*) &tables,sizeof(tables));
3733 3734 3735
    tables.table= from;
    tables.alias= tables.table_name= (char*) from->s->table_name;
    tables.db=    (char*) from->s->db;
3736 3737
    error=1;

pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
3738
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
3739
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
3740
		    &tables, fields, all_fields, order) ||
3741 3742 3743 3744 3745
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
					     (SQL_SELECT *) 0, HA_POS_ERROR,
					     &examined_rows))
	== HA_POS_ERROR)
3746 3747 3748
      goto err;
  };

3749 3750 3751 3752 3753
  /*
    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
  */
3754
  from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3755
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
3756
  if (ignore ||
3757
      handle_duplicates == DUP_REPLACE)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3758
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
3759
  thd->row_count= 0;
3760
  restore_record(to, s->default_values);        // Create empty record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3761 3762 3763 3764
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
3765
      thd->send_kill_message();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3766 3767 3768
      error= 1;
      break;
    }
3769
    thd->row_count++;
3770 3771
    if (to->next_number_field)
    {
3772
      if (auto_increment_field_copied)
3773
        to->auto_increment_field_not_null= TRUE;
3774 3775 3776
      else
        to->next_number_field->reset();
    }
3777
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3778
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
3779
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3780
      copy_ptr->do_copy(copy_ptr);
3781
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3782 3783
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
3784
      if ((!ignore &&
3785
	   handle_duplicates != DUP_REPLACE) ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3786 3787 3788 3789 3790 3791
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
      {
	to->file->print_error(error,MYF(0));
	break;
      }
3792
      to->file->restore_auto_increment();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3793 3794 3795
      delete_count++;
    }
    else
3796
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3797 3798
  }
  end_read_record(&info);
3799
  free_io_cache(from);
3800
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
3801

3802
  if (to->file->end_bulk_insert() && error <= 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3803
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
3804
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3805 3806
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3807
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
3808

3809
  ha_enable_transaction(thd,TRUE);
3810

3811 3812 3813 3814 3815 3816 3817 3818
  /*
    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;
3819

3820
 err:
3821
  thd->variables.sql_mode= save_sql_mode;
3822
  thd->abort_on_warning= 0;
3823
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3824 3825
  *copied= found_count;
  *deleted=delete_count;
3826 3827
  if (to->file->external_lock(thd,F_UNLCK))
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3828 3829
  DBUG_RETURN(error > 0 ? -1 : 0);
}
3830

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

3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843
/*
  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().
*/
3844 3845
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                          bool do_send_ok)
3846 3847 3848 3849 3850 3851 3852 3853 3854 3855
{
  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;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
3856
  create_info.row_type=ROW_TYPE_NOT_USED;
3857
  create_info.default_table_charset=default_charset_info;
monty@mysql.com's avatar
monty@mysql.com committed
3858 3859
  /* Force alter table to recreate table */
  lex->alter_info.flags= ALTER_CHANGE_COLUMN;
3860 3861 3862
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
3863
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
3864 3865 3866
}


3867
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
3868 3869 3870 3871 3872
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
3873
  DBUG_ENTER("mysql_checksum_table");
3874 3875 3876 3877 3878

  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;
3879 3880
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
3881
    DBUG_RETURN(TRUE);
3882

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3883
  for (table= tables; table; table= table->next_local)
3884 3885
  {
    char table_name[NAME_LEN*2+2];
3886
    TABLE *t;
3887

3888
    strxmov(table_name, table->db ,".", table->table_name, NullS);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3889 3890 3891

    t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT);
    thd->clear_error();			// these errors shouldn't get client
3892 3893 3894 3895

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

3896
    if (!t)
3897
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3898
      /* Table didn't exist */
3899
      protocol->store_null();
3900
      thd->clear_error();
3901 3902 3903
    }
    else
    {
3904
      t->pos_in_table_list= table;
3905

3906
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
3907 3908
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
3909
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3910
	       (check_opt->flags & T_QUICK))
3911
	protocol->store_null();
3912 3913
      else
      {
3914 3915 3916 3917 3918 3919 3920 3921
	/* calculating table's checksum */
	ha_checksum crc= 0;

	/* InnoDB must be told explicitly to retrieve all columns, because
	this function does not set field->query_id in the columns to the
	current query id */
	t->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);

3922
	if (t->file->ha_rnd_init(1))
3923 3924 3925 3926 3927 3928 3929 3930
	  protocol->store_null();
	else
	{
	  while (!t->file->rnd_next(t->record[0]))
	  {
	    ha_checksum row_crc= 0;
	    if (t->record[0] != (byte*) t->field[0]->ptr)
	      row_crc= my_checksum(row_crc, t->record[0],
3931
				   ((byte*) t->field[0]->ptr) - t->record[0]);
3932

3933
	    for (uint i= 0; i < t->s->fields; i++ )
3934 3935 3936 3937 3938 3939 3940 3941 3942 3943
	    {
	      Field *f= t->field[i];
	      if (f->type() == FIELD_TYPE_BLOB)
	      {
		String tmp;
		f->val_str(&tmp);
		row_crc= my_checksum(row_crc, (byte*) tmp.ptr(), tmp.length());
	      }
	      else
		row_crc= my_checksum(row_crc, (byte*) f->ptr,
3944
				     f->pack_length());
3945
	    }
3946

3947 3948 3949
	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
3950
          t->file->ha_rnd_end();
3951
	}
3952
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3953
      thd->clear_error();
3954 3955 3956 3957 3958 3959 3960 3961
      close_thread_tables(thd);
      table->table=0;				// For query cache
    }
    if (protocol->write())
      goto err;
  }

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

3964 3965 3966 3967
 err:
  close_thread_tables(thd);			// Shouldn't be needed
  if (table)
    table->table=0;
3968
  DBUG_RETURN(TRUE);
3969
}