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

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

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

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


/* drop and alter of tables */

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

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

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

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

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

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

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

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

  RETURN
60 61
    0		ok.  In this case ok packet is sent to user
    -1		Error  (Error message given but not sent to user)
unknown's avatar
unknown committed
62 63

*/
unknown's avatar
unknown committed
64

65
int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
66
		   my_bool drop_temporary)
unknown's avatar
unknown committed
67
{
unknown's avatar
merge  
unknown committed
68
  int error= 0;
unknown's avatar
unknown committed
69 70 71 72 73 74 75 76
  DBUG_ENTER("mysql_rm_table");

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

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

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

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

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

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

/*
 delete (drop) tables.

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

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

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

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

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

  pthread_mutex_unlock(&LOCK_open);

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

unknown's avatar
unknown committed
149

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

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

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

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

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

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

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

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

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

284
  unlock_table_names(thd, tables);
285
  DBUG_RETURN(error);
unknown's avatar
unknown committed
286 287 288 289
}


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

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

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

348 349
/*
  Check TYPELIB (set or enum) for duplicates
350

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

  DESCRIPTION
358
    This function prints an warning for each value in list
359 360 361 362 363 364 365
    which has some duplicates on its right

  RETURN VALUES
    void
*/

void check_duplicates_in_interval(const char *set_or_name,
366
				  const char *name, TYPELIB *typelib)
367 368 369 370 371 372 373 374 375 376 377 378 379
{
  unsigned int old_count= typelib->count;
  const char **old_type_names= typelib->type_names;

  old_count= typelib->count;
  old_type_names= typelib->type_names;
  const char **cur_value= typelib->type_names;
  for ( ; typelib->count > 1; cur_value++)
  {
    typelib->type_names++;
    typelib->count--;
    if (find_type((char*)*cur_value,typelib,1))
    {
unknown's avatar
unknown committed
380
      push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
381 382 383
			  ER_DUPLICATED_VALUE_IN_TYPE,
			  ER(ER_DUPLICATED_VALUE_IN_TYPE),
			  name,*cur_value,set_or_name);
384 385 386 387 388
    }
  }
  typelib->count= old_count;
  typelib->type_names= old_type_names;
}
389

unknown's avatar
unknown committed
390
/*
391
  Preparation for table creation
unknown's avatar
unknown committed
392 393

  SYNOPSIS
394
    mysql_prepare_table()
395 396 397 398
    thd			Thread object
    create_info		Create information (like MAX_ROWS)
    fields		List of fields to create
    keys		List of keys to create
unknown's avatar
unknown committed
399

400
  DESCRIPTION
401
    Prepares the table and key structures for table creation.
unknown's avatar
unknown committed
402 403

  RETURN VALUES
404 405
    0	ok
    -1	error
unknown's avatar
unknown committed
406
*/
unknown's avatar
unknown committed
407

408
int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
409 410 411 412
			List<create_field> &fields,
			List<Key> &keys, bool tmp_table, uint &db_options,
			handler *file, KEY *&key_info_buffer,
			uint *key_count, int select_field_count)
unknown's avatar
unknown committed
413
{
414 415 416 417 418
  const char	*key_name;
  create_field	*sql_field,*dup_field;
  uint		field,null_fields,blob_columns;
  ulong		pos;
  KEY		*key_info;
unknown's avatar
unknown committed
419
  KEY_PART_INFO *key_part_info;
420 421 422
  int		timestamps= 0, timestamps_with_niladic= 0;
  int		field_no,dup_no;
  int		select_field_pos,auto_increment=0;
423
  DBUG_ENTER("mysql_prepare_table");
unknown's avatar
unknown committed
424 425

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

429
  for (field_no=0; (sql_field=it++) ; field_no++)
unknown's avatar
unknown committed
430
  {
431
    if (!sql_field->charset)
432 433 434
      sql_field->charset= create_info->default_table_charset;
    /*
      table_charset is set in ALTER TABLE if we want change character set
435 436 437
      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.
438
    */
439
    if (create_info->table_charset && sql_field->charset != &my_charset_bin)
440
      sql_field->charset= create_info->table_charset;
441 442 443 444 445 446 447 448 449 450 451

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

453
    sql_field->create_length_to_internal_length();
454

455
    /* Don't pack keys in old tables if the user has requested this */
unknown's avatar
unknown committed
456
    if ((sql_field->flags & BLOB_FLAG) ||
457 458
	sql_field->sql_type == FIELD_TYPE_VAR_STRING &&
	create_info->row_type != ROW_TYPE_FIXED)
unknown's avatar
unknown committed
459 460 461 462 463
    {
      db_options|=HA_OPTION_PACK_RECORD;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      null_fields++;
464

unknown's avatar
unknown committed
465 466
    if (check_column_name(sql_field->field_name))
    {
467
      my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
unknown's avatar
unknown committed
468 469
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
470

471 472
    /* Check if we have used the same field name before */
    for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
unknown's avatar
unknown committed
473
    {
474
      if (my_strcasecmp(system_charset_info,
475 476
			sql_field->field_name,
			dup_field->field_name) == 0)
unknown's avatar
unknown committed
477
      {
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
	/*
	  If this was a CREATE ... SELECT statement, accept a field
	  redefinition if we are changing a field in the SELECT part
	*/
	if (field_no < select_field_pos || dup_no >= select_field_pos)
	{
	  my_error(ER_DUP_FIELDNAME,MYF(0),sql_field->field_name);
	  DBUG_RETURN(-1);
	}
	else
	{
	  /* Field redefined */
	  sql_field->sql_type=		dup_field->sql_type;
	  sql_field->charset=		(dup_field->charset ?
					 dup_field->charset :
					 create_info->default_table_charset);
	  sql_field->length=		dup_field->length;
	  sql_field->pack_length=	dup_field->pack_length;
	  sql_field->create_length_to_internal_length();
	  sql_field->decimals=		dup_field->decimals;
	  sql_field->flags=		dup_field->flags;
	  sql_field->unireg_check=	dup_field->unireg_check;
	  it2.remove();			// Remove first (create) definition
	  select_field_pos--;
	  break;
	}
unknown's avatar
unknown committed
504 505 506 507
      }
    }
    it2.rewind();
  }
508
  /* If fixed row records, we need one bit to check for deleted rows */
unknown's avatar
unknown committed
509 510 511 512 513 514 515
  if (!(db_options & HA_OPTION_PACK_RECORD))
    null_fields++;
  pos=(null_fields+7)/8;

  it.rewind();
  while ((sql_field=it++))
  {
516 517
    DBUG_ASSERT(sql_field->charset);

unknown's avatar
unknown committed
518 519 520 521 522 523
    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 |
524 525
	pack_length_to_packflag(sql_field->pack_length -
				portable_sizeof_char_ptr);
526
      if (sql_field->charset->state & MY_CS_BINSORT)
527 528
	sql_field->pack_flag|=FIELDFLAG_BINARY;
      sql_field->length=8;			// Unireg field length
unknown's avatar
unknown committed
529 530 531
      sql_field->unireg_check=Field::BLOB_FIELD;
      blob_columns++;
      break;
unknown's avatar
unknown committed
532
    case FIELD_TYPE_GEOMETRY:
unknown's avatar
unknown committed
533
#ifdef HAVE_SPATIAL
unknown's avatar
unknown committed
534
      if (!(file->table_flags() & HA_CAN_GEOMETRY))
535
      {
536 537 538
	my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
			MYF(0), "GEOMETRY");
	DBUG_RETURN(-1);
539
      }
unknown's avatar
unknown committed
540
      sql_field->pack_flag=FIELDFLAG_GEOM |
541 542
	pack_length_to_packflag(sql_field->pack_length -
				portable_sizeof_char_ptr);
unknown's avatar
unknown committed
543
      if (sql_field->charset->state & MY_CS_BINSORT)
544 545
	sql_field->pack_flag|=FIELDFLAG_BINARY;
      sql_field->length=8;			// Unireg field length
unknown's avatar
unknown committed
546 547 548
      sql_field->unireg_check=Field::BLOB_FIELD;
      blob_columns++;
      break;
unknown's avatar
unknown committed
549 550
#else
      my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
551
		      sym_group_geom.name, sym_group_geom.needed_define);
unknown's avatar
unknown committed
552 553
      DBUG_RETURN(-1);
#endif /*HAVE_SPATIAL*/
unknown's avatar
unknown committed
554 555 556
    case FIELD_TYPE_VAR_STRING:
    case FIELD_TYPE_STRING:
      sql_field->pack_flag=0;
557
      if (sql_field->charset->state & MY_CS_BINSORT)
558
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
559 560 561
      break;
    case FIELD_TYPE_ENUM:
      sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
562
	FIELDFLAG_INTERVAL;
563
      if (sql_field->charset->state & MY_CS_BINSORT)
564
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
565
      sql_field->unireg_check=Field::INTERVAL_FIELD;
566
      check_duplicates_in_interval("ENUM",sql_field->field_name,
567
				   sql_field->interval);
unknown's avatar
unknown committed
568 569 570
      break;
    case FIELD_TYPE_SET:
      sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
571
	FIELDFLAG_BITFIELD;
572
      if (sql_field->charset->state & MY_CS_BINSORT)
573
	sql_field->pack_flag|=FIELDFLAG_BINARY;
unknown's avatar
unknown committed
574
      sql_field->unireg_check=Field::BIT_FIELD;
575
      check_duplicates_in_interval("SET",sql_field->field_name,
576
				   sql_field->interval);
unknown's avatar
unknown committed
577
      break;
578
    case FIELD_TYPE_DATE:			// Rest of string types
unknown's avatar
unknown committed
579 580 581 582 583 584 585
    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:
586 587 588
      /* We should replace old TIMESTAMP fields with their newer analogs */
      if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
      {
589 590 591 592 593 594 595
	if (!timestamps)
	{
	  sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
	  timestamps_with_niladic++;
	}
	else
	  sql_field->unireg_check= Field::NONE;
596
      }
597
      else if (sql_field->unireg_check != Field::NONE)
598 599
	timestamps_with_niladic++;

600
      timestamps++;
601
      /* fall-through */
unknown's avatar
unknown committed
602 603
    default:
      sql_field->pack_flag=(FIELDFLAG_NUMBER |
604 605 606 607 608 609
			    (sql_field->flags & UNSIGNED_FLAG ? 0 :
			     FIELDFLAG_DECIMAL) |
			    (sql_field->flags & ZEROFILL_FLAG ?
			     FIELDFLAG_ZEROFILL : 0) |
			    f_settype((uint) sql_field->sql_type) |
			    (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
unknown's avatar
unknown committed
610 611 612 613 614 615 616 617 618
      break;
    }
    if (!(sql_field->flags & NOT_NULL_FLAG))
      sql_field->pack_flag|=FIELDFLAG_MAYBE_NULL;
    sql_field->offset= pos;
    if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
      auto_increment++;
    pos+=sql_field->pack_length;
  }
619 620 621 622 623
  if (timestamps_with_niladic > 1)
  {
    my_error(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,MYF(0));
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
624 625 626 627 628 629
  if (auto_increment > 1)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
  if (auto_increment &&
630
      (file->table_flags() & HA_NO_AUTO_INCREMENT))
unknown's avatar
unknown committed
631 632 633 634 635
  {
    my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0));
    DBUG_RETURN(-1);
  }

636
  if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
unknown's avatar
unknown committed
637 638 639 640 641 642
  {
    my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0));
    DBUG_RETURN(-1);
  }

  /* Create keys */
643

644
  List_iterator<Key> key_iterator(keys), key_iterator2(keys);
645
  uint key_parts=0, fk_key_count=0;
646
  bool primary_key=0,unique_key=0;
647
  Key *key, *key2;
unknown's avatar
unknown committed
648
  uint tmp, key_number;
649 650
  /* special marker for keys to be ignored */
  static char ignore_key[1];
651

652
  /* Calculate number of key segements */
653
  *key_count= 0;
654

unknown's avatar
unknown committed
655 656
  while ((key=key_iterator++))
  {
657 658 659 660 661
    if (key->type == Key::FOREIGN_KEY)
    {
      fk_key_count++;
      foreign_key *fk_key= (foreign_key*) key;
      if (fk_key->ref_columns.elements &&
662
	  fk_key->ref_columns.elements != fk_key->columns.elements)
663
      {
664 665 666 667
	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);
668 669 670
      }
      continue;
    }
671
    (*key_count)++;
unknown's avatar
unknown committed
672
    tmp=file->max_key_parts();
unknown's avatar
unknown committed
673 674 675 676 677
    if (key->columns.elements > tmp)
    {
      my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
      DBUG_RETURN(-1);
    }
678
    if (key->name && strlen(key->name) > NAME_LEN)
unknown's avatar
unknown committed
679
    {
680
      my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
unknown's avatar
unknown committed
681 682
      DBUG_RETURN(-1);
    }
683
    key_iterator2.rewind ();
684
    if (key->type != Key::FOREIGN_KEY)
685
    {
686
      while ((key2 = key_iterator2++) != key)
687
      {
unknown's avatar
unknown committed
688
	/*
689 690 691
          foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
          'generated', and a generated key is a prefix of the other key.
          Then we do not need the generated shorter key.
unknown's avatar
unknown committed
692
        */
693 694 695
        if ((key2->type != Key::FOREIGN_KEY &&
             key2->name != ignore_key &&
             !foreign_key_prefix(key, key2)))
696
        {
697
          /* TODO: issue warning message */
698 699 700 701 702 703 704 705 706 707 708 709 710
          /* 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;
        }
711 712 713 714 715 716
      }
    }
    if (key->name != ignore_key)
      key_parts+=key->columns.elements;
    else
      (*key_count)--;
717
    if (key->name && !tmp_table &&
718
	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
719 720 721 722
    {
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
      DBUG_RETURN(-1);
    }
723
  }
unknown's avatar
unknown committed
724
  tmp=file->max_keys();
725
  if (*key_count > tmp)
726 727 728 729
  {
    my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
    DBUG_RETURN(-1);
  }
730

731
  key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)* *key_count);
732 733
  key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
  if (!key_info_buffer || ! key_part_info)
734
    DBUG_RETURN(-1);				// Out of memory
735

736
  key_iterator.rewind();
unknown's avatar
unknown committed
737
  key_number=0;
unknown's avatar
unknown committed
738
  for (; (key=key_iterator++) ; key_number++)
739 740 741 742
  {
    uint key_length=0;
    key_part_spec *column;

743 744 745 746 747 748 749 750 751 752
    if (key->name == ignore_key)
    {
      /* ignore redundant keys */
      do
	key=key_iterator++;
      while (key && key->name == ignore_key);
      if (!key)
	break;
    }

unknown's avatar
unknown committed
753
    switch(key->type){
unknown's avatar
unknown committed
754
    case Key::MULTIPLE:
755
	key_info->flags= 0;
756
	break;
unknown's avatar
unknown committed
757
    case Key::FULLTEXT:
758
	key_info->flags= HA_FULLTEXT;
759
	break;
unknown's avatar
unknown committed
760
    case Key::SPATIAL:
unknown's avatar
unknown committed
761
#ifdef HAVE_SPATIAL
762
	key_info->flags= HA_SPATIAL;
763
	break;
unknown's avatar
unknown committed
764
#else
765 766 767
	my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0),
			sym_group_geom.name, sym_group_geom.needed_define);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
768
#endif
769
    case Key::FOREIGN_KEY:
770
      key_number--;				// Skip this key
771 772
      continue;
    default:
773 774
      key_info->flags = HA_NOSAME;
      break;
unknown's avatar
unknown committed
775
    }
776 777
    if (key->generated)
      key_info->flags|= HA_GENERATED_KEY;
unknown's avatar
unknown committed
778

unknown's avatar
unknown committed
779 780
    key_info->key_parts=(uint8) key->columns.elements;
    key_info->key_part=key_part_info;
unknown's avatar
unknown committed
781
    key_info->usable_key_parts= key_number;
782
    key_info->algorithm=key->algorithm;
unknown's avatar
unknown committed
783

784 785
    if (key->type == Key::FULLTEXT)
    {
786
      if (!(file->table_flags() & HA_CAN_FULLTEXT))
787
      {
788 789
	my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0));
	DBUG_RETURN(-1);
790 791
      }
    }
unknown's avatar
unknown committed
792 793 794
    /*
       Make SPATIAL to be RTREE by default
       SPATIAL only on BLOB or at least BINARY, this
795
       actually should be replaced by special GEOM type
unknown's avatar
unknown committed
796 797 798
       in near future when new frm file is ready
       checking for proper key parts number:
    */
799

800
    /* TODO: Add proper checks if handler supports key_type and algorithm */
801
    if (key_info->flags & HA_SPATIAL)
802 803 804
    {
      if (key_info->key_parts != 1)
      {
805 806 807
	my_printf_error(ER_WRONG_ARGUMENTS,
			ER(ER_WRONG_ARGUMENTS),MYF(0),"SPATIAL INDEX");
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
808
      }
809
    }
unknown's avatar
unknown committed
810 811
    else
    if (key_info->algorithm == HA_KEY_ALG_RTREE)
unknown's avatar
unknown committed
812
    {
unknown's avatar
unknown committed
813
#ifdef HAVE_RTREE_KEYS
814 815
      if ((key_info->key_parts & 1) == 1)
      {
816 817 818
	my_printf_error(ER_WRONG_ARGUMENTS,
			ER(ER_WRONG_ARGUMENTS),MYF(0),"RTREE INDEX");
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
819
      }
820 821
      /* TODO: To be deleted */
      my_printf_error(ER_NOT_SUPPORTED_YET, ER(ER_NOT_SUPPORTED_YET),
822
		      MYF(0), "RTREE INDEX");
823
      DBUG_RETURN(-1);
unknown's avatar
unknown committed
824 825
#else
      my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0),
826
		      sym_group_rtree.name, sym_group_rtree.needed_define);
unknown's avatar
unknown committed
827 828
      DBUG_RETURN(-1);
#endif
unknown's avatar
unknown committed
829
    }
830

unknown's avatar
unknown committed
831
    List_iterator<key_part_spec> cols(key->columns);
832
    CHARSET_INFO *ft_key_charset=0;  // for FULLTEXT
unknown's avatar
unknown committed
833 834 835 836 837
    for (uint column_nr=0 ; (column=cols++) ; column_nr++)
    {
      it.rewind();
      field=0;
      while ((sql_field=it++) &&
838 839 840 841
	     my_strcasecmp(system_charset_info,
			   column->field_name,
			   sql_field->field_name))
	field++;
unknown's avatar
unknown committed
842 843
      if (!sql_field)
      {
844 845 846 847
	my_printf_error(ER_KEY_COLUMN_DOES_NOT_EXITS,
			ER(ER_KEY_COLUMN_DOES_NOT_EXITS),MYF(0),
			column->field_name);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
848
      }
849
      /* for fulltext keys keyseg length is 1 for blobs (it's ignored in
850 851 852 853
	 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).
854 855
      */
      if (key->type == Key::FULLTEXT)
856
      {
857 858 859 860
	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 ||
861
	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
	    (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));
877
      }
878
      else
879
      {
880 881 882 883
	column->length*= sql_field->charset->mbmaxlen;

	if (f_is_blob(sql_field->pack_flag))
	{
unknown's avatar
unknown committed
884
	  if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
885 886 887 888 889 890 891 892 893 894 895 896 897
	  {
	    my_printf_error(ER_BLOB_USED_AS_KEY,ER(ER_BLOB_USED_AS_KEY),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	  }
	  if (!column->length)
	  {
	    my_printf_error(ER_BLOB_KEY_WITHOUT_LENGTH,
			    ER(ER_BLOB_KEY_WITHOUT_LENGTH),MYF(0),
			    column->field_name);
	    DBUG_RETURN(-1);
	  }
	}
unknown's avatar
unknown committed
898
#ifdef HAVE_SPATIAL
899 900 901 902 903 904 905 906 907 908 909
	if (key->type  == Key::SPATIAL)
	{
	  if (!column->length )
	  {
	    /*
	    BAR: 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
		 Lately we'll extend this code to support more dimensions
	    */
	    column->length=4*sizeof(double);
	  }
	}
unknown's avatar
unknown committed
910
#endif
911 912 913 914 915 916 917 918 919 920
	if (!(sql_field->flags & NOT_NULL_FLAG))
	{
	  if (key->type == Key::PRIMARY)
	  {
	    /* Implicitly set primary key fields to NOT NULL for ISO conf. */
	    sql_field->flags|= NOT_NULL_FLAG;
	    sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
	  }
	  else
	     key_info->flags|= HA_NULL_PART_KEY;
unknown's avatar
unknown committed
921
	  if (!(file->table_flags() & HA_NULL_IN_KEY))
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
	  {
	    my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
			    MYF(0),column->field_name);
	    DBUG_RETURN(-1);
	  }
	  if (key->type == Key::SPATIAL)
	  {
	    my_error(ER_SPATIAL_CANT_HAVE_NULL, MYF(0));
	    DBUG_RETURN(-1);
	  }
	}
	if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
	{
	  if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY))
	    auto_increment--;			// Field is used
	}
unknown's avatar
unknown committed
938
      }
939

unknown's avatar
unknown committed
940 941 942 943 944 945
      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)
      {
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
	if (f_is_blob(sql_field->pack_flag))
	{
	  if ((length=column->length) > file->max_key_length() ||
	      length > file->max_key_part_length())
	  {
	    length=min(file->max_key_length(), file->max_key_part_length());
	    if (key->type == Key::MULTIPLE)
	    {
	      /* not a critical problem */
	      char warn_buff[MYSQL_ERRMSG_SIZE];
	      my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
			  length);
	      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			   ER_TOO_LONG_KEY, warn_buff);
	    }
	    else
	    {
	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
	      DBUG_RETURN(-1);
	    }
	  }
	}
	else if (!f_is_geom(sql_field->pack_flag) &&
		  (column->length > length ||
		   ((f_is_packed(sql_field->pack_flag) ||
		     ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
		      (key_info->flags & HA_NOSAME))) &&
		    column->length != length)))
	{
	  my_error(ER_WRONG_SUB_KEY,MYF(0));
	  DBUG_RETURN(-1);
	}
	else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
	  length=column->length;
unknown's avatar
unknown committed
980 981 982
      }
      else if (length == 0)
      {
983 984 985
	my_printf_error(ER_WRONG_KEY_COLUMN, ER(ER_WRONG_KEY_COLUMN), MYF(0),
			column->field_name);
	  DBUG_RETURN(-1);
unknown's avatar
unknown committed
986
      }
987 988
      if (length > file->max_key_part_length())
      {
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
	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);
	}
1004 1005
      }
      key_part_info->length=(uint16) length;
unknown's avatar
unknown committed
1006 1007
      /* Use packed keys for long strings on the first column */
      if (!(db_options & HA_OPTION_NO_PACK_KEYS) &&
1008 1009 1010 1011
	  (length >= KEY_DEFAULT_PACK_LENGTH &&
	   (sql_field->sql_type == FIELD_TYPE_STRING ||
	    sql_field->sql_type == FIELD_TYPE_VAR_STRING ||
	    sql_field->pack_flag & FIELDFLAG_BLOB)))
unknown's avatar
unknown committed
1012
      {
1013 1014 1015 1016
	if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB))
	  key_info->flags|= HA_BINARY_PACK_KEY;
	else
	  key_info->flags|= HA_PACK_KEY;
unknown's avatar
unknown committed
1017 1018 1019 1020 1021 1022 1023
      }
      key_length+=length;
      key_part_info++;

      /* Create the key name based on the first column (if not given) */
      if (column_nr == 0)
      {
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
	if (key->type == Key::PRIMARY)
	{
	  if (primary_key)
	  {
	    my_error(ER_MULTIPLE_PRI_KEY,MYF(0));
	    DBUG_RETURN(-1);
	  }
	  key_name=primary_key_name;
	  primary_key=1;
	}
	else if (!(key_name = key->name))
	  key_name=make_unique_key_name(sql_field->field_name,
					key_info_buffer,key_info);
	if (check_if_keyname_exists(key_name,key_info_buffer,key_info))
	{
	  my_error(ER_DUP_KEYNAME,MYF(0),key_name);
	  DBUG_RETURN(-1);
	}
	key_info->name=(char*) key_name;
unknown's avatar
unknown committed
1043 1044
      }
    }
1045 1046
    if (!key_info->name || check_column_name(key_info->name))
    {
1047
      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
1048 1049
      DBUG_RETURN(-1);
    }
1050 1051
    if (!(key_info->flags & HA_NULL_PART_KEY))
      unique_key=1;
unknown's avatar
unknown committed
1052
    key_info->key_length=(uint16) key_length;
unknown's avatar
unknown committed
1053
    uint max_key_length= file->max_key_length();
1054
    if (key_length > max_key_length && key->type != Key::FULLTEXT)
unknown's avatar
unknown committed
1055
    {
1056
      my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
unknown's avatar
unknown committed
1057 1058
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
1059
    key_info++;
unknown's avatar
unknown committed
1060
  }
1061
  if (!unique_key && !primary_key &&
1062
      (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
1063 1064 1065 1066
  {
    my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1067 1068 1069 1070 1071
  if (auto_increment > 0)
  {
    my_error(ER_WRONG_AUTO_KEY,MYF(0));
    DBUG_RETURN(-1);
  }
1072
  /* Sort keys in optimized order */
1073 1074
  qsort((gptr) key_info_buffer, *key_count, sizeof(KEY),
	(qsort_cmp) sort_keys);
unknown's avatar
unknown committed
1075

1076 1077 1078
  DBUG_RETURN(0);
}

1079

1080 1081 1082 1083 1084
/*
  Create a table

  SYNOPSIS
    mysql_create_table()
1085 1086 1087 1088 1089 1090 1091 1092
    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)
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102

  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
1103 1104
    0	ok
    -1	error
1105 1106 1107
*/

int mysql_create_table(THD *thd,const char *db, const char *table_name,
1108 1109
		       HA_CREATE_INFO *create_info,
		       List<create_field> &fields,
1110
		       List<Key> &keys,bool tmp_table,
1111
		       uint select_field_count)
1112
{
1113 1114 1115 1116 1117 1118 1119
  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;
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
  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,
1133 1134 1135 1136
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			table_name);
1137 1138 1139 1140 1141 1142 1143
  }
  db_options=create_info->table_options;
  if (create_info->row_type == ROW_TYPE_DYNAMIC)
    db_options|=HA_OPTION_PACK_RECORD;
  alias= table_case_name(create_info, table_name);
  file=get_new_handler((TABLE*) 0, create_info->db_type);

unknown's avatar
unknown committed
1144 1145 1146 1147 1148 1149 1150 1151
#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
  */
1152 1153 1154 1155 1156 1157
  if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      (file->table_flags() & HA_NO_TEMP_TABLES))
  {
    my_error(ER_ILLEGAL_HA,MYF(0),table_name);
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1158
#endif
1159

1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
  /*
    If the table character set was not given explicitely,
    let's fetch the database default character set and
    apply it to the table.
  */
  if (!create_info->default_table_charset)
  {
    HA_CREATE_INFO db_info;
    uint length;
    char  path[FN_REFLEN];
unknown's avatar
unknown committed
1170
    strxmov(path, mysql_data_home, "/", db, NullS);
1171 1172 1173 1174 1175 1176
    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;
  }

1177
  if (mysql_prepare_table(thd, create_info, fields,
1178 1179 1180
			  keys, tmp_table, db_options, file,
			  key_info_buffer, &key_count,
			  select_field_count))
1181 1182
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
1183 1184 1185
      /* Check if table exists */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
1186 1187 1188
    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);
1189 1190
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, path);
unknown's avatar
unknown committed
1191 1192 1193
    create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
  }
  else
1194 1195
    my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home, db,
		alias, reg_ext);
unknown's avatar
unknown committed
1196 1197 1198 1199 1200
  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))
  {
1201
    if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
1202
    {
1203
      create_info->table_existed= 1;		// Mark that table existed
1204
      DBUG_RETURN(0);
1205
    }
unknown's avatar
unknown committed
1206
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
unknown's avatar
unknown committed
1207 1208
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
1209
  if (wait_if_global_read_lock(thd, 0, 1))
1210
    DBUG_RETURN(error);
unknown's avatar
unknown committed
1211 1212 1213 1214 1215 1216
  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)
1217
      {
1218 1219 1220
	create_info->table_existed= 1;		// Mark that table existed
	error= 0;
      }
1221
      else
1222
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
1223
      goto end;
unknown's avatar
unknown committed
1224 1225 1226
    }
  }

unknown's avatar
unknown committed
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
  /*
    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;
    if (!create_table_from_handler(db, table_name,
                                 create_if_not_exists))
    {
      DBUG_PRINT("info", ("Table already existed in handler"));

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

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

unknown's avatar
unknown committed
1259
  if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
1260
    create_info->data_file_name= create_info->index_file_name= 0;
unknown's avatar
unknown committed
1261
  create_info->table_options=db_options;
1262

1263
  if (rea_create_table(thd, path, create_info, fields, key_count,
1264
		       key_info_buffer))
unknown's avatar
unknown committed
1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
  {
    /* my_error(ER_CANT_CREATE_TABLE,MYF(0),table_name,my_errno); */
    goto end;
  }
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    /* Open table and put in temporary table list */
    if (!(open_temporary_table(thd, path, db, table_name, 1)))
    {
      (void) rm_temporary_table(create_info->db_type, path);
      goto end;
    }
unknown's avatar
unknown committed
1277
    thd->tmp_table_used= 1;
unknown's avatar
unknown committed
1278
  }
1279
  if (!tmp_table)
1280 1281 1282 1283 1284
  {
    // Must be written before unlock
    mysql_update_log.write(thd,thd->query, thd->query_length);
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
1285
      thd->clear_error();
1286
      Query_log_event qinfo(thd, thd->query, thd->query_length,
1287 1288
			    test(create_info->options &
				 HA_LEX_CREATE_TMP_TABLE));
1289 1290 1291
      mysql_bin_log.write(&qinfo);
    }
  }
unknown's avatar
unknown committed
1292 1293 1294
  error=0;
end:
  VOID(pthread_mutex_unlock(&LOCK_open));
1295
  start_waiting_global_read_lock(thd);
unknown's avatar
unknown committed
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
  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++)
1308
    if (!my_strcasecmp(system_charset_info,name,key->name))
unknown's avatar
unknown committed
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
      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;

1319 1320
  if (!check_if_keyname_exists(field_name,start,end) &&
      my_strcasecmp(system_charset_info,field_name,primary_key_name))
1321 1322 1323 1324 1325 1326 1327
    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
  */
1328
  for (uint i=2 ; i< 100; i++)
unknown's avatar
unknown committed
1329
  {
1330 1331
    *buff_end= '_';
    int10_to_str(i, buff_end+1, 10);
unknown's avatar
unknown committed
1332 1333 1334
    if (!check_if_keyname_exists(buff,start,end))
      return sql_strdup(buff);
  }
1335
  return (char*) "not_specified";		// Should never happen
unknown's avatar
unknown committed
1336 1337
}

1338

unknown's avatar
unknown committed
1339 1340 1341 1342 1343
/****************************************************************************
** Create table from a list of fields and items
****************************************************************************/

TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
1344 1345 1346 1347 1348
			       const char *db, const char *name,
			       List<create_field> *extra_fields,
			       List<Key> *keys,
			       List<Item> *items,
			       MYSQL_LOCK **lock)
unknown's avatar
unknown committed
1349
{
1350
  TABLE tmp_table;		// Used during 'create_field()'
unknown's avatar
unknown committed
1351 1352
  TABLE *table;
  tmp_table.table_name=0;
1353
  uint select_field_count= items->elements;
1354
  Disable_binlog disable_binlog(thd);
unknown's avatar
unknown committed
1355 1356 1357
  DBUG_ENTER("create_table_from_items");

  /* Add selected items to field list */
unknown's avatar
unknown committed
1358
  List_iterator_fast<Item> it(*items);
unknown's avatar
unknown committed
1359 1360 1361 1362 1363 1364
  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 ||
1365
				    create_info->db_type == DB_TYPE_HEAP);
unknown's avatar
unknown committed
1366 1367 1368 1369

  while ((item=it++))
  {
    create_field *cr_field;
1370 1371 1372 1373 1374
    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(),
1375
				  (Item ***) 0, &tmp_field,0,0);
1376
    if (!field ||
1377 1378 1379
	!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
					   ((Item_field *)item)->field :
					   (Field*) 0))))
unknown's avatar
unknown committed
1380 1381 1382 1383 1384
      DBUG_RETURN(0);
    extra_fields->push_back(cr_field);
  }
  /* create and lock table */
  /* QQ: This should be done atomic ! */
1385
  /* We don't log the statement, it will be logged later */
unknown's avatar
unknown committed
1386
  if (mysql_create_table(thd,db,name,create_info,*extra_fields,
1387
			 *keys,0,select_field_count))
unknown's avatar
unknown committed
1388
    DBUG_RETURN(0);
1389 1390 1391 1392 1393 1394 1395
  /*
    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
    TABLE, which is a wrong order. So we keep binary logging disabled.
  */
unknown's avatar
unknown committed
1396 1397
  if (!(table=open_table(thd,db,name,name,(bool*) 0)))
  {
unknown's avatar
unknown committed
1398
    quick_rm_table(create_info->db_type,db,table_case_name(create_info,name));
unknown's avatar
unknown committed
1399 1400 1401 1402 1403
    DBUG_RETURN(0);
  }
  table->reginfo.lock_type=TL_WRITE;
  if (!((*lock)=mysql_lock_tables(thd,&table,1)))
  {
1404
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1405
    hash_delete(&open_cache,(byte*) table);
1406
    VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
1407
    quick_rm_table(create_info->db_type,db,table_case_name(create_info, name));
unknown's avatar
unknown committed
1408 1409 1410 1411
    DBUG_RETURN(0);
  }
  table->file->extra(HA_EXTRA_WRITE_CACHE);
  DBUG_RETURN(table);
1412
  /* Note that leaving the function resets binlogging properties */
unknown's avatar
unknown committed
1413 1414 1415 1416 1417 1418 1419
}


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

1420
bool
unknown's avatar
unknown committed
1421
mysql_rename_table(enum db_type base,
1422 1423 1424 1425
		   const char *old_db,
		   const char *old_name,
		   const char *new_db,
		   const char *new_name)
unknown's avatar
unknown committed
1426
{
unknown's avatar
unknown committed
1427 1428
  char from[FN_REFLEN], to[FN_REFLEN];
  char tmp_from[NAME_LEN+1], tmp_to[NAME_LEN+1];
unknown's avatar
unknown committed
1429
  handler *file=get_new_handler((TABLE*) 0, base);
unknown's avatar
unknown committed
1430
  int error=0;
unknown's avatar
unknown committed
1431
  DBUG_ENTER("mysql_rename_table");
unknown's avatar
unknown committed
1432 1433 1434 1435 1436

  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);
1437
    my_casedn_str(files_charset_info, tmp_from);
unknown's avatar
unknown committed
1438 1439 1440
    old_name= tmp_from;

    strmov(tmp_to, new_name);
1441
    my_casedn_str(files_charset_info, tmp_to);
unknown's avatar
unknown committed
1442 1443
    new_name= tmp_to;
  }
1444 1445 1446 1447
  my_snprintf(from, sizeof(from), "%s/%s/%s",
	      mysql_data_home, old_db, old_name);
  my_snprintf(to, sizeof(to), "%s/%s/%s",
	      mysql_data_home, new_db, new_name);
unknown's avatar
unknown committed
1448 1449
  fn_format(from,from,"","",4);
  fn_format(to,to,    "","",4);
unknown's avatar
unknown committed
1450

unknown's avatar
unknown committed
1451
  if (!(error=file->rename_table((const char*) from,(const char *) to)))
1452 1453 1454
  {
    if (rename_file_ext(from,to,reg_ext))
    {
unknown's avatar
unknown committed
1455
      error=my_errno;
1456 1457 1458 1459
      /* Restore old file name */
      file->rename_table((const char*) to,(const char *) from);
    }
  }
unknown's avatar
unknown committed
1460
  delete file;
unknown's avatar
unknown committed
1461 1462 1463
  if (error)
    my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
  DBUG_RETURN(error != 0);
unknown's avatar
unknown committed
1464 1465
}

unknown's avatar
unknown committed
1466

unknown's avatar
unknown committed
1467
/*
1468 1469 1470 1471
  Force all other threads to stop using the table

  SYNOPSIS
    wait_while_table_is_used()
1472 1473 1474 1475
    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
1476 1477 1478 1479 1480 1481 1482
  NOTES
   When returning, the table will be unusable for other threads until
   the table is closed.

  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
unknown's avatar
unknown committed
1483 1484
*/

1485
static void wait_while_table_is_used(THD *thd,TABLE *table,
1486
				     enum ha_extra_function function)
unknown's avatar
unknown committed
1487
{
1488 1489 1490
  DBUG_PRINT("enter",("table: %s", table->real_name));
  DBUG_ENTER("wait_while_table_is_used");
  safe_mutex_assert_owner(&LOCK_open);
unknown's avatar
unknown committed
1491

1492
  VOID(table->file->extra(function));
1493
  /* Mark all tables that are in use as 'old' */
1494
  mysql_lock_abort(thd, table);			// end threads waiting on lock
1495 1496 1497

  /* Wait until all there are no other threads that has this table open */
  while (remove_table_from_cache(thd,table->table_cache_key,
1498
				 table->real_name))
unknown's avatar
unknown committed
1499
  {
1500 1501 1502
    dropping_tables++;
    (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
    dropping_tables--;
1503 1504 1505
  }
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1506

1507 1508
/*
  Close a cached table
unknown's avatar
unknown committed
1509

1510
  SYNOPSIS
unknown's avatar
unknown committed
1511
    close_cached_table()
1512 1513
    thd			Thread handler
    table		Table to remove from cache
1514 1515 1516 1517

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

1519 1520 1521 1522
  PREREQUISITES
    Lock on LOCK_open
    Win32 clients must also have a WRITE LOCK on the table !
*/
1523

1524
static bool close_cached_table(THD *thd, TABLE *table)
1525 1526
{
  DBUG_ENTER("close_cached_table");
1527

1528
  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
1529 1530
  /* Close lock if this is not got with LOCK TABLES */
  if (thd->lock)
1531
  {
1532
    mysql_unlock_tables(thd, thd->lock);
1533
    thd->lock=0;			// Start locked threads
unknown's avatar
unknown committed
1534
  }
1535 1536 1537 1538 1539
  /* 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);
1540
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1541 1542
}

1543
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
1544
			     const char* operator_name, const char* errmsg)
1545

unknown's avatar
unknown committed
1546
{
1547 1548
  Protocol *protocol= thd->protocol;
  protocol->prepare_for_resend();
1549 1550 1551 1552
  protocol->store(table->alias, system_charset_info);
  protocol->store((char*) operator_name, system_charset_info);
  protocol->store("error", 5, system_charset_info);
  protocol->store(errmsg, system_charset_info);
unknown's avatar
unknown committed
1553
  thd->net.last_error[0]=0;
1554
  if (protocol->write())
unknown's avatar
unknown committed
1555 1556 1557 1558
    return -1;
  return 1;
}

1559

unknown's avatar
unknown committed
1560
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
1561
			       HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1562
{
unknown's avatar
unknown committed
1563
  DBUG_ENTER("prepare_for_restore");
1564

unknown's avatar
unknown committed
1565 1566 1567
  if (table->table) // do not overwrite existing tables on restore
  {
    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1568 1569
				  "table exists, will not overwrite on restore"
				  ));
unknown's avatar
unknown committed
1570
  }
unknown's avatar
unknown committed
1571
  else
unknown's avatar
unknown committed
1572
  {
1573
    char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
1574
    char src_path[FN_REFLEN], dst_path[FN_REFLEN];
1575
    char* table_name = table->real_name;
unknown's avatar
unknown committed
1576
    char* db = thd->db ? thd->db : table->db;
1577

1578
    if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
1579
					reg_ext))
unknown's avatar
unknown committed
1580
      DBUG_RETURN(-1); // protect buffer overflow
unknown's avatar
unknown committed
1581

1582
    my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
1583
		mysql_real_data_home, db, table_name);
1584

1585
    if (lock_and_wait_for_table_name(thd,table))
unknown's avatar
unknown committed
1586
      DBUG_RETURN(-1);
1587

1588
    if (my_copy(src_path,
1589 1590
		fn_format(dst_path, dst_path,"", reg_ext, 4),
		MYF(MY_WME)))
unknown's avatar
unknown committed
1591
    {
1592
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1593
      unlock_table_name(thd, table);
1594
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1595
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1596
				    "Failed copying .frm file"));
unknown's avatar
unknown committed
1597
    }
1598
    if (mysql_truncate(thd, table, 1))
unknown's avatar
unknown committed
1599
    {
1600
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1601
      unlock_table_name(thd, table);
1602
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1603
      DBUG_RETURN(send_check_errmsg(thd, table, "restore",
1604
				    "Failed generating table from .frm file"));
unknown's avatar
unknown committed
1605
    }
unknown's avatar
unknown committed
1606
  }
unknown's avatar
unknown committed
1607

1608 1609 1610 1611
  /*
    Now we should be able to open the partially restored table
    to finish the restore in the handler later on
  */
unknown's avatar
unknown committed
1612
  if (!(table->table = reopen_name_locked_table(thd, table)))
1613 1614
  {
    pthread_mutex_lock(&LOCK_open);
1615
    unlock_table_name(thd, table);
1616 1617
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1618
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1619
}
1620

1621

unknown's avatar
unknown committed
1622
static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
1623
			      HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
1624
{
unknown's avatar
unknown committed
1625 1626
  int error= 0;
  TABLE tmp_table, *table;
unknown's avatar
unknown committed
1627 1628 1629 1630
  DBUG_ENTER("prepare_for_repair");

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

1632
  if (!(table= table_list->table))		/* if open_ltable failed */
unknown's avatar
unknown committed
1633
  {
unknown's avatar
unknown committed
1634 1635
    char name[FN_REFLEN];
    strxmov(name, mysql_data_home, "/", table_list->db, "/",
1636
	    table_list->real_name, NullS);
unknown's avatar
unknown committed
1637
    if (openfrm(name, "", 0, 0, 0, &tmp_table))
1638
      DBUG_RETURN(0);				// Can't open frm file
unknown's avatar
unknown committed
1639
    table= &tmp_table;
1640
  }
unknown's avatar
unknown committed
1641

unknown's avatar
unknown committed
1642 1643 1644 1645 1646 1647 1648 1649 1650
  /*
    User gave us USE_FRM which means that the header in the index file is
    trashed.
    In this case we will try to fix the table the following way:
    - Rename the data file to a temporary name
    - Truncate the table
    - Replace the new data file with the old one
    - Run a normal repair using the new index file and the old data file
  */
unknown's avatar
unknown committed
1651

unknown's avatar
unknown committed
1652 1653 1654
  char from[FN_REFLEN],tmp[FN_REFLEN+32];
  const char **ext= table->file->bas_ext();
  MY_STAT stat_info;
unknown's avatar
unknown committed
1655

unknown's avatar
unknown committed
1656 1657 1658 1659 1660
  /*
    Check if this is a table type that stores index and data separately,
    like ISAM or MyISAM
  */
  if (!ext[0] || !ext[1])
1661
    goto end;					// No data file
unknown's avatar
unknown committed
1662

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

1667 1668
  my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
	      from, current_pid, thd->thread_id);
unknown's avatar
unknown committed
1669

1670 1671 1672 1673 1674 1675 1676
  /* If we could open the table, close it */
  if (table_list->table)
  {
    pthread_mutex_lock(&LOCK_open);
    close_cached_table(thd, table);
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1677
  if (lock_and_wait_for_table_name(thd,table_list))
unknown's avatar
unknown committed
1678
  {
unknown's avatar
unknown committed
1679 1680
    error= -1;
    goto end;
unknown's avatar
unknown committed
1681
  }
unknown's avatar
unknown committed
1682
  if (my_rename(from, tmp, MYF(MY_WME)))
unknown's avatar
unknown committed
1683
  {
1684
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1685
    unlock_table_name(thd, table_list);
1686
    pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
1687
    error= send_check_errmsg(thd, table_list, "repair",
1688
			     "Failed renaming data file");
unknown's avatar
unknown committed
1689 1690 1691 1692 1693 1694 1695 1696
    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",
1697
			     "Failed generating table from .frm file");
unknown's avatar
unknown committed
1698 1699 1700 1701 1702 1703 1704 1705
    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",
1706
			     "Failed restoring .MYD file");
unknown's avatar
unknown committed
1707
    goto end;
unknown's avatar
unknown committed
1708 1709
  }

1710 1711 1712 1713
  /*
    Now we should be able to open the partially repaired table
    to finish the repair in the handler later on.
  */
unknown's avatar
unknown committed
1714
  if (!(table_list->table = reopen_name_locked_table(thd, table_list)))
unknown's avatar
unknown committed
1715 1716
  {
    pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1717
    unlock_table_name(thd, table_list);
unknown's avatar
unknown committed
1718 1719
    pthread_mutex_unlock(&LOCK_open);
  }
unknown's avatar
unknown committed
1720 1721 1722

end:
  if (table == &tmp_table)
1723
    closefrm(table);				// Free allocated memory
unknown's avatar
unknown committed
1724
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1725
}
1726

1727

1728
static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
1729 1730 1731 1732 1733 1734 1735 1736 1737
			     HA_CHECK_OPT* check_opt,
			     const char *operator_name,
			     thr_lock_type lock_type,
			     bool open_for_modify,
			     uint extra_open_options,
			     int (*prepare_func)(THD *, TABLE_LIST *,
						 HA_CHECK_OPT *),
			     int (handler::*operator_func)
			     (THD *, HA_CHECK_OPT *))
unknown's avatar
unknown committed
1738 1739 1740
{
  TABLE_LIST *table;
  List<Item> field_list;
1741 1742
  Item *item;
  Protocol *protocol= thd->protocol;
1743
  DBUG_ENTER("mysql_admin_table");
unknown's avatar
unknown committed
1744 1745 1746 1747 1748 1749 1750 1751 1752

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

1756
  mysql_ha_close(thd, tables, /*dont_send_ok*/ 1, /*dont_lock*/ 1);
unknown's avatar
unknown committed
1757 1758 1759 1760
  for (table = tables; table; table = table->next)
  {
    char table_name[NAME_LEN*2+2];
    char* db = (table->db) ? table->db : thd->db;
1761
    bool fatal_error=0;
1762
    strxmov(table_name,db ? db : "",".",table->real_name,NullS);
unknown's avatar
unknown committed
1763

unknown's avatar
unknown committed
1764
    thd->open_options|= extra_open_options;
1765
    table->table = open_ltable(thd, table, lock_type);
1766 1767 1768
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
unknown's avatar
unknown committed
1769
    thd->open_options&= ~extra_open_options;
1770

unknown's avatar
unknown committed
1771
    if (prepare_func)
1772
    {
unknown's avatar
unknown committed
1773
      switch ((*prepare_func)(thd, table, check_opt)) {
1774 1775 1776
	case  1: continue; // error, message written to net
	case -1: goto err; // error, message could be written to net
	default:	 ; // should be 0 otherwise
unknown's avatar
unknown committed
1777
      }
1778
    }
1779

unknown's avatar
unknown committed
1780 1781 1782
    if (!table->table)
    {
      const char *err_msg;
1783
      protocol->prepare_for_resend();
1784 1785 1786
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error",5, system_charset_info);
unknown's avatar
unknown committed
1787
      if (!(err_msg=thd->net.last_error))
1788
	err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
1789
      protocol->store(err_msg, system_charset_info);
unknown's avatar
unknown committed
1790
      thd->net.last_error[0]=0;
1791
      if (protocol->write())
1792
	goto err;
unknown's avatar
unknown committed
1793 1794
      continue;
    }
unknown's avatar
unknown committed
1795
    table->table->pos_in_table_list= table;
1796
    if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
unknown's avatar
unknown committed
1797
    {
unknown's avatar
unknown committed
1798
      char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
1799
      protocol->prepare_for_resend();
1800 1801 1802
      protocol->store(table_name, system_charset_info);
      protocol->store(operator_name, system_charset_info);
      protocol->store("error", 5, system_charset_info);
1803
      my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name);
1804
      protocol->store(buff, system_charset_info);
1805
      close_thread_tables(thd);
1806
      table->table=0;				// For query cache
1807
      if (protocol->write())
1808
	goto err;
unknown's avatar
unknown committed
1809 1810 1811
      continue;
    }

1812
    /* Close all instances of the table to allow repair to rename files */
1813
    if (lock_type == TL_WRITE && table->table->version)
1814 1815
    {
      pthread_mutex_lock(&LOCK_open);
1816
      const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
1817
					      "Waiting to get writelock");
1818 1819
      mysql_lock_abort(thd,table->table);
      while (remove_table_from_cache(thd, table->table->table_cache_key,
1820 1821
				     table->table->real_name) &&
	     ! thd->killed)
1822
      {
1823 1824 1825
	dropping_tables++;
	(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
	dropping_tables--;
1826
      }
1827
      thd->exit_cond(old_message);
1828
      if (thd->killed)
1829
	goto err;
unknown's avatar
unknown committed
1830
      open_for_modify=0;
1831 1832
    }

1833
    int result_code = (table->table->file->*operator_func)(thd, check_opt);
unknown's avatar
unknown committed
1834 1835 1836
#ifdef EMBEDDED_LIBRARY
    thd->net.last_errno= 0;  // these errors shouldn't get client
#endif
1837
    protocol->prepare_for_resend();
1838 1839
    protocol->store(table_name, system_charset_info);
    protocol->store(operator_name, system_charset_info);
unknown's avatar
unknown committed
1840

1841 1842 1843
send_result_message:

    DBUG_PRINT("info", ("result_code: %d", result_code));
1844 1845
    switch (result_code) {
    case HA_ADMIN_NOT_IMPLEMENTED:
1846
      {
1847 1848 1849
	char buf[ERRMSGSIZE+20];
	uint length=my_snprintf(buf, ERRMSGSIZE,
				ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
1850
	protocol->store("note", 4, system_charset_info);
1851
	protocol->store(buf, length, system_charset_info);
1852
      }
unknown's avatar
unknown committed
1853 1854
      break;

1855
    case HA_ADMIN_OK:
1856 1857
      protocol->store("status", 6, system_charset_info);
      protocol->store("OK",2, system_charset_info);
unknown's avatar
unknown committed
1858 1859
      break;

1860
    case HA_ADMIN_FAILED:
1861 1862
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation failed",16, system_charset_info);
unknown's avatar
unknown committed
1863 1864
      break;

unknown's avatar
unknown committed
1865 1866 1867
    case HA_ADMIN_REJECT:
      protocol->store("status", 6, system_charset_info);
      protocol->store("Operation need committed state",30, system_charset_info);
unknown's avatar
unknown committed
1868
      open_for_modify= FALSE;
unknown's avatar
unknown committed
1869 1870
      break;

1871
    case HA_ADMIN_ALREADY_DONE:
1872 1873
      protocol->store("status", 6, system_charset_info);
      protocol->store("Table is already up to date", 27, system_charset_info);
1874 1875
      break;

1876
    case HA_ADMIN_CORRUPT:
1877
      protocol->store("error", 5, system_charset_info);
1878
      protocol->store("Corrupt", 7, system_charset_info);
1879
      fatal_error=1;
unknown's avatar
unknown committed
1880 1881
      break;

unknown's avatar
unknown committed
1882
    case HA_ADMIN_INVALID:
1883 1884
      protocol->store("error", 5, system_charset_info);
      protocol->store("Invalid argument",16, system_charset_info);
unknown's avatar
unknown committed
1885 1886
      break;

1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897
    case HA_ADMIN_TRY_ALTER:
    {
      /*
        This is currently used only by InnoDB. ha_innobase::optimize() answers
        "try with alter", so here we close the table, do an ALTER TABLE,
        reopen the table and do ha_innobase::analyze() on it.
      */
      close_thread_tables(thd);
      TABLE_LIST *save_next= table->next;
      table->next= 0;
      result_code= mysql_recreate_table(thd, table, 0);
unknown's avatar
unknown committed
1898
      close_thread_tables(thd);
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909
      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;
    }

1910
    default:				// Probably HA_ADMIN_INTERNAL_ERROR
1911 1912
      protocol->store("error", 5, system_charset_info);
      protocol->store("Unknown - internal error during operation", 41
1913
		      , system_charset_info);
1914
      fatal_error=1;
unknown's avatar
unknown committed
1915 1916
      break;
    }
1917
    if (fatal_error)
1918
      table->table->version=0;			// Force close of table
unknown's avatar
unknown committed
1919
    else if (open_for_modify)
1920
    {
1921
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
1922
      remove_table_from_cache(thd, table->table->table_cache_key,
1923
			      table->table->real_name);
1924
      pthread_mutex_unlock(&LOCK_open);
1925 1926 1927
      /* May be something modified consequently we have to invalidate cache */
      query_cache_invalidate3(thd, table->table, 0);
    }
unknown's avatar
unknown committed
1928
    close_thread_tables(thd);
1929
    table->table=0;				// For query cache
1930
    if (protocol->write())
unknown's avatar
unknown committed
1931 1932 1933
      goto err;
  }

1934
  send_eof(thd);
unknown's avatar
unknown committed
1935 1936
  DBUG_RETURN(0);
 err:
1937
  close_thread_tables(thd);			// Shouldn't be needed
unknown's avatar
unknown committed
1938 1939
  if (table)
    table->table=0;
unknown's avatar
unknown committed
1940 1941 1942
  DBUG_RETURN(-1);
}

unknown's avatar
unknown committed
1943

unknown's avatar
unknown committed
1944 1945 1946 1947
int mysql_backup_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_backup_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
1948 1949
				"backup", TL_READ, 0, 0, 0,
				&handler::backup));
unknown's avatar
unknown committed
1950
}
unknown's avatar
unknown committed
1951

1952

unknown's avatar
unknown committed
1953 1954 1955 1956
int mysql_restore_table(THD* thd, TABLE_LIST* table_list)
{
  DBUG_ENTER("mysql_restore_table");
  DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
1957 1958 1959
				"restore", TL_WRITE, 1, 0,
				&prepare_for_restore,
				&handler::restore));
unknown's avatar
unknown committed
1960
}
unknown's avatar
unknown committed
1961

1962

1963 1964 1965 1966
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,
1967 1968 1969
				"repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR,
				&prepare_for_repair,
				&handler::repair));
1970 1971
}

1972

1973 1974 1975 1976
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,
1977 1978
				"optimize", TL_WRITE, 1,0,0,
				&handler::optimize));
1979 1980 1981
}


unknown's avatar
unknown committed
1982 1983 1984 1985 1986
/*
  Assigned specified indexes for a table into key cache

  SYNOPSIS
    mysql_assign_to_keycache()
1987 1988
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
1989 1990

  RETURN VALUES
1991 1992
    0	  ok
   -1	  error
unknown's avatar
unknown committed
1993 1994
*/

1995
int mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
1996
			     LEX_STRING *key_cache_name)
unknown's avatar
unknown committed
1997
{
1998
  HA_CHECK_OPT check_opt;
unknown's avatar
unknown committed
1999
  KEY_CACHE *key_cache;
unknown's avatar
unknown committed
2000
  DBUG_ENTER("mysql_assign_to_keycache");
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012

  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,
2013 2014
				"assign_to_keycache", TL_READ_NO_INSERT, 0,
				0, 0, &handler::assign_to_keycache));
unknown's avatar
unknown committed
2015 2016
}

unknown's avatar
unknown committed
2017 2018 2019 2020 2021 2022

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

  SYNOPSIS
    reassign_keycache_tables()
2023 2024 2025
    thd		Thread object
    src_cache	Reference to the key cache to clean up
    dest_cache	New key cache
unknown's avatar
unknown committed
2026

2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
  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
2040
    0	  ok
unknown's avatar
unknown committed
2041 2042
*/

2043 2044
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
			     KEY_CACHE *dst_cache)
unknown's avatar
unknown committed
2045 2046 2047
{
  DBUG_ENTER("reassign_keycache_tables");

2048 2049
  DBUG_ASSERT(src_cache != dst_cache);
  DBUG_ASSERT(src_cache->in_init);
2050
  src_cache->param_buff_size= 0;		// Free key cache
2051 2052
  ha_resize_key_cache(src_cache);
  ha_change_key_cache(src_cache, dst_cache);
2053
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2054 2055 2056
}


unknown's avatar
unknown committed
2057 2058 2059 2060 2061
/*
  Preload specified indexes for a table into key cache

  SYNOPSIS
    mysql_preload_keys()
2062 2063
    thd		Thread object
    tables	Table list (one table only)
unknown's avatar
unknown committed
2064 2065

  RETURN VALUES
2066 2067
    0	  ok
   -1	  error
unknown's avatar
unknown committed
2068 2069 2070 2071 2072 2073
*/

int mysql_preload_keys(THD* thd, TABLE_LIST* tables)
{
  DBUG_ENTER("mysql_preload_keys");
  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
2074 2075
				"preload_keys", TL_READ, 0, 0, 0,
				&handler::preload_keys));
unknown's avatar
unknown committed
2076 2077 2078
}


unknown's avatar
unknown committed
2079 2080 2081 2082 2083
/*
  Create a table identical to the specified table

  SYNOPSIS
    mysql_create_like_table()
2084 2085
    thd		Thread object
    table	Table list (one table only)
unknown's avatar
unknown committed
2086 2087 2088 2089
    create_info Create info
    table_ident Src table_ident

  RETURN VALUES
2090 2091
    0	  ok
    -1	error
unknown's avatar
unknown committed
2092 2093
*/

2094 2095 2096
int mysql_create_like_table(THD* thd, TABLE_LIST* table,
			    HA_CREATE_INFO *create_info,
			    Table_ident *table_ident)
unknown's avatar
unknown committed
2097 2098 2099 2100 2101 2102 2103
{
  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;
2104 2105
  int  err, res= -1;
  TABLE_LIST src_tables_list;
unknown's avatar
unknown committed
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115
  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)))
  {
2116
    my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
unknown's avatar
unknown committed
2117
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
2118
  }
2119

2120 2121 2122
  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;
2123

2124 2125
  if (lock_and_wait_for_table_name(thd, &src_tables_list))
    goto err;
unknown's avatar
unknown committed
2126 2127 2128 2129 2130

  if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
    strxmov(src_path, (*tmp_table)->path, reg_ext, NullS);
  else
  {
2131 2132
    strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table,
	    reg_ext, NullS);
unknown's avatar
unknown committed
2133 2134 2135
    if (access(src_path, F_OK))
    {
      my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
2136
      goto err;
unknown's avatar
unknown committed
2137 2138 2139 2140 2141 2142
    }
  }

  /*
    Validate the destination table

2143
    skip the destination table name checking as this is already
unknown's avatar
unknown committed
2144 2145 2146 2147 2148 2149
    validated.
  */
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (find_temporary_table(thd, db, table_name))
      goto table_exists;
2150 2151 2152
    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);
2153 2154
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, dst_path);
unknown's avatar
unknown committed
2155 2156 2157 2158
    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
  }
  else
  {
2159 2160
    strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
	    reg_ext, NullS);
unknown's avatar
unknown committed
2161 2162 2163 2164
    if (!access(dst_path, F_OK))
      goto table_exists;
  }

2165
  /*
unknown's avatar
unknown committed
2166
    Create a new table by copying from source table
2167
  */
2168 2169
  if (my_copy(src_path, dst_path, MYF(MY_WME|MY_DONT_OVERWRITE_FILE)))
    goto err;
unknown's avatar
unknown committed
2170 2171

  /*
2172 2173
    As mysql_truncate don't work on a new table at this stage of
    creation, instead create the table directly (for both normal
unknown's avatar
unknown committed
2174 2175
    and temporary tables).
  */
2176
  *fn_ext(dst_path)= 0;
unknown's avatar
unknown committed
2177
  err= ha_create_table(dst_path, create_info, 1);
2178

unknown's avatar
unknown committed
2179 2180 2181 2182
  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  {
    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
    {
2183 2184
      (void) rm_temporary_table(create_info->db_type,
				dst_path); /* purecov: inspected */
2185
      goto err;     /* purecov: inspected */
unknown's avatar
unknown committed
2186 2187 2188 2189
    }
  }
  else if (err)
  {
2190 2191 2192
    (void) quick_rm_table(create_info->db_type, db,
			  table_name); /* purecov: inspected */
    goto err;	    /* purecov: inspected */
unknown's avatar
unknown committed
2193
  }
2194 2195 2196 2197

  // Must be written before unlock
  mysql_update_log.write(thd,thd->query, thd->query_length);
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
2198
  {
2199 2200
    thd->clear_error();
    Query_log_event qinfo(thd, thd->query, thd->query_length,
2201 2202
			  test(create_info->options &
			       HA_LEX_CREATE_TMP_TABLE));
2203
    mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
2204
  }
2205
  res= 0;
2206
  goto err;
2207

unknown's avatar
unknown committed
2208 2209 2210 2211
table_exists:
  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
  {
    char warn_buff[MYSQL_ERRMSG_SIZE];
2212 2213 2214 2215
    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);
2216
    res= 0;
unknown's avatar
unknown committed
2217
  }
2218 2219 2220 2221 2222 2223 2224 2225
  else
    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);

err:
  pthread_mutex_lock(&LOCK_open);
  unlock_table_name(thd, &src_tables_list);
  pthread_mutex_unlock(&LOCK_open);
  DBUG_RETURN(res);
unknown's avatar
unknown committed
2226 2227 2228
}


2229 2230
int mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
unknown's avatar
unknown committed
2231 2232 2233 2234 2235 2236
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2237 2238
  DBUG_ENTER("mysql_analyze_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2239 2240
				"analyze", lock_type, 1,0,0,
				&handler::analyze));
2241 2242 2243 2244 2245
}


int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
{
unknown's avatar
unknown committed
2246 2247 2248 2249 2250 2251
#ifdef OS2
  thr_lock_type lock_type = TL_WRITE;
#else
  thr_lock_type lock_type = TL_READ_NO_INSERT;
#endif

2252 2253
  DBUG_ENTER("mysql_check_table");
  DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
2254 2255 2256
				"check", lock_type,
				0, HA_OPEN_FOR_REPAIR, 0,
				&handler::check));
2257 2258
}

unknown's avatar
unknown committed
2259 2260
/* table_list should contain just one table */
int mysql_discard_or_import_tablespace(THD *thd,
2261 2262
		      TABLE_LIST *table_list,
		      enum tablespace_op_type tablespace_op)
unknown's avatar
unknown committed
2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279
{
  TABLE *table;
  my_bool discard;
  int error;
  DBUG_ENTER("mysql_discard_or_import_tablespace");

  /* Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
  ALTER TABLE */

  thd->proc_info="discard_or_import_tablespace";

  if (tablespace_op == DISCARD_TABLESPACE)
    discard = TRUE;
  else
    discard = FALSE;

  thd->tablespace_op=TRUE; /* we set this flag so that ha_innobase::open
2280 2281
			   and ::external_lock() do not complain when we
			   lock the table */
2282
  mysql_ha_close(thd, table_list, /*dont_send_ok*/ 1, /*dont_lock*/ 1);
unknown's avatar
unknown committed
2283 2284 2285 2286 2287 2288

  if (!(table=open_ltable(thd,table_list,TL_WRITE)))
  {
    thd->tablespace_op=FALSE;
    DBUG_RETURN(-1);
  }
2289

unknown's avatar
unknown committed
2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314
  error=table->file->discard_or_import_tablespace(discard);

  thd->proc_info="end";

  if (error)
    goto err;

  /* The 0 in the call below means 'not in a transaction', which means
  immediate invalidation; that is probably what we wish here */
  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())
  {
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
    mysql_bin_log.write(&qinfo);
  }
err:
  close_thread_tables(thd);
unknown's avatar
unknown committed
2315
  thd->tablespace_op=FALSE;
unknown's avatar
unknown committed
2316 2317
  if (error == 0)
  {
unknown's avatar
unknown committed
2318
    send_ok(thd);
unknown's avatar
unknown committed
2319
    DBUG_RETURN(0);
unknown's avatar
unknown committed
2320 2321 2322
  }
  DBUG_RETURN(error);
}
unknown's avatar
unknown committed
2323

2324 2325

#ifdef NOT_USED
2326 2327 2328 2329 2330 2331 2332
/*
  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.
*/

2333
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
2334 2335 2336 2337 2338
{
  List<create_field> fields;
  List<Alter_drop>   drop;
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2339 2340 2341 2342 2343 2344 2345 2346
  int		     rc;
  uint		     idx;
  uint		     db_options;
  uint		     key_count;
  TABLE		     *table;
  Field		     **f_ptr;
  KEY		     *key_info_buffer;
  char		     path[FN_REFLEN+1];
2347 2348 2349
  DBUG_ENTER("mysql_create_index");

  /*
2350 2351 2352
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2353

2354 2355
    Open the table to have access to the correct table handler.
  */
2356 2357 2358 2359
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2360 2361 2362 2363 2364
    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.
  */
2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375
  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,
2376 2377 2378
			  keys, /*tmp_table*/ 0, db_options, table->file,
			  key_info_buffer, key_count,
			  /*select_field_count*/ 0))
2379 2380 2381
    DBUG_RETURN(-1);

  /*
2382 2383 2384
    Check if all keys can be generated with the add_index method.
    If anyone cannot, then take the old way.
  */
2385 2386 2387 2388
  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)&
2389
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2390 2391
      break ;
  }
2392
  if ((idx < key_count)|| !key_count)
2393 2394 2395 2396 2397 2398 2399 2400
  {
    /* 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,
2401 2402 2403 2404
			 &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);*/
2405 2406 2407 2408 2409
      DBUG_RETURN(-1);
  }
  else
  {
    if (table->file->add_index(table, key_info_buffer, key_count)||
2410 2411 2412 2413 2414 2415 2416 2417
	(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);*/
2418 2419
      DBUG_RETURN(-1);
  }
2420
  /* don't need to free((gptr) key_info_buffer);*/
2421 2422 2423 2424
  DBUG_RETURN(0);
}


2425 2426
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
		       List<Alter_drop> &drop)
2427 2428
{
  List<create_field> fields;
2429
  List<Key>	     keys;
2430 2431
  List<Alter_column> alter;
  HA_CREATE_INFO     create_info;
2432 2433 2434 2435 2436 2437 2438 2439 2440
  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];
2441 2442 2443
  DBUG_ENTER("mysql_drop_index");

  /*
2444 2445 2446
    Try to use online generation of index.
    This requires that all indexes can be created online.
    Otherwise, the old alter table procedure is executed.
2447

2448 2449
    Open the table to have access to the correct table handler.
  */
2450 2451 2452 2453
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

  /*
2454 2455 2456
    The drop_index method takes an array of key numbers.
    It cannot get more entries than keys in the table.
  */
2457 2458 2459 2460
  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
  key_count= 0;

  /*
2461 2462
    Get the number of each key and check if it can be created online.
  */
2463 2464 2465 2466 2467 2468 2469 2470 2471
  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))
2472
	break;
2473 2474 2475 2476 2477 2478 2479 2480
    }
    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);
    }
    /*
2481 2482 2483
      Check if the key can be generated with the add_index method.
      If anyone cannot, then take the old way.
    */
2484 2485
    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
    if (!(table->file->index_ddl_flags(table->key_info+idx)&
2486
	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497
      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,
2498 2499 2500
			 &create_info, table_list, table,
			 fields, keys, drop, alter, 0, (ORDER*)0,
			 ALTER_DROP_INDEX, DUP_ERROR))
2501 2502 2503 2504 2505 2506 2507
      /*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)||
2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518
	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))
2519 2520 2521 2522 2523 2524 2525
      /*don't need to free((gptr) key_numbers);*/
      DBUG_RETURN(-1);
  }

  /*don't need to free((gptr) key_numbers);*/
  DBUG_RETURN(0);
}
2526
#endif /* NOT_USED */
2527 2528


2529 2530 2531
/*
  Alter table
*/
2532

unknown's avatar
unknown committed
2533
int mysql_alter_table(THD *thd,char *new_db, char *new_name,
2534 2535
		      HA_CREATE_INFO *create_info,
		      TABLE_LIST *table_list,
2536 2537
		      List<create_field> &fields, List<Key> &keys,
		      uint order_num, ORDER *order,
2538
		      enum enum_duplicates handle_duplicates,
2539
		      ALTER_INFO *alter_info, bool do_send_ok)
unknown's avatar
unknown committed
2540
{
2541
  TABLE *table,*new_table;
unknown's avatar
unknown committed
2542
  int error;
unknown's avatar
unknown committed
2543 2544
  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
2545
  char index_file[FN_REFLEN], data_file[FN_REFLEN];
unknown's avatar
unknown committed
2546 2547
  ha_rows copied,deleted;
  ulonglong next_insert_id;
2548
  uint db_create_options, used_fields;
unknown's avatar
unknown committed
2549
  enum db_type old_db_type,new_db_type;
2550
  DBUG_ENTER("mysql_alter_table");
unknown's avatar
unknown committed
2551 2552 2553

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

unknown's avatar
unknown committed
2556
  db=table_list->db;
unknown's avatar
unknown committed
2557
  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
2558
    new_db= db;
2559
  used_fields=create_info->used_fields;
unknown's avatar
unknown committed
2560

2561
  mysql_ha_close(thd, table_list, /*dont_send_ok*/ 1, /*dont_lock*/ 1);
2562 2563

  /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
2564
  if (alter_info->tablespace_op != NO_TABLESPACE_OP)
2565
    DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
2566
						   alter_info->tablespace_op));
2567 2568 2569
  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
2570 2571 2572 2573
  /* Check that we are not trying to rename to an existing table */
  if (new_name)
  {
    strmov(new_name_buff,new_name);
2574
    strmov(new_alias= new_alias_buff, new_name);
unknown's avatar
unknown committed
2575
    if (lower_case_table_names)
unknown's avatar
unknown committed
2576 2577 2578
    {
      if (lower_case_table_names != 2)
      {
2579
	my_casedn_str(files_charset_info, new_name_buff);
2580
	new_alias= new_name;			// Create lower case table name
unknown's avatar
unknown committed
2581
      }
2582
      my_casedn_str(files_charset_info, new_name);
unknown's avatar
unknown committed
2583
    }
2584
    if (new_db == db &&
2585
	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
2586 2587
    {
      /*
2588 2589
	Source and destination table names are equal: make later check
	easier.
2590
      */
unknown's avatar
unknown committed
2591
      new_alias= new_name= table_name;
2592
    }
unknown's avatar
unknown committed
2593 2594 2595 2596
    else
    {
      if (table->tmp_table)
      {
2597 2598 2599 2600 2601
	if (find_temporary_table(thd,new_db,new_name_buff))
	{
	  my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
	  DBUG_RETURN(-1);
	}
unknown's avatar
unknown committed
2602 2603 2604
      }
      else
      {
2605 2606 2607 2608 2609 2610 2611
	if (!access(fn_format(new_name_buff,new_name_buff,new_db,reg_ext,0),
		    F_OK))
	{
	  /* Table will be closed in do_command() */
	  my_error(ER_TABLE_EXISTS_ERROR,MYF(0), new_alias);
	  DBUG_RETURN(-1);
	}
unknown's avatar
unknown committed
2612 2613 2614 2615
      }
    }
  }
  else
2616 2617 2618 2619
  {
    new_alias= (lower_case_table_names == 2) ? alias : table_name;
    new_name= table_name;
  }
unknown's avatar
unknown committed
2620 2621 2622 2623

  old_db_type=table->db_type;
  if (create_info->db_type == DB_TYPE_DEFAULT)
    create_info->db_type=old_db_type;
2624 2625 2626 2627 2628
  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,
2629 2630 2631 2632
			ER_WARN_USING_OTHER_HANDLER,
			ER(ER_WARN_USING_OTHER_HANDLER),
			ha_get_storage_engine(new_db_type),
			new_name);
2633
  }
2634
  if (create_info->row_type == ROW_TYPE_NOT_USED)
unknown's avatar
unknown committed
2635 2636 2637
    create_info->row_type=table->row_type;

  thd->proc_info="setup";
2638
  if (alter_info->is_simple && !table->tmp_table)
unknown's avatar
unknown committed
2639 2640
  {
    error=0;
2641
    if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
2642
    {
2643 2644 2645 2646 2647 2648
      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))
      {
2649 2650
	my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
	error= -1;
2651 2652 2653
      }
      else
      {
2654 2655 2656 2657
	*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;
2658 2659
      }
      VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
2660
    }
unknown's avatar
unknown committed
2661

2662
    if (!error)
2663
    {
2664
      switch (alter_info->keys_onoff) {
2665
      case LEAVE_AS_IS:
2666
	break;
2667
      case ENABLE:
2668
	VOID(pthread_mutex_lock(&LOCK_open));
2669
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
2670
	VOID(pthread_mutex_unlock(&LOCK_open));
2671
	error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2672
	/* COND_refresh will be signaled in close_thread_tables() */
2673 2674
	break;
      case DISABLE:
2675 2676 2677
	VOID(pthread_mutex_lock(&LOCK_open));
	wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
	VOID(pthread_mutex_unlock(&LOCK_open));
2678
	error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
2679
	/* COND_refresh will be signaled in close_thread_tables() */
2680
	break;
2681
      }
2682
    }
2683
    if (error == HA_ERR_WRONG_COMMAND)
unknown's avatar
unknown committed
2684 2685
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
2686 2687
			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
			  table->table_name);
unknown's avatar
unknown committed
2688 2689
      error=0;
    }
unknown's avatar
unknown committed
2690 2691
    if (!error)
    {
2692 2693 2694
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2695 2696 2697
	thd->clear_error();
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
	mysql_bin_log.write(&qinfo);
2698
      }
2699 2700
      if (do_send_ok)
        send_ok(thd);
unknown's avatar
unknown committed
2701
    }
2702
    else if (error > 0)
2703 2704
    {
      table->file->print_error(error, MYF(0));
2705
      error= -1;
2706
    }
2707
    table_list->table=0;				// For query cache
2708
    query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
2709 2710 2711 2712
    DBUG_RETURN(error);
  }

  /* Full alter table */
2713 2714 2715 2716 2717 2718 2719 2720

  /* 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;
2721 2722
  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
    create_info->default_table_charset= table->table_charset;
2723

2724
  restore_record(table,default_values);		// Empty record for DEFAULT
2725
  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
unknown's avatar
unknown committed
2726
  List_iterator<create_field> def_it(fields);
2727
  List_iterator<Alter_column> alter_it(alter_info->alter_list);
2728 2729
  List<create_field> create_list;		// Add new fields here
  List<Key> key_list;				// Add new keys here
2730 2731
  create_field *def;

unknown's avatar
unknown committed
2732
  /*
2733
    First collect all fields from table which isn't in drop_list
unknown's avatar
unknown committed
2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744
  */

  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 &&
2745
	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
2746
      {
2747 2748 2749 2750 2751 2752 2753 2754
	/* 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;
2755
      }
unknown's avatar
unknown committed
2756 2757 2758 2759 2760 2761 2762 2763 2764 2765
    }
    if (drop)
    {
      drop_it.remove();
      continue;
    }
    /* Check if field is changed */
    def_it.rewind();
    while ((def=def_it++))
    {
2766
      if (def->change &&
2767 2768
	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
	break;
unknown's avatar
unknown committed
2769 2770
    }
    if (def)
2771
    {						// Field is changed
unknown's avatar
unknown committed
2772
      def->field=field;
2773 2774
      if (!def->after)
      {
2775 2776
	create_list.push_back(def);
	def_it.remove();
2777
      }
unknown's avatar
unknown committed
2778 2779
    }
    else
2780
    {						// Use old field value
2781
      create_list.push_back(def=new create_field(field,field));
2782
      alter_it.rewind();			// Change default if ALTER
unknown's avatar
unknown committed
2783 2784 2785
      Alter_column *alter;
      while ((alter=alter_it++))
      {
2786 2787
	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
	  break;
unknown's avatar
unknown committed
2788 2789 2790
      }
      if (alter)
      {
2791 2792 2793 2794 2795 2796 2797
	if (def->sql_type == FIELD_TYPE_BLOB)
	{
	  my_error(ER_BLOB_CANT_HAVE_DEFAULT,MYF(0),def->change);
	  DBUG_RETURN(-1);
	}
	def->def=alter->def;			// Use new default
	alter_it.remove();
unknown's avatar
unknown committed
2798 2799 2800 2801 2802
      }
    }
  }
  def_it.rewind();
  List_iterator<create_field> find_it(create_list);
2803
  while ((def=def_it++))			// Add new columns
unknown's avatar
unknown committed
2804
  {
2805
    if (def->change && ! def->field)
unknown's avatar
unknown committed
2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817
    {
      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();
2818
      while ((find=find_it++))			// Add new columns
unknown's avatar
unknown committed
2819
      {
2820 2821
	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
	  break;
unknown's avatar
unknown committed
2822 2823 2824
      }
      if (!find)
      {
2825 2826
	my_error(ER_BAD_FIELD_ERROR,MYF(0),def->after,table_name);
	DBUG_RETURN(-1);
unknown's avatar
unknown committed
2827
      }
2828
      find_it.after(def);			// Put element after this
unknown's avatar
unknown committed
2829 2830
    }
  }
2831
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
2832
  {
2833 2834
    my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_info->alter_list.head()->name,
	     table_name);
unknown's avatar
unknown committed
2835 2836 2837 2838 2839 2840 2841 2842 2843
    DBUG_RETURN(-1);
  }
  if (!create_list.elements)
  {
    my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
    DBUG_RETURN(-1);
  }

  /*
2844 2845
    Collect all keys which isn't in drop list. Add only those
    for which some fields exists.
unknown's avatar
unknown committed
2846 2847 2848 2849 2850 2851 2852 2853 2854
  */

  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++)
  {
2855
    char *key_name= key_info->name;
unknown's avatar
unknown committed
2856 2857 2858 2859 2860
    Alter_drop *drop;
    drop_it.rewind();
    while ((drop=drop_it++))
    {
      if (drop->type == Alter_drop::KEY &&
2861 2862
	  !my_strcasecmp(system_charset_info,key_name, drop->name))
	break;
unknown's avatar
unknown committed
2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874
    }
    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)
2875
	continue;				// Wrong field (from UNIREG)
unknown's avatar
unknown committed
2876 2877 2878 2879 2880
      const char *key_part_name=key_part->field->field_name;
      create_field *cfield;
      field_it.rewind();
      while ((cfield=field_it++))
      {
2881 2882 2883 2884 2885 2886 2887 2888 2889
	if (cfield->change)
	{
	  if (!my_strcasecmp(system_charset_info, key_part_name,
			     cfield->change))
	    break;
	}
	else if (!my_strcasecmp(system_charset_info,
				key_part_name, cfield->field_name))
	  break;
unknown's avatar
unknown committed
2890 2891
      }
      if (!cfield)
2892
	continue;				// Field is removed
unknown's avatar
unknown committed
2893
      uint key_part_length=key_part->length;
2894 2895 2896 2897 2898 2899 2900
      if (cfield->field)			// Not new field
      {						// Check if sub key
	if (cfield->field->type() != FIELD_TYPE_BLOB &&
	    (cfield->field->pack_length() == key_part_length ||
	     cfield->length <= key_part_length /
			       key_part->field->charset()->mbmaxlen))
	  key_part_length=0;			// Use whole field
unknown's avatar
unknown committed
2901
      }
2902
      key_part_length /= key_part->field->charset()->mbmaxlen;
unknown's avatar
unknown committed
2903
      key_parts.push_back(new key_part_spec(cfield->field_name,
2904
					    key_part_length));
unknown's avatar
unknown committed
2905 2906
    }
    if (key_parts.elements)
unknown's avatar
unknown committed
2907
      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
2908 2909 2910 2911 2912 2913 2914 2915
				 (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,
2916
                                 test(key_info->flags & HA_GENERATED_KEY),
2917
				 key_parts));
unknown's avatar
unknown committed
2918 2919 2920
  }
  {
    Key *key;
2921
    while ((key=key_it++))			// Add new keys
2922 2923
    {
      if (key->type != Key::FOREIGN_KEY)
2924
	key_list.push_back(key);
2925
      if (key->name &&
2926
	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
2927
      {
2928 2929
	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
	DBUG_RETURN(-1);
2930
      }
2931
    }
unknown's avatar
unknown committed
2932 2933
  }

2934
  if (alter_info->drop_list.elements)
unknown's avatar
unknown committed
2935
  {
2936 2937
    my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),
	     alter_info->drop_list.head()->name);
unknown's avatar
unknown committed
2938 2939
    goto err;
  }
2940
  if (alter_info->alter_list.elements)
unknown's avatar
unknown committed
2941
  {
2942 2943
    my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),
	     alter_info->alter_list.head()->name);
unknown's avatar
unknown committed
2944 2945 2946
    goto err;
  }

2947
  db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD);
2948 2949
  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
2950 2951
  /* Safety fix for innodb */
  if (lower_case_table_names)
2952
    my_casedn_str(files_charset_info, tmp_name);
unknown's avatar
unknown committed
2953 2954 2955
  create_info->db_type=new_db_type;
  if (!create_info->comment)
    create_info->comment=table->comment;
2956 2957 2958 2959 2960

  table->file->update_create_info(create_info);
  if ((create_info->table_options &
       (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
      (used_fields & HA_CREATE_USED_PACK_KEYS))
unknown's avatar
unknown committed
2961 2962 2963 2964 2965 2966 2967
    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 |
2968
			  HA_OPTION_NO_DELAY_KEY_WRITE);
unknown's avatar
unknown committed
2969 2970 2971 2972 2973
  create_info->table_options|= db_create_options;

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

2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998
  /*
    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.
  */

2999
  if (!strcmp(db, new_db))		// Ignore symlink if db changed
3000 3001 3002 3003 3004 3005
  {
    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,
3006 3007
					   create_info->index_file_name,
					   1);
3008 3009 3010 3011 3012 3013
    }
    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,
3014 3015
					  create_info->data_file_name,
					  1);
3016 3017
    }
  }
3018 3019
  else
    create_info->data_file_name=create_info->index_file_name=0;
3020 3021 3022 3023 3024 3025 3026 3027
  {
    /* We don't log the statement, it will be logged later */
    Disable_binlog disable_binlog(thd);
    if ((error=mysql_create_table(thd, new_db, tmp_name,
                                  create_info,
                                  create_list,key_list,1,0)))
      DBUG_RETURN(error);
  }
unknown's avatar
unknown committed
3028 3029 3030
  if (table->tmp_table)
    new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
  else
unknown's avatar
unknown committed
3031
  {
unknown's avatar
unknown committed
3032
    char path[FN_REFLEN];
3033 3034
    my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
		new_db, tmp_name);
unknown's avatar
unknown committed
3035 3036 3037 3038 3039 3040 3041
    fn_format(path,path,"","",4);
    new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
  }
  if (!new_table)
  {
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
    goto err;
unknown's avatar
unknown committed
3042 3043
  }

3044 3045 3046

  /*
    We don't want update TIMESTAMP fields during ALTER TABLE
3047 3048 3049 3050
    and copy_data_between_tables uses only write_row() for new_table so
    don't need to set up timestamp_on_update_now member.
  */
  new_table->timestamp_default_now= 0;
unknown's avatar
unknown committed
3051
  new_table->next_number_field=new_table->found_next_number_field;
3052
  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
unknown's avatar
unknown committed
3053 3054
  thd->cuted_fields=0L;
  thd->proc_info="copy to tmp table";
3055
  next_insert_id=thd->next_insert_id;		// Remember for loggin
unknown's avatar
unknown committed
3056 3057 3058
  copied=deleted=0;
  if (!new_table->is_view)
    error=copy_data_between_tables(table,new_table,create_list,
3059 3060 3061
				   handle_duplicates,
				   order_num, order, &copied, &deleted);
  thd->last_insert_id=next_insert_id;		// Needed for correct log
3062
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
unknown's avatar
unknown committed
3063 3064 3065 3066 3067 3068

  if (table->tmp_table)
  {
    /* We changed a temporary table */
    if (error)
    {
unknown's avatar
unknown committed
3069
      /*
3070 3071
	The following function call will free the new_table pointer,
	in close_temporary_table(), so we can safely directly jump to err
3072
      */
unknown's avatar
unknown committed
3073 3074 3075
      close_temporary_table(thd,new_db,tmp_name);
      goto err;
    }
3076 3077 3078 3079 3080 3081
    /* Close lock if this is a transactional table */
    if (thd->lock)
    {
      mysql_unlock_tables(thd, thd->lock);
      thd->lock=0;
    }
unknown's avatar
unknown committed
3082 3083
    /* Remove link to old table and rename the new one */
    close_temporary_table(thd,table->table_cache_key,table_name);
unknown's avatar
unknown committed
3084
    if (rename_temporary_table(thd, new_table, new_db, new_alias))
3085
    {						// Fatal error
unknown's avatar
unknown committed
3086 3087 3088 3089
      close_temporary_table(thd,new_db,tmp_name);
      my_free((gptr) new_table,MYF(0));
      goto err;
    }
3090 3091 3092
    mysql_update_log.write(thd, thd->query,thd->query_length);
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
3093
      thd->clear_error();
3094
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
3095 3096
      mysql_bin_log.write(&qinfo);
    }
unknown's avatar
unknown committed
3097 3098 3099
    goto end_temporary;
  }

3100
  intern_close_table(new_table);		/* close temporary table */
unknown's avatar
unknown committed
3101 3102 3103 3104 3105 3106 3107 3108
  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;
  }
3109

unknown's avatar
unknown committed
3110
  /*
3111 3112 3113
    Data is copied.  Now we rename the old table to a temp name,
    rename the new one to the old name, remove all entries from the old table
    from the cash, free all locks, close the old table and remove it.
unknown's avatar
unknown committed
3114 3115 3116
  */

  thd->proc_info="rename result table";
3117 3118
  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
	      current_pid, thd->thread_id);
3119 3120
  if (lower_case_table_names)
    my_casedn_str(files_charset_info, old_name);
3121
  if (new_name != table_name || new_db != db)
unknown's avatar
unknown committed
3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132
  {
    if (!access(new_name_buff,F_OK))
    {
      error=1;
      my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }

unknown's avatar
unknown committed
3133 3134 3135 3136 3137
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  if (table->file->has_transactions())
#endif
  {
    /*
3138
      Win32 and InnoDB can't drop a table that is in use, so we must
3139
      close the original table at before doing the rename
unknown's avatar
unknown committed
3140
    */
3141
    table_name=thd->strdup(table_name);		// must be saved
3142
    if (close_cached_table(thd, table))
3143
    {						// Aborted
unknown's avatar
unknown committed
3144 3145 3146 3147
      VOID(quick_rm_table(new_db_type,new_db,tmp_name));
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
3148
    table=0;					// Marker that table is closed
unknown's avatar
unknown committed
3149
  }
unknown's avatar
unknown committed
3150 3151
#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
  else
3152
    table->file->extra(HA_EXTRA_FORCE_REOPEN);	// Don't use this file anymore
unknown's avatar
unknown committed
3153 3154
#endif

unknown's avatar
unknown committed
3155

unknown's avatar
unknown committed
3156 3157 3158 3159 3160 3161 3162
  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,
3163 3164
			      new_alias))
  {						// Try to get everything back
unknown's avatar
unknown committed
3165
    error=1;
unknown's avatar
unknown committed
3166
    VOID(quick_rm_table(new_db_type,new_db,new_alias));
unknown's avatar
unknown committed
3167
    VOID(quick_rm_table(new_db_type,new_db,tmp_name));
unknown's avatar
unknown committed
3168
    VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
unknown's avatar
unknown committed
3169 3170 3171
  }
  if (error)
  {
unknown's avatar
unknown committed
3172 3173 3174 3175
    /*
      This shouldn't happen.  We solve this the safe way by
      closing the locked table.
    */
3176 3177
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3178 3179 3180
    VOID(pthread_mutex_unlock(&LOCK_open));
    goto err;
  }
3181
  if (thd->lock || new_name != table_name)	// True if WIN32
unknown's avatar
unknown committed
3182
  {
unknown's avatar
unknown committed
3183 3184 3185 3186
    /*
      Not table locking or alter table with rename
      free locks and remove old table
    */
3187 3188
    if (table)
      close_cached_table(thd,table);
unknown's avatar
unknown committed
3189 3190 3191 3192
    VOID(quick_rm_table(old_db_type,db,old_name));
  }
  else
  {
unknown's avatar
unknown committed
3193 3194 3195 3196 3197
    /*
      Using LOCK TABLES without rename.
      This code is never executed on WIN32!
      Remove old renamed table, reopen table and get new locks
    */
unknown's avatar
unknown committed
3198 3199 3200 3201
    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
3202
      mysql_lock_abort(thd,table);		 // end threads waiting on lock
unknown's avatar
unknown committed
3203 3204 3205
    }
    VOID(quick_rm_table(old_db_type,db,old_name));
    if (close_data_tables(thd,db,table_name) ||
3206 3207
	reopen_tables(thd,1,0))
    {						// This shouldn't happen
3208
      if (table)
3209
	close_cached_table(thd,table);		// Remove lock for table
unknown's avatar
unknown committed
3210 3211 3212 3213
      VOID(pthread_mutex_unlock(&LOCK_open));
      goto err;
    }
  }
unknown's avatar
unknown committed
3214
  /* The ALTER TABLE is always in its own transaction */
3215 3216 3217 3218
  error = ha_commit_stmt(thd);
  if (ha_commit(thd))
    error=1;
  if (error)
unknown's avatar
unknown committed
3219 3220
  {
    VOID(pthread_mutex_unlock(&LOCK_open));
3221
    VOID(pthread_cond_broadcast(&COND_refresh));
unknown's avatar
unknown committed
3222 3223 3224
    goto err;
  }
  thd->proc_info="end";
3225 3226
  mysql_update_log.write(thd, thd->query,thd->query_length);
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
3227
  {
unknown's avatar
unknown committed
3228
    thd->clear_error();
3229
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
unknown's avatar
unknown committed
3230 3231 3232 3233
    mysql_bin_log.write(&qinfo);
  }
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_mutex_unlock(&LOCK_open));
3234 3235 3236
#ifdef HAVE_BERKELEY_DB
  if (old_db_type == DB_TYPE_BERKELEY_DB)
  {
unknown's avatar
unknown committed
3237 3238 3239 3240 3241
    /*
      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.
    */
3242
    char path[FN_REFLEN];
3243 3244
    my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
		new_db, table_name);
3245 3246 3247
    fn_format(path,path,"","",4);
    table=open_temporary_table(thd, path, new_db, tmp_name,0);
    if (table)
unknown's avatar
unknown committed
3248
    {
3249 3250
      intern_close_table(table);
      my_free((char*) table, MYF(0));
unknown's avatar
unknown committed
3251
    }
3252
    else
unknown's avatar
unknown committed
3253 3254
      sql_print_warning("Could not open BDB table %s.%s after rename\n",
                        new_db,table_name);
3255
    (void) berkeley_flush_logs();
3256 3257
  }
#endif
3258
  table_list->table=0;				// For query cache
unknown's avatar
unknown committed
3259
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
3260 3261

end_temporary:
3262 3263 3264
  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
	      (ulong) (copied + deleted), (ulong) deleted,
	      (ulong) thd->cuted_fields);
3265 3266
  if (do_send_ok)
    send_ok(thd,copied+deleted,0L,tmp_name);
unknown's avatar
unknown committed
3267 3268 3269 3270 3271 3272 3273 3274 3275
  thd->some_tables_deleted=0;
  DBUG_RETURN(0);

 err:
  DBUG_RETURN(-1);
}


static int
unknown's avatar
unknown committed
3276
copy_data_between_tables(TABLE *from,TABLE *to,
3277 3278 3279 3280 3281
			 List<create_field> &create,
			 enum enum_duplicates handle_duplicates,
			 uint order_num, ORDER *order,
			 ha_rows *copied,
			 ha_rows *deleted)
unknown's avatar
unknown committed
3282 3283
{
  int error;
3284
  Copy_field *copy,*copy_end;
unknown's avatar
unknown committed
3285 3286
  ulong found_count,delete_count;
  THD *thd= current_thd;
unknown's avatar
unknown committed
3287 3288 3289 3290 3291 3292
  uint length;
  SORT_FIELD *sortorder;
  READ_RECORD info;
  TABLE_LIST   tables;
  List<Item>   fields;
  List<Item>   all_fields;
3293
  ha_rows examined_rows;
3294
  bool auto_increment_field_copied= 0;
unknown's avatar
unknown committed
3295 3296 3297
  DBUG_ENTER("copy_data_between_tables");

  if (!(copy= new Copy_field[to->fields]))
3298
    DBUG_RETURN(-1);				/* purecov: inspected */
unknown's avatar
unknown committed
3299 3300

  to->file->external_lock(thd,F_WRLCK);
3301
  from->file->info(HA_STATUS_VARIABLE);
3302
  to->file->start_bulk_insert(from->file->records);
unknown's avatar
unknown committed
3303 3304 3305 3306 3307 3308 3309 3310

  List_iterator<create_field> it(create);
  create_field *def;
  copy_end=copy;
  for (Field **ptr=to->field ; *ptr ; ptr++)
  {
    def=it++;
    if (def->field)
3311 3312
    {
      if (*ptr == to->next_number_field)
3313
        auto_increment_field_copied= TRUE;
unknown's avatar
unknown committed
3314
      (copy_end++)->set(*ptr,def->field,0);
3315 3316
    }

unknown's avatar
unknown committed
3317 3318
  }

3319 3320
  found_count=delete_count=0;

unknown's avatar
unknown committed
3321 3322
  if (order)
  {
unknown's avatar
unknown committed
3323
    from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
3324
					      MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
3325 3326
    bzero((char*) &tables,sizeof(tables));
    tables.table = from;
3327
    tables.alias = tables.real_name= from->real_name;
3328
    tables.db	 = from->table_cache_key;
unknown's avatar
unknown committed
3329 3330
    error=1;

3331
    if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
3332 3333 3334 3335 3336 3337 3338
	setup_order(thd, thd->lex->select_lex.ref_pointer_array,
		    &tables, fields, all_fields, order) ||
	!(sortorder=make_unireg_sortorder(order, &length)) ||
	(from->sort.found_records = filesort(thd, from, sortorder, length,
					     (SQL_SELECT *) 0, HA_POS_ERROR,
					     &examined_rows))
	== HA_POS_ERROR)
unknown's avatar
unknown committed
3339 3340 3341
      goto err;
  };

unknown's avatar
unknown committed
3342 3343 3344 3345 3346
  /*
    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.
  */
  error= ha_recovery_logging(thd,FALSE);
3347
  if (error)
3348
  {
unknown's avatar
unknown committed
3349
    error= 1;
3350 3351
    goto err;
  }
3352

3353 3354 3355 3356
  /* Handler must be told explicitly to retrieve all columns, because
     this function does not set field->query_id in the columns to the
     current query id */
  from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
3357
  init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
3358 3359
  if (handle_duplicates == DUP_IGNORE ||
      handle_duplicates == DUP_REPLACE)
unknown's avatar
unknown committed
3360
    to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
3361
  thd->row_count= 0;
unknown's avatar
unknown committed
3362 3363 3364 3365 3366 3367 3368 3369
  while (!(error=info.read_record(&info)))
  {
    if (thd->killed)
    {
      my_error(ER_SERVER_SHUTDOWN,MYF(0));
      error= 1;
      break;
    }
3370
    thd->row_count++;
3371 3372
    if (to->next_number_field)
    {
3373
      if (auto_increment_field_copied)
3374
        to->auto_increment_field_not_null= TRUE;
3375 3376 3377 3378 3379
      else
        to->next_number_field->reset();
    }
    for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
    {
unknown's avatar
unknown committed
3380
      copy_ptr->do_copy(copy_ptr);
3381
    }
unknown's avatar
unknown committed
3382 3383
    if ((error=to->file->write_row((byte*) to->record[0])))
    {
3384
      if ((handle_duplicates != DUP_IGNORE &&
3385 3386 3387
	   handle_duplicates != DUP_REPLACE) ||
	  (error != HA_ERR_FOUND_DUPP_KEY &&
	   error != HA_ERR_FOUND_DUPP_UNIQUE))
unknown's avatar
unknown committed
3388
      {
3389 3390
	to->file->print_error(error,MYF(0));
	break;
unknown's avatar
unknown committed
3391 3392 3393 3394
      }
      delete_count++;
    }
    else
3395
      found_count++;
unknown's avatar
unknown committed
3396 3397
  }
  end_read_record(&info);
3398
  free_io_cache(from);
3399
  delete [] copy;				// This is never 0
unknown's avatar
unknown committed
3400 3401

  if (to->file->end_bulk_insert() && !error)
unknown's avatar
unknown committed
3402
  {
unknown's avatar
unknown committed
3403
    to->file->print_error(my_errno,MYF(0));
unknown's avatar
unknown committed
3404 3405
    error=1;
  }
unknown's avatar
unknown committed
3406
  to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
3407

unknown's avatar
unknown committed
3408
  ha_recovery_logging(thd,TRUE);
3409 3410 3411 3412 3413 3414 3415 3416 3417
  /*
    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;
  if (to->file->external_lock(thd,F_UNLCK))
unknown's avatar
unknown committed
3418
    error=1;
unknown's avatar
unknown committed
3419 3420
 err:
  free_io_cache(from);
unknown's avatar
unknown committed
3421 3422 3423 3424
  *copied= found_count;
  *deleted=delete_count;
  DBUG_RETURN(error > 0 ? -1 : 0);
}
3425

unknown's avatar
unknown committed
3426

3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448
/*
  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();
3449
  lex->alter_info.is_simple= 0;                 // Force full recreate
3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460
  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,
                                DUP_ERROR, &lex->alter_info, do_send_ok));
}


unknown's avatar
unknown committed
3461
int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
3462 3463 3464 3465 3466
{
  TABLE_LIST *table;
  List<Item> field_list;
  Item *item;
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
3467
  DBUG_ENTER("mysql_checksum_table");
3468 3469 3470 3471 3472 3473 3474 3475

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

unknown's avatar
unknown committed
3476
  for (table= tables; table; table= table->next)
3477 3478
  {
    char table_name[NAME_LEN*2+2];
3479
    TABLE *t;
3480

unknown's avatar
unknown committed
3481 3482 3483
    strxmov(table_name, table->db ,".", table->real_name, NullS);

    t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT);
3484
    thd->clear_error();			// these errors shouldn't get client
3485 3486 3487 3488

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

3489
    if (!t)
3490
    {
unknown's avatar
unknown committed
3491
      /* Table didn't exist */
3492 3493 3494 3495 3496
      protocol->store_null();
      thd->net.last_error[0]=0;
    }
    else
    {
3497
      t->pos_in_table_list= table;
3498

3499
      if (t->file->table_flags() & HA_HAS_CHECKSUM &&
3500 3501
	  !(check_opt->flags & T_EXTEND))
	protocol->store((ulonglong)t->file->checksum());
3502
      else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
3503 3504
	       (check_opt->flags & T_QUICK))
	protocol->store_null();
3505 3506
      else
      {
3507 3508 3509 3510 3511 3512 3513 3514
	/* calculating table's checksum */
	ha_checksum crc= 0;

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

unknown's avatar
unknown committed
3515
	if (t->file->ha_rnd_init(1))
3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542
	  protocol->store_null();
	else
	{
	  while (!t->file->rnd_next(t->record[0]))
	  {
	    ha_checksum row_crc= 0;
	    if (t->record[0] != (byte*) t->field[0]->ptr)
	      row_crc= my_checksum(row_crc, t->record[0],
				   ((byte*) t->field[0]->ptr) - t->record[0]);

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

	    crc+= row_crc;
	  }
	  protocol->store((ulonglong)crc);
unknown's avatar
unknown committed
3543
          t->file->ha_rnd_end();
3544
	}
3545
      }
unknown's avatar
unknown committed
3546
      thd->clear_error();
3547
      close_thread_tables(thd);
3548
      table->table=0;				// For query cache
3549 3550 3551 3552 3553 3554 3555
    }
    if (protocol->write())
      goto err;
  }

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

3557
 err:
3558
  close_thread_tables(thd);			// Shouldn't be needed
3559 3560 3561 3562
  if (table)
    table->table=0;
  DBUG_RETURN(-1);
}