sql_table.cc 106 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 20

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

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

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


/* drop and alter of tables */

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

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

serg@serg.mylan's avatar
serg@serg.mylan committed
32
const char *primary_key_name="PRIMARY";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33 34 35 36

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

43 44 45 46 47
/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table()
48 49 50
   thd			Thread handle
   tables		List of tables to delete
   if_exists		If 1, don't give error if one table doesn't exists
51 52 53 54 55 56 57 58 59 60

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

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

  RETURN
61 62
    0		ok.  In this case ok packet is sent to user
    -1		Error  (Error message given but not sent to user)
63 64

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

66
int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
67
		   my_bool drop_temporary)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
68
{
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
69
  int error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
70 71 72 73 74 75 76 77
  DBUG_ENTER("mysql_rm_table");

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

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

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

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

95
 err:
96 97 98 99 100 101 102 103 104
  pthread_mutex_unlock(&LOCK_open);

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

  if (error)
    DBUG_RETURN(-1);
105
  send_ok(thd);
106 107 108
  DBUG_RETURN(0);
}

109 110 111 112 113 114

/*
 delete (drop) tables.

  SYNOPSIS
   mysql_rm_table_part2_with_lock()
115 116 117 118
   thd			Thread handle
   tables		List of tables to delete
   if_exists		If 1, don't give error if one table doesn't exists
   dont_log_query	Don't write query to log files
119 120 121 122 123 124

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

 RETURN
125 126
  0	ok
  1	error
127 128
*/

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

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

  pthread_mutex_unlock(&LOCK_open);

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

150

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

  SYNOPSIS
    mysql_rm_table_part2()
156 157 158 159 160 161
    thd			Thread handler
    tables		Tables to drop
    if_exists		If set, don't give an error if table doesn't exists.
			In this case we give an warning of level 'NOTE'
    drop_temporary	Only drop temporary tables
    dont_log_query	Don't log the query
162

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

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

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

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

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

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

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

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

288
  unlock_table_names(thd, tables);
289
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
290 291 292 293
}


int quick_rm_table(enum db_type base,const char *db,
294
		   const char *table_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
295 296 297
{
  char path[FN_REFLEN];
  int error=0;
298 299
  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
300 301 302
  unpack_filename(path,path);
  if (my_delete(path,MYF(0)))
    error=1; /* purecov: inspected */
303
  my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home, db, table_name);
304
  unpack_filename(path,path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
305 306 307
  return ha_delete_table(base,path) || error;
}

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

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

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

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

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

  RETURN VALUES
    void
*/

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

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

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


427
/*
428
  Preparation for table creation
429 430

  SYNOPSIS
431
    mysql_prepare_table()
432 433 434 435
    thd			Thread object
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create
436

437
  DESCRIPTION
438
    Prepares the table and key structures for table creation.
439 440

  RETURN VALUES
441 442
    0	ok
    -1	error
443
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
444

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

  List_iterator<create_field> it(fields),it2(fields);
463
  select_field_pos=fields.elements - select_field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
464
  null_fields=blob_columns=0;
465

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

    CHARSET_INFO *savecs= sql_field->charset;
    if ((sql_field->flags & BINCMP_FLAG) &&
	!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
						    MY_CS_BINSORT,MYF(0))))
    {
      char tmp[64];
      strmake(strmake(tmp, savecs->csname, sizeof(tmp)-4), "_bin", 4);
      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
      DBUG_RETURN(-1);
    }
489

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

      /*
        Create typelib from interval_list, and if necessary
        convert strings from client character set to the
        column character set.
      */
502
      if (!interval)
503
      {
504 505 506 507
        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++)
508
        {
509 510 511 512 513 514 515 516 517 518 519
          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();
          }
520

521 522 523 524 525 526 527
          // Strip trailing spaces.
          uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
                                            interval->type_lengths[i]);
          interval->type_lengths[i]= lengthsp;
          ((uchar *)interval->type_names[i])[lengthsp]= '\0';
        }
        sql_field->interval_list.empty(); // Don't need interval_list anymore
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
      }

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

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

577
    sql_field->create_length_to_internal_length();
578

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

589 590
    if (check_column_name(sql_field->field_name))
    {
591
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
592 593
      DBUG_RETURN(-1);
    }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
594

595 596
    /* 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
597
    {
598
      if (my_strcasecmp(system_charset_info,
599 600
			sql_field->field_name,
			dup_field->field_name) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
601
      {
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
	  my_error(ER_DUP_FIELDNAME,MYF(0),sql_field->field_name);
	  DBUG_RETURN(-1);
	}
	else
	{
	  /* Field redefined */
	  sql_field->sql_type=		dup_field->sql_type;
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
	  sql_field->length=		dup_field->length;
	  sql_field->pack_length=	dup_field->pack_length;
	  sql_field->create_length_to_internal_length();
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->flags=		dup_field->flags;
	  sql_field->unireg_check=	dup_field->unireg_check;
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
628 629 630 631
      }
    }
    it2.rewind();
  }
632 633 634

  /* record_offset will be increased with 'length-of-null-bits' later */
  record_offset= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
635 636 637 638

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

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

725
      timestamps++;
726
      /* fall-through */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
727 728
    default:
      sql_field->pack_flag=(FIELDFLAG_NUMBER |
729 730 731 732 733 734
			    (sql_field->flags & UNSIGNED_FLAG ? 0 :
			     FIELDFLAG_DECIMAL) |
			    (sql_field->flags & ZEROFILL_FLAG ?
			     FIELDFLAG_ZEROFILL : 0) |
			    f_settype((uint) sql_field->sql_type) |
			    (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
735 736 737 738
      break;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      sql_field->pack_flag|=FIELDFLAG_MAYBE_NULL;
739
    sql_field->offset= record_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
740 741
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
742
    record_offset+= sql_field->pack_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
743
  }
744 745 746 747 748
  if (timestamps_with_niladic > 1)
  {
    my_error(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,MYF(0));
    DBUG_RETURN(-1);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
749 750 751 752 753 754
  if (auto_increment > 1)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
755
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
756 757 758 759 760
  {
    my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0));
    DBUG_RETURN(-1);
  }

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

  /* Create keys */
768

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
780 781
  while ((key=key_iterator++))
  {
782 783 784 785 786
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
787
	  fk_key->ref_columns.elements != fk_key->columns.elements)
788
      {
789 790 791 792
	my_error(ER_WRONG_FK_DEF, MYF(0), fk_key->name ? fk_key->name :
		 "foreign key without name",
		 ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
	DBUG_RETURN(-1);
793 794 795
      }
      continue;
    }
796
    (*key_count)++;
797
    tmp=file->max_key_parts();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
798 799 800 801 802
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
803
    if (key->name && strlen(key->name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
804
    {
805
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
806 807
      DBUG_RETURN(-1);
    }
808
    key_iterator2.rewind ();
809
    if (key->type != Key::FOREIGN_KEY)
810
    {
811
      while ((key2 = key_iterator2++) != key)
812
      {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
813
	/*
814 815 816
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
817
        */
818 819 820
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
821
        {
822
          /* TODO: issue warning message */
823 824 825 826 827 828 829 830 831 832 833 834 835
          /* mark that the generated key should be ignored */
          if (!key2->generated ||
              (key->generated && key->columns.elements <
               key2->columns.elements))
            key->name= ignore_key;
          else
          {
            key2->name= ignore_key;
            key_parts-= key2->columns.elements;
            (*key_count)--;
          }
          break;
        }
836 837 838 839 840 841
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
842
    if (key->name && !tmp_table &&
843
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
844 845 846 847
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
848
  }
849
  tmp=file->max_keys();
850
  if (*key_count > tmp)
851 852 853 854
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
855

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

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

868 869 870 871 872 873 874 875 876 877
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

878
    switch(key->type){
879
    case Key::MULTIPLE:
880
	key_info->flags= 0;
881
	break;
882
    case Key::FULLTEXT:
883
	key_info->flags= HA_FULLTEXT;
884
	break;
885
    case Key::SPATIAL:
hf@deer.(none)'s avatar
hf@deer.(none) committed
886
#ifdef HAVE_SPATIAL
887
	key_info->flags= HA_SPATIAL;
888
	break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
889
#else
890 891 892
	my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0),
			sym_group_geom.name, sym_group_geom.needed_define);
	DBUG_RETURN(-1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
893
#endif
894
    case Key::FOREIGN_KEY:
895
      key_number--;				// Skip this key
896 897
      continue;
    default:
898 899
      key_info->flags = HA_NOSAME;
      break;
900
    }
901 902
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
903

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

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

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
961 962 963
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
964 965 966 967
	     my_strcasecmp(system_charset_info,
			   column->field_name,
			   sql_field->field_name))
	field++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
968 969
      if (!sql_field)
      {
970 971 972 973
	my_printf_error(ER_KEY_COLUMN_DOES_NOT_EXITS,
			ER(ER_KEY_COLUMN_DOES_NOT_EXITS),MYF(0),
			column->field_name);
	DBUG_RETURN(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
974
      }
975
      while ((dup_column= cols2++) != column)
976 977 978 979 980 981 982 983 984 985 986
      {
        if (!my_strcasecmp(system_charset_info,
	     	           column->field_name, dup_column->field_name))
	{
	  my_printf_error(ER_DUP_FIELDNAME,
			  ER(ER_DUP_FIELDNAME),MYF(0),
			  column->field_name);
	  DBUG_RETURN(-1);
	}
      }
      cols2.rewind();
987
      if (key->type == Key::FULLTEXT)
988
      {
989 990 991 992
	if ((sql_field->sql_type != FIELD_TYPE_STRING &&
	     sql_field->sql_type != FIELD_TYPE_VAR_STRING &&
	     !f_is_blob(sql_field->pack_flag)) ||
	    sql_field->charset == &my_charset_bin ||
993
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
	    (ft_key_charset && sql_field->charset != ft_key_charset))
	{
	    my_printf_error(ER_BAD_FT_COLUMN,ER(ER_BAD_FT_COLUMN),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	}
	ft_key_charset=sql_field->charset;
	/*
	  for fulltext keys keyseg length is 1 for blobs (it's ignored in ft
	  code anyway, and 0 (set to column width later) for char's. it has
	  to be correct col width for char's, as char data are not prefixed
	  with length (unlike blobs, where ft code takes data length from a
	  data prefix, ignoring column->length).
	*/
	column->length=test(f_is_blob(sql_field->pack_flag));
1009
      }
1010
      else
1011
      {
1012 1013 1014 1015
	column->length*= sql_field->charset->mbmaxlen;

	if (f_is_blob(sql_field->pack_flag))
	{
1016
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
	  {
	    my_printf_error(ER_BLOB_USED_AS_KEY,ER(ER_BLOB_USED_AS_KEY),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	  }
	  if (!column->length)
	  {
	    my_printf_error(ER_BLOB_KEY_WITHOUT_LENGTH,
			    ER(ER_BLOB_KEY_WITHOUT_LENGTH),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
1030
#ifdef HAVE_SPATIAL
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
	if (key->type  == Key::SPATIAL)
	{
	  if (!column->length )
	  {
	    /*
	    BAR: 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
		 Lately we'll extend this code to support more dimensions
	    */
	    column->length=4*sizeof(double);
	  }
	}
hf@deer.(none)'s avatar
hf@deer.(none) committed
1042
#endif
1043 1044 1045 1046 1047 1048 1049
	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;
monty@mysql.com's avatar
monty@mysql.com committed
1050
            null_fields--;
1051 1052 1053
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
1054
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
	  {
	    my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
			    MYF(0),column->field_name);
	    DBUG_RETURN(-1);
	  }
	  if (key->type == Key::SPATIAL)
	  {
	    my_error(ER_SPATIAL_CANT_HAVE_NULL, MYF(0));
	    DBUG_RETURN(-1);
	  }
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
	  if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY))
	    auto_increment--;			// Field is used
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1071
      }
1072

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1073 1074 1075 1076 1077 1078
      key_part_info->fieldnr= field;
      key_part_info->offset=  (uint16) sql_field->offset;
      key_part_info->key_type=sql_field->pack_flag;
      uint length=sql_field->pack_length;
      if (column->length)
      {
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
	if (f_is_blob(sql_field->pack_flag))
	{
	  if ((length=column->length) > file->max_key_length() ||
	      length > file->max_key_part_length())
	  {
	    length=min(file->max_key_length(), file->max_key_part_length());
	    if (key->type == Key::MULTIPLE)
	    {
	      /* not a critical problem */
	      char warn_buff[MYSQL_ERRMSG_SIZE];
	      my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
			  length);
	      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			   ER_TOO_LONG_KEY, warn_buff);
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
	      DBUG_RETURN(-1);
	    }
	  }
	}
	else if (!f_is_geom(sql_field->pack_flag) &&
		  (column->length > length ||
		   ((f_is_packed(sql_field->pack_flag) ||
		     ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
	  my_error(ER_WRONG_SUB_KEY,MYF(0));
	  DBUG_RETURN(-1);
	}
	else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
	  length=column->length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1113 1114 1115
      }
      else if (length == 0)
      {
1116 1117 1118
	my_printf_error(ER_WRONG_KEY_COLUMN, ER(ER_WRONG_KEY_COLUMN), MYF(0),
			column->field_name);
	  DBUG_RETURN(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1119
      }
1120 1121
      if (length > file->max_key_part_length())
      {
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
	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);
	}
1137 1138
      }
      key_part_info->length=(uint16) length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1139 1140
      /* Use packed keys for long strings on the first column */
      if (!(db_options & HA_OPTION_NO_PACK_KEYS) &&
1141 1142 1143 1144
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
	   (sql_field->sql_type == FIELD_TYPE_STRING ||
	    sql_field->sql_type == FIELD_TYPE_VAR_STRING ||
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1145
      {
1146 1147 1148 1149
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB))
	  key_info->flags|= HA_BINARY_PACK_KEY;
	else
	  key_info->flags|= HA_PACK_KEY;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1150 1151 1152 1153 1154 1155 1156
      }
      key_length+=length;
      key_part_info++;

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

1210 1211 1212
  DBUG_RETURN(0);
}

1213

1214 1215 1216 1217 1218
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1219 1220 1221 1222 1223 1224 1225 1226
    thd			Thread object
    db			Database
    table_name		Table name
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create
    tmp_table		Set to 1 if this is an internal temporary table
			(From ALTER TABLE)
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236

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

    no_log is needed for the case of CREATE ... SELECT,
    as the logging will be done later in sql_insert.cc
    select_field_count is also used for CREATE ... SELECT,
    and must be zero for standard create of table.

  RETURN VALUES
1237 1238
    0	ok
    -1	error
1239 1240 1241
*/

int mysql_create_table(THD *thd,const char *db, const char *table_name,
1242 1243
		       HA_CREATE_INFO *create_info,
		       List<create_field> &fields,
1244
		       List<Key> &keys,bool tmp_table,
1245
		       uint select_field_count)
1246
{
1247 1248 1249 1250 1251 1252 1253
  char		path[FN_REFLEN];
  const char	*alias;
  int		error= -1;
  uint		db_options, key_count;
  KEY		*key_info_buffer;
  handler	*file;
  enum db_type	new_db_type;
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
  DBUG_ENTER("mysql_create_table");

  /* Check for duplicate fields and check type of table to create */
  if (!fields.elements)
  {
    my_error(ER_TABLE_MUST_HAVE_COLUMNS,MYF(0));
    DBUG_RETURN(-1);
  }
  if ((new_db_type= ha_checktype(create_info->db_type)) !=
      create_info->db_type)
  {
    create_info->db_type= new_db_type;
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
1267 1268 1269 1270
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			table_name);
1271 1272 1273 1274 1275 1276 1277
  }
  db_options=create_info->table_options;
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
  file=get_new_handler((TABLE*) 0, create_info->db_type);

1278 1279 1280 1281 1282 1283 1284 1285
#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
  */
1286 1287 1288 1289 1290 1291
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      (file->table_flags() & HA_NO_TEMP_TABLES))
  {
    my_error(ER_ILLEGAL_HA,MYF(0),table_name);
    DBUG_RETURN(-1);
  }
1292
#endif
1293

1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
  /*
    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];
1304
    strxmov(path, mysql_data_home, "/", db, NullS);
1305 1306 1307 1308 1309 1310
    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;
  }

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1317 1318 1319
      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1320 1321 1322
    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);
1323 1324
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, path);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1325 1326 1327
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
1328 1329
    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
1330 1331 1332 1333 1334
  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))
  {
1335
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1336
    {
1337
      create_info->table_existed= 1;		// Mark that table existed
1338
      DBUG_RETURN(0);
1339
    }
monty@mysql.com's avatar
monty@mysql.com committed
1340
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1341 1342
    DBUG_RETURN(-1);
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
1343
  if (wait_if_global_read_lock(thd, 0, 1))
1344
    DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1345 1346 1347 1348 1349 1350
  VOID(pthread_mutex_lock(&LOCK_open));
  if (!tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
  {
    if (!access(path,F_OK))
    {
      if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1351
      {
1352 1353 1354
	create_info->table_existed= 1;		// Mark that table existed
	error= 0;
      }
1355
      else
1356
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1357
      goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1358 1359 1360
    }
  }

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

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

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1393
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1394
    create_info->data_file_name= create_info->index_file_name= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1395
  create_info->table_options=db_options;
1396

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

1454 1455
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
1456 1457 1458 1459 1460 1461 1462
    return (char*) field_name;			// Use fieldname
  buff_end=strmake(buff,field_name, sizeof(buff)-4);

  /*
    Only 3 chars + '\0' left, so need to limit to 2 digit
    This is ok as we can't have more than 100 keys anyway
  */
1463
  for (uint i=2 ; i< 100; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1464
  {
1465 1466
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1467 1468 1469
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1470
  return (char*) "not_specified";		// Should never happen
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1471 1472
}

1473

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1474 1475 1476 1477 1478
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
1479 1480 1481 1482 1483
			       const char *db, const char *name,
			       List<create_field> *extra_fields,
			       List<Key> *keys,
			       List<Item> *items,
			       MYSQL_LOCK **lock)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1484
{
1485
  TABLE tmp_table;		// Used during 'create_field()'
1486
  TABLE *table= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1487
  tmp_table.table_name=0;
1488
  uint select_field_count= items->elements;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1489 1490 1491
  DBUG_ENTER("create_table_from_items");

  /* Add selected items to field list */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
1492
  List_iterator_fast<Item> it(*items);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1493 1494 1495 1496 1497 1498
  Item *item;
  Field *tmp_field;
  tmp_table.db_create_options=0;
  tmp_table.null_row=tmp_table.maybe_null=0;
  tmp_table.blob_ptr_size=portable_sizeof_char_ptr;
  tmp_table.db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
1499
				    create_info->db_type == DB_TYPE_HEAP);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1500 1501 1502 1503

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


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

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

  if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
  {
    /* Table handler expects to get all file names as lower case */
    strmov(tmp_from, old_name);
1573
    my_casedn_str(files_charset_info, tmp_from);
1574 1575 1576
    old_name= tmp_from;

    strmov(tmp_to, new_name);
1577
    my_casedn_str(files_charset_info, tmp_to);
1578 1579
    new_name= tmp_to;
  }
1580 1581 1582 1583
  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
1584 1585
  fn_format(from,from,"","",4);
  fn_format(to,to,    "","",4);
1586

1587
  if (!(error=file->rename_table((const char*) from,(const char *) to)))
1588 1589 1590
  {
    if (rename_file_ext(from,to,reg_ext))
    {
1591
      error=my_errno;
1592 1593 1594 1595
      /* Restore old file name */
      file->rename_table((const char*) to,(const char *) from);
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1596
  delete file;
1597 1598 1599
  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
1600 1601
}

1602

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1603
/*
1604 1605 1606 1607
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
1608 1609 1610 1611
    thd			Thread handler
    table		Table to remove from cache
    function		HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
			HA_EXTRA_FORCE_REOPEN if table is not be used
1612 1613 1614 1615 1616 1617 1618
  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
1619 1620
*/

1621
static void wait_while_table_is_used(THD *thd,TABLE *table,
1622
				     enum ha_extra_function function)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1623
{
1624 1625 1626
  DBUG_PRINT("enter",("table: %s", table->real_name));
  DBUG_ENTER("wait_while_table_is_used");
  safe_mutex_assert_owner(&LOCK_open);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1627

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

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

1643 1644
/*
  Close a cached table
1645

1646
  SYNOPSIS
1647
    close_cached_table()
1648 1649
    thd			Thread handler
    table		Table to remove from cache
1650 1651 1652 1653

  NOTES
    Function ends by signaling threads waiting for the table to try to
    reopen the table.
1654

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

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

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

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

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

1695

serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1696
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1697
			       HA_CHECK_OPT *check_opt)
1698
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1699
  DBUG_ENTER("prepare_for_restore");
1700

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

1714
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
1715
					reg_ext))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1716
      DBUG_RETURN(-1); // protect buffer overflow
1717

1718
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1719
		mysql_real_data_home, db, table_name);
1720

1721
    if (lock_and_wait_for_table_name(thd,table))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1722
      DBUG_RETURN(-1);
1723

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

1744 1745 1746 1747
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
1748
  if (!(table->table = reopen_name_locked_table(thd, table)))
1749 1750
  {
    pthread_mutex_lock(&LOCK_open);
1751
    unlock_table_name(thd, table);
1752 1753
    pthread_mutex_unlock(&LOCK_open);
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1754
  DBUG_RETURN(0);
1755
}
1756

1757

1758
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
1759
			      HA_CHECK_OPT *check_opt)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1760
{
1761 1762
  int error= 0;
  TABLE tmp_table, *table;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1763 1764 1765 1766
  DBUG_ENTER("prepare_for_repair");

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

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

1778 1779 1780 1781 1782 1783 1784 1785 1786
  /*
    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
1787

1788 1789 1790
  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
1791

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

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

1803 1804
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
1805

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

1846 1847 1848 1849
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
1850
  if (!(table_list->table = reopen_name_locked_table(thd, table_list)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1851 1852
  {
    pthread_mutex_lock(&LOCK_open);
1853
    unlock_table_name(thd, table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1854 1855
    pthread_mutex_unlock(&LOCK_open);
  }
1856 1857 1858

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

1863

1864 1865 1866 1867 1868 1869
/*
  RETURN VALUES
    0   Message sent to net (admin operation went ok)
   -1   Message should be sent by caller 
        (admin operation or network communication failed)
*/
1870
static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
1871 1872 1873 1874 1875 1876 1877 1878 1879
			     HA_CHECK_OPT* check_opt,
			     const char *operator_name,
			     thr_lock_type lock_type,
			     bool open_for_modify,
			     uint extra_open_options,
			     int (*prepare_func)(THD *, TABLE_LIST *,
						 HA_CHECK_OPT *),
			     int (handler::*operator_func)
			     (THD *, HA_CHECK_OPT *))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1880 1881 1882
{
  TABLE_LIST *table;
  List<Item> field_list;
1883 1884
  Item *item;
  Protocol *protocol= thd->protocol;
1885
  DBUG_ENTER("mysql_admin_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1886 1887 1888 1889 1890 1891 1892 1893 1894

  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;
1895
  if (protocol->send_fields(&field_list, 1))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1896 1897
    DBUG_RETURN(-1);

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

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

1913
    if (prepare_func)
1914
    {
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
1915
      switch ((*prepare_func)(thd, table, check_opt)) {
1916 1917 1918 1919 1920 1921 1922
      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
        ;
1923
      }
1924
    }
1925

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

1958
    /* Close all instances of the table to allow repair to rename files */
1959
    if (lock_type == TL_WRITE && table->table->version)
1960 1961
    {
      pthread_mutex_lock(&LOCK_open);
1962
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
1963
					      "Waiting to get writelock");
1964 1965
      mysql_lock_abort(thd,table->table);
      while (remove_table_from_cache(thd, table->table->table_cache_key,
1966 1967
				     table->table->real_name) &&
	     ! thd->killed)
1968
      {
1969 1970 1971
	dropping_tables++;
	(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
	dropping_tables--;
1972
      }
1973
      thd->exit_cond(old_message);
1974
      if (thd->killed)
1975
	goto err;
1976 1977 1978
      /* Flush entries in the query cache involving this table. */
      query_cache_invalidate3(thd, table->table, 0);
      open_for_modify= 0;
1979 1980
    }

1981
    int result_code = (table->table->file->*operator_func)(thd, check_opt);
hf@bisonxp.(none)'s avatar
hf@bisonxp.(none) committed
1982 1983 1984
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
1985
    protocol->prepare_for_resend();
1986 1987
    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
1988

1989 1990 1991
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
1992 1993
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
1994
      {
1995 1996 1997
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
1998
	protocol->store("note", 4, system_charset_info);
1999
	protocol->store(buf, length, system_charset_info);
2000
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2001 2002
      break;

2003
    case HA_ADMIN_OK:
2004 2005
      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
2006 2007
      break;

2008
    case HA_ADMIN_FAILED:
2009 2010
      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
2011 2012
      break;

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2013 2014 2015
    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
2016
      open_for_modify= FALSE;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2017 2018
      break;

2019
    case HA_ADMIN_ALREADY_DONE:
2020 2021
      protocol->store("status", 6, system_charset_info);
      protocol->store("Table is already up to date", 27, system_charset_info);
2022 2023
      break;

2024
    case HA_ADMIN_CORRUPT:
2025
      protocol->store("error", 5, system_charset_info);
2026
      protocol->store("Corrupt", 7, system_charset_info);
2027
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2028 2029
      break;

2030
    case HA_ADMIN_INVALID:
2031 2032
      protocol->store("error", 5, system_charset_info);
      protocol->store("Invalid argument",16, system_charset_info);
2033 2034
      break;

2035 2036 2037 2038 2039 2040 2041 2042 2043 2044
    case HA_ADMIN_TRY_ALTER:
    {
      /*
        This is currently used only by InnoDB. ha_innobase::optimize() answers
        "try with alter", so here we close the table, do an ALTER TABLE,
        reopen the table and do ha_innobase::analyze() on it.
      */
      close_thread_tables(thd);
      TABLE_LIST *save_next= table->next;
      table->next= 0;
2045
      tmp_disable_binlog(thd); // binlogging is done by caller if wanted
2046
      result_code= mysql_recreate_table(thd, table, 0);
2047
      reenable_binlog(thd);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2048
      close_thread_tables(thd);
2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059
      if (!result_code) // recreation went ok
      {
        if ((table->table= open_ltable(thd, table, lock_type)) &&
            ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
          result_code= 0; // analyze went ok
      }
      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
      table->next= save_next;
      goto send_result_message;
    }

2060
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
2061 2062
      protocol->store("error", 5, system_charset_info);
      protocol->store("Unknown - internal error during operation", 41
2063
		      , system_charset_info);
2064
      fatal_error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2065 2066
      break;
    }
2067
    if (fatal_error)
2068
      table->table->version=0;			// Force close of table
2069
    else if (open_for_modify)
2070
    {
2071
      pthread_mutex_lock(&LOCK_open);
2072
      remove_table_from_cache(thd, table->table->table_cache_key,
2073
			      table->table->real_name);
2074
      pthread_mutex_unlock(&LOCK_open);
2075 2076 2077
      /* 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
2078
    close_thread_tables(thd);
2079
    table->table=0;				// For query cache
2080
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2081 2082 2083
      goto err;
  }

2084
  send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2085 2086
  DBUG_RETURN(0);
 err:
2087
  close_thread_tables(thd);			// Shouldn't be needed
2088 2089
  if (table)
    table->table=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2090 2091 2092
  DBUG_RETURN(-1);
}

2093

2094 2095 2096 2097
int mysql_backup_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2098 2099
				"backup", TL_READ, 0, 0, 0,
				&handler::backup));
2100
}
2101

2102

2103 2104 2105 2106
int mysql_restore_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
2107 2108 2109
				"restore", TL_WRITE, 1, 0,
				&prepare_for_restore,
				&handler::restore));
2110
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2111

2112

2113 2114 2115 2116
int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
  DBUG_ENTER("mysql_repair_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2117 2118 2119
				"repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR,
				&prepare_for_repair,
				&handler::repair));
2120 2121
}

2122

2123 2124 2125 2126
int mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
  DBUG_ENTER("mysql_optimize_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2127 2128
				"optimize", TL_WRITE, 1,0,0,
				&handler::optimize));
2129 2130 2131
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2132 2133 2134 2135 2136
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
2137 2138
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2139 2140

  RETURN VALUES
2141 2142
    0	  ok
   -1	  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2143 2144
*/

2145
int mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
2146
			     LEX_STRING *key_cache_name)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2147
{
2148
  HA_CHECK_OPT check_opt;
2149
  KEY_CACHE *key_cache;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2150
  DBUG_ENTER("mysql_assign_to_keycache");
2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162

  check_opt.init();
  pthread_mutex_lock(&LOCK_global_system_variables);
  if (!(key_cache= get_key_cache(key_cache_name)))
  {
    pthread_mutex_unlock(&LOCK_global_system_variables);
    my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
    DBUG_RETURN(-1);
  }
  pthread_mutex_unlock(&LOCK_global_system_variables);
  check_opt.key_cache= key_cache;
  DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
2163 2164
				"assign_to_keycache", TL_READ_NO_INSERT, 0,
				0, 0, &handler::assign_to_keycache));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2165 2166
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2167 2168 2169 2170 2171 2172

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

  SYNOPSIS
    reassign_keycache_tables()
2173 2174 2175
    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
2176

2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189
  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
2190
    0	  ok
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2191 2192
*/

2193 2194
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
2195 2196 2197
{
  DBUG_ENTER("reassign_keycache_tables");

2198 2199
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
2200
  src_cache->param_buff_size= 0;		// Free key cache
2201 2202
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2203
  DBUG_RETURN(0);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2204 2205 2206
}


igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2207 2208 2209 2210 2211
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2212 2213
    thd		Thread object
    tables	Table list (one table only)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2214 2215

  RETURN VALUES
2216 2217
    0	  ok
   -1	  error
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2218 2219 2220 2221 2222 2223
*/

int mysql_preload_keys(THD* thd, TABLE_LIST* tables)
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2224 2225
				"preload_keys", TL_READ, 0, 0, 0,
				&handler::preload_keys));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2226 2227 2228
}


venu@myvenu.com's avatar
venu@myvenu.com committed
2229 2230 2231 2232 2233
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2234 2235
    thd		Thread object
    table	Table list (one table only)
venu@myvenu.com's avatar
venu@myvenu.com committed
2236 2237 2238 2239
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
2240 2241
    0	  ok
    -1	error
venu@myvenu.com's avatar
venu@myvenu.com committed
2242 2243
*/

2244 2245 2246
int 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
2247 2248 2249 2250 2251 2252 2253
{
  TABLE **tmp_table;
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char *db= table->db;
  char *table_name= table->real_name;
  char *src_db= thd->db;
  char *src_table= table_ident->table.str;
2254 2255
  int  err, res= -1;
  TABLE_LIST src_tables_list;
venu@myvenu.com's avatar
venu@myvenu.com committed
2256 2257 2258 2259 2260 2261 2262 2263 2264 2265
  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)))
  {
2266
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
2267
    DBUG_RETURN(-1);
venu@myvenu.com's avatar
venu@myvenu.com committed
2268
  }
2269

2270 2271 2272
  src_tables_list.db= table_ident->db.str ? table_ident->db.str : thd->db;
  src_tables_list.real_name= table_ident->table.str;
  src_tables_list.next= 0;
2273

2274 2275
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
2276 2277 2278 2279 2280

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
    strxmov(src_path, (*tmp_table)->path, reg_ext, NullS);
  else
  {
2281 2282 2283 2284
    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));
2285 2286
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, src_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
2287 2288 2289
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2290
      goto err;
venu@myvenu.com's avatar
venu@myvenu.com committed
2291 2292 2293 2294 2295 2296
    }
  }

  /*
    Validate the destination table

2297
    skip the destination table name checking as this is already
venu@myvenu.com's avatar
venu@myvenu.com committed
2298 2299 2300 2301 2302 2303
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2304 2305 2306
    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);
2307 2308
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
venu@myvenu.com's avatar
venu@myvenu.com committed
2309 2310 2311 2312
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2313 2314 2315
    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
2316 2317 2318 2319
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2320
  /*
venu@myvenu.com's avatar
venu@myvenu.com committed
2321
    Create a new table by copying from source table
2322
  */
2323 2324
  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
2325 2326

  /*
2327 2328
    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
2329 2330
    and temporary tables).
  */
2331
  *fn_ext(dst_path)= 0;
venu@myvenu.com's avatar
venu@myvenu.com committed
2332
  err= ha_create_table(dst_path, create_info, 1);
2333

venu@myvenu.com's avatar
venu@myvenu.com committed
2334 2335 2336 2337
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2338 2339
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2340
      goto err;     /* purecov: inspected */
venu@myvenu.com's avatar
venu@myvenu.com committed
2341 2342 2343 2344
    }
  }
  else if (err)
  {
2345 2346 2347
    (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
2348
  }
2349 2350 2351 2352

  // Must be written before unlock
  mysql_update_log.write(thd,thd->query, thd->query_length);
  if (mysql_bin_log.is_open())
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2353
  {
2354 2355
    thd->clear_error();
    Query_log_event qinfo(thd, thd->query, thd->query_length,
2356
			  test(create_info->options &
2357 2358
			       HA_LEX_CREATE_TMP_TABLE), 
			  FALSE);
2359
    mysql_bin_log.write(&qinfo);
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2360
  }
2361
  res= 0;
2362
  goto err;
2363

venu@myvenu.com's avatar
venu@myvenu.com committed
2364 2365 2366 2367
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2368 2369 2370 2371
    my_snprintf(warn_buff, sizeof(warn_buff),
		ER(ER_TABLE_EXISTS_ERROR), table_name);
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
		 ER_TABLE_EXISTS_ERROR,warn_buff);
2372
    res= 0;
venu@myvenu.com's avatar
venu@myvenu.com committed
2373
  }
2374 2375 2376 2377 2378 2379 2380 2381
  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
2382 2383 2384
}


2385 2386
int mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2387 2388 2389 2390 2391 2392
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2393 2394
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2395 2396
				"analyze", lock_type, 1,0,0,
				&handler::analyze));
2397 2398 2399 2400 2401
}


int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2402 2403 2404 2405 2406 2407
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2408 2409
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2410 2411 2412
				"check", lock_type,
				0, HA_OPEN_FOR_REPAIR, 0,
				&handler::check));
2413 2414
}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2416
/* table_list should contain just one table */
monty@mysql.com's avatar
monty@mysql.com committed
2417 2418 2419 2420
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
2421 2422 2423 2424 2425 2426
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

monty@mysql.com's avatar
monty@mysql.com committed
2427 2428 2429 2430
  /*
    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
2431 2432 2433

  thd->proc_info="discard_or_import_tablespace";

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

monty@mysql.com's avatar
monty@mysql.com committed
2436 2437 2438 2439 2440
 /*
   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
2441 2442 2443 2444 2445
  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2446

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2447 2448 2449 2450 2451 2452 2453
  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
2454 2455 2456 2457
  /*
    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
2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
  query_cache_invalidate3(thd, table_list, 0);

  /* The ALTER TABLE is always in its own transaction */
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
    goto err;
  mysql_update_log.write(thd, thd->query,thd->query_length);
  if (mysql_bin_log.is_open())
  {
2469
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2470 2471 2472 2473
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2474
  thd->tablespace_op=FALSE;
monty@mysql.com's avatar
monty@mysql.com committed
2475 2476
  if (error == 0)
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2477
    send_ok(thd);
monty@mysql.com's avatar
monty@mysql.com committed
2478
    DBUG_RETURN(0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2479
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2480 2481 2482 2483 2484

  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
2485
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2486

2487 2488

#ifdef NOT_USED
2489 2490 2491 2492 2493 2494 2495
/*
  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.
*/

2496
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2497 2498 2499 2500 2501
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2502 2503 2504 2505 2506 2507 2508 2509
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2510 2511 2512
  DBUG_ENTER("mysql_create_index");

  /*
2513 2514 2515
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2516

2517 2518
    Open the table to have access to the correct table handler.
  */
2519 2520 2521 2522
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2523 2524 2525 2526 2527
    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.
  */
2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538
  for (f_ptr= table->field; *f_ptr; f_ptr++)
  {
    create_field *c_fld= new create_field(*f_ptr, *f_ptr);
    c_fld->unireg_check= Field::NONE; /*avoid multiple auto_increments*/
    fields.push_back(c_fld);
  }
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.default_table_charset= thd->variables.collation_database;
  db_options= 0;
  if (mysql_prepare_table(thd, &create_info, fields,
2539 2540 2541
			  keys, /*tmp_table*/ 0, db_options, table->file,
			  key_info_buffer, key_count,
			  /*select_field_count*/ 0))
2542 2543 2544
    DBUG_RETURN(-1);

  /*
2545 2546 2547
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2548 2549 2550 2551
  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)&
2552
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2553 2554
      break ;
  }
2555
  if ((idx < key_count)|| !key_count)
2556 2557 2558 2559 2560 2561 2562 2563
  {
    /* Re-initialize the create_info, which was changed by prepare table. */
    bzero((char*) &create_info,sizeof(create_info));
    create_info.db_type=DB_TYPE_DEFAULT;
    create_info.default_table_charset= thd->variables.collation_database;
    /* Cleanup the fields list. We do not want to create existing fields. */
    fields.delete_elements();
    if (real_alter_table(thd, table_list->db, table_list->real_name,
2564 2565 2566 2567
			 &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);*/
2568 2569 2570 2571 2572
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
2573 2574 2575 2576 2577 2578 2579 2580
	(my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
		     table_list->db, (lower_case_table_names == 2) ?
		     table_list->alias: table_list->real_name, reg_ext) >=
	 (int) sizeof(path)) ||
	! unpack_filename(path, path) ||
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
      /* don't need to free((gptr) key_info_buffer);*/
2581 2582
      DBUG_RETURN(-1);
  }
2583
  /* don't need to free((gptr) key_info_buffer);*/
2584 2585 2586 2587
  DBUG_RETURN(0);
}


2588 2589
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
2590 2591
{
  List<create_field> fields;
2592
  List<Key>	     keys;
2593 2594
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2595 2596 2597 2598 2599 2600 2601 2602 2603
  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];
2604 2605 2606
  DBUG_ENTER("mysql_drop_index");

  /*
2607 2608 2609
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2610

2611 2612
    Open the table to have access to the correct table handler.
  */
2613 2614 2615 2616
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2617 2618 2619
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
2620 2621 2622 2623
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
2624 2625
    Get the number of each key and check if it can be created online.
  */
2626 2627 2628 2629 2630 2631 2632 2633 2634
  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))
2635
	break;
2636 2637 2638 2639 2640 2641 2642 2643
    }
    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);
    }
    /*
2644 2645 2646
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
2647 2648
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
2649
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660
      break ;
    key_numbers[key_count++]= idx;
  }

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

  if ((drop_key)|| (drop.elements<= 0))
  {
    if (real_alter_table(thd, table_list->db, table_list->real_name,
2661 2662 2663
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
2664 2665 2666 2667 2668 2669 2670
      /*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)||
2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681
	mysql_prepare_table(thd, &create_info, fields,
			    keys, /*tmp_table*/ 0, db_options, table->file,
			    key_info_buffer, key_count,
			    /*select_field_count*/ 0)||
	(snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
		  table_list->db, (lower_case_table_names == 2)?
		  table_list->alias: table_list->real_name, reg_ext)>=
	 (int)sizeof(path))||
	! unpack_filename(path, path)||
	mysql_create_frm(thd, path, &create_info,
			 fields, key_count, key_info_buffer, table->file))
2682 2683 2684 2685 2686 2687 2688
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
2689
#endif /* NOT_USED */
2690 2691


2692 2693 2694
/*
  Alter table
*/
2695

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2696
int mysql_alter_table(THD *thd,char *new_db, char *new_name,
2697 2698
		      HA_CREATE_INFO *create_info,
		      TABLE_LIST *table_list,
2699 2700
		      List<create_field> &fields, List<Key> &keys,
		      uint order_num, ORDER *order,
2701
		      enum enum_duplicates handle_duplicates, bool ignore,
2702
		      ALTER_INFO *alter_info, bool do_send_ok)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2703
{
2704
  TABLE *table,*new_table;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2705
  int error;
2706 2707
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
2708
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
2709 2710
  ha_rows copied,deleted;
  ulonglong next_insert_id;
2711
  uint db_create_options, used_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2712
  enum db_type old_db_type,new_db_type;
2713
  DBUG_ENTER("mysql_alter_table");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2714 2715 2716

  thd->proc_info="init";
  table_name=table_list->real_name;
2717 2718
  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2719
  db=table_list->db;
monty@mysql.com's avatar
monty@mysql.com committed
2720
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
2721
    new_db= db;
2722
  used_fields=create_info->used_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2723

2724
  mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL);
2725
  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
2726
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
2727
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
2728
						   alter_info->tablespace_op));
2729 2730 2731
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2732 2733 2734 2735
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
2736
    strmov(new_alias= new_alias_buff, new_name);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2737
    if (lower_case_table_names)
2738 2739 2740
    {
      if (lower_case_table_names != 2)
      {
2741
	my_casedn_str(files_charset_info, new_name_buff);
2742
	new_alias= new_name;			// Create lower case table name
2743
      }
2744
      my_casedn_str(files_charset_info, new_name);
2745
    }
2746
    if (new_db == db &&
2747
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
2748 2749
    {
      /*
2750 2751
	Source and destination table names are equal: make later check
	easier.
2752
      */
2753
      new_alias= new_name= table_name;
2754
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2755 2756 2757 2758
    else
    {
      if (table->tmp_table)
      {
2759 2760 2761 2762 2763
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
	  my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
	  DBUG_RETURN(-1);
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2764 2765 2766
      }
      else
      {
2767 2768 2769
	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),
2770 2771 2772 2773 2774 2775
		    F_OK))
	{
	  /* Table will be closed in do_command() */
	  my_error(ER_TABLE_EXISTS_ERROR,MYF(0), new_alias);
	  DBUG_RETURN(-1);
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2776 2777 2778 2779
      }
    }
  }
  else
2780 2781 2782 2783
  {
    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
2784 2785 2786 2787

  old_db_type=table->db_type;
  if (create_info->db_type == DB_TYPE_DEFAULT)
    create_info->db_type=old_db_type;
2788 2789 2790 2791 2792
  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,
2793 2794 2795 2796
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			new_name);
2797
  }
2798
  if (create_info->row_type == ROW_TYPE_NOT_USED)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2799 2800 2801
    create_info->row_type=table->row_type;

  thd->proc_info="setup";
2802
  if (alter_info->is_simple && !table->tmp_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2803 2804
  {
    error=0;
2805
    if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2806
    {
2807 2808 2809 2810 2811 2812
      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))
      {
2813 2814
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
	error= -1;
2815 2816 2817
      }
      else
      {
2818 2819 2820 2821
	*fn_ext(new_name)=0;
	close_cached_table(thd, table);
	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
	  error= -1;
2822 2823
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2824
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2825

2826
    if (!error)
2827
    {
2828
      switch (alter_info->keys_onoff) {
2829
      case LEAVE_AS_IS:
2830
	break;
2831
      case ENABLE:
2832
	VOID(pthread_mutex_lock(&LOCK_open));
2833
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
2834
	VOID(pthread_mutex_unlock(&LOCK_open));
2835
	error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2836
	/* COND_refresh will be signaled in close_thread_tables() */
2837 2838
	break;
      case DISABLE:
2839 2840 2841
	VOID(pthread_mutex_lock(&LOCK_open));
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
	VOID(pthread_mutex_unlock(&LOCK_open));
2842
	error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2843
	/* COND_refresh will be signaled in close_thread_tables() */
2844
	break;
2845
      }
2846
    }
2847
    if (error == HA_ERR_WRONG_COMMAND)
serg@serg.mylan's avatar
serg@serg.mylan committed
2848 2849
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2850 2851
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->table_name);
serg@serg.mylan's avatar
serg@serg.mylan committed
2852 2853
      error=0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2854 2855
    if (!error)
    {
2856 2857 2858
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2859
	thd->clear_error();
2860
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2861
	mysql_bin_log.write(&qinfo);
2862
      }
2863 2864
      if (do_send_ok)
        send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2865
    }
2866
    else if (error > 0)
2867 2868
    {
      table->file->print_error(error, MYF(0));
2869
      error= -1;
2870
    }
2871
    table_list->table=0;				// For query cache
2872
    query_cache_invalidate3(thd, table_list, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2873 2874 2875 2876
    DBUG_RETURN(error);
  }

  /* Full alter table */
2877 2878 2879 2880 2881 2882 2883 2884

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

2888
  restore_record(table,default_values);		// Empty record for DEFAULT
2889
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2890
  List_iterator<create_field> def_it(fields);
2891
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
2892 2893
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
2894 2895
  create_field *def;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2896
  /*
2897
    First collect all fields from table which isn't in drop_list
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908
  */

  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 &&
2909
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
2910
      {
2911 2912 2913 2914 2915 2916 2917 2918
	/* Reset auto_increment value if it was dropped */
	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
	    !(used_fields & HA_CREATE_USED_AUTO))
	{
	  create_info->auto_increment_value=0;
	  create_info->used_fields|=HA_CREATE_USED_AUTO;
	}
	break;
2919
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2920 2921 2922 2923 2924 2925 2926 2927 2928 2929
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
2930
      if (def->change &&
2931 2932
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2933 2934
    }
    if (def)
2935
    {						// Field is changed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2936
      def->field=field;
2937 2938
      if (!def->after)
      {
2939 2940
	create_list.push_back(def);
	def_it.remove();
2941
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2942 2943
    }
    else
2944
    {						// Use old field value
2945
      create_list.push_back(def=new create_field(field,field));
2946
      alter_it.rewind();			// Change default if ALTER
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2947 2948 2949
      Alter_column *alter;
      while ((alter=alter_it++))
      {
2950 2951
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2952 2953 2954
      }
      if (alter)
      {
2955 2956 2957 2958 2959 2960 2961
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT,MYF(0),def->change);
	  DBUG_RETURN(-1);
	}
	def->def=alter->def;			// Use new default
	alter_it.remove();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2962 2963 2964 2965 2966
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
2967
  while ((def=def_it++))			// Add new columns
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2968
  {
2969
    if (def->change && ! def->field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981
    {
      my_error(ER_BAD_FIELD_ERROR,MYF(0),def->change,table_name);
      DBUG_RETURN(-1);
    }
    if (!def->after)
      create_list.push_back(def);
    else if (def->after == first_keyword)
      create_list.push_front(def);
    else
    {
      create_field *find;
      find_it.rewind();
2982
      while ((find=find_it++))			// Add new columns
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2983
      {
2984 2985
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2986 2987 2988
      }
      if (!find)
      {
2989 2990
	my_error(ER_BAD_FIELD_ERROR,MYF(0),def->after,table_name);
	DBUG_RETURN(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2991
      }
2992
      find_it.after(def);			// Put element after this
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2993 2994
    }
  }
2995
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2996
  {
2997 2998
    my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_info->alter_list.head()->name,
	     table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2999 3000 3001 3002 3003 3004 3005 3006 3007
    DBUG_RETURN(-1);
  }
  if (!create_list.elements)
  {
    my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
    DBUG_RETURN(-1);
  }

  /*
3008 3009
    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
3010 3011 3012 3013 3014 3015 3016 3017 3018
  */

  List_iterator<Key> key_it(keys);
  List_iterator<create_field> field_it(create_list);
  List<key_part_spec> key_parts;

  KEY *key_info=table->key_info;
  for (uint i=0 ; i < table->keys ; i++,key_info++)
  {
3019
    char *key_name= key_info->name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3020 3021 3022 3023 3024
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
3025 3026
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
	break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038
    }
    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)
3039
	continue;				// Wrong field (from UNIREG)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3040 3041 3042 3043 3044
      const char *key_part_name=key_part->field->field_name;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
3045 3046 3047 3048 3049 3050 3051 3052 3053
	if (cfield->change)
	{
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
	    break;
	}
	else if (!my_strcasecmp(system_charset_info,
				key_part_name, cfield->field_name))
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3054 3055
      }
      if (!cfield)
3056
	continue;				// Field is removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3057
      uint key_part_length=key_part->length;
3058 3059 3060 3061 3062 3063 3064
      if (cfield->field)			// Not new field
      {						// Check if sub key
	if (cfield->field->type() != FIELD_TYPE_BLOB &&
	    (cfield->field->pack_length() == key_part_length ||
	     cfield->length <= key_part_length /
			       key_part->field->charset()->mbmaxlen))
	  key_part_length=0;			// Use whole field
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3065
      }
3066
      key_part_length /= key_part->field->charset()->mbmaxlen;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3067
      key_parts.push_back(new key_part_spec(cfield->field_name,
3068
					    key_part_length));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3069 3070
    }
    if (key_parts.elements)
3071
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
3072 3073 3074 3075 3076 3077 3078 3079
				 (key_info->flags & HA_NOSAME ?
				 (!my_strcasecmp(system_charset_info,
						 key_name, primary_key_name) ?
				  Key::PRIMARY	: Key::UNIQUE) :
				  (key_info->flags & HA_FULLTEXT ?
				   Key::FULLTEXT : Key::MULTIPLE)),
				 key_name,
				 key_info->algorithm,
3080
                                 test(key_info->flags & HA_GENERATED_KEY),
3081
				 key_parts));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3082 3083 3084
  }
  {
    Key *key;
3085
    while ((key=key_it++))			// Add new keys
3086 3087
    {
      if (key->type != Key::FOREIGN_KEY)
3088
	key_list.push_back(key);
3089
      if (key->name &&
3090
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
3091
      {
3092 3093
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
	DBUG_RETURN(-1);
3094
      }
3095
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3096 3097
  }

3098
  if (alter_info->drop_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3099
  {
3100 3101
    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
3102 3103
    goto err;
  }
3104
  if (alter_info->alter_list.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3105
  {
3106 3107
    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
3108 3109 3110
    goto err;
  }

3111
  db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD);
3112 3113
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3114 3115
  /* Safety fix for innodb */
  if (lower_case_table_names)
3116
    my_casedn_str(files_charset_info, tmp_name);
3117 3118 3119 3120
  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
3121 3122 3123
  create_info->db_type=new_db_type;
  if (!create_info->comment)
    create_info->comment=table->comment;
3124 3125 3126 3127 3128

  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
3129 3130 3131 3132 3133 3134 3135
    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 |
3136
			  HA_OPTION_NO_DELAY_KEY_WRITE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3137 3138 3139 3140 3141
  create_info->table_options|= db_create_options;

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

3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166
  /*
    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.
  */

3167
  if (!strcmp(db, new_db))		// Ignore symlink if db changed
3168 3169 3170 3171 3172 3173
  {
    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,
3174 3175
					   create_info->index_file_name,
					   1);
3176 3177 3178 3179 3180 3181
    }
    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,
3182 3183
					  create_info->data_file_name,
					  1);
3184 3185
    }
  }
3186 3187
  else
    create_info->data_file_name=create_info->index_file_name=0;
3188
  {
3189 3190
    /* We don't log the statement, it will be logged later. */
    tmp_disable_binlog(thd);
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3191
    error= mysql_create_table(thd, new_db, tmp_name,
serg@serg.mylan's avatar
serg@serg.mylan committed
3192
                              create_info,create_list,key_list,1,0);
3193 3194
    reenable_binlog(thd);
    if (error)
3195 3196
      DBUG_RETURN(error);
  }
3197 3198 3199
  if (table->tmp_table)
    new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
  else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3200
  {
3201
    char path[FN_REFLEN];
3202 3203
    my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
		new_db, tmp_name);
3204 3205 3206 3207 3208 3209 3210
    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
3211 3212
  }

3213

3214 3215
  /* We don't want update TIMESTAMP fields during ALTER TABLE. */
  new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3216
  new_table->next_number_field=new_table->found_next_number_field;
3217
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3218 3219
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3220
  next_insert_id=thd->next_insert_id;		// Remember for loggin
3221 3222 3223
  copied=deleted=0;
  if (!new_table->is_view)
    error=copy_data_between_tables(table,new_table,create_list,
3224
				   handle_duplicates, ignore,
3225 3226
				   order_num, order, &copied, &deleted);
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3227
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3228 3229 3230 3231 3232 3233

  if (table->tmp_table)
  {
    /* We changed a temporary table */
    if (error)
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3234
      /*
3235 3236
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3237
      */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3238 3239 3240
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3241 3242 3243 3244 3245 3246
    /* 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
3247 3248
    /* Remove link to old table and rename the new one */
    close_temporary_table(thd,table->table_cache_key,table_name);
3249 3250
    /* Should pass the 'new_name' as we store table name in the cache */
    if (rename_temporary_table(thd, new_table, new_db, new_name))
3251
    {						// Fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3252 3253 3254 3255
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3256 3257 3258
    mysql_update_log.write(thd, thd->query,thd->query_length);
    if (mysql_bin_log.is_open())
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3259
      thd->clear_error();
3260
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3261 3262
      mysql_bin_log.write(&qinfo);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3263 3264 3265
    goto end_temporary;
  }

3266
  intern_close_table(new_table);		/* close temporary table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3267 3268 3269 3270 3271 3272 3273 3274
  my_free((gptr) new_table,MYF(0));
  VOID(pthread_mutex_lock(&LOCK_open));
  if (error)
  {
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
3275

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3276
  /*
3277 3278 3279
    Data is copied.  Now we rename the old table to a temp name,
    rename the new one to the old name, remove all entries from the old table
    from the cash, free all locks, close the old table and remove it.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3280 3281 3282
  */

  thd->proc_info="rename result table";
3283 3284
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3285 3286
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3287
  if (new_name != table_name || new_db != db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

3299 3300 3301 3302 3303
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3304
      Win32 and InnoDB can't drop a table that is in use, so we must
3305
      close the original table at before doing the rename
3306
    */
3307
    table_name=thd->strdup(table_name);		// must be saved
3308
    if (close_cached_table(thd, table))
3309
    {						// Aborted
3310 3311 3312 3313
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
3314
    table=0;					// Marker that table is closed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3315
  }
3316 3317
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  else
3318
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3319 3320
#endif

3321

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

end_temporary:
3428 3429 3430
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3431 3432
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3433 3434 3435 3436 3437 3438 3439 3440 3441
  thd->some_tables_deleted=0;
  DBUG_RETURN(0);

 err:
  DBUG_RETURN(-1);
}


static int
3442
copy_data_between_tables(TABLE *from,TABLE *to,
3443 3444
			 List<create_field> &create,
			 enum enum_duplicates handle_duplicates,
3445
                         bool ignore,
3446 3447 3448
			 uint order_num, ORDER *order,
			 ha_rows *copied,
			 ha_rows *deleted)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3449 3450
{
  int error;
3451
  Copy_field *copy,*copy_end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3452 3453
  ulong found_count,delete_count;
  THD *thd= current_thd;
3454 3455 3456 3457 3458 3459
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3460
  ha_rows examined_rows;
3461
  bool auto_increment_field_copied= 0;
3462
  ulong save_sql_mode;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3463 3464
  DBUG_ENTER("copy_data_between_tables");

3465 3466 3467 3468 3469 3470
  /*
    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
  */
3471
  error= ha_enable_transaction(thd, FALSE);
3472 3473
  if (error)
    DBUG_RETURN(-1);
3474
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3475
  if (!(copy= new Copy_field[to->fields]))
3476
    DBUG_RETURN(-1);				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3477

3478 3479
  if (to->file->external_lock(thd, F_WRLCK))
    DBUG_RETURN(-1);
3480
  from->file->info(HA_STATUS_VARIABLE);
3481
  to->file->start_bulk_insert(from->file->records);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3482

3483 3484
  save_sql_mode= thd->variables.sql_mode;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3485 3486 3487 3488 3489 3490 3491
  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
3492 3493
    {
      if (*ptr == to->next_number_field)
3494
      {
3495
        auto_increment_field_copied= TRUE;
3496 3497 3498 3499 3500 3501 3502 3503 3504
        /*
          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
3505
      (copy_end++)->set(*ptr,def->field,0);
3506 3507
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3508 3509
  }

3510 3511
  found_count=delete_count=0;

monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
3512 3513
  if (order)
  {
igor@hundin.mysql.fi's avatar
igor@hundin.mysql.fi committed
3514
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
3515
					      MYF(MY_FAE | MY_ZEROFILL));
3516 3517
    bzero((char*) &tables,sizeof(tables));
    tables.table = from;
3518
    tables.alias = tables.real_name= from->real_name;
3519
    tables.db	 = from->table_cache_key;
3520 3521
    error=1;

3522
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
3523 3524 3525 3526 3527 3528 3529
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
		    &tables, fields, all_fields, order) ||
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
					     (SQL_SELECT *) 0, HA_POS_ERROR,
					     &examined_rows))
	== HA_POS_ERROR)
3530 3531 3532
      goto err;
  };

3533 3534 3535 3536
  /* Handler must be told explicitly to retrieve all columns, because
     this function does not set field->query_id in the columns to the
     current query id */
  from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3537
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
3538
  if (ignore ||
3539
      handle_duplicates == DUP_REPLACE)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3540
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
3541
  thd->row_count= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3542 3543 3544 3545 3546 3547 3548 3549
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
      my_error(ER_SERVER_SHUTDOWN,MYF(0));
      error= 1;
      break;
    }
3550
    thd->row_count++;
3551 3552
    if (to->next_number_field)
    {
3553
      if (auto_increment_field_copied)
3554
        to->auto_increment_field_not_null= TRUE;
3555 3556 3557 3558 3559
      else
        to->next_number_field->reset();
    }
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3560
      copy_ptr->do_copy(copy_ptr);
3561
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3562 3563
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
3564
      if ((!ignore &&
3565 3566 3567
	   handle_duplicates != DUP_REPLACE) ||
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3568
      {
3569 3570
	to->file->print_error(error,MYF(0));
	break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3571 3572 3573 3574
      }
      delete_count++;
    }
    else
3575
      found_count++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3576 3577
  }
  end_read_record(&info);
3578
  free_io_cache(from);
3579
  delete [] copy;				// This is never 0
serg@serg.mylan's avatar
serg@serg.mylan committed
3580 3581

  if (to->file->end_bulk_insert() && !error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3582
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
3583
    to->file->print_error(my_errno,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3584 3585
    error=1;
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3586
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
3587

3588
  ha_enable_transaction(thd,TRUE);
3589

3590 3591 3592 3593 3594 3595 3596 3597
  /*
    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;
3598

3599
 err:
3600
  thd->variables.sql_mode= save_sql_mode;
3601
  free_io_cache(from);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3602 3603
  *copied= found_count;
  *deleted=delete_count;
3604 3605
  if (to->file->external_lock(thd,F_UNLCK))
    error=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3606 3607
  DBUG_RETURN(error > 0 ? -1 : 0);
}
3608

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

3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631
/*
  Recreates tables by calling mysql_alter_table().

  SYNOPSIS
    mysql_recreate_table()
    thd			Thread handler
    tables		Tables to recreate
    do_send_ok          If we should send_ok() or leave it to caller

 RETURN
    Like mysql_alter_table().
*/
int mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
                         bool do_send_ok)
{
  DBUG_ENTER("mysql_recreate_table");
  LEX *lex= thd->lex;
  HA_CREATE_INFO create_info;
  lex->create_list.empty();
  lex->key_list.empty();
  lex->col_list.empty();
  lex->alter_info.reset();
3632
  lex->alter_info.is_simple= 0;                 // Force full recreate
3633 3634 3635 3636 3637 3638 3639
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.row_type=ROW_TYPE_DEFAULT;
  create_info.default_table_charset=default_charset_info;
  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
                                table_list, lex->create_list,
                                lex->key_list, 0, (ORDER *) 0,
3640
                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
3641 3642 3643
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3644
int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
3645 3646 3647 3648 3649
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
3650
  DBUG_ENTER("mysql_checksum_table");
3651 3652 3653 3654 3655 3656 3657 3658

  field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
  item->maybe_null= 1;
  field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21));
  item->maybe_null= 1;
  if (protocol->send_fields(&field_list, 1))
    DBUG_RETURN(-1);

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3659
  for (table= tables; table; table= table->next)
3660 3661
  {
    char table_name[NAME_LEN*2+2];
3662
    TABLE *t;
3663

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3664 3665 3666
    strxmov(table_name, table->db ,".", table->real_name, NullS);

    t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT);
3667
    thd->clear_error();			// these errors shouldn't get client
3668 3669 3670 3671

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

3672
    if (!t)
3673
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3674
      /* Table didn't exist */
3675 3676 3677 3678 3679
      protocol->store_null();
      thd->net.last_error[0]=0;
    }
    else
    {
3680
      t->pos_in_table_list= table;
3681

3682
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
3683 3684
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
3685
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
3686 3687
	       (check_opt->flags & T_QUICK))
	protocol->store_null();
3688 3689
      else
      {
3690 3691 3692 3693 3694 3695 3696 3697
	/* 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);

3698
	if (t->file->ha_rnd_init(1))
3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725
	  protocol->store_null();
	else
	{
	  while (!t->file->rnd_next(t->record[0]))
	  {
	    ha_checksum row_crc= 0;
	    if (t->record[0] != (byte*) t->field[0]->ptr)
	      row_crc= my_checksum(row_crc, t->record[0],
				   ((byte*) t->field[0]->ptr) - t->record[0]);

	    for (uint i= 0; i < t->fields; i++ )
	    {
	      Field *f= t->field[i];
	      if (f->type() == FIELD_TYPE_BLOB)
	      {
		String tmp;
		f->val_str(&tmp);
		row_crc= my_checksum(row_crc, (byte*) tmp.ptr(), tmp.length());
	      }
	      else
		row_crc= my_checksum(row_crc, (byte*) f->ptr,
				     f->pack_length());
	    }

	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
3726
          t->file->ha_rnd_end();
3727
	}
3728
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3729
      thd->clear_error();
3730
      close_thread_tables(thd);
3731
      table->table=0;				// For query cache
3732 3733 3734 3735 3736 3737 3738
    }
    if (protocol->write())
      goto err;
  }

  send_eof(thd);
  DBUG_RETURN(0);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3739

3740
 err:
3741
  close_thread_tables(thd);			// Shouldn't be needed
3742 3743 3744 3745
  if (table)
    table->table=0;
  DBUG_RETURN(-1);
}