diff options
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 1559 |
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); +} |