From 4e886689b2095185cd675ec88f4e40f77b6074b1 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 3 Jun 2001 17:07:26 +0300 Subject: These are actually two changesets. One for splitting LEX in two and the other for multi-table delete sql/filesort.cc: Fixed some bugs for Unique class sql/item.cc: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/item_sum.cc: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/mysql_priv.h: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/sql_class.h: Adding multi table delete sql/sql_delete.cc: Added multi-table delete sql/sql_lex.cc: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/sql_lex.h: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/sql_parse.cc: Changes caused by splitting lex into two parts, in order to implement UNION's etc, plus added multi-table delete sql/sql_select.cc: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/sql_update.cc: Changes caused by splitting lex into two parts, in order to implement UNION's etc sql/sql_yacc.yy: Changes caused by splitting lex into two parts, in order to implement UNION's etc, plus added multi-table delete sql/uniques.cc: Fixed some bugs in duplicate stripping BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- sql/sql_delete.cc | 551 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 548 insertions(+), 3 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 85b1c947460..4f3fe80473e 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -150,8 +150,8 @@ int mysql_delete(THD *thd, (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 + if (!innobase_skip) + use_generate_table=0; // Innobase can't use re-generate table #endif if (use_generate_table && ! thd->open_tables) { @@ -186,7 +186,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; @@ -286,3 +286,548 @@ int mysql_delete(THD *thd, } + + +/*************************************************************************** +** delete multiple tables from join +***************************************************************************/ + + +#define MEM_STRIP_BUF_SIZE 2048 + +int +multi_delete::prepare(List &values) +{ + DBUG_ENTER("multi_delete::prepare"); + tempfiles = (IO_CACHE **) sql_calloc(sizeof(IO_CACHE *)*(num_of_tables)); + memory_lane = (byte *)sql_alloc(MAX_REFLENGTH*MEM_STRIP_BUF_SIZE); uint counter = 0; + dup_checking = (byte *) sql_calloc(MAX_REFLENGTH * (num_of_tables + 1)); + memset(dup_checking,'\xFF', MAX_REFLENGTH * (num_of_tables + 1)); do_delete = true; + for (table_being_deleted=delete_tables; table_being_deleted; table_being_deleted=table_being_deleted->next, counter++) + { + TABLE *table=table_being_deleted->table; + if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys) + { + my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0)); + DBUG_RETURN(1); + } + (void) table->file->extra(HA_EXTRA_NO_READCHECK); + if (counter < num_of_tables) + { + 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); + DBUG_RETURN(1); + } + } + } + thd->proc_info="updating"; + DBUG_RETURN(0); +} + +multi_delete::~multi_delete() +{ + for (uint counter = 0; counter < num_of_tables; counter++) + if (tempfiles[counter]) + end_io_cache(tempfiles[counter]); +// Here it crashes ... +} + +bool multi_delete::send_data(List &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; + table->file->position(table->record[0]); int rl = table->file->ref_length; + byte *dup_check = dup_checking + (secure_counter + 1)*MAX_REFLENGTH; + if (!table->null_row && memcmp(dup_check,table->file->ref, rl) && memcmp(table->file->ref,wrong_record,rl)) + { + memcpy(dup_check,table->file->ref,rl); + if (secure_counter == -1) + { + if (!(error=table->file->delete_row(table->record[0]))) + deleted++; + else + { + send_error(error,"An error occured in deleting rows"); + return 1; + } + } + else + { + if (my_b_write(tempfiles[secure_counter],table->file->ref,rl)) + { + 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; + IO_CACHE *tempptr = (IO_CACHE *) sql_alloc(sizeof(IO_CACHE)); + byte dup_record[MAX_REFLENGTH]; memset(dup_record,'\xFF',MAX_REFLENGTH); + if (reinit_io_cache(ptr,READ_CACHE,0L,0,0)) + return ptr; + if (open_cached_file(tempptr, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME))) + 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_countnext; deleting ; deleting=deleting->next) + { + if (!(deleting->table->file->has_transactions())) + return true; + } + return false; +} + + +void multi_delete::send_error(uint errcode,const char *err) +{ + ::send_error(&thd->net,errcode,err); + if (!deleted) return; + if (!table_being_deleted) table_being_deleted=delete_tables; + if ((table_being_deleted->table->file->has_transactions() && table_being_deleted == delete_tables) || !some_table_is_not_transaction_safe(delete_tables)) + ha_rollback(current_thd); + else if (do_delete) + VOID(do_deletes(true)); +} + +int multi_delete::do_deletes (bool from_send_error) +{ + TABLE *table; + int error = 0, counter = 0, count; + if (from_send_error) + { + 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 ; counter++, table_being_deleted=table_being_deleted->next) + { + table = table_being_deleted->table; int rl = table->file->ref_length; + int num_of_positions = (int)my_b_tell(tempfiles[counter])/rl; + if (!num_of_positions) continue; +#ifdef SINISAS_STRIP + tempfiles[counter] = strip_duplicates_from_temp(memory_lane, tempfiles[counter],rl,&num_of_positions); + if (!num_of_positions) + { + error=1; break; + } +#else + Unique strip_it(refposcmp2,(void *)rl,rl,MEM_STRIP_BUF_SIZE); + if (reinit_io_cache(tempfiles[counter],READ_CACHE,0L,0,0)) + { + error=1; break; + } + for (count = 0; count < num_of_positions; count++) + { + byte tmp [MAX_REFLENGTH]; + if (my_b_read(tempfiles[counter], tmp, rl) || strip_it.unique_add(tmp)) + { + error = 1; + break; + } + } + strip_it.get(table); +#endif +#if 0 + if (num_of_positions == table->file->records) // nice little optimization .... + { // but Monty has to fix generate_table... + 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; + } + else + { +#endif + SQL_SELECT *select= new SQL_SELECT; + READ_RECORD info; + select->head=table; + select->file=*tempfiles[counter]; + init_read_record(&info,thd,table,select,0,0); error=0; + while (!(error=info.read_record(&info)) && (!thd->killed || from_send_error)) + { + error=table->file->delete_row(table->record[0]); + if (error) + { + table->file->print_error(error,MYF(0)); + break; + } + else + deleted++; + } + end_read_record(&info); delete select; + if (error = -1) error = 0; // Monty, that is what read_record returns on end of the file !! +#if 0 + } +#endif + } + return error; +} + +bool multi_delete::send_eof() +{ + int error = 0; + error = do_deletes(false); + thd->proc_info="end"; + if (error && error != -1) + { + ::send_error(&thd->net); + return 1; + } + for (table_being_deleted=delete_tables ; table_being_deleted ; table_being_deleted=table_being_deleted->next) + { + TABLE *table=table_being_deleted->table; + if (table) + VOID(table->file->extra(HA_EXTRA_READCHECK)); + } + 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; + VOID(ha_autocommit_or_rollback(thd,error >= 0)); + } + if (thd->lock) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock=0; + } + ::send_ok(&thd->net,deleted); + return 0; + +} -- cgit v1.2.1