summaryrefslogtreecommitdiff
path: root/sql/sql_delete.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_delete.cc')
-rw-r--r--sql/sql_delete.cc672
1 files changed, 668 insertions, 4 deletions
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 85b1c947460..0f70bd71ddd 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sinisa
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
@@ -19,6 +19,7 @@
#include "mysql_priv.h"
#include "ha_innobase.h"
+#include "sql_select.h"
/*
Optimize delete of all rows by doing a full generate of the table
@@ -151,7 +152,7 @@ int mysql_delete(THD *thd,
#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
+ use_generate_table=0; // Innobase can't use re-generate table
#endif
if (use_generate_table && ! thd->open_tables)
{
@@ -186,7 +187,7 @@ int mysql_delete(THD *thd,
/* If running in safe sql mode, don't allow updates without keys */
if (!table->quick_keys)
{
- thd->lex.options|=QUERY_NO_INDEX_USED;
+ thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
if ((thd->options & OPTION_SAFE_UPDATES) && limit == HA_POS_ERROR)
{
delete select;
@@ -214,7 +215,7 @@ int mysql_delete(THD *thd,
MYF(MY_FAE | MY_ZEROFILL));
if (setup_order(thd, &tables, fields, all_fields, order) ||
!(sortorder=make_unireg_sortorder(order, &length)) ||
- (table->found_records = filesort(&table, sortorder, length,
+ (table->found_records = filesort(table, sortorder, length,
(SQL_SELECT *) 0, 0L, HA_POS_ERROR,
&examined_rows))
== HA_POS_ERROR)
@@ -286,3 +287,666 @@ int mysql_delete(THD *thd,
}
+/***************************************************************************
+** delete multiple tables from join
+***************************************************************************/
+
+#define MEM_STRIP_BUF_SIZE sortbuff_size
+
+#ifndef SINISAS_STRIP
+int refposcmp2(void* arg, const void *a,const void *b)
+{
+ return memcmp(a,b,(int) arg);
+}
+#endif
+
+multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
+ thr_lock_type lock_option_arg,
+ uint num_of_tables_arg)
+ : delete_tables (dt), thd(thd_arg), deleted(0),
+ num_of_tables(num_of_tables_arg), error(0), lock_option(lock_option_arg),
+ do_delete(false)
+{
+ uint counter=0;
+#ifdef SINISAS_STRIP
+ tempfiles = (IO_CACHE **) sql_calloc(sizeof(IO_CACHE *)* num_of_tables);
+ memory_lane = (byte *)sql_alloc(MAX_REFLENGTH*MEM_STRIP_BUF_SIZE);
+#else
+ tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1));
+#endif
+
+ (void) dt->table->file->extra(HA_EXTRA_NO_READCHECK);
+ (void) dt->table->file->extra(HA_EXTRA_NO_KEYREAD);
+ /* Don't use key read with MULTI-TABLE-DELETE */
+ dt->table->used_keys=0;
+ for (dt=dt->next ; dt ; dt=dt->next,counter++)
+ {
+ TABLE *table=dt->table;
+ (void) dt->table->file->extra(HA_EXTRA_NO_READCHECK);
+ (void) dt->table->file->extra(HA_EXTRA_NO_KEYREAD);
+#ifdef SINISAS_STRIP
+ tempfiles[counter]=(IO_CACHE *) sql_alloc(sizeof(IO_CACHE));
+ if (open_cached_file(tempfiles[counter], mysql_tmpdir,TEMP_PREFIX,
+ DISK_BUFFER_SIZE, MYF(MY_WME)))
+ {
+ my_error(ER_CANT_OPEN_FILE,MYF(0),(tempfiles[counter])->file_name,errno);
+ thd->fatal_error=1;
+ return;
+ }
+#else
+ tempfiles[counter] = new Unique (refposcmp2,
+ (void *) table->file->ref_length,
+ table->file->ref_length,
+ MEM_STRIP_BUF_SIZE);
+#endif
+ }
+}
+
+
+int
+multi_delete::prepare(List<Item> &values)
+{
+ DBUG_ENTER("multi_delete::prepare");
+ do_delete = true;
+ thd->proc_info="deleting from main table";
+
+ if (thd->options & OPTION_SAFE_UPDATES)
+ {
+ TABLE_LIST *table_ref;
+ for (table_ref=delete_tables; table_ref; table_ref=table_ref->next)
+ {
+ TABLE *table=table_ref->table;
+ if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys)
+ {
+ my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+inline static void
+link_in_list(SQL_LIST *list,byte *element,byte **next)
+{
+ list->elements++;
+ (*list->next)=element;
+ list->next=next;
+ *next=0;
+}
+
+void
+multi_delete::initialize_tables(JOIN *join)
+{
+ SQL_LIST *new_list=(SQL_LIST *) sql_alloc(sizeof(SQL_LIST));
+ new_list->elements=0; new_list->first=0;
+ new_list->next= (byte**) &(new_list->first);
+ for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
+ tab < end;
+ tab++)
+ {
+ TABLE_LIST *walk;
+ for (walk=(TABLE_LIST*) delete_tables ; walk ; walk=walk->next)
+ if (!strcmp(tab->table->path,walk->table->path))
+ break;
+ if (walk) // Table need not be the one to be deleted
+ {
+ register TABLE_LIST *ptr = (TABLE_LIST *) sql_alloc(sizeof(TABLE_LIST));
+ memcpy(ptr,walk,sizeof(TABLE_LIST)); ptr->next=0;
+ link_in_list(new_list,(byte*) ptr,(byte**) &ptr->next);
+ }
+ }
+ delete_tables=(TABLE_LIST *)new_list->first;
+ return;
+}
+
+multi_delete::~multi_delete()
+{
+
+ /* Add back EXTRA_READCHECK; In 4.0.1 we shouldn't need this anymore */
+ for (table_being_deleted=delete_tables ;
+ table_being_deleted ;
+ table_being_deleted=table_being_deleted->next)
+ {
+ VOID(table_being_deleted->table->file->extra(HA_EXTRA_READCHECK));
+ }
+ for (uint counter = 0; counter < num_of_tables-1; counter++)
+ {
+ if (tempfiles[counter])
+ {
+#ifdef SINISAS_STRIP
+ end_io_cache(tempfiles[counter]);
+#else
+ delete tempfiles[counter];
+#endif
+ }
+ }
+}
+
+
+bool multi_delete::send_data(List<Item> &values)
+{
+ int secure_counter= -1;
+ for (table_being_deleted=delete_tables ;
+ table_being_deleted ;
+ table_being_deleted=table_being_deleted->next, secure_counter++)
+ {
+ TABLE *table=table_being_deleted->table;
+
+ /* Check if we are using outer join and we didn't find the row */
+ if (table->status & (STATUS_NULL_ROW | STATUS_DELETED))
+ continue;
+
+ table->file->position(table->record[0]);
+ int rl = table->file->ref_length;
+
+ if (secure_counter < 0)
+ {
+ table->status|= STATUS_DELETED;
+ if (!(error=table->file->delete_row(table->record[0])))
+ deleted++;
+ else
+ {
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ }
+ else
+ {
+#ifdef SINISAS_STRIP
+ error=my_b_write(tempfiles[secure_counter],table->file->ref,rl);
+#else
+ error=tempfiles[secure_counter]->unique_add(table->file->ref);
+#endif
+ if (error)
+ {
+ error=-1;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+#ifdef SINISAS_STRIP
+static inline int COMP (byte *ml,uint len,unsigned int left, unsigned int right)
+{
+ return memcmp(ml + left*len,ml + right*len,len);
+}
+
+#define EX(ML,LEN,LLLEFT,RRRIGHT) \
+ptr1 = ML + LLLEFT*LEN;\
+ptr2 = ML + RRRIGHT*LEN;\
+memcpy(tmp,ptr1,LEN);\
+memcpy(ptr1,ptr2,LEN);\
+memcpy(ptr2,tmp,LEN);\
+
+
+
+static void qsort_mem_pieces(byte *ml, uint length, unsigned short pivotP, unsigned int nElem)
+{
+ unsigned int leftP, rightP, pivotEnd, pivotTemp, leftTemp;
+ unsigned int lNum; byte tmp [MAX_REFLENGTH], *ptr1, *ptr2;
+ int retval;
+tailRecursion:
+ if (nElem <= 1) return;
+ if (nElem == 2)
+ {
+ if (COMP(ml,length,pivotP, rightP = pivotP + 1) > 0)
+ {
+ EX(ml,length,pivotP, rightP);
+ }
+ return;
+ }
+
+ rightP = (nElem - 1) + pivotP;
+ leftP = (nElem >> 1) + pivotP;
+
+/* sort the pivot, left, and right elements for "median of 3" */
+
+ if (COMP (ml,length,leftP, rightP) > 0)
+ {
+ EX (ml,length,leftP, rightP);
+ }
+ if (COMP (ml,length,leftP, pivotP) > 0)
+ {
+ EX (ml,length,leftP, pivotP);
+ }
+ else if (COMP (ml,length, pivotP, rightP) > 0)
+ {
+ EX (ml,length,pivotP, rightP);
+ }
+
+ if (nElem == 3) {
+ EX (ml,length,pivotP, leftP);
+ return;
+ }
+
+/* now for the classic Hoare algorithm */
+
+ leftP = pivotEnd = pivotP + 1;
+
+ do {
+ while ((retval = COMP (ml,length, leftP, pivotP)) <= 0)
+ {
+ if (retval == 0) {
+ EX(ml,length,leftP, pivotEnd);
+ pivotEnd++;
+ }
+ if (leftP < rightP)
+ leftP++;
+ else
+ goto qBreak;
+ }
+ while (leftP < rightP) {
+ if ((retval = COMP(ml,length,pivotP, rightP)) < 0)
+ rightP--;
+ else
+ {
+ EX (ml,length,leftP, rightP);
+ if (retval != 0) {
+ leftP++;
+ rightP--;
+ }
+ break;
+ }
+ }
+ } while (leftP < rightP);
+
+qBreak:
+
+ if (COMP(ml,length,leftP, pivotP) <= 0)
+ leftP++;
+
+ leftTemp = leftP - 1; pivotTemp = pivotP;
+
+ while ((pivotTemp < pivotEnd) && (leftTemp >= pivotEnd))
+ {
+ EX(ml,length,pivotTemp, leftTemp);
+ pivotTemp++; leftTemp--;
+ }
+
+ lNum = leftP - pivotEnd; nElem = (nElem + pivotP) - leftP;
+
+ /* Sort smaller partition first to reduce stack usage */
+ if (nElem < lNum)
+ {
+ qsort_mem_pieces(ml,length,leftP, nElem); nElem = lNum;
+ }
+ else
+ {
+ qsort_mem_pieces(ml,length,pivotP, lNum);
+ pivotP = leftP;
+ }
+ goto tailRecursion;
+}
+
+static byte * btree_search(byte *lane, byte *key,register int last, uint length)
+{
+ register int first = 0;
+ if (last == first)
+ {
+ if (!memcmp(lane,key,length)) return lane;
+ return (byte *)0;
+ }
+Recursion_is_too_slow:
+ if (last - first < 3)
+ {
+ if (!memcmp(lane + first*length,key,length)) return lane + first * length;
+ if (last == first + 1) return (byte *)0;
+ if (!memcmp(lane + last*length,key,length)) return lane + last * length;
+ return (byte *)0;
+ }
+ else
+ {
+ int half = first + (last - first)/2;
+ int result = memcmp(lane + half*length,key,length);
+ if (!result) return lane + half*length;
+ if (result < 0)
+ {
+ first = half + 1; goto Recursion_is_too_slow;
+ }
+ else
+ {
+ last = half + 1; goto Recursion_is_too_slow;
+ }
+ }
+}
+
+struct written_block {
+ byte first[MAX_REFLENGTH], last[MAX_REFLENGTH];
+ my_off_t offset;
+ uint how_many;
+};
+
+static IO_CACHE *strip_duplicates_from_temp (byte *memory_lane, IO_CACHE *ptr, uint ref_length, int *written)
+{
+ byte *mem_ptr; my_off_t off = 0;
+ int read_error, write_error, how_many_to_read, total_to_read = *written, pieces_in_memory = 0, mem_count,written_rows;
+ int offset = written_rows=*written=0;
+ int mem_pool_size = MEM_STRIP_BUF_SIZE * MAX_REFLENGTH / ref_length;
+ byte dup_record[MAX_REFLENGTH]; memset(dup_record,'\xFF',MAX_REFLENGTH);
+ if (reinit_io_cache(ptr,READ_CACHE,0L,0,0))
+ return ptr;
+ IO_CACHE *tempptr = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL));
+ if (open_cached_file(tempptr, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME)))
+ {
+ my_free((gptr) tempptr, MYF (0));
+ return ptr;
+ }
+ DYNAMIC_ARRAY written_blocks;
+ VOID(init_dynamic_array(&written_blocks,sizeof(struct written_block),20,50));
+ for (;pieces_in_memory < total_to_read;)
+ {
+ how_many_to_read = total_to_read - pieces_in_memory; read_error=write_error=0;
+ if (how_many_to_read > mem_pool_size)
+ how_many_to_read = mem_pool_size;
+ if (my_b_read(ptr, memory_lane, (uint) how_many_to_read * ref_length))
+ {
+ read_error = 1;
+ break;
+ }
+ pieces_in_memory += how_many_to_read;
+ qsort_mem_pieces(memory_lane,0, how_many_to_read, ref_length);
+ byte *checking = dup_record, *cursor=NULL, *mem_end = memory_lane + how_many_to_read * ref_length;
+ int opt_unique_pieces, unique_pieces_in_memory=0; write_error=0;
+ for (mem_ptr=memory_lane; mem_ptr < mem_end ; mem_ptr += ref_length)
+ {
+ if (memcmp(mem_ptr,checking, ref_length))
+ {
+ if (cursor)
+ {
+ memmove(cursor,mem_ptr,mem_end - mem_ptr);
+ mem_end -= mem_ptr - cursor;
+ mem_ptr = cursor; cursor = NULL;
+ }
+ unique_pieces_in_memory++;
+ checking = mem_ptr;
+ }
+ else if (!cursor) cursor = mem_ptr;
+ }
+ opt_unique_pieces=unique_pieces_in_memory;
+ if (written_rows)
+ {
+ if (reinit_io_cache(tempptr,READ_CACHE,0L,0,0)) {write_error = -1; break;}
+ for (uint i=0 ; i < written_blocks.elements ; i++)
+ {
+ struct written_block *wbp=dynamic_element(&written_blocks,i,struct written_block*);
+ if ((memcmp(memory_lane,wbp->last,ref_length) == 1) || (memcmp(memory_lane + (unique_pieces_in_memory - 1) * ref_length, wbp->first, ref_length) == -1))
+ continue;
+ else
+ {
+ if (wbp->how_many < 3)
+ {
+ if ((mem_ptr=btree_search(memory_lane,wbp->first,unique_pieces_in_memory-1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ if (wbp->how_many == 2 && (mem_ptr=btree_search(memory_lane,wbp->last,unique_pieces_in_memory-1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ }
+ else
+ {
+ byte block[MAX_REFLENGTH * MEM_STRIP_BUF_SIZE]; // 16 K maximum and only temporary !!
+ if (my_b_read(tempptr, block, (uint) wbp->how_many * ref_length))
+ {
+ read_error = 1; goto skip_writting;
+ }
+ if (unique_pieces_in_memory < 3)
+ {
+ if ((mem_ptr=btree_search(block,memory_lane,wbp->how_many - 1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(memory_lane,dup_record,ref_length);
+ }
+ if (unique_pieces_in_memory == 2 && (mem_ptr=btree_search(block,memory_lane + ref_length,wbp->how_many - 1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ }
+ else
+ {
+ byte *cursor; bool do_check_past;
+ if (unique_pieces_in_memory < wbp->how_many)
+ {
+ do_check_past = (memcmp(memory_lane + (unique_pieces_in_memory - 1)*ref_length,wbp->last,ref_length) == 1);
+ for (cursor=memory_lane; cursor < memory_lane + unique_pieces_in_memory*ref_length; cursor += ref_length)
+ {
+ if ((mem_ptr=btree_search(block,cursor,wbp->how_many - 1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(cursor,dup_record,ref_length);
+ }
+ else if (do_check_past && (memcmp(cursor,wbp->last,ref_length) == 1)) break;
+ }
+ }
+ else
+ {
+ do_check_past = (memcmp(memory_lane + (unique_pieces_in_memory - 1)*ref_length,wbp->last,ref_length) == -1);
+ for (cursor=block; cursor < block + wbp->how_many*ref_length;cursor += ref_length)
+ {
+ if ((mem_ptr=btree_search(memory_lane,cursor,unique_pieces_in_memory-1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ else if (do_check_past && (memcmp(cursor,memory_lane + (unique_pieces_in_memory - 1)*ref_length,ref_length) == 1)) break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ reinit_io_cache(tempptr, WRITE_CACHE,off,0,0);
+ struct written_block wb; wb.offset = off; wb.how_many=opt_unique_pieces; byte *last;
+ if (opt_unique_pieces < unique_pieces_in_memory)
+ {
+ for (mem_count=0, mem_ptr=memory_lane; mem_count<unique_pieces_in_memory;mem_count++, mem_ptr += ref_length)
+ {
+ if (memcmp(mem_ptr,dup_record,ref_length))
+ {
+ if (my_b_write(tempptr,mem_ptr,ref_length))
+ {
+ if (write_error == 9 || write_error == -1) write_error = 0;
+ if (write_error) break;
+ }
+ if (!mem_count) memcpy(wb.first,mem_ptr,ref_length);
+ last = mem_ptr;
+ written_rows++;
+ }
+ }
+ memcpy(wb.last,last,ref_length);
+ }
+ else
+ {
+ memcpy(wb.first,memory_lane,ref_length); memcpy(wb.last,memory_lane + (unique_pieces_in_memory -1)*ref_length,ref_length);
+ if (my_b_write(tempptr, memory_lane,unique_pieces_in_memory * ref_length))
+ {
+ write_error = 1; break;
+ }
+ written_rows += unique_pieces_in_memory;
+ }
+ off = my_b_tell(tempptr);
+ VOID(push_dynamic(&written_blocks,(gptr) &wb));
+ skip_writting:
+ if (write_error || read_error) break;
+ }
+ delete_dynamic(&written_blocks);
+ if (read_error || write_error)
+ {
+ close_cached_file(tempptr); end_io_cache(tempptr);
+ return ptr;
+ }
+ else
+ {
+ close_cached_file(ptr); *written=written_rows; end_io_cache(ptr);
+ reinit_io_cache(tempptr,READ_CACHE,0L,0,0);
+ return tempptr;
+ }
+}
+
+#endif /* SINISAS_STRIP */
+
+/* Return true if some table is not transaction safe */
+
+static bool some_table_is_not_transaction_safe (TABLE_LIST *tl)
+{
+ for (; tl ; tl=tl->next)
+ {
+ if (!(tl->table->file->has_transactions()))
+ return true;
+ }
+ return false;
+}
+
+
+void multi_delete::send_error(uint errcode,const char *err)
+{
+ /* First send error what ever it is ... */
+ ::send_error(&thd->net,errcode,err);
+ /* If nothing deleted return */
+ if (!deleted)
+ return;
+ /* Below can happen when thread is killed early ... */
+ if (!table_being_deleted)
+ table_being_deleted=delete_tables;
+
+ /*
+ If rows from the first table only has been deleted and it is transactional,
+ just do rollback.
+ The same if all tables are transactional, regardless of where we are.
+ In all other cases do attempt deletes ...
+ */
+ if ((table_being_deleted->table->file->has_transactions() &&
+ table_being_deleted == delete_tables) ||
+ !some_table_is_not_transaction_safe(delete_tables->next))
+ ha_rollback(thd);
+ else if (do_delete)
+ VOID(do_deletes(true));
+}
+
+
+int multi_delete::do_deletes (bool from_send_error)
+{
+ int error = 0, counter = 0, count;
+
+ if (from_send_error)
+ {
+ /* Found out table number for 'table_being_deleted' */
+ for (TABLE_LIST *aux=delete_tables;
+ aux != table_being_deleted;
+ aux=aux->next)
+ counter++;
+ }
+ else
+ table_being_deleted = delete_tables;
+
+ do_delete = false;
+ for (table_being_deleted=table_being_deleted->next;
+ table_being_deleted ;
+ table_being_deleted=table_being_deleted->next, counter++)
+ {
+ TABLE *table = table_being_deleted->table;
+ int rl = table->file->ref_length;
+#ifdef SINISAS_STRIP
+ int num_of_positions = (int)my_b_tell(tempfiles[counter])/rl;
+ if (!num_of_positions) continue;
+ tempfiles[counter] = strip_duplicates_from_temp(memory_lane, tempfiles[counter],rl,&num_of_positions);
+ if (!num_of_positions)
+ {
+ error=1; break;
+ }
+#else
+ if (tempfiles[counter]->get(table))
+ {
+ error=1;
+ break;
+ }
+#endif
+
+#if USE_REGENERATE_TABLE
+ // nice little optimization ....
+ // but Monty has to fix generate_table...
+ // This will not work for transactional tables because for other types
+ // records is not absolute
+ if (num_of_positions == table->file->records)
+ {
+ TABLE_LIST table_list;
+ bzero((char*) &table_list,sizeof(table_list));
+ table_list.name=table->table_name; table_list.real_name=table_being_deleted->real_name;
+ table_list.table=table;
+ table_list.grant=table->grant;
+ table_list.db = table_being_deleted->db;
+ error=generate_table(thd,&table_list,(TABLE *)0);
+ if (error <= 0) {error = 1; break;}
+ deleted += num_of_positions;
+ continue;
+ }
+#endif /* USE_REGENERATE_TABLE */
+
+ READ_RECORD info;
+ error=0;
+#ifdef SINISAS_STRIP
+ SQL_SELECT *select= new SQL_SELECT;
+ select->head=table;
+ select->file=*tempfiles[counter];
+ init_read_record(&info,thd,table,select,0,0);
+#else
+ init_read_record(&info,thd,table,NULL,0,0);
+#endif
+ bool not_trans_safe = some_table_is_not_transaction_safe(delete_tables);
+ while (!(error=info.read_record(&info)) &&
+ (!thd->killed || from_send_error || not_trans_safe))
+ {
+ error=table->file->delete_row(table->record[0]);
+ if (error)
+ {
+ table->file->print_error(error,MYF(0));
+ break;
+ }
+ else
+ deleted++;
+ }
+ end_read_record(&info);
+#ifdef SINISAS_STRIP
+ delete select;
+#endif
+ if (error == -1)
+ error = 0;
+ }
+ return error;
+}
+
+
+bool multi_delete::send_eof()
+{
+ thd->proc_info="deleting from reference tables";
+ int error = do_deletes(false);
+
+ thd->proc_info="end";
+ if (error && error != -1)
+ {
+ ::send_error(&thd->net);
+ return 1;
+ }
+
+ if (deleted &&
+ (error <= 0 || some_table_is_not_transaction_safe(delete_tables)))
+ {
+ mysql_update_log.write(thd,thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ if (mysql_bin_log.write(&qinfo) &&
+ !some_table_is_not_transaction_safe(delete_tables))
+ error=1; // Rollback
+ VOID(ha_autocommit_or_rollback(thd,error >= 0));
+ }
+ ::send_ok(&thd->net,deleted);
+ return 0;
+}