/* 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" #include "ha_innobase.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 */ 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 (global_read_lock) { if(thd->global_read_lock) { my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0), table_list->real_name); DBUG_RETURN(-1); } pthread_mutex_lock(&LOCK_open); while (global_read_lock && ! thd->killed || thd->version != refresh_version) { (void) pthread_cond_wait(&COND_refresh,&LOCK_open); } pthread_mutex_unlock(&LOCK_open); } /* 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) { mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { Query_log_event qinfo(thd, thd->query); mysql_bin_log.write(&qinfo); } send_ok(&thd->net); // This should return record count } DBUG_RETURN(error ? -1 : 0); } int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, thr_lock_type lock_type, ulong options) { int error; TABLE *table; SQL_SELECT *select; READ_RECORD info; bool using_limit=limit != HA_POS_ERROR; bool use_generate_table,using_transactions; 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_NOT_AUTO_COMMIT | OPTION_BEGIN))); #ifdef HAVE_INNOBASE_DB /* We need to add code to not generate table based on the table type */ if (!innodb_skip) use_generate_table=0; // Innodb can't use re-generate table #endif 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); table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); 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 (!table->quick_keys) { thd->lex.options|=QUERY_NO_INDEX_USED; if ((thd->options & OPTION_SAFE_UPDATES) && 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); if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_QUICK); 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 (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); using_transactions=table->file->has_transactions(); if (deleted && (error <= 0 || !using_transactions)) { mysql_update_log.write(thd,thd->query, thd->query_length); if (mysql_bin_log.is_open()) { Query_log_event qinfo(thd, thd->query, using_transactions); if (mysql_bin_log.write(&qinfo) && using_transactions) error=1; } if (!using_transactions) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } if (using_transactions && 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); }