diff options
Diffstat (limited to 'sql/sql_delete.cc')
-rw-r--r-- | sql/sql_delete.cc | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc new file mode 100644 index 00000000000..5d21a089283 --- /dev/null +++ b/sql/sql_delete.cc @@ -0,0 +1,213 @@ +/* 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 */ + + +/* Delete of records */ + +#include "mysql_priv.h" + +/* + Optimize delete of all rows by doing a full generate of the table + This will work even if the .ISM and .ISD tables are destroyed +*/ + +static int generate_table(THD *thd, TABLE_LIST *table_list, + TABLE *locked_table) +{ + char path[FN_REFLEN]; + int error; + TABLE **table_ptr; + DBUG_ENTER("generate_table"); + + thd->proc_info="generate_table"; + /* If it is a temporary table, close and regenerate it */ + if ((table_ptr=find_temporary_table(thd,table_list->db, + table_list->real_name))) + { + TABLE *table= *table_ptr; + HA_CREATE_INFO create_info; + table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); + bzero((char*) &create_info,sizeof(create_info)); + create_info.auto_increment_value= table->file->auto_increment_value; + db_type table_type=table->db_type; + + strmov(path,table->path); + *table_ptr= table->next; // Unlink table from list + close_temporary(table,0); + *fn_ext(path)=0; // Remove the .frm extension + ha_create_table(path, &create_info,1); + if ((error= (int) !(open_temporary_table(thd, path, table_list->db, + table_list->real_name, 1)))) + { + (void) rm_temporary_table(table_type, path); + } + } + else + { + (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, + table_list->real_name,reg_ext); + fn_format(path,path,"","",4); + VOID(pthread_mutex_lock(&LOCK_open)); + if (locked_table) + mysql_lock_abort(thd,locked_table); // end threads waiting on lock + // close all copies in use + if (remove_table_from_cache(thd,table_list->db,table_list->real_name)) + { + if (!locked_table) + { + VOID(pthread_mutex_unlock(&LOCK_open)); + DBUG_RETURN(1); // We must get a lock on table + } + } + if (locked_table) + locked_table->file->extra(HA_EXTRA_FORCE_REOPEN); + if (thd->locked_tables) + close_data_tables(thd,table_list->db,table_list->real_name); + else + close_thread_tables(thd,1); + HA_CREATE_INFO create_info; + bzero((char*) &create_info,sizeof(create_info)); + *fn_ext(path)=0; // Remove the .frm extension + error= ha_create_table(path,&create_info,1) ? -1 : 0; + if (thd->locked_tables && reopen_tables(thd,1,0)) + error= -1; + VOID(pthread_mutex_unlock(&LOCK_open)); + } + if (!error) + { + send_ok(&thd->net); // This should return record count + mysql_update_log.write(thd->query,thd->query_length); + Query_log_event qinfo(thd, thd->query); + mysql_bin_log.write(&qinfo); + } + DBUG_RETURN(error ? -1 : 0); +} + + +int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, + thr_lock_type lock_type) +{ + int error; + TABLE *table; + SQL_SELECT *select; + READ_RECORD info; + bool using_limit=limit != HA_POS_ERROR; + bool use_generate_table; + DBUG_ENTER("mysql_delete"); + + if (!table_list->db) + table_list->db=thd->db; + if ((thd->options & OPTION_SAFE_UPDATES) && !conds) + { + send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); + DBUG_RETURN(1); + } + + use_generate_table= (!using_limit && !conds && + !(specialflag & + (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && + (thd->options & OPTION_AUTO_COMMIT)); + if (use_generate_table && ! thd->open_tables) + { + error=generate_table(thd,table_list,(TABLE*) 0); + if (error <= 0) + DBUG_RETURN(error); // Error or ok + } + if (!(table = open_ltable(thd,table_list, + limit != HA_POS_ERROR ? TL_WRITE_LOW_PRIORITY : + lock_type))) + DBUG_RETURN(-1); + thd->proc_info="init"; + if (use_generate_table) + DBUG_RETURN(generate_table(thd,table_list,table)); + table->map=1; + if (setup_conds(thd,table_list,&conds)) + DBUG_RETURN(-1); + + table->used_keys=table->quick_keys=0; // Can't use 'only index' + select=make_select(table,0,0,conds,&error); + if (error) + DBUG_RETURN(-1); + if (select && select->check_quick(test(thd->options & SQL_SAFE_UPDATES), + limit)) + { + delete select; + send_ok(&thd->net,0L); + DBUG_RETURN(0); + } + + /* If running in safe sql mode, don't allow updates without keys */ + if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys && + limit == HA_POS_ERROR) + { + delete select; + send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); + DBUG_RETURN(1); + } + + (void) table->file->extra(HA_EXTRA_NO_READCHECK); + init_read_record(&info,thd,table,select,1,1); + ulong deleted=0L; + thd->proc_info="updating"; + while (!(error=info.read_record(&info)) && !thd->killed) + { + if (!(select && select->skipp_record())) + { + if (!(error=table->file->delete_row(table->record[0]))) + { + deleted++; + if (!--limit && using_limit) + { + error= -1; + break; + } + } + else + { + table->file->print_error(error,MYF(0)); + error=0; + break; + } + } + } + thd->proc_info="end"; + end_read_record(&info); + VOID(table->file->extra(HA_EXTRA_READCHECK)); + if (deleted) + { + mysql_update_log.write(thd->query, thd->query_length); + Query_log_event qinfo(thd, thd->query); + mysql_bin_log.write(&qinfo); + } + if (ha_autocommit_or_rollback(thd,error >= 0)) + error=1; + if (thd->lock) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock=0; + } + delete select; + if (error >= 0) // Fatal error + send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN: 0); + else + { + send_ok(&thd->net,deleted); + DBUG_PRINT("info",("%d records deleted",deleted)); + } + DBUG_RETURN(0); +} + + |