summaryrefslogtreecommitdiff
path: root/sql/sql_table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r--sql/sql_table.cc1559
1 files changed, 1559 insertions, 0 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
new file mode 100644
index 00000000000..ed63b12ceb1
--- /dev/null
+++ b/sql/sql_table.cc
@@ -0,0 +1,1559 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ 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"
+#include <hash.h>
+#include <myisam.h>
+
+#ifdef __WIN__
+#include <io.h>
+#endif
+
+extern HASH open_cache;
+
+static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
+static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
+static int copy_data_between_tables(TABLE *from,TABLE *to,
+ List<create_field> &create,
+ enum enum_duplicates handle_duplicates,
+ ulong *copied,ulong *deleted);
+
+/*****************************************************************************
+** Remove all possbile tables and give a compact errormessage for all
+** wrong tables.
+** This will wait for all users to free the table before dropping it
+*****************************************************************************/
+
+int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
+{
+ char path[FN_REFLEN];
+ String wrong_tables;
+ bool some_tables_deleted=0;
+ uint error;
+ db_type table_type;
+ DBUG_ENTER("mysql_rm_table");
+
+ /* mark for close and remove all cached entries */
+
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= &LOCK_open;
+ thd->mysys_var->current_cond= &COND_refresh;
+ VOID(pthread_mutex_lock(&LOCK_open));
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ for (TABLE_LIST *table=tables ; table ; table=table->next)
+ {
+ char *db=table->db ? table->db : thd->db;
+ if (!close_temporary_table(thd, db, table->real_name))
+ continue; // removed temporary table
+
+ abort_locked_tables(thd,db,table->real_name);
+ while (remove_table_from_cache(thd,db,table->real_name) && !thd->killed)
+ {
+ dropping_tables++;
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ dropping_tables--;
+ }
+ drop_locked_tables(thd,db,table->real_name);
+ if (thd->killed)
+ {
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+ VOID(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);
+ DBUG_RETURN(-1);
+ }
+ /* remove form file and isam files */
+ (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table->real_name,
+ reg_ext);
+ (void) unpack_filename(path,path);
+ error=0;
+
+ table_type=get_table_type(path);
+
+ if (my_delete(path,MYF(0))) /* Delete the table definition file */
+ {
+ if (errno != ENOENT || !if_exists)
+ {
+ error=1;
+ if (errno != ENOENT)
+ {
+ my_error(ER_CANT_DELETE_FILE,MYF(0),path,errno);
+ }
+ }
+ }
+ else
+ {
+ some_tables_deleted=1;
+ *fn_ext(path)=0; // Remove extension;
+ error=ha_delete_table(table_type, path);
+ if (error == ENOENT && if_exists)
+ error = 0;
+ }
+ if (error)
+ {
+ if (wrong_tables.length())
+ wrong_tables.append(',');
+ wrong_tables.append(String(table->real_name));
+ }
+ }
+ if (some_tables_deleted)
+ {
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+ 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 (wrong_tables.length())
+ {
+ my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr());
+ DBUG_RETURN(-1);
+ }
+ send_ok(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+int quick_rm_table(enum db_type base,const char *db,
+ const char *table_name)
+{
+ char path[FN_REFLEN];
+ int error=0;
+ (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table_name,reg_ext);
+ unpack_filename(path,path);
+ if (my_delete(path,MYF(0)))
+ error=1; /* purecov: inspected */
+ sprintf(path,"%s/%s/%s",mysql_data_home,db,table_name);
+ return ha_delete_table(base,path) || error;
+}
+
+/*****************************************************************************
+ * Create at table.
+ * If one creates a temporary table, this is automaticly opened
+ ****************************************************************************/
+
+int mysql_create_table(THD *thd,const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ List<create_field> &fields,
+ List<Key> &keys,bool tmp_table,bool no_log)
+{
+ char path[FN_REFLEN];
+ const char *key_name;
+ create_field *sql_field,*dup_field;
+ int error= -1;
+ uint db_options,field,null_fields,blob_columns;
+ ulong pos;
+ KEY *key_info,*key_info_buffer;
+ KEY_PART_INFO *key_part_info;
+ int auto_increment=0;
+ handler *file;
+ DBUG_ENTER("mysql_create_table");
+
+ /*
+ ** Check for dupplicate fields and check type of table to create
+ */
+
+ if (!fields.elements)
+ {
+ my_error(ER_TABLE_MUST_HAVE_COLUMNS,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ List_iterator<create_field> it(fields),it2(fields);
+ null_fields=blob_columns=0;
+ db_options=create_info->table_options;
+ if (create_info->row_type == ROW_TYPE_DYNAMIC)
+ db_options|=HA_OPTION_PACK_RECORD;
+ file=get_new_handler((TABLE*) 0, create_info->db_type);
+
+ /* Don't pack keys in old tables if the user has requested this */
+
+ while ((sql_field=it++))
+ {
+ if ((sql_field->flags & BLOB_FLAG) ||
+ sql_field->sql_type == FIELD_TYPE_VAR_STRING &&
+ create_info->row_type != ROW_TYPE_FIXED)
+ {
+ db_options|=HA_OPTION_PACK_RECORD;
+ }
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ null_fields++;
+ while ((dup_field=it2++) != sql_field)
+ {
+ if (my_strcasecmp(sql_field->field_name, dup_field->field_name) == 0)
+ {
+ my_error(ER_DUP_FIELDNAME,MYF(0),sql_field->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ it2.rewind();
+ }
+ /* If fixed row records, we need on bit to check for deleted rows */
+ if (!(db_options & HA_OPTION_PACK_RECORD))
+ null_fields++;
+ pos=(null_fields+7)/8;
+
+ it.rewind();
+ while ((sql_field=it++))
+ {
+ switch (sql_field->sql_type) {
+ case FIELD_TYPE_BLOB:
+ case FIELD_TYPE_MEDIUM_BLOB:
+ case FIELD_TYPE_TINY_BLOB:
+ case FIELD_TYPE_LONG_BLOB:
+ sql_field->pack_flag=FIELDFLAG_BLOB |
+ pack_length_to_packflag(sql_field->pack_length -
+ portable_sizeof_char_ptr);
+ if (sql_field->flags & BINARY_FLAG)
+ sql_field->pack_flag|=FIELDFLAG_BINARY;
+ sql_field->length=8; // Unireg field length
+ sql_field->unireg_check=Field::BLOB_FIELD;
+ blob_columns++;
+ break;
+ case FIELD_TYPE_VAR_STRING:
+ case FIELD_TYPE_STRING:
+ sql_field->pack_flag=0;
+ if (sql_field->flags & BINARY_FLAG)
+ sql_field->pack_flag|=FIELDFLAG_BINARY;
+ break;
+ case FIELD_TYPE_ENUM:
+ sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
+ FIELDFLAG_INTERVAL;
+ sql_field->unireg_check=Field::INTERVAL_FIELD;
+ break;
+ case FIELD_TYPE_SET:
+ sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
+ FIELDFLAG_BITFIELD;
+ sql_field->unireg_check=Field::BIT_FIELD;
+ break;
+ case FIELD_TYPE_DATE: // Rest of string types
+ case FIELD_TYPE_NEWDATE:
+ case FIELD_TYPE_TIME:
+ case FIELD_TYPE_DATETIME:
+ case FIELD_TYPE_NULL:
+ sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
+ break;
+ case FIELD_TYPE_TIMESTAMP:
+ sql_field->unireg_check=Field::TIMESTAMP_FIELD;
+ /* fall through */
+ default:
+ sql_field->pack_flag=(FIELDFLAG_NUMBER |
+ (sql_field->flags & UNSIGNED_FLAG ? 0 :
+ FIELDFLAG_DECIMAL) |
+ (sql_field->flags & ZEROFILL_FLAG ?
+ FIELDFLAG_ZEROFILL : 0) |
+ f_settype((uint) sql_field->sql_type) |
+ (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
+ break;
+ }
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ sql_field->pack_flag|=FIELDFLAG_MAYBE_NULL;
+ sql_field->offset= pos;
+ if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
+ auto_increment++;
+ pos+=sql_field->pack_length;
+ }
+ if (auto_increment > 1)
+ {
+ my_error(ER_WRONG_AUTO_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (auto_increment &&
+ (file->option_flag() & HA_WRONG_ASCII_ORDER))
+ {
+ my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ if (blob_columns && (file->option_flag() & HA_NO_BLOBS))
+ {
+ my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /* Create keys */
+ List_iterator<Key> key_iterator(keys);
+ uint key_parts=0,key_count=keys.elements;
+ bool primary_key=0,unique_key=0;
+ Key *key;
+ uint tmp;
+ tmp=max(file->max_keys(), MAX_KEY);
+
+ if (key_count > tmp)
+ {
+ my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
+ DBUG_RETURN(-1);
+ }
+ while ((key=key_iterator++))
+ {
+ tmp=max(file->max_key_parts(),MAX_REF_PARTS);
+ if (key->columns.elements > tmp)
+ {
+ my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
+ DBUG_RETURN(-1);
+ }
+ if (key->name() && strlen(key->name()) > NAME_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), key->name());
+ DBUG_RETURN(-1);
+ }
+ key_parts+=key->columns.elements;
+ }
+ key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)*key_count);
+ key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
+ if (!key_info_buffer || ! key_part_info)
+ DBUG_RETURN(-1); // Out of memory
+
+ key_iterator.rewind();
+ for (; (key=key_iterator++) ; key_info++)
+ {
+ uint key_length=0;
+ key_part_spec *column;
+ if (key->type == Key::PRIMARY)
+ {
+ if (primary_key)
+ {
+ my_error(ER_MULTIPLE_PRI_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ primary_key=1;
+ }
+ else if (key->type == Key::UNIQUE)
+ unique_key=1;
+ key_info->flags= (key->type == Key::MULTIPLE) ? 0 :
+ (key->type == Key::FULLTEXT) ? HA_FULLTEXT : HA_NOSAME;
+ key_info->key_parts=(uint8) key->columns.elements;
+ key_info->key_part=key_part_info;
+
+ List_iterator<key_part_spec> cols(key->columns);
+ for (uint column_nr=0 ; (column=cols++) ; column_nr++)
+ {
+ it.rewind();
+ field=0;
+ while ((sql_field=it++) &&
+ my_strcasecmp(column->field_name,sql_field->field_name))
+ field++;
+ if (!sql_field)
+ {
+ my_printf_error(ER_KEY_COLUMN_DOES_NOT_EXITS,
+ ER(ER_KEY_COLUMN_DOES_NOT_EXITS),MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ if (f_is_blob(sql_field->pack_flag))
+ {
+ if (!(file->option_flag() & HA_BLOB_KEY))
+ {
+ 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)
+ {
+ if (key->type == Key::FULLTEXT)
+ column->length=1; /* ft-code ignores it anyway :-) */
+ else
+ {
+ my_printf_error(ER_BLOB_KEY_WITHOUT_LENGTH,
+ ER(ER_BLOB_KEY_WITHOUT_LENGTH),MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ {
+ if (key->type == Key::PRIMARY)
+ {
+ my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (!(file->option_flag() & HA_NULL_KEY))
+ {
+ my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
+ MYF(0),column->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
+ {
+ if (column_nr == 0 || (file->option_flag() & HA_AUTO_PART_KEY))
+ auto_increment--; // Field is used
+ }
+ 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)
+ {
+ if (f_is_blob(sql_field->pack_flag))
+ {
+ if ((length=column->length) > file->max_key_length())
+ {
+ my_error(ER_WRONG_SUB_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ }
+ else if (column->length > length ||
+ (f_is_packed(sql_field->pack_flag) && column->length != length))
+ {
+ my_error(ER_WRONG_SUB_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ length=column->length;
+ }
+ else if (length == 0)
+ {
+ my_printf_error(ER_WRONG_KEY_COLUMN, ER(ER_WRONG_KEY_COLUMN), MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ key_part_info->length=(uint8) length;
+ /* Use packed keys for long strings on the first column */
+ if (!(db_options & HA_OPTION_NO_PACK_KEYS) &&
+ (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)))
+ {
+ if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB))
+ key_info->flags|= HA_BINARY_PACK_KEY;
+ else
+ key_info->flags|= HA_PACK_KEY;
+ }
+ key_length+=length;
+ key_part_info++;
+
+ /* Create the key name based on the first column (if not given) */
+ if (column_nr == 0)
+ {
+ if (key->type == Key::PRIMARY)
+ key_name="PRIMARY";
+ 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;
+ }
+ }
+ key_info->key_length=(uint16) key_length;
+ if (key_length > file->max_key_length())
+ {
+ my_error(ER_TOO_LONG_KEY,MYF(0),file->max_key_length());
+ DBUG_RETURN(-1);
+ }
+ }
+ if (auto_increment > 0)
+ {
+ my_error(ER_WRONG_AUTO_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (!primary_key && !unique_key &&
+ (file->option_flag() & HA_REQUIRE_PRIMARY_KEY))
+ {
+ my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /* Check if table exists */
+ if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ {
+ sprintf(path,"%s%s%lx_%lx_%x%s",mysql_tmpdir,tmp_file_prefix,
+ current_pid, thd->thread_id, thd->tmp_table++,reg_ext);
+ create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
+ }
+ else
+ (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table_name,reg_ext);
+ 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))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
+ DBUG_RETURN(-1);
+ }
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (!tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ {
+ if (!access(path,F_OK))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
+ DBUG_RETURN(0);
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ thd->proc_info="creating table";
+
+ create_info->table_options=db_options;
+ if (rea_create_table(path, create_info, fields, key_count,
+ key_info_buffer))
+ {
+ /* my_error(ER_CANT_CREATE_TABLE,MYF(0),table_name,my_errno); */
+ goto end;
+ }
+ if (!tmp_table && !no_log)
+ {
+ // Must be written before unlock
+ mysql_update_log.write(thd->query, thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ 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;
+ }
+ }
+ error=0;
+end:
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ 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++)
+ if (!my_strcasecmp(name,key->name))
+ 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;
+
+ if (!check_if_keyname_exists(field_name,start,end))
+ return (char*) field_name; // Use fieldname
+ buff_end=strmake(buff,field_name,MAX_FIELD_NAME-4);
+ for (uint i=2 ; ; i++)
+ {
+ sprintf(buff_end,"_%d",i);
+ if (!check_if_keyname_exists(buff,start,end))
+ return sql_strdup(buff);
+ }
+}
+
+/****************************************************************************
+** Create table from a list of fields and items
+****************************************************************************/
+
+TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
+ const char *db, const char *name,
+ List<create_field> *extra_fields,
+ List<Key> *keys,
+ List<Item> *items,
+ MYSQL_LOCK **lock)
+{
+ TABLE tmp_table; // Used during 'create_field()'
+ TABLE *table;
+ tmp_table.table_name=0;
+ DBUG_ENTER("create_table_from_items");
+
+ /* Add selected items to field list */
+ List_iterator<Item> it(*items);
+ 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 ||
+ create_info->db_type == DB_TYPE_HEAP);
+
+ while ((item=it++))
+ {
+ create_field *cr_field;
+ if (strlen(item->name) > NAME_LEN ||
+ check_column_name(item->name))
+ {
+ my_error(ER_WRONG_COLUMN_NAME,MYF(0),item->name);
+ DBUG_RETURN(0);
+ }
+
+ Field *field=create_tmp_field(&tmp_table,item,item->type(),
+ (Item_result_field***) 0, &tmp_field,0,0);
+ if (!field || !(cr_field=new create_field(field,1)))
+ DBUG_RETURN(0);
+ extra_fields->push_back(cr_field);
+ }
+ /* create and lock table */
+ /* QQ: This should be done atomic ! */
+ if (mysql_create_table(thd,db,name,create_info,*extra_fields,
+ *keys,0,1)) // no logging
+ DBUG_RETURN(0);
+ if (!(table=open_table(thd,db,name,name,(bool*) 0)))
+ {
+ quick_rm_table(create_info->db_type,db,name);
+ DBUG_RETURN(0);
+ }
+ table->reginfo.lock_type=TL_WRITE;
+ if (!((*lock)=mysql_lock_tables(thd,&table,1)))
+ {
+ hash_delete(&open_cache,(byte*) table);
+ quick_rm_table(create_info->db_type,db,name);
+ DBUG_RETURN(0);
+ }
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ DBUG_RETURN(table);
+}
+
+
+/****************************************************************************
+** Alter a table definition
+****************************************************************************/
+
+static bool
+mysql_rename_table(enum db_type base,
+ const char *old_db,
+ const char * old_name,
+ const char *new_db,
+ const char * new_name)
+{
+ char from[FN_REFLEN],to[FN_REFLEN];
+ handler *file=get_new_handler((TABLE*) 0, base);
+ bool error=0;
+ DBUG_ENTER("mysql_rename_table");
+ (void) sprintf(from,"%s/%s/%s",mysql_data_home,old_db,old_name);
+ (void) sprintf(to,"%s/%s/%s",mysql_data_home,new_db,new_name);
+ fn_format(from,from,"","",4);
+ fn_format(to,to, "","",4);
+ if (file->rename_table((const char*) from,(const char *) to) ||
+ rename_file_ext(from,to,reg_ext))
+ error=1;
+ delete file;
+ DBUG_RETURN(error);
+}
+
+/*
+ close table in this thread and force close + reopen in other threads
+ This assumes that the calling thread has lock on LOCK_open
+ Win32 clients must also have a WRITE LOCK on the table !
+*/
+
+bool close_cached_table(THD *thd,TABLE *table)
+{
+ bool result=0;
+ DBUG_ENTER("close_cached_table");
+ if (table)
+ {
+ VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Close all data files
+ /* Mark all tables that are in use as 'old' */
+ mysql_lock_abort(thd,table); // end threads waiting on lock
+
+#ifdef REMOVE_LOCKS
+ /* Wait until all there are no other threads that has this table open */
+ while (remove_table_from_cache(thd,table->table_cache_key,
+ table->table_name))
+ {
+ dropping_tables++;
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ dropping_tables--;
+ }
+#else
+ (void) remove_table_from_cache(thd,table->table_cache_key,
+ table->table_name);
+#endif
+ /* When lock on LOCK_open is freed other threads can continue */
+ pthread_cond_broadcast(&COND_refresh);
+
+ /* Close lock if this is not got with LOCK TABLES */
+ if (thd->lock)
+ {
+ mysql_unlock_tables(thd, thd->lock); thd->lock=0; // Start locked threads
+ }
+ /* Close all copies of 'table'. This also frees all LOCK TABLES lock */
+ thd->open_tables=unlink_open_table(thd,thd->open_tables,table);
+ }
+ DBUG_RETURN(result);
+}
+
+int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
+{
+ TABLE_LIST *table;
+ List<Item> field_list;
+ Item* item;
+ String* packet = &thd->packet;
+
+ DBUG_ENTER("mysql_repair_table");
+
+ 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;
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+
+ for (table = tables; table; table = table->next)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = (table->db) ? table->db : thd->db;
+ strxmov(table_name,db ? db : "",".",table->name,NullS);
+
+ table->table = open_ltable(thd, table, TL_WRITE);
+ packet->length(0);
+
+ if (!table->table)
+ {
+ const char *err_msg;
+ net_store_data(packet, table_name);
+ net_store_data(packet, "repair");
+ net_store_data(packet, "error");
+ if (!(err_msg=thd->net.last_error))
+ err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
+ net_store_data(packet, err_msg);
+ thd->net.last_error[0]=0;
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+
+ int repair_code = table->table->file->repair(thd, check_opt);
+ packet->length(0);
+ net_store_data(packet, table_name);
+ net_store_data(packet, "repair");
+
+ switch(repair_code) {
+ case HA_REPAIR_NOT_IMPLEMENTED:
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED));
+ break;
+
+ case HA_REPAIR_OK:
+ net_store_data(packet, "status");
+ net_store_data(packet, "OK");
+ break;
+
+ case HA_REPAIR_FAILED:
+ net_store_data(packet, "status");
+ net_store_data(packet, "Not repaired");
+ break;
+
+ default:
+ net_store_data(packet, "Unknown - internal error during repair");
+ break;
+ }
+ close_thread_tables(thd);
+ if (my_net_write(&thd->net, (char*) packet->ptr(),
+ packet->length()))
+ goto err;
+ }
+
+ close_thread_tables(thd);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+ err:
+ close_thread_tables(thd);
+ DBUG_RETURN(-1);
+}
+
+int mysql_optimize_table(THD* thd, TABLE_LIST* tables)
+{
+ net_printf(&thd->net, ER_PARSE_ERROR, "Sorry; This doesn't work yet", "",
+ 0);
+ return 1;
+}
+
+int mysql_analyze_table(THD* thd, TABLE_LIST* tables)
+{
+ TABLE_LIST *table;
+ List<Item> field_list;
+ Item* item;
+ String* packet = &thd->packet;
+ DBUG_ENTER("mysql_analyze_table");
+
+ 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;
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+
+ for (table = tables; table; table = table->next)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = (table->db) ? table->db : thd->db;
+ strxmov(table_name,db ? db : "",".",table->name,NullS);
+
+ table->table = open_ltable(thd, table, TL_READ_NO_INSERT);
+ packet->length(0);
+
+ if (!table->table)
+ {
+ const char *err_msg;
+ net_store_data(packet, table_name);
+ net_store_data(packet, "analyze");
+ net_store_data(packet, "error");
+ if (!(err_msg=thd->net.last_error))
+ err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
+ net_store_data(packet, err_msg);
+ thd->net.last_error[0]=0;
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+ if (table->table->db_stat & HA_READ_ONLY)
+ {
+ net_store_data(packet, table_name);
+ net_store_data(packet, "analyze");
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_OPEN_AS_READONLY));
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+
+ int analyze_code = table->table->file->analyze(thd);
+ packet->length(0);
+ net_store_data(packet, table_name);
+ net_store_data(packet, "analyze");
+
+ switch(analyze_code) {
+ case HA_ANALYZE_NOT_IMPLEMENTED:
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED));
+ break;
+
+ case HA_ANALYZE_OK:
+ net_store_data(packet, "status");
+ net_store_data(packet, "OK");
+ break;
+
+ default:
+ net_store_data(packet, "Unknown - internal error during analyze");
+ break;
+ }
+ close_thread_tables(thd);
+ if (my_net_write(&thd->net, (char*) packet->ptr(),
+ packet->length()))
+ goto err;
+ }
+
+ close_thread_tables(thd);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+ err:
+ close_thread_tables(thd);
+ DBUG_RETURN(-1);
+}
+
+int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
+{
+ TABLE_LIST *table;
+ List<Item> field_list;
+ Item* item;
+ String* packet = &thd->packet;
+
+ DBUG_ENTER("mysql_check_table");
+
+ 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;
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+
+ for (table = tables; table; table = table->next)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = (table->db) ? table->db : thd->db;
+ strxmov(table_name,db ? db : "",".",table->name,NullS);
+
+ table->table = open_ltable(thd, table, TL_READ_NO_INSERT);
+ packet->length(0);
+
+ if (!table->table)
+ {
+ const char *err_msg;
+ net_store_data(packet, table_name);
+ net_store_data(packet, "check");
+ net_store_data(packet, "error");
+ if (!(err_msg=thd->net.last_error))
+ err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
+ net_store_data(packet, err_msg);
+ thd->net.last_error[0]=0;
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+
+ int check_code = table->table->file->check(thd, check_opt);
+ packet->length(0);
+ net_store_data(packet, table_name);
+ net_store_data(packet, "check");
+
+ switch(check_code) {
+ case HA_CHECK_NOT_IMPLEMENTED:
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED));
+ break;
+
+ case HA_CHECK_OK:
+ net_store_data(packet, "status");
+ net_store_data(packet, "OK");
+ break;
+
+ case HA_CHECK_CORRUPT:
+ net_store_data(packet, "status");
+ net_store_data(packet, "Corrupt");
+ break;
+
+ default:
+ net_store_data(packet, "Unknown - internal error during check");
+ break;
+ }
+ close_thread_tables(thd);
+ if (my_net_write(&thd->net, (char*) packet->ptr(),
+ packet->length()))
+ goto err;
+ }
+
+ close_thread_tables(thd);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+ err:
+ close_thread_tables(thd);
+ DBUG_RETURN(-1);
+}
+
+
+
+int mysql_alter_table(THD *thd,char *new_db, char *new_name,
+ HA_CREATE_INFO *create_info,
+ TABLE_LIST *table_list,
+ List<create_field> &fields,
+ List<Key> &keys,List<Alter_drop> &drop_list,
+ List<Alter_column> &alter_list,
+ bool drop_primary,
+ enum enum_duplicates handle_duplicates)
+{
+ TABLE *table,*new_table;
+ int error;
+ char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN],
+ *table_name,*db;
+ bool use_timestamp=0;
+ ulong copied,deleted;
+ uint save_time_stamp,db_create_options;
+ enum db_type old_db_type,new_db_type;
+ DBUG_ENTER("mysql_alter_table");
+
+ thd->proc_info="init";
+ table_name=table_list->real_name;
+ db=table_list->db;
+ if (!new_db)
+ new_db=db;
+
+ if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
+ DBUG_RETURN(-1);
+
+ /* Check that we are not trying to rename to an existing table */
+ if (new_name)
+ {
+ strmov(new_name_buff,new_name);
+ fn_same(new_name_buff,table_name,3);
+ if (!strcmp(new_name_buff,table_name)) // Check if name changed
+ new_name=table_name; // No. Make later check easier
+ else
+ {
+ if (table->tmp_table)
+ {
+ if (find_temporary_table(thd,new_db,new_name_buff))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ else
+ {
+ 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_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ }
+ else
+ new_name=table_name;
+
+ old_db_type=table->db_type;
+ if (create_info->db_type == DB_TYPE_DEFAULT)
+ create_info->db_type=old_db_type;
+ if (create_info->row_type == ROW_TYPE_DEFAULT)
+ create_info->row_type=table->row_type;
+ new_db_type=create_info->db_type;
+
+ /* Check if the user only wants to do a simple RENAME */
+
+ thd->proc_info="setup";
+ if (new_name != table_name &&
+ !fields.elements && !keys.elements && ! drop_list.elements &&
+ !alter_list.elements && !drop_primary &&
+ new_db_type == old_db_type && create_info->max_rows == 0 &&
+ create_info->auto_increment_value == 0 && !table->tmp_table)
+ {
+ 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))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
+ error= -1;
+ }
+ else
+ {
+ *fn_ext(new_name)=0;
+ close_cached_table(thd,table);
+ if (mysql_rename_table(old_db_type,db,table_name,new_db,new_name))
+ error= -1;
+ }
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (!error)
+ {
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ send_ok(&thd->net);
+ }
+
+ DBUG_RETURN(error);
+ }
+
+ /* Full alter table */
+ restore_record(table,2); // Empty record for DEFAULT
+ List_iterator<Alter_drop> drop_it(drop_list);
+ List_iterator<create_field> def_it(fields);
+ List_iterator<Alter_column> alter_it(alter_list);
+ List<create_field> create_list; // Add new fields here
+ List<Key> key_list; // Add new keys here
+
+ /*
+ ** First collect all fields from table which isn't in drop_list
+ */
+
+ create_field *def;
+ 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 &&
+ !my_strcasecmp(field->field_name, drop->name))
+ break;
+ }
+ if (drop)
+ {
+ drop_it.remove();
+ continue;
+ }
+ /* Check if field is changed */
+ def_it.rewind();
+ while ((def=def_it++))
+ {
+ if (def->change && !my_strcasecmp(field->field_name, def->change))
+ break;
+ }
+ if (def)
+ { // Field is changed
+ def->field=field;
+ if (def->sql_type == FIELD_TYPE_TIMESTAMP)
+ use_timestamp=1;
+ create_list.push_back(def);
+ def_it.remove();
+ }
+ else
+ { // Use old field value
+ create_list.push_back(def=new create_field(field));
+ if (def->sql_type == FIELD_TYPE_TIMESTAMP)
+ use_timestamp=1;
+
+ alter_it.rewind(); // Change default if ALTER
+ Alter_column *alter;
+ while ((alter=alter_it++))
+ {
+ if (!my_strcasecmp(field->field_name, alter->name))
+ break;
+ }
+ if (alter)
+ {
+ def->def=alter->def; // Use new default
+ alter_it.remove();
+ }
+ }
+ }
+ def_it.rewind();
+ List_iterator<create_field> find_it(create_list);
+ while ((def=def_it++)) // Add new columns
+ {
+ if (def->change)
+ {
+ 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();
+ while ((find=find_it++)) // Add new columns
+ {
+ if (!my_strcasecmp(def->after, find->field_name))
+ break;
+ }
+ if (!find)
+ {
+ my_error(ER_BAD_FIELD_ERROR,MYF(0),def->after,table_name);
+ DBUG_RETURN(-1);
+ }
+ find_it.after(def); // Put element after this
+ }
+ }
+ if (alter_list.elements)
+ {
+ my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_list.head()->name,table_name);
+ DBUG_RETURN(-1);
+ }
+ if (!create_list.elements)
+ {
+ my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /*
+ ** Collect all keys which isn't in drop list. Add only those
+ ** for which some fields exists.
+ */
+
+ 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++)
+ {
+ if (drop_primary && (key_info->flags & HA_NOSAME))
+ {
+ drop_primary=0;
+ continue;
+ }
+
+ char *key_name=key_info->name;
+ Alter_drop *drop;
+ drop_it.rewind();
+ while ((drop=drop_it++))
+ {
+ if (drop->type == Alter_drop::KEY &&
+ !my_strcasecmp(key_name, drop->name))
+ break;
+ }
+ if (drop)
+ {
+ drop_it.remove();
+ continue;
+ }
+
+ KEY_PART_INFO *key_part= key_info->key_part;
+ key_parts.empty();
+ for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ {
+ if (!key_part->field)
+ continue; // Wrong field (from UNIREG)
+ const char *key_part_name=key_part->field->field_name;
+ create_field *cfield;
+ field_it.rewind();
+ while ((cfield=field_it++))
+ {
+ if (cfield->change)
+ {
+ if (!my_strcasecmp(key_part_name, cfield->change))
+ break;
+ }
+ else if (!my_strcasecmp(key_part_name, cfield->field_name))
+ break;
+ }
+ if (!cfield)
+ continue; // Field is removed
+ uint key_part_length=key_part->length;
+ if (cfield->field) // Not new field
+ { // Check if sub key
+ if (cfield->field->type() != FIELD_TYPE_BLOB &&
+ (cfield->field->pack_length() == key_part_length ||
+ cfield->length != cfield->pack_length ||
+ cfield->pack_length <= key_part_length))
+ key_part_length=0; // Use whole field
+ }
+ key_parts.push_back(new key_part_spec(cfield->field_name,
+ key_part_length));
+ }
+ if (key_parts.elements)
+ key_list.push_back(new Key(key_info->flags & HA_NOSAME ?
+ (!my_strcasecmp(key_name, "PRIMARY") ?
+ Key::PRIMARY : Key::UNIQUE) :
+ (key_info->flags & HA_FULLTEXT ?
+ Key::FULLTEXT : Key::MULTIPLE),
+ key_name,key_parts));
+ }
+ key_it.rewind();
+ {
+ Key *key;
+ while ((key=key_it++)) // Add new keys
+ key_list.push_back(key);
+ }
+
+ if (drop_list.elements)
+ {
+ my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),drop_list.head()->name);
+ goto err;
+ }
+ if (alter_list.elements)
+ {
+ my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),alter_list.head()->name);
+ goto err;
+ }
+
+ (void) sprintf(tmp_name,"%s-%lx_%lx", tmp_file_prefix, current_pid,
+ thd->thread_id);
+ create_info->db_type=new_db_type;
+ if (!create_info->max_rows)
+ create_info->max_rows=table->max_rows;
+ if (!create_info->avg_row_length)
+ create_info->avg_row_length=table->avg_row_length;
+ table->file->update_create_info(create_info);
+ if (!create_info->comment)
+ create_info->comment=table->comment;
+ /* let new create options override the old ones */
+ db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD);
+ if (create_info->table_options &
+ (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS))
+ db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
+ if (create_info->table_options &
+ (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
+ db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
+ if (create_info->table_options &
+ (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
+ db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
+ HA_OPTION_NO_DELAY_KEY_WRITE);
+ create_info->table_options|= db_create_options;
+
+ if (table->tmp_table)
+ create_info->options|=HA_LEX_CREATE_TMP_TABLE;
+
+ if ((error=mysql_create_table(thd, new_db, tmp_name,
+ create_info,
+ create_list,key_list,1,1))) // no logging
+ DBUG_RETURN(error);
+ {
+ if (table->tmp_table)
+ new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
+ else
+ {
+ char path[FN_REFLEN];
+ (void) sprintf(path,"%s/%s/%s",mysql_data_home,new_db,tmp_name);
+ fn_format(path,path,"","",4+16+32);
+ 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;
+ }
+ }
+
+ save_time_stamp=new_table->time_stamp;
+ if (use_timestamp)
+ new_table->time_stamp=0;
+ new_table->next_number_field=new_table->found_next_number_field;
+ thd->count_cuted_fields=1; /* calc cuted fields */
+ thd->cuted_fields=0L;
+ thd->proc_info="copy to tmp table";
+ error=copy_data_between_tables(table,new_table,create_list,handle_duplicates,
+ &copied,&deleted);
+ thd->count_cuted_fields=0; /* Don`t calc cuted fields */
+ new_table->time_stamp=save_time_stamp;
+
+ if (table->tmp_table)
+ {
+ /* We changed a temporary table */
+ if (error)
+ {
+ close_temporary_table(thd,new_db,tmp_name);
+ my_free((gptr) new_table,MYF(0));
+ goto err;
+ }
+ /* Remove link to old table and rename the new one */
+ close_temporary_table(thd,table->table_cache_key,table_name);
+ if (rename_temporary_table(new_table, new_db, new_name))
+ { // Fatal error
+ close_temporary_table(thd,new_db,tmp_name);
+ my_free((gptr) new_table,MYF(0));
+ goto err;
+ }
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+
+ goto end_temporary;
+ DBUG_RETURN(0);
+ }
+
+ intern_close_table(new_table); /* close temporary table */
+ 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;
+ }
+ /*
+ ** 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.
+ */
+
+ thd->proc_info="rename result table";
+ sprintf(old_name,"%s2-%lx-%lx", tmp_file_prefix, current_pid,
+ thd->thread_id);
+ if (new_name != table_name)
+ {
+ 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;
+ }
+ }
+
+#ifdef __WIN__
+ // Win32 can't rename an open table, so we must close the org table!
+ table_name=sql_strdup(table_name); // must be saved
+ if (close_cached_table(thd,table))
+ { // Aborted
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ table=0; // Marker for win32 version
+#endif
+
+ 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,
+ new_name))
+ { // Try to get everything back
+ error=1;
+ VOID(quick_rm_table(new_db_type,new_db,new_name));
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(mysql_rename_table(old_db_type,db,old_name,db,table_name));
+ }
+ if (error)
+ {
+ // This shouldn't happen. We solve this the safe way by
+ // closing the locked table.
+ close_cached_table(thd,table);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ if (thd->lock || new_name != table_name) // True if WIN32
+ {
+ // Not table locking or alter table with rename
+ // free locks and remove old table
+ close_cached_table(thd,table);
+ VOID(quick_rm_table(old_db_type,db,old_name));
+ }
+ else
+ {
+ // Using LOCK TABLES without rename.
+ // This code is never executed on WIN32!
+ // Remove old renamed table, reopen table and get new locks
+ 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
+ mysql_lock_abort(thd,table); // end threads waiting on lock
+ }
+ VOID(quick_rm_table(old_db_type,db,old_name));
+ if (close_data_tables(thd,db,table_name) ||
+ reopen_tables(thd,1,0))
+ { // This shouldn't happen
+ close_cached_table(thd,table); // Remove lock for table
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ }
+ if ((error = ha_commit(thd)))
+ {
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+
+ thd->proc_info="end";
+ mysql_update_log.write(thd->query,thd->query_length);
+ {
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+
+end_temporary:
+ sprintf(tmp_name,ER(ER_INSERT_INFO),copied+deleted,deleted,
+ thd->cuted_fields);
+ send_ok(&thd->net,copied+deleted,0L,tmp_name);
+ thd->some_tables_deleted=0;
+ DBUG_RETURN(0);
+
+ err:
+ DBUG_RETURN(-1);
+}
+
+
+static int
+copy_data_between_tables(TABLE *from,TABLE *to,List<create_field> &create,
+ enum enum_duplicates handle_duplicates,
+ ulong *copied,ulong *deleted)
+{
+ int error;
+ Copy_field *copy,*copy_end;
+ ulong found_count,delete_count;
+ THD *thd= current_thd;
+ DBUG_ENTER("copy_data_between_tables");
+
+ if (!(copy= new Copy_field[to->fields]))
+ DBUG_RETURN(-1); /* purecov: inspected */
+
+ to->file->external_lock(thd,F_WRLCK);
+ to->file->extra(HA_EXTRA_WRITE_CACHE);
+
+ List_iterator<create_field> it(create);
+ create_field *def;
+ copy_end=copy;
+ for (Field **ptr=to->field ; *ptr ; ptr++)
+ {
+ def=it++;
+ if (def->field)
+ (copy_end++)->set(*ptr,def->field,0);
+ }
+
+ READ_RECORD info;
+ init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
+
+ found_count=delete_count=0;
+ Field *next_field=to->next_number_field;
+ while (!(error=info.read_record(&info)))
+ {
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ error= 1;
+ break;
+ }
+ if (next_field)
+ next_field->reset();
+ for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
+ copy_ptr->do_copy(copy_ptr);
+ if ((error=to->file->write_row((byte*) to->record[0])))
+ {
+ if (handle_duplicates != DUP_IGNORE ||
+ (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE))
+ {
+ to->file->print_error(error,MYF(0));
+ break;
+ }
+ delete_count++;
+ }
+ else
+ found_count++;
+ }
+ end_read_record(&info);
+ delete [] copy;
+ uint tmp_error;
+ if ((tmp_error=to->file->extra(HA_EXTRA_NO_CACHE)))
+ {
+ to->file->print_error(tmp_error,MYF(0));
+ error=1;
+ }
+ if (ha_commit(thd) || to->file->external_lock(thd,F_UNLCK))
+ error=1;
+ *copied= found_count;
+ *deleted=delete_count;
+ DBUG_RETURN(error > 0 ? -1 : 0);
+}