diff options
Diffstat (limited to 'sql')
55 files changed, 1456 insertions, 968 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index b1d9149ddf4..a4858ab2b38 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -83,9 +83,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ slave.cc sql_repl.cc sql_union.cc sql_derived.cc \ - mini_client.cc mini_client_errors.c \ + mini_client.cc mini_client_errors.c pack.c\ stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\ - gstream.cc spatial.cc sql_help.cc + gstream.cc spatial.cc sql_help.cc protocol_cursor.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) @@ -102,6 +102,8 @@ AM_YFLAGS = -d link_sources: rm -f mini_client_errors.c @LN_CP_F@ ../libmysql/errmsg.c mini_client_errors.c + rm -f pack.c + @LN_CP_F@ ../sql-common/pack.c pack.c gen_lex_hash.o: gen_lex_hash.cc lex.h $(CXXCOMPILE) -c $(INCLUDES) $< diff --git a/sql/cache_manager.cc b/sql/cache_manager.cc deleted file mode 100644 index 307fe331e5c..00000000000 --- a/sql/cache_manager.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* 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 */ - -#ifdef __GNUC__ -#pragma implementation /* gcc: Class implementation */ -#endif - -#include <my_global.h> -#include <my_sys.h> -#include "cache_manager.h" - -/* - cache_manager.cc - ----------------------------------------------------------------------- - The cache_manager class manages a number of blocks (which are allocatable - units of memory). - ----------------------------------------------------------------------- -*/ - -#define HEADER_LENGTH ALIGN_SIZE(8) -#define SUFFIX_LENGTH 4 - -#define ALLOC_MASK 0x3FFFFFFFL -#define FREE_BIT (1L << 31) -#define LOCK_BIT (1L << 30) -#define SMALLEST_BLOCK 32 - - - -/* -** Internal Methods -** -------------------- -*/ - - -/* list manipulation methods */ -void *cache_manager::init_list(void) -{ - -return; -} - - -void *cache_manager::link_into_abs(void *ptr) -{ - for (int i(0); (*abs_list)[i] != NULL; i++); - - (*abs_list)[i] = ptr; - - return (abs_list)[i]; // ??? -} - - - -bool *cache_manager::unlink_from_abs(void *ptr) -{ - (*ptr) = NULL; - -return; -} - - - -/* memory allocation methods */ -void *cache_manager::find_in_llist(uint) -{ - -return; -} - - -void cache_manager::defrag(void) -{ - printf("Defragging: .........."); - - return; -} - - - -/* -** Public Methods -** ------------------ -*/ - -cache_manager::cache_manager(uint size) -{ - base_ptr = my_malloc(size, MY_WME); /* MY_WME = write mesg on err */ - - return; -} - - -cache_manager::~cache_manager(void) -{ - free(base_ptr); - delete base_ptr; - - return; -} - - -void *cache_manager::alloc(uint size) -{ - void *llist; - void *abs_ptr; - - size=ALIGN_SIZE(size+HEADER_LENGTH+SUFFIX_LENGTH); - if (!(llist = find_in_llist(size))) - { - //defrag(); - if (!(llist = find_in_llist(size))) - return 0; /* return null pointer, buffer exhausted! */ - } - size_of_found_block=int4korr((char*) llist) & ALLOC_MASK; - // if (size_of_found_block < SMALLEST_BLOCK) - - abs_ptr = link_into_abs(llist); - return abs_ptr; -} - - -void cache_manager::dealloc(void) -{ - printf("Deallocating: ..........\n"); - - return; -} - - - -void cache_manager::clear(void) -{ - // reset the internal linked list, forgetting all pointers to mem blks - - return; -} diff --git a/sql/cache_manager.h b/sql/cache_manager.h deleted file mode 100644 index d422a86ea8e..00000000000 --- a/sql/cache_manager.h +++ /dev/null @@ -1,61 +0,0 @@ -/* 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 */ - -#ifdef __GNUC__ -#pragma interface /* gcc class implementation */ -#endif - -#ifndef _CACHE_MANAGER_H_ -#define _CACHE_MANAGER_H_ -#endif - -/* - cache_manager.h - ----------------------------------------------------------------------- - The cache_manager class manages a number of blocks (which are allocatable - units of memory). - ----------------------------------------------------------------------- -*/ - - - -class cache_manager { - void **abs_list; /* List holding block abstraction ptrs */ - - typedef struct free_blks { - struct free_blks *next, **prev; - uint Size; - } FREE_BLKS; - FREE_BLKS *base_ptr; /* Pointer to newly allocated sys mem */ - - - /* list manipulation methods */ - void *link_into_abs(void *); /* Return an abstract pointer to blk */ - bool *unlink_from_abs(void *); /* Used to dealloc a blk */ - void *find_in_fblist(uint); /* */ - - /* memory allocation methods */ - void defrag(void); /* Defragment the cache */ - bool *init_blk(void *); /* Return a pointer to new list */ - - public: - cache_manager(uint); /* Get allocation of size from system */ - ~cache_manager(void); /* Destructor; return the cache */ - - void *alloc(uint); /* Alloc size bytes from the cache */ - bool *dealloc(void *); /* Deallocate blocks (with *ptr_arg) */ - void clear(void); /* Clear the cache */ -}; diff --git a/sql/field.cc b/sql/field.cc index 0ea206a4fa7..3695268a888 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5462,9 +5462,9 @@ create_field::create_field(Field *old_field,Field *orig_field) char buff[MAX_FIELD_WIDTH],*pos; String tmp(buff,sizeof(buff), charset); - /* Get the value from record[2] (the default value row) */ + /* Get the value from default_values */ my_ptrdiff_t diff= (my_ptrdiff_t) (orig_field->table->rec_buff_length*2); - orig_field->move_field(diff); // Points now at record[2] + orig_field->move_field(diff); // Points now at default_values bool is_null=orig_field->is_real_null(); orig_field->val_str(&tmp,&tmp); orig_field->move_field(-diff); // Back to record[0] diff --git a/sql/field.h b/sql/field.h index 7334ff66dd8..80bfc516ef7 100644 --- a/sql/field.h +++ b/sql/field.h @@ -57,7 +57,7 @@ public: GEOM_GEOMETRYCOLLECTION = 7 }; enum imagetype { itRAW, itMBR}; - + utype unireg_check; uint32 field_length; // Length of field uint16 flags; @@ -83,7 +83,7 @@ public: virtual void reset_fields() {} virtual void set_default() { - my_ptrdiff_t offset = table->default_values() - table->record[0]; + my_ptrdiff_t offset = table->default_values - table->record[0]; memcpy(ptr, ptr + offset, pack_length()); if (null_ptr) *null_ptr= ((*null_ptr & (uchar) ~null_bit) | diff --git a/sql/filesort.cc b/sql/filesort.cc index 4c2ba1e1a59..a3d24cd9242 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1,4 +1,5 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + +/* 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 @@ -51,6 +52,10 @@ static int merge_index(SORTPARAM *param,uchar *sort_buffer, static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count); static uint sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset); +static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield, + uint sortlength, uint *plength); +static void unpack_addon_fields(struct st_sort_addon_field *addon_field, + byte *buff); /* Creates a set of pointers that can be used to read the rows @@ -82,16 +87,48 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DBUG_PUSH(""); /* No DBUG here */ #endif - outfile= table->io_cache; + outfile= table->sort.io_cache; my_b_clear(&tempfile); my_b_clear(&buffpek_pointers); buffpek=0; sort_keys= (uchar **) NULL; error= 1; bzero((char*) ¶m,sizeof(param)); + param.sort_length= sortlength(sortorder, s_length, &multi_byte_charset); param.ref_length= table->file->ref_length; - param.sort_length= (sortlength(sortorder,s_length, &multi_byte_charset)+ - param.ref_length); + param.addon_field= 0; + param.addon_length= 0; + if (!(table->tmp_table || table->fulltext_searched)) + { + /* + Get the descriptors of all fields whose values are appended + to sorted fields and get its total length in param.spack_length. + */ + param.addon_field= get_addon_fields(thd, table->field, + param.sort_length, + ¶m.addon_length); + } + table->sort.addon_buf= 0; + table->sort.addon_length= param.addon_length; + table->sort.addon_field= param.addon_field; + table->sort.unpack= unpack_addon_fields; + if (param.addon_field) + { + param.res_length= param.addon_length; + if (!(table->sort.addon_buf= (byte *) my_malloc(param.addon_length, + MYF(MY_WME)))) + goto err; + } + else + { + param.res_length= param.ref_length; + /* + The reference to the record is considered + as an additional sorted field + */ + param.sort_length+= param.ref_length; + } + param.rec_length= param.sort_length+param.addon_length; param.max_rows= max_rows; if (select && select->quick) @@ -115,7 +152,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, records=table->file->estimate_number_of_rows(); selected_records_file= 0; } - if (param.sort_length == param.ref_length && records > param.max_rows) + if (param.rec_length == param.ref_length && records > param.max_rows) records=param.max_rows; /* purecov: inspected */ if (multi_byte_charset && @@ -127,9 +164,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, while (memavl >= min_sort_memory) { ulong old_memavl; - ulong keys= memavl/(param.sort_length+sizeof(char*)); + ulong keys= memavl/(param.rec_length+sizeof(char*)); param.keys=(uint) min(records+1, keys); - if ((sort_keys= (uchar **) make_char_array(param.keys, param.sort_length, + if ((sort_keys= (uchar **) make_char_array(param.keys, param.rec_length, MYF(0)))) break; old_memavl=memavl; @@ -176,8 +213,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, Use also the space previously used by string pointers in sort_buffer for temporary key storage. */ - param.keys=((param.keys*(param.sort_length+sizeof(char*))) / - param.sort_length-1); + param.keys=((param.keys*(param.rec_length+sizeof(char*))) / + param.rec_length-1); maxbuffer--; // Offset from 0 if (merge_many_buff(¶m,(uchar*) sort_keys,buffpek,&maxbuffer, &tempfile)) @@ -225,6 +262,22 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, } /* filesort */ +void filesort_free_buffers(TABLE *table) +{ + if (table->sort.record_pointers) + { + my_free((gptr) table->sort.record_pointers,MYF(0)); + table->sort.record_pointers=0; + } + if (table->sort.addon_buf) + { + my_free((char *) table->sort.addon_buf, MYF(0)); + my_free((char *) table->sort.addon_field, MYF(MY_ALLOW_ZERO_PTR)); + table->sort.addon_buf=0; + table->sort.addon_field=0; + } +} + /* Make a array of string pointers */ static char **make_char_array(register uint fields, uint length, myf my_flag) @@ -356,8 +409,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) DBUG_RETURN(HA_POS_ERROR); idx=0; - if (param->ref_length == param->sort_length && - my_b_tell(tempfile)/param->sort_length >= param->max_rows) + if (param->ref_length == param->rec_length && + my_b_tell(tempfile)/param->rec_length >= param->max_rows) { /* We are writing the result index file and have found all @@ -385,7 +438,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ DBUG_RETURN(my_b_inited(tempfile) ? - (ha_rows) (my_b_tell(tempfile)/param->sort_length) : + (ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx); } /* find_all_keys */ @@ -394,29 +447,30 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, - IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) + IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) { - uint sort_length; + uint sort_length, rec_length; uchar **end; BUFFPEK buffpek; DBUG_ENTER("write_keys"); - sort_length=param->sort_length; + sort_length= param->sort_length; + rec_length= param->rec_length; #ifdef MC68000 quicksort(sort_keys,count,sort_length); #else - my_string_ptr_sort((gptr) sort_keys,(uint) count,sort_length); + my_string_ptr_sort((gptr) sort_keys, (uint) count, sort_length); #endif if (!my_b_inited(tempfile) && - open_cached_file(tempfile,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE, - MYF(MY_WME))) - goto err; /* purecov: inspected */ - buffpek.file_pos=my_b_tell(tempfile); + open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE, + MYF(MY_WME))) + goto err; /* purecov: inspected */ + buffpek.file_pos= my_b_tell(tempfile); if ((ha_rows) count > param->max_rows) - count=(uint) param->max_rows; /* purecov: inspected */ + count=(uint) param->max_rows; /* purecov: inspected */ buffpek.count=(ha_rows) count; for (end=sort_keys+count ; sort_keys != end ; sort_keys++) - if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length)) + if (my_b_write(tempfile, (byte*) *sort_keys, (uint) rec_length)) goto err; if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek))) goto err; @@ -505,10 +559,10 @@ static void make_sortkey(register SORTPARAM *param, } else { - my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length); - bzero((char *)to+length,diff); + my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length); + bzero((char *)to+length,diff); } - break; + break; } case INT_RESULT: { @@ -577,29 +631,56 @@ static void make_sortkey(register SORTPARAM *param, else to+= sort_field->length; } - memcpy((byte*) to,ref_pos,(size_s) param->ref_length);/* Save filepos last */ + + if (param->addon_field) + { + /* + Save field values appended to sorted fields. + First null bit indicators are appended then field values follow. + In this implementation we use fixed layout for field values - + the same for all records. + */ + SORT_ADDON_FIELD *addonf= param->addon_field; + uchar *nulls= to; + DBUG_ASSERT(addonf); + bzero((char *) nulls, addonf->offset); + to+= addonf->offset; + for ( ; (field= addonf->field) ; addonf++) + { + if (addonf->null_bit && field->is_null()) + nulls[addonf->null_offset]|= addonf->null_bit; + else + field->pack((char *) to, field->ptr); + to+= addonf->length; + } + } + else + { + /* Save filepos last */ + memcpy((byte*) to, ref_pos, (size_s) param->ref_length); + } return; } static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count) { - uint offset,ref_length; + uint offset,res_length; byte *to; DBUG_ENTER("save_index"); - my_string_ptr_sort((gptr) sort_keys,(uint) count,param->sort_length); - ref_length=param->ref_length; - offset=param->sort_length-ref_length; + my_string_ptr_sort((gptr) sort_keys, (uint) count, param->sort_length); + res_length= param->res_length; + offset= param->rec_length-res_length; if ((ha_rows) count > param->max_rows) count=(uint) param->max_rows; - if (!(to=param->sort_form->record_pointers= - (byte*) my_malloc(ref_length*count,MYF(MY_WME)))) - DBUG_RETURN(1); /* purecov: inspected */ - for (uchar **end=sort_keys+count ; sort_keys != end ; sort_keys++) + if (!(to= param->sort_form->sort.record_pointers= + (byte*) my_malloc(res_length*count, MYF(MY_WME)))) + DBUG_RETURN(1); /* purecov: inspected */ + for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++) { - memcpy(to,*sort_keys+offset,ref_length); - to+=ref_length; + memcpy(to, *sort_keys+offset, res_length); + to+= res_length; } DBUG_RETURN(0); } @@ -654,7 +735,7 @@ int merge_many_buff(SORTPARAM *param, uchar *sort_buffer, /* This returns (uint) -1 if something goes wrong */ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, - uint sort_length) + uint rec_length) { register uint count; uint length; @@ -662,33 +743,35 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count))) { if (my_pread(fromfile->file,(byte*) buffpek->base, - (length= sort_length*count),buffpek->file_pos,MYF_RW)) + (length= rec_length*count),buffpek->file_pos,MYF_RW)) return((uint) -1); /* purecov: inspected */ buffpek->key=buffpek->base; buffpek->file_pos+= length; /* New filepos */ buffpek->count-= count; buffpek->mem_count= count; } - return (count*sort_length); + return (count*rec_length); } /* read_to_buffer */ - /* Merge buffers to one buffer */ +/* + Merge buffers to one buffer +*/ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, - IO_CACHE *to_file, uchar *sort_buffer, - BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb, - int flag) + IO_CACHE *to_file, uchar *sort_buffer, + BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb, + int flag) { int error; - uint sort_length,offset; + uint rec_length,sort_length,res_length,offset; ulong maxcount; ha_rows max_rows,org_max_rows; my_off_t to_start_filepos; uchar *strpos; BUFFPEK *buffpek,**refpek; QUEUE queue; - qsort2_cmp cmp; + qsort2_cmp cmp; volatile bool *killed= ¤t_thd->killed; bool not_killable; DBUG_ENTER("merge_buffers"); @@ -697,29 +780,32 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, if (param->not_killable) { killed= ¬_killable; - not_killable=0; + not_killable= 0; } error=0; - offset=(sort_length=param->sort_length)-param->ref_length; - maxcount=(ulong) (param->keys/((uint) (Tb-Fb) +1)); - to_start_filepos=my_b_tell(to_file); - strpos=(uchar*) sort_buffer; - org_max_rows=max_rows=param->max_rows; - - if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0, - (queue_compare) - (cmp=get_ptr_compare(sort_length)),(void*) &sort_length)) - DBUG_RETURN(1); /* purecov: inspected */ + rec_length= param->rec_length; + res_length= param->res_length; + sort_length= param->sort_length; + offset= rec_length-res_length; + maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1)); + to_start_filepos= my_b_tell(to_file); + strpos= (uchar*) sort_buffer; + org_max_rows=max_rows= param->max_rows; + + if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0, + (queue_compare) (cmp= get_ptr_compare(sort_length)), + (void*) &sort_length)) + DBUG_RETURN(1); /* purecov: inspected */ for (buffpek= Fb ; buffpek <= Tb ; buffpek++) { buffpek->base= strpos; - buffpek->max_keys=maxcount; - strpos+= (uint) (error=(int) read_to_buffer(from_file,buffpek, - sort_length)); + buffpek->max_keys= maxcount; + strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek, + rec_length)); if (error == -1) - goto err; /* purecov: inspected */ - queue_insert(&queue,(byte*) buffpek); + goto err; /* purecov: inspected */ + queue_insert(&queue, (byte*) buffpek); } if (param->unique_buff) @@ -732,98 +818,101 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, This is safe as we know that there is always more than one element in each block to merge (This is guaranteed by the Unique:: algorithm */ - buffpek=(BUFFPEK*) queue_top(&queue); - memcpy(param->unique_buff, buffpek->key, sort_length); - if (my_b_write(to_file,(byte*) buffpek->key, sort_length)) + buffpek= (BUFFPEK*) queue_top(&queue); + memcpy(param->unique_buff, buffpek->key, rec_length); + if (my_b_write(to_file, (byte*) buffpek->key, rec_length)) { - error=1; goto err; /* purecov: inspected */ + error=1; goto err; /* purecov: inspected */ } - buffpek->key+=sort_length; + buffpek->key+= rec_length; buffpek->mem_count--; if (!--max_rows) { - error=0; /* purecov: inspected */ - goto end; /* purecov: inspected */ + error= 0; /* purecov: inspected */ + goto end; /* purecov: inspected */ } - queue_replaced(&queue); // Top element has been used + queue_replaced(&queue); // Top element has been used } else - cmp=0; // Not unique + cmp= 0; // Not unique while (queue.elements > 1) { if (*killed) { - error=1; goto err; /* purecov: inspected */ + error= 1; goto err; /* purecov: inspected */ } for (;;) { - buffpek=(BUFFPEK*) queue_top(&queue); - if (cmp) // Remove duplicates + buffpek= (BUFFPEK*) queue_top(&queue); + if (cmp) // Remove duplicates { - if (!(*cmp)(&sort_length, &(param->unique_buff), - (uchar**) &buffpek->key)) - goto skip_duplicate; - memcpy(param->unique_buff, (uchar*) buffpek->key,sort_length); + if (!(*cmp)(&sort_length, &(param->unique_buff), + (uchar**) &buffpek->key)) + goto skip_duplicate; + memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length); } if (flag == 0) { - if (my_b_write(to_file,(byte*) buffpek->key, sort_length)) - { - error=1; goto err; /* purecov: inspected */ - } + if (my_b_write(to_file,(byte*) buffpek->key, rec_length)) + { + error=1; goto err; /* purecov: inspected */ + } } else { - WRITE_REF(to_file,(byte*) buffpek->key+offset); + if (my_b_write(to_file, (byte*) buffpek->key+offset, res_length)) + { + error=1; goto err; /* purecov: inspected */ + } } if (!--max_rows) { - error=0; /* purecov: inspected */ - goto end; /* purecov: inspected */ + error= 0; /* purecov: inspected */ + goto end; /* purecov: inspected */ } skip_duplicate: - buffpek->key+=sort_length; + buffpek->key+= rec_length; if (! --buffpek->mem_count) { - if (!(error=(int) read_to_buffer(from_file,buffpek, - sort_length))) - { - uchar *base=buffpek->base; - ulong max_keys=buffpek->max_keys; - - VOID(queue_remove(&queue,0)); - - /* Put room used by buffer to use in other buffer */ - for (refpek= (BUFFPEK**) &queue_top(&queue); - refpek <= (BUFFPEK**) &queue_end(&queue); - refpek++) - { - buffpek= *refpek; - if (buffpek->base+buffpek->max_keys*sort_length == base) - { - buffpek->max_keys+=max_keys; - break; - } - else if (base+max_keys*sort_length == buffpek->base) - { - buffpek->base=base; - buffpek->max_keys+=max_keys; - break; - } - } - break; /* One buffer have been removed */ - } - else if (error == -1) - goto err; /* purecov: inspected */ + if (!(error= (int) read_to_buffer(from_file,buffpek, + rec_length))) + { + uchar *base= buffpek->base; + ulong max_keys= buffpek->max_keys; + + VOID(queue_remove(&queue,0)); + + /* Put room used by buffer to use in other buffer */ + for (refpek= (BUFFPEK**) &queue_top(&queue); + refpek <= (BUFFPEK**) &queue_end(&queue); + refpek++) + { + buffpek= *refpek; + if (buffpek->base+buffpek->max_keys*rec_length == base) + { + buffpek->max_keys+= max_keys; + break; + } + else if (base+max_keys*rec_length == buffpek->base) + { + buffpek->base= base; + buffpek->max_keys+= max_keys; + break; + } + } + break; /* One buffer have been removed */ + } + else if (error == -1) + goto err; /* purecov: inspected */ } - queue_replaced(&queue); /* Top element has been replaced */ + queue_replaced(&queue); /* Top element has been replaced */ } } - buffpek=(BUFFPEK*) queue_top(&queue); + buffpek= (BUFFPEK*) queue_top(&queue); buffpek->base= sort_buffer; - buffpek->max_keys=param->keys; + buffpek->max_keys= param->keys; /* As we know all entries in the buffer are unique, we only have to @@ -833,7 +922,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, { if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key)) { - buffpek->key+=sort_length; // Remove duplicate + buffpek->key+= rec_length; // Remove duplicate --buffpek->mem_count; } } @@ -841,37 +930,40 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, do { if ((ha_rows) buffpek->mem_count > max_rows) - { /* Don't write too many records */ - buffpek->mem_count=(uint) max_rows; - buffpek->count=0; /* Don't read more */ + { /* Don't write too many records */ + buffpek->mem_count= (uint) max_rows; + buffpek->count= 0; /* Don't read more */ } - max_rows-=buffpek->mem_count; + max_rows-= buffpek->mem_count; if (flag == 0) { if (my_b_write(to_file,(byte*) buffpek->key, - (sort_length*buffpek->mem_count))) + (rec_length*buffpek->mem_count))) { - error=1; goto err; /* purecov: inspected */ + error= 1; goto err; /* purecov: inspected */ } } else { register uchar *end; strpos= buffpek->key+offset; - for (end=strpos+buffpek->mem_count*sort_length; - strpos != end ; - strpos+=sort_length) - { - WRITE_REF(to_file,strpos); + for (end= strpos+buffpek->mem_count*rec_length ; + strpos != end ; + strpos+= rec_length) + { + if (my_b_write(to_file, (byte *) strpos, res_length)) + { + error=1; goto err; + } } } } - while ((error=(int) read_to_buffer(from_file,buffpek,sort_length)) - != -1 && error != 0); + while ((error=(int) read_to_buffer(from_file,buffpek, rec_length)) + != -1 && error != 0); end: - lastbuff->count=min(org_max_rows-max_rows,param->max_rows); - lastbuff->file_pos=to_start_filepos; + lastbuff->count= min(org_max_rows-max_rows, param->max_rows); + lastbuff->file_pos= to_start_filepos; err: delete_queue(&queue); DBUG_RETURN(error); @@ -925,7 +1017,6 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) sortorder->need_strxnfrm= 0; if (sortorder->field) { - if (sortorder->field->type() == FIELD_TYPE_BLOB) sortorder->length= thd->variables.max_sort_length; else @@ -947,7 +1038,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) case STRING_RESULT: sortorder->length=sortorder->item->max_length; if (use_strnxfrm((cs=sortorder->item->charset()))) - { + { sortorder->length= sortorder->length*cs->strxfrm_multiply; sortorder->need_strxnfrm= 1; *multi_byte_charset= 1; @@ -982,6 +1073,148 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) /* + Get descriptors of fields appended to sorted fields and + calculate its total length + + SYNOPSIS + get_addon_fields() + thd Current thread + ptabfields Array of references to the table fields + sortlength Total length of sorted fields + plength out: Total length of appended fields + + DESCRIPTION + The function first finds out what fields are used in the result set. + Then it calculates the length of the buffer to store the values of + these fields together with the value of sort values. + If the calculated length is not greater than max_length_for_sort_data + the function allocates memory for an array of descriptors containing + layouts for the values of the non-sorted fields in the buffer and + fills them. + + NOTES + The null bits for the appended values are supposed to be put together + and stored the buffer just ahead of the value of the first field. + + RETURN + Pointer to the layout descriptors for the appended fields, if any + NULL - if we do not store field values with sort data. +*/ + +static SORT_ADDON_FIELD * +get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength) +{ + Field **pfield; + Field *field; + SORT_ADDON_FIELD *addonf; + uint length= 0; + uint fields= 0; + uint null_fields= 0; + + /* + If there is a reference to a field in the query add it + to the the set of appended fields. + Note for future refinement: + This this a too strong condition. + Actually we need only the fields referred in the + result set. And for some of them it makes sense to use + the values directly from sorted fields. + */ + *plength= 0; + /* + The following statement is added to avoid sorting in alter_table. + The fact is the filter 'field->query_id != thd->query_id' + doesn't work for alter table + */ + if (thd->lex.sql_command != SQLCOM_SELECT) + return 0; + for (pfield= ptabfield; (field= *pfield) ; pfield++) + { + if (field->query_id != thd->query_id) + continue; + if (field->flags & BLOB_FLAG) + return 0; + length+= field->max_packed_col_length(field->pack_length()); + if (field->maybe_null()) + null_fields++; + fields++; + } + if (!fields) + return 0; + length+= (null_fields+7)/8; + + if (length+sortlength > thd->variables.max_length_for_sort_data || + !(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)* + (fields+1), MYF(MY_WME)))) + return 0; + + *plength= length; + length= (null_fields+7)/8; + null_fields= 0; + for (pfield= ptabfield; (field= *pfield) ; pfield++) + { + if (field->query_id != thd->query_id) + continue; + addonf->field= field; + addonf->offset= length; + if (field->maybe_null()) + { + addonf->null_offset= null_fields/8; + addonf->null_bit= 1<<(null_fields & 7); + null_fields++; + } + else + { + addonf->null_offset= 0; + addonf->null_bit= 0; + } + addonf->length= field->max_packed_col_length(field->pack_length()); + length+= addonf->length; + addonf++; + } + addonf->field= 0; // Put end marker + + DBUG_PRINT("info",("addon_length: %d",length)); + return (addonf-fields); +} + + +/* + Copy (unpack) values appended to sorted fields from a buffer back to + their regular positions specified by the Field::ptr pointers. + + SYNOPSIS + unpack_addon_fields() + addon_field Array of descriptors for appended fields + buff Buffer which to unpack the value from + + NOTES + The function is supposed to be used only as a callback function + when getting field values for the sorted result set. + + RETURN + void. +*/ + +static void +unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff) +{ + Field *field; + SORT_ADDON_FIELD *addonf= addon_field; + + for ( ; (field= addonf->field) ; addonf++) + { + if (addonf->null_bit && (addonf->null_bit & buff[addonf->null_offset])) + { + field->set_null(); + continue; + } + field->set_notnull(); + field->unpack(field->ptr, (char *) buff+addonf->offset); + } +} + +/* ** functions to change a double or float to a sortable string ** The following should work for IEEE */ diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index aa5d88bf177..4f955d8f79e 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -916,11 +916,11 @@ innobase_commit_low( /* Update the replication position info inside InnoDB */ trx->mysql_master_log_file_name - = active_mi->rli.master_log_name; + = active_mi->rli.group_master_log_name; trx->mysql_master_log_pos = ((ib_longlong) - (active_mi->rli.master_log_pos + - active_mi->rli.event_len + - active_mi->rli.pending)); + (active_mi->rli.group_master_log_pos + + active_mi->rli.event_len + )); } #endif /* HAVE_REPLICATION */ trx_commit_for_mysql(trx); diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 5dc294b3b9f..154429ecc0d 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -46,7 +46,7 @@ class ha_myisam: public handler ha_myisam(TABLE *table): handler(table), file(0), int_table_flags(HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | HA_NULL_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | - HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY), + HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY | HA_HAS_GEOMETRY), enable_activate_all_index(1) {} ~ha_myisam() {} diff --git a/sql/handler.cc b/sql/handler.cc index 762e07eec64..e4317506ed7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -34,6 +34,8 @@ #endif #ifdef HAVE_INNOBASE_DB #include "ha_innodb.h" +#else +#define innobase_query_caching_of_table_permitted(X,Y,Z) 1 #endif #include <myisampack.h> #include <errno.h> @@ -877,7 +879,7 @@ int handler::delete_all_rows() return (my_errno=HA_ERR_WRONG_COMMAND); } -bool handler::caching_allowed(THD* thd, char* table_key, +bool handler::caching_allowed(THD* thd, char* table_key, uint key_length, uint8 cache_type) { if (cache_type == HA_CACHE_TBL_ASKTRANSACT) diff --git a/sql/handler.h b/sql/handler.h index 557fec0de0e..8c23a3625e0 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -48,6 +48,7 @@ #define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ #define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; It returns a position to ha_r_rnd */ +#define HA_HAS_GEOMETRY 16 #define HA_NO_INDEX 32 /* No index needed for next/prev */ #define HA_KEY_READ_WRONG_STR 64 /* keyread returns converted strings */ #define HA_NULL_KEY 128 /* One can have keys with NULL */ diff --git a/sql/item.cc b/sql/item.cc index 2bb9fa62ff4..bd2b929cee7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -349,7 +349,7 @@ table_map Item_field::used_tables() const { if (field->table->const_table) return 0; // const item - return (depended_from ? RAND_TABLE_BIT : field->table->map); + return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); } Item *Item_field::get_tmp_table_item(THD *thd) @@ -772,9 +772,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) (last= sl)->get_table_list(), &where, 0)) != not_found_field) break; - if ((refer= find_item_in_list(this, sl->item_list, &counter, - REPORT_EXCEPT_NOT_FOUND)) != - (Item **)not_found_item) + if (sl->item_list.elements && + ((refer= find_item_in_list(this, sl->item_list, &counter, + REPORT_EXCEPT_NOT_FOUND)) != + (Item **)not_found_item)) break; if (sl->master_unit()->first_select()->linkage == DERIVED_TABLE_TYPE) @@ -1388,8 +1389,8 @@ bool Item_default_value::fix_fields(THD *thd, struct st_table_list *table_list, if (!def_field) return 1; memcpy(def_field, field_arg->field, field_arg->field->size_of()); - def_field->move_field(def_field->table->default_values() - - def_field->table->record[0]); + def_field->move_field(def_field->table->default_values - + def_field->table->record[0]); set_field(def_field); return 0; } @@ -1406,6 +1407,59 @@ void Item_default_value::print(String *str) str->append(')'); } +bool Item_insert_value::eq(const Item *item, bool binary_cmp) const +{ + return item->type() == INSERT_VALUE_ITEM && + ((Item_default_value *)item)->arg->eq(arg, binary_cmp); +} + + +bool Item_insert_value::fix_fields(THD *thd, struct st_table_list *table_list, Item **items) +{ + bool res= arg->fix_fields(thd, table_list, items); + if (res) + return res; + /* + arg->type() can be only REF_ITEM or FIELD_ITEM as arg is + a simple_ident in sql_yacc.yy + */ + if (arg->type() == REF_ITEM) + { + Item_ref *ref= (Item_ref *)arg; + if (ref->ref[0]->type() != FIELD_ITEM) + { + return 1; + } + arg= ref->ref[0]; + } + Item_field *field_arg= (Item_field *)arg; + if (field_arg->field->table->insert_values) + { + Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); + if (!def_field) + return 1; + memcpy(def_field, field_arg->field, field_arg->field->size_of()); + def_field->move_field(def_field->table->insert_values - + def_field->table->record[0]); + set_field(def_field); + } + else + { + Field *field=field_arg->field; + /* charset doesn't matter here, it's to avoid sigsegv only */ + set_field(new Field_null(0,0,Field::NONE,field->field_name,field->table, + default_charset_info)); + } + return 0; +} + +void Item_insert_value::print(String *str) +{ + str->append("VALUE("); + arg->print(str); + str->append(')'); +} + /* If item is a const function, calculate it and return a const item The original item is freed if not returned diff --git a/sql/item.h b/sql/item.h index 6603e11166b..111892df688 100644 --- a/sql/item.h +++ b/sql/item.h @@ -34,13 +34,13 @@ public: enum Type {FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM, INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM, COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM, - PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, - FIELD_VARIANCE_ITEM, CONST_ITEM, + PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, + FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; - enum coercion { COER_COERCIBLE=3, COER_IMPLICIT=2, - COER_NOCOLL=1, COER_EXPLICIT=0 }; + enum coercion { COER_COERCIBLE=3, COER_IMPLICIT=2, + COER_NOCOLL=1, COER_EXPLICIT=0 }; String str_value; /* used to store value */ my_string name; /* Name from select */ @@ -201,6 +201,7 @@ public: bool is_null() { return field->is_null(); } Item *get_tmp_table_item(THD *thd); friend class Item_default_value; + friend class Item_insert_value; }; class Item_null :public Item @@ -734,7 +735,7 @@ public: Item *arg; Item_default_value() : Item_field((const char *)NULL, (const char *)NULL, (const char *)NULL), arg(NULL) {} - Item_default_value(Item *a) : + Item_default_value(Item *a) : Item_field((const char *)NULL, (const char *)NULL, (const char *)NULL), arg(a) {} enum Type type() const { return DEFAULT_VALUE_ITEM; } bool eq(const Item *item, bool binary_cmp) const; @@ -754,6 +755,24 @@ public: table_map used_tables() const { return (table_map)0L; } }; +class Item_insert_value : public Item_field +{ +public: + Item *arg; + Item_insert_value(Item *a) : + Item_field((const char *)NULL, (const char *)NULL, (const char *)NULL), arg(a) {} + bool eq(const Item *item, bool binary_cmp) const; + bool fix_fields(THD *, struct st_table_list *, Item **); + void set_outer_resolving() { arg->set_outer_resolving(); } + void print(String *str); + virtual bool basic_const_item() const { return true; } + int save_in_field(Field *field, bool no_conversions) + { + return Item_field::save_in_field(field, no_conversions); + } + table_map used_tables() const { return (table_map)0L; } +}; + class Item_cache: public Item { table_map used_table_map; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 0f21cf5a774..7cc07690fcc 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -346,7 +346,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, if (cache->cols() == 1) { if (args[0]->used_tables()) - cache->set_used_tables(RAND_TABLE_BIT); + cache->set_used_tables(OUTER_REF_TABLE_BIT); else cache->set_used_tables(0); } @@ -356,7 +356,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, for (uint i= 0; i < n; i++) { if (args[0]->el(i)->used_tables()) - ((Item_cache *)cache->el(i))->set_used_tables(RAND_TABLE_BIT); + ((Item_cache *)cache->el(i))->set_used_tables(OUTER_REF_TABLE_BIT); else ((Item_cache *)cache->el(i))->set_used_tables(0); } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 56e3eb2cb5f..07ec66e9f9d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1457,11 +1457,13 @@ String *Item_func_user::val_str(String *str) { THD *thd=current_thd; CHARSET_INFO *cs= default_charset(); - const char *host=thd->host ? thd->host : thd->ip ? thd->ip : ""; + const char *host= thd->host_or_ip; + uint res_length; + // For system threads (e.g. replication SQL thread) user may be empty if (!thd->user) return &empty_string; - uint32 res_length=(strlen(thd->user)+strlen(host)+2) * cs->mbmaxlen; + res_length= (strlen(thd->user)+strlen(host)+2) * cs->mbmaxlen; if (str->alloc(res_length)) { @@ -2943,7 +2945,7 @@ String *Item_func_compress::val_str(String *str) */ ulong new_size= (ulong)((res->length()*120)/100)+12; - buffer.realloc((uint32)new_size + 4); + buffer.realloc((uint32)new_size + 4 + 1); Byte *body= ((Byte*)buffer.c_ptr()) + 4; if ((err= compress(body, &new_size, @@ -2954,8 +2956,18 @@ String *Item_func_compress::val_str(String *str) null_value= 1; return 0; } - - int4store(buffer.c_ptr(),res->length() & 0x3FFFFFFF); + + char *tmp= buffer.c_ptr(); // int4store is a macro; avoid side effects + int4store(tmp, res->length() & 0x3FFFFFFF); + + /* This is for the stupid char fields which trim ' ': */ + char *last_char= ((char*)body)+new_size-1; + if (*last_char == ' ') + { + *++last_char= '.'; + new_size++; + } + buffer.length((uint32)new_size + 4); return &buffer; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 6c0b799b4de..c749fba616f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -128,7 +128,7 @@ void Item_subselect::fix_length_and_dec() inline table_map Item_subselect::used_tables() const { return (table_map) (engine->dependent() ? 1L : - (engine->uncacheable() ? RAND_TABLE_BIT : 0L)); + (engine->uncacheable() ? OUTER_REF_TABLE_BIT : 0L)); } Item_singlerow_subselect::Item_singlerow_subselect(THD *thd, diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 653f71e3d79..11c850f9d5f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -993,7 +993,7 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)), int error; /* The first item->rec_offset bytes are taken care of with - restore_record(table,2) in setup() + restore_record(table,default_values) in setup() */ memcpy(buf + item->rec_offset, key, item->tree->size_of_element); if ((error = item->table->file->write_row(buf))) @@ -1075,7 +1075,7 @@ bool Item_sum_count_distinct::setup(THD *thd) void* cmp_arg; // to make things easier for dump_leaf if we ever have to dump to MyISAM - restore_record(table,2); + restore_record(table,default_values); if (table->fields == 1) { @@ -1348,7 +1348,7 @@ static int group_concat_key_cmp_with_distinct(void* arg, byte* key1, byte* key2) { Item_func_group_concat* item= (Item_func_group_concat*)arg; - for (int i= 0; i<item->arg_count_field; i++) + for (uint i= 0; i < item->arg_count_field; i++) { Item *field_item= item->expr[i]; Field *field= field_item->tmp_table_field(); @@ -1377,7 +1377,7 @@ static int group_concat_key_cmp_with_distinct(void* arg, byte* key1, static int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) { Item_func_group_concat* item= (Item_func_group_concat*)arg; - for (int i=0; i<item->arg_count_order; i++) + for (uint i=0; i < item->arg_count_order; i++) { ORDER *order_item= item->order[i]; Item *item= *order_item->item; @@ -1404,9 +1404,10 @@ static int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) GROUP_CONCAT(DISTINCT expr,... ORDER BY col,... ) */ -static int group_concat_key_cmp_with_distinct_and_order(void* arg, byte* key1, byte* key2) +static int group_concat_key_cmp_with_distinct_and_order(void* arg, + byte* key1, + byte* key2) { - Item_func_group_concat* item= (Item_func_group_concat*)arg; if (!group_concat_key_cmp_with_distinct(arg,key1,key2)) return 0; return(group_concat_key_cmp_with_order(arg,key1,key2)); @@ -1427,7 +1428,7 @@ static int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), tmp.length(0); - for (int i= 0; i < group_concat_item->arg_show_fields; i++) + for (uint i= 0; i < group_concat_item->arg_show_fields; i++) { Item *show_item= group_concat_item->expr[i]; if (!show_item->const_item()) @@ -1481,13 +1482,13 @@ static int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), is_separator - string value of separator */ -Item_func_group_concat::Item_func_group_concat(int is_distinct, +Item_func_group_concat::Item_func_group_concat(bool is_distinct, List<Item> *is_select, SQL_LIST *is_order, String *is_separator) :Item_sum(), tmp_table_param(0), warning_available(false), - separator(is_separator), tree(&tree_base), table(0), distinct(is_distinct), - tree_mode(0), count_cut_values(0) + separator(is_separator), tree(&tree_base), table(0), + count_cut_values(0), tree_mode(0), distinct(is_distinct) { original= 0; quick_group= 0; @@ -1506,39 +1507,31 @@ Item_func_group_concat::Item_func_group_concat(int is_distinct, expr - arg_count_field order - arg_count_order */ - args= (Item**)sql_alloc(sizeof(Item*)*(arg_count+arg_count_order+arg_count_field)+ - sizeof(ORDER*)*arg_count_order); + args= (Item**) sql_alloc(sizeof(Item*)*(arg_count+arg_count_order+ + arg_count_field)+ + sizeof(ORDER*)*arg_count_order); if (!args) - { - my_error(ER_OUTOFMEMORY,MYF(0)); - } + return; // thd->fatal is set expr= args; expr+= arg_count+arg_count_order; - if (arg_count_order) - { - order= (ORDER**)(expr + arg_count_field); - } - /* - fill args items of show and sort - */ + + /* fill args items of show and sort */ int i= 0; List_iterator_fast<Item> li(*is_select); Item *item_select; - while ((item_select= li++)) - { + for ( ; (item_select= li++) ; i++) args[i]= expr[i]= item_select; - i++; - } - - if (order) + + if (arg_count_order) { - uint j= 0; - for (ORDER *order_item= (ORDER*)is_order->first; + i= 0; + order= (ORDER**)(expr + arg_count_field); + for (ORDER *order_item= (ORDER*) is_order->first; order_item != NULL; order_item= order_item->next) { - order[j++]= order_item; + order[i++]= order_item; } } } @@ -1561,8 +1554,7 @@ Item_func_group_concat::~Item_func_group_concat() } if (table) free_tmp_table(thd, table); - if (tmp_table_param) - delete tmp_table_param; + delete tmp_table_param; if (tree_mode) delete_tree(tree); } @@ -1593,14 +1585,17 @@ bool Item_func_group_concat::add() copy_funcs(tmp_table_param->items_to_copy); bool record_is_null= TRUE; - for (int i= 0; i < arg_show_fields; i++) + for (uint i= 0; i < arg_show_fields; i++) { Item *show_item= expr[i]; if (!show_item->const_item()) { Field *f= show_item->tmp_table_field(); if (!f->is_null()) + { record_is_null= FALSE; + break; + } } } if (record_is_null) @@ -1631,7 +1626,7 @@ void Item_func_group_concat::reset_field() bool Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { - int i; /* for loop variable */ + uint i; /* for loop variable */ if (!thd->allow_sum_func) { @@ -1641,11 +1636,11 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) thd->allow_sum_func= 0; maybe_null= 0; - for (uint ui= 0 ; ui < arg_count ; ui++) + for (i= 0 ; i < arg_count ; i++) { - if (args[ui]->fix_fields(thd, tables, args + ui) || args[ui]->check_cols(1)) + if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1)) return 1; - maybe_null |= args[ui]->maybe_null; + maybe_null |= args[i]->maybe_null; } for (i= 0 ; i < arg_count_field ; i++) { diff --git a/sql/item_sum.h b/sql/item_sum.h index 1b3e993fffc..37d7e7f79d0 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -643,21 +643,20 @@ class Item_func_group_concat : public Item_sum public: String result; String *separator; - uint show_elements; TREE tree_base; TREE *tree; TABLE *table; - int arg_count_order; - int arg_count_field; - int arg_show_fields; - int distinct; Item **expr; ORDER **order; - bool tree_mode; - int count_cut_values; + TABLE_LIST *tables_list; ulong group_concat_max_len; + uint show_elements; + uint arg_count_order; + uint arg_count_field; + uint arg_show_fields; + uint count_cut_values; + bool tree_mode, distinct; bool warning_for_row; - TABLE_LIST *tables_list; bool always_null; /* Following is 0 normal object and pointer to original one for copy @@ -665,7 +664,7 @@ class Item_func_group_concat : public Item_sum */ Item_func_group_concat *original; - Item_func_group_concat(int is_distinct,List<Item> *is_select, + Item_func_group_concat(bool is_distinct,List<Item> *is_select, SQL_LIST *is_order,String *is_separator); Item_func_group_concat(THD *thd, Item_func_group_concat &item) @@ -675,20 +674,20 @@ class Item_func_group_concat : public Item_sum warning(item.warning), warning_available(item.warning_available), separator(item.separator), - show_elements(item.show_elements), tree(item.tree), table(item.table), + expr(item.expr), + order(item.order), + tables_list(item.tables_list), + group_concat_max_len(item.group_concat_max_len), + show_elements(item.show_elements), arg_count_order(item.arg_count_order), arg_count_field(item.arg_count_field), arg_show_fields(item.arg_show_fields), - distinct(item.distinct), - expr(item.expr), - order(item.order), - tree_mode(0), count_cut_values(item.count_cut_values), - group_concat_max_len(item.group_concat_max_len), + tree_mode(0), + distinct(item.distinct), warning_for_row(item.warning_for_row), - tables_list(item.tables_list), original(&item) { quick_group = 0; diff --git a/sql/log.cc b/sql/log.cc index 51b1c572601..c9e20bc0cc9 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -592,24 +592,32 @@ err: /* - Delete the current log file, remove it from index file and start on next + Delete relay log files prior to rli->group_relay_log_name + (i.e. all logs which are not involved in a non-finished group + (transaction)), remove them from the index file and start on next relay log. SYNOPSIS purge_first_log() - rli Relay log information - + rli Relay log information + included If false, all relay logs that are strictly before + rli->group_relay_log_name are deleted ; if true, the latter is + deleted too (i.e. all relay logs + read by the SQL slave thread are deleted). + NOTE - This is only called from the slave-execute thread when it has read - all commands from a log and want to switch to a new log. - - When this happens, we should never be in an active transaction as - a transaction is always written as a single block to the binary log. + all commands from a relay log and want to switch to a new relay log. + - When this happens, we can be in an active transaction as + a transaction can span over two relay logs + (although it is always written as a single block to the master's binary + log, hence cannot span over two master's binary logs). IMPLEMENTATION - Protects index file with LOCK_index - - Delete first log file, - - Copy all file names after this one to the front of the index file + - Delete relevant relay log files + - Copy all file names after these ones to the front of the index file - If the OS has truncate, truncate the file, else fill it with \n' - - Read the first file name from the index file and store in rli->linfo + - Read the next file name from the index file and store in rli->linfo RETURN VALUES 0 ok @@ -620,66 +628,68 @@ err: #ifdef HAVE_REPLICATION -int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli) +int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) { int error; DBUG_ENTER("purge_first_log"); - /* - Test pre-conditions. - - Assume that we have previously read the first log and - stored it in rli->relay_log_name - */ DBUG_ASSERT(is_open()); DBUG_ASSERT(rli->slave_running == 1); - DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->relay_log_name)); - DBUG_ASSERT(rli->linfo.index_file_offset == - strlen(rli->relay_log_name) + 1); + DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name)); - /* We have already processed the relay log, so it's safe to delete it */ - my_delete(rli->relay_log_name, MYF(0)); pthread_mutex_lock(&LOCK_index); - if (copy_up_file_and_fill(&index_file, rli->linfo.index_file_offset)) - { - error= LOG_INFO_IO; - goto err; - } + pthread_mutex_lock(&rli->log_space_lock); + rli->relay_log.purge_logs(rli->group_relay_log_name, included, + 0, 0, &rli->log_space_total); + // Tell the I/O thread to take the relay_log_space_limit into account + rli->ignore_log_space_limit= 0; + pthread_mutex_unlock(&rli->log_space_lock); /* - Update the space counter used by all relay logs Ok to broadcast after the critical region as there is no risk of the mutex being destroyed by this thread later - this helps save context switches */ - pthread_mutex_lock(&rli->log_space_lock); - rli->log_space_total -= rli->relay_log_pos; - //tell the I/O thread to take the relay_log_space_limit into account - rli->ignore_log_space_limit= 0; - pthread_mutex_unlock(&rli->log_space_lock); pthread_cond_broadcast(&rli->log_space_cond); /* Read the next log file name from the index file and pass it back to the caller + If included is true, we want the first relay log; + otherwise we want the one after event_relay_log_name. */ - if ((error=find_log_pos(&rli->linfo, NullS, 0 /*no mutex*/))) + if ((included && (error=find_log_pos(&rli->linfo, NullS, 0))) || + (!included && + ((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) || + (error=find_next_log(&rli->linfo, 0))))) { char buff[22]; - sql_print_error("next log error: %d offset: %s log: %s", - error, - llstr(rli->linfo.index_file_offset,buff), - rli->linfo.log_file_name); + sql_print_error("next log error: %d offset: %s log: %s included: %d", + error, + llstr(rli->linfo.index_file_offset,buff), + rli->group_relay_log_name, + included); goto err; } + /* - Reset position to current log. This involves setting both of the - position variables: + Reset rli's coordinates to the current log. */ - rli->relay_log_pos = BIN_LOG_HEADER_SIZE; - rli->pending = 0; - strmake(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)-1); + rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); + + /* + If we removed the rli->group_relay_log_name file, + we must update the rli->group* coordinates, otherwise do not touch it as the + group's execution is not finished (e.g. COMMIT not executed) + */ + if (included) + { + rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE; + strmake(rli->group_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->group_relay_log_name)-1); + } /* Store where we are in the new file for the execution thread */ flush_relay_log_info(rli); @@ -693,13 +703,14 @@ err: Update log index_file */ -int MYSQL_LOG::update_log_index(LOG_INFO* log_info) +int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) { if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset)) return LOG_INFO_IO; // now update offsets in index file for running threads - adjust_linfo_offsets(log_info->index_file_start_offset); + if (need_update_threads) + adjust_linfo_offsets(log_info->index_file_start_offset); return 0; } @@ -708,9 +719,13 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info) SYNOPSIS purge_logs() - thd Thread pointer - to_log Delete all log file name before this file. This file is not - deleted + to_log Delete all log file name before this file. + included If true, to_log is deleted too. + need_mutex + need_update_threads If we want to update the log coordinates of + all threads. False for relay logs, true otherwise. + freed_log_space If not null, decrement this variable of + the amount of log space freed NOTES If any of the logs before the deleted one is in use, @@ -722,31 +737,60 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info) LOG_INFO_EOF to_log not found */ -int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) +int MYSQL_LOG::purge_logs(const char *to_log, + bool included, + bool need_mutex, + bool need_update_threads, + ulonglong *decrease_log_space) { int error; + bool exit_loop= 0; LOG_INFO log_info; DBUG_ENTER("purge_logs"); + DBUG_PRINT("info",("to_log= %s",to_log)); if (no_rotate) DBUG_RETURN(LOG_INFO_PURGE_NO_ROTATE); - pthread_mutex_lock(&LOCK_index); + if (need_mutex) + pthread_mutex_lock(&LOCK_index); if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) goto err; /* - File name exists in index file; Delete until we find this file + File name exists in index file; delete until we find this file or a file that is used. */ if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))) goto err; - while (strcmp(to_log,log_info.log_file_name) && - !log_in_use(log_info.log_file_name)) + while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) && + !log_in_use(log_info.log_file_name)) { - /* It's not fatal even if we can't delete a log file */ - my_delete(log_info.log_file_name, MYF(0)); - if (find_next_log(&log_info, 0)) + ulong tmp; + LINT_INIT(tmp); + if (decrease_log_space) //stat the file we want to delete + { + MY_STAT s; + if (my_stat(log_info.log_file_name,&s,MYF(0))) + tmp= s.st_size; + else + { + /* + If we could not stat, we can't know the amount + of space that deletion will free. In most cases, + deletion won't work either, so it's not a problem. + */ + tmp= 0; + } + } + /* + It's not fatal if we can't delete a log file ; + if we could delete it, take its size into account + */ + DBUG_PRINT("info",("purging %s",log_info.log_file_name)); + if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space) + *decrease_log_space-= tmp; + if (find_next_log(&log_info, 0) || exit_loop) break; } @@ -754,10 +798,11 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) If we get killed -9 here, the sysadmin would have to edit the log index file after restart - otherwise, this should be safe */ - error= update_log_index(&log_info); + error= update_log_index(&log_info, need_update_threads); err: - pthread_mutex_unlock(&LOCK_index); + if (need_mutex) + pthread_mutex_unlock(&LOCK_index); DBUG_RETURN(error); } @@ -779,7 +824,7 @@ err: LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated */ -int MYSQL_LOG::purge_logs_before_date(THD* thd, time_t purge_time) +int MYSQL_LOG::purge_logs_before_date(time_t purge_time) { int error; LOG_INFO log_info; @@ -816,7 +861,7 @@ int MYSQL_LOG::purge_logs_before_date(THD* thd, time_t purge_time) If we get killed -9 here, the sysadmin would have to edit the log index file after restart - otherwise, this should be safe */ - error= update_log_index(&log_info); + error= update_log_index(&log_info, 1); err: pthread_mutex_unlock(&LOCK_index); @@ -1269,7 +1314,7 @@ err: { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) - error= purge_logs_before_date(current_thd, purge_time); + error= purge_logs_before_date(purge_time); } #endif @@ -1534,7 +1579,6 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, If you don't do it this way, you will get a deadlock in THD::awake() */ - void MYSQL_LOG:: wait_for_update(THD* thd) { safe_mutex_assert_owner(&LOCK_log); diff --git a/sql/log_event.cc b/sql/log_event.cc index d4efb65bf42..39db264d898 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -310,11 +310,36 @@ int Log_event::exec_event(struct st_relay_log_info* rli) */ if (rli) { - if (rli->inside_transaction) - rli->inc_pending(get_event_len()); + /* + If in a transaction, and if the slave supports transactions, + just inc_event_relay_log_pos(). We only have to check for OPTION_BEGIN + (not OPTION_NOT_AUTOCOMMIT) as transactions are logged + with BEGIN/COMMIT, not with SET AUTOCOMMIT= . + + CAUTION: opt_using_transactions means + innodb || bdb ; suppose the master supports InnoDB and BDB, + but the slave supports only BDB, problems + will arise: + - suppose an InnoDB table is created on the master, + - then it will be MyISAM on the slave + - but as opt_using_transactions is true, the slave will believe he is + transactional with the MyISAM table. And problems will come when one + does START SLAVE; STOP SLAVE; START SLAVE; (the slave will resume at BEGIN + whereas there has not been any rollback). This is the problem of + using opt_using_transactions instead of a finer + "does the slave support _the_transactional_handler_used_on_the_master_". + + More generally, we'll have problems when a query mixes a transactional + handler and MyISAM and STOP SLAVE is issued in the middle of the + "transaction". START SLAVE will resume at BEGIN while the MyISAM table has + already been updated. + + */ + if ((thd->options & OPTION_BEGIN) && opt_using_transactions) + rli->inc_event_relay_log_pos(get_event_len()); else { - rli->inc_pos(get_event_len(),log_pos); + rli->inc_group_relay_log_pos(get_event_len(),log_pos); flush_relay_log_info(rli); } } @@ -878,9 +903,13 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) thd->db = rewrite_db((char*)db); /* - InnoDB internally stores the master log position it has processed so far; - position to store is really pos + pending + event_len - since we must store the pos of the END of the current log event + InnoDB internally stores the master log position it has executed so far, + i.e. the position just after the COMMIT event. + When InnoDB will want to store, the positions in rli won't have + been updated yet, so group_master_log_* will point to old BEGIN + and event_master_log* will point to the beginning of current COMMIT. + So the position to store is event_master_log_pos + event_len + since we must store the pos of the END of the current log event (COMMIT). */ rli->event_len= get_event_len(); @@ -909,18 +938,6 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) DBUG_PRINT("query",("%s",thd->query)); mysql_parse(thd, thd->query, q_len); - /* - Set a flag if we are inside an transaction so that we can restart - the transaction from the start if we are killed - - This will only be done if we are supporting transactional tables - in the slave. - */ - if (!strcmp(thd->query,"BEGIN")) - rli->inside_transaction= opt_using_transactions; - else if (!strcmp(thd->query,"COMMIT")) - rli->inside_transaction=0; - DBUG_PRINT("info",("expected_error: %d last_errno: %d", expected_error, thd->net.last_errno)); if ((expected_error != (actual_error= thd->net.last_errno)) && @@ -1776,14 +1793,15 @@ int Rotate_log_event::write_data(IO_CACHE* file) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int Rotate_log_event::exec_event(struct st_relay_log_info* rli) { - char* log_name = rli->master_log_name; + char* log_name = rli->group_master_log_name; DBUG_ENTER("Rotate_log_event::exec_event"); pthread_mutex_lock(&rli->data_lock); memcpy(log_name, new_log_ident, ident_len+1); - rli->master_log_pos = pos; - rli->relay_log_pos += get_event_len(); - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) rli->master_log_pos)); + rli->group_master_log_pos = pos; + rli->event_relay_log_pos += get_event_len(); + rli->group_relay_log_pos = rli->event_relay_log_pos; + DBUG_PRINT("info", ("group_master_log_pos: %d", (ulong) rli->group_master_log_pos)); pthread_mutex_unlock(&rli->data_lock); pthread_cond_broadcast(&rli->data_cond); flush_relay_log_info(rli); @@ -1905,7 +1923,7 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli) thd->next_insert_id = val; break; } - rli->inc_pending(get_event_len()); + rli->inc_event_relay_log_pos(get_event_len()); return 0; } #endif @@ -1967,7 +1985,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli) { thd->rand.seed1= (ulong) seed1; thd->rand.seed2= (ulong) seed2; - rli->inc_pending(get_event_len()); + rli->inc_event_relay_log_pos(get_event_len()); return 0; } #endif // !MYSQL_CLIENT @@ -2199,7 +2217,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) e.update_hash(val, val_len, type, charset, Item::COER_NOCOLL); free_root(&thd->mem_root,0); - rli->inc_pending(get_event_len()); + rli->inc_event_relay_log_pos(get_event_len()); return 0; } #endif // !MYSQL_CLIENT @@ -2241,7 +2259,7 @@ Slave_log_event::Slave_log_event(THD* thd_arg, pthread_mutex_lock(&mi->data_lock); pthread_mutex_lock(&rli->data_lock); master_host_len = strlen(mi->host); - master_log_len = strlen(rli->master_log_name); + master_log_len = strlen(rli->group_master_log_name); // on OOM, just do not initialize the structure and print the error if ((mem_pool = (char*)my_malloc(get_data_size() + 1, MYF(MY_WME)))) @@ -2249,9 +2267,9 @@ Slave_log_event::Slave_log_event(THD* thd_arg, master_host = mem_pool + SL_MASTER_HOST_OFFSET ; memcpy(master_host, mi->host, master_host_len + 1); master_log = master_host + master_host_len + 1; - memcpy(master_log, rli->master_log_name, master_log_len + 1); + memcpy(master_log, rli->group_master_log_name, master_log_len + 1); master_port = mi->port; - master_pos = rli->master_log_pos; + master_pos = rli->group_master_log_pos; DBUG_PRINT("info", ("master_log: %s pos: %d", master_log, (ulong) master_pos)); } @@ -2381,19 +2399,19 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db) int Stop_log_event::exec_event(struct st_relay_log_info* rli) { // do not clean up immediately after rotate event - if (rli->master_log_pos > BIN_LOG_HEADER_SIZE) + if (rli->group_master_log_pos > BIN_LOG_HEADER_SIZE) { close_temporary_tables(thd); cleanup_load_tmpdir(); } /* We do not want to update master_log pos because we get a rotate event - before stop, so by now master_log_name is set to the next log. + before stop, so by now group_master_log_name is set to the next log. If we updated it, we will have incorrect master coordinates and this could give false triggers in MASTER_POS_WAIT() that we have reached the target position when in fact we have not. */ - rli->inc_pos(get_event_len(), 0); + rli->inc_group_relay_log_pos(get_event_len(), 0); flush_relay_log_info(rli); return 0; } diff --git a/sql/mini_client.cc b/sql/mini_client.cc index db3a51712f2..afcee5fbb02 100644 --- a/sql/mini_client.cc +++ b/sql/mini_client.cc @@ -23,6 +23,7 @@ */ #include <my_global.h> + #ifdef HAVE_EXTERNAL_CLIENT /* my_pthread must be included early to be able to fix things */ @@ -80,8 +81,6 @@ void mc_end_server(MYSQL *mysql); static int mc_sock_connect(File s, const struct sockaddr *name, uint namelen, uint to); static void mc_free_old_query(MYSQL *mysql); static int mc_send_file_to_server(MYSQL *mysql, const char *filename); -static my_ulonglong mc_net_field_length_ll(uchar **packet); -static ulong mc_net_field_length(uchar **packet); static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths); static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, @@ -1085,15 +1084,15 @@ int mc_mysql_read_query_result(MYSQL *mysql) mc_free_old_query(mysql); /* Free old result */ get_info: pos=(uchar*) mysql->net.read_pos; - if ((field_count= mc_net_field_length(&pos)) == 0) + if ((field_count= net_field_length(&pos)) == 0) { - mysql->affected_rows= mc_net_field_length_ll(&pos); - mysql->insert_id= mc_net_field_length_ll(&pos); + mysql->affected_rows= net_field_length_ll(&pos); + mysql->insert_id= net_field_length_ll(&pos); if (mysql->server_capabilities & CLIENT_TRANSACTIONS) { mysql->server_status=uint2korr(pos); pos+=2; } - if (pos < mysql->net.read_pos+length && mc_net_field_length(&pos)) + if (pos < mysql->net.read_pos+length && net_field_length(&pos)) mysql->info=(char*) pos; DBUG_RETURN(0); } @@ -1107,7 +1106,7 @@ get_info: if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) mysql->server_status|= SERVER_STATUS_IN_TRANS; - mysql->extra_info= mc_net_field_length_ll(&pos); /* Maybe number of rec */ + mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ if (!(fields=mc_read_rows(mysql,(MYSQL_FIELD*) 0,5))) DBUG_RETURN(-1); if (!(mysql->fields=mc_unpack_fields(fields,&mysql->field_alloc, @@ -1190,68 +1189,6 @@ err: DBUG_RETURN(result); } - -/* Get the length of next field. Change parameter to point at fieldstart */ -static ulong mc_net_field_length(uchar **packet) -{ - reg1 uchar *pos= *packet; - if (*pos < 251) - { - (*packet)++; - return (ulong) *pos; - } - if (*pos == 251) - { - (*packet)++; - return NULL_LENGTH; - } - if (*pos == 252) - { - (*packet)+=3; - return (ulong) uint2korr(pos+1); - } - if (*pos == 253) - { - (*packet)+=4; - return (ulong) uint3korr(pos+1); - } - (*packet)+=9; /* Must be 254 when here */ - return (ulong) uint4korr(pos+1); -} - -/* Same as above, but returns ulonglong values */ - -static my_ulonglong mc_net_field_length_ll(uchar **packet) -{ - reg1 uchar *pos= *packet; - if (*pos < 251) - { - (*packet)++; - return (my_ulonglong) *pos; - } - if (*pos == 251) - { - (*packet)++; - return (my_ulonglong) NULL_LENGTH; - } - if (*pos == 252) - { - (*packet)+=3; - return (my_ulonglong) uint2korr(pos+1); - } - if (*pos == 253) - { - (*packet)+=4; - return (my_ulonglong) uint3korr(pos+1); - } - (*packet)+=9; /* Must be 254 when here */ -#ifdef NO_CLIENT_LONGLONG - return (my_ulonglong) uint4korr(pos+1); -#else - return (my_ulonglong) uint8korr(pos+1); -#endif -} - /* Read all rows (fields or data) from server */ static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, @@ -1301,7 +1238,7 @@ static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, to= (char*) (cur->data+fields+1); for (field=0 ; field < fields ; field++) { - if ((len=(ulong) mc_net_field_length(&cp)) == NULL_LENGTH) + if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH) { /* null field */ cur->data[field] = 0; } @@ -1342,7 +1279,8 @@ static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, { uint field; ulong pkt_len,len; - uchar *pos,*prev_pos; + uchar *pos; + uchar *prev_pos; if ((pkt_len=mc_net_safe_read(mysql)) == packet_error) return -1; @@ -1352,7 +1290,7 @@ static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, pos=mysql->net.read_pos; for (field=0 ; field < fields ; field++) { - if ((len=(ulong) mc_net_field_length(&pos)) == NULL_LENGTH) + if ((len=(ulong) net_field_length(&pos)) == NULL_LENGTH) { /* null field */ row[field] = 0; *lengths++=0; @@ -1365,7 +1303,7 @@ static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, } if (prev_pos) *prev_pos=0; /* Terminate prev field */ - prev_pos=pos; + prev_pos= pos; } row[field]=(char*) prev_pos+1; /* End of last field */ *prev_pos=0; /* Terminate last field */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4203d440667..4ecccbf4511 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -30,7 +30,7 @@ #undef write /* remove pthread.h macro definition for EMX */ #endif -typedef ulong table_map; /* Used for table bits in join */ +typedef ulonglong table_map; /* Used for table bits in join */ typedef ulong key_map; /* Used for finding keys */ typedef ulong key_part_map; /* Used for finding key parts */ @@ -721,7 +721,7 @@ extern ulong max_binlog_size, rpl_recovery_rank, thread_cache_size; extern ulong com_stat[(uint) SQLCOM_END], com_other, back_log; extern ulong specialflag, current_pid; extern ulong expire_logs_days; - +extern my_bool relay_log_purge; extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version,dropping_tables; extern uint delay_key_write_options; @@ -846,6 +846,7 @@ void end_read_record(READ_RECORD *info); ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, ha_rows max_rows, ha_rows *examined_rows); +void filesort_free_buffers(TABLE *table); void change_double_for_sort(double nr,byte *to); int get_quick_record(SQL_SELECT *select); int calc_weekday(long daynr,bool sunday_first_day_of_week); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ea0311bafc6..6907da7d8e1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -314,7 +314,7 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0; -my_bool opt_console= 0; +my_bool opt_console= 0, opt_bdb, opt_innodb; volatile bool mqh_used = 0; FILE *bootstrap_file=0; @@ -442,6 +442,7 @@ const char **errmesg; /* Error messages */ const char *myisam_recover_options_str="OFF"; const char *sql_mode_str="OFF"; ulong rpl_recovery_rank=0; +my_bool relay_log_purge=1; my_string mysql_unix_port=NULL, opt_mysql_tmpdir=NULL; MY_TMPDIR mysql_tmpdir_list; @@ -2208,7 +2209,7 @@ static int init_server_components() { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) - mysql_bin_log.purge_logs_before_date(current_thd, purge_time); + mysql_bin_log.purge_logs_before_date(purge_time); } #endif } @@ -3400,7 +3401,7 @@ enum options OPT_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, OPT_BDB_HOME, OPT_BDB_LOG, OPT_BDB_TMP, OPT_BDB_NOSYNC, - OPT_BDB_LOCK, OPT_BDB_SKIP, + OPT_BDB_LOCK, OPT_BDB, OPT_BDB_NO_RECOVER, OPT_BDB_SHARED, OPT_MASTER_HOST, OPT_MASTER_USER, OPT_MASTER_PASSWORD, OPT_MASTER_PORT, @@ -3430,7 +3431,7 @@ enum options OPT_INNODB_FLUSH_METHOD, OPT_INNODB_FAST_SHUTDOWN, OPT_SAFE_SHOW_DB, - OPT_INNODB_SKIP, OPT_SKIP_SAFEMALLOC, + OPT_INNODB, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, @@ -3457,7 +3458,7 @@ enum options OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, - OPT_MAX_JOIN_SIZE, OPT_MAX_SORT_LENGTH, + OPT_MAX_JOIN_SIZE, OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_SORT_LENGTH, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, OPT_MAX_ERROR_COUNT, OPT_MAX_PREP_STMT, @@ -3468,7 +3469,7 @@ enum options OPT_OPEN_FILES_LIMIT, OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE, OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER, - OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, + OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_RELAY_LOG_PURGE, OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME, OPT_SORT_BUFFER, OPT_TABLE_CACHE, OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, @@ -3529,8 +3530,10 @@ struct my_option my_long_options[] = (gptr*) &berkeley_tmpdir, (gptr*) &berkeley_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_BERKELEY_DB */ - {"skip-bdb", OPT_BDB_SKIP, "Don't use berkeley db (will save memory)", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb", OPT_BDB, "Enable Berkeley DB (if this version of MySQL supports it). \ +Disable with --skip-bdb (will save memory)", + (gptr*) &opt_bdb, (gptr*) &opt_bdb, 0, GET_BOOL, NO_ARG, 1, 0, 0, + 0, 0, 0}, {"big-tables", OPT_BIG_TABLES, "Allow big result sets by saving all temporary sets on file (Solves most 'table full' errors)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -3885,8 +3888,10 @@ struct my_option my_long_options[] = "Start without grant tables. This gives all users FULL ACCESS to all tables!", (gptr*) &opt_noacl, (gptr*) &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"skip-innodb", OPT_INNODB_SKIP, "Don't use Innodb (will save memory)", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb", OPT_INNODB, "Enable InnoDB (if this version of MySQL supports it). \ +Disable with --skip-innodb (will save memory)", + (gptr*) &opt_innodb, (gptr*) &opt_innodb, 0, GET_BOOL, NO_ARG, 1, 0, 0, + 0, 0, 0}, {"skip-locking", OPT_SKIP_LOCK, "Deprecated option, use --skip-external-locking instead", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -4159,6 +4164,11 @@ struct my_option my_long_options[] = (gptr*) &global_system_variables.max_join_size, (gptr*) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"max_length_for_sort_data", OPT_MAX_LENGTH_FOR_SORT_DATA, + "Max number of bytes in sorted records", + (gptr*) &global_system_variables.max_length_for_sort_data, + (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, + REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, {"max_prepared_statements", OPT_MAX_PREP_STMT, "Max number of prepared_statements for a thread", (gptr*) &global_system_variables.max_prep_stmt_count, @@ -4272,6 +4282,11 @@ struct my_option my_long_options[] = (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, #ifdef HAVE_REPLICATION + {"relay_log_purge", OPT_RELAY_LOG_PURGE, + "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.", + (gptr*) &relay_log_purge, + (gptr*) &relay_log_purge, 0, GET_BOOL, NO_ARG, + 1, 0, 1, 0, 1, 0}, {"relay_log_space_limit", OPT_RELAY_LOG_SPACE_LIMIT, "Max space to use for all relay logs", (gptr*) &relay_log_space_limit, @@ -5016,16 +5031,32 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), berkeley_shared_data=1; break; #endif /* HAVE_BERKELEY_DB */ - case OPT_BDB_SKIP: + case OPT_BDB: #ifdef HAVE_BERKELEY_DB - berkeley_skip=1; - have_berkeley_db=SHOW_OPTION_DISABLED; + if (opt_bdb) + { + berkeley_skip=0; + have_berkeley_db=SHOW_OPTION_YES; + } + else + { + berkeley_skip=1; + have_berkeley_db=SHOW_OPTION_DISABLED; + } #endif break; - case OPT_INNODB_SKIP: + case OPT_INNODB: #ifdef HAVE_INNOBASE_DB - innodb_skip=1; - have_innodb=SHOW_OPTION_DISABLED; + if (opt_innodb) + { + innodb_skip=0; + have_innodb=SHOW_OPTION_YES; + } + else + { + innodb_skip=1; + have_innodb=SHOW_OPTION_DISABLED; + } #endif break; case OPT_INNODB_DATA_FILE_PATH: diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 92bab76bedd..7526772cd09 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -349,13 +349,13 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, select->head=head; select->cond=conds; - if (head->io_cache) + if (head->sort.io_cache) { - select->file= *head->io_cache; + select->file= *head->sort.io_cache; select->records=(ha_rows) (select->file.end_of_file/ head->file->ref_length); - my_free((gptr) (head->io_cache),MYF(0)); - head->io_cache=0; + my_free((gptr) (head->sort.io_cache),MYF(0)); + head->sort.io_cache=0; } DBUG_RETURN(select); } diff --git a/sql/protocol.h b/sql/protocol.h index 89b838ff6e4..dd644afd335 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -22,9 +22,9 @@ class i_string; class THD; -#ifdef EMBEDDED_LIBRARY typedef struct st_mysql_field MYSQL_FIELD; -#endif +typedef struct st_mysql_rows MYSQL_ROWS; + class Protocol { protected: @@ -135,6 +135,28 @@ public: virtual bool store(Field *field); }; +class Protocol_cursor :public Protocol_simple +{ +public: + MEM_ROOT *alloc; + MYSQL_FIELD *fields; + MYSQL_ROWS *data; + MYSQL_ROWS **prev_record; + ulong row_count; + + Protocol_cursor() {} + Protocol_cursor(THD *thd, MEM_ROOT *ini_alloc) :Protocol_simple(thd), alloc(ini_alloc) {} + bool prepare_for_send(List<Item> *item_list) + { + fields= NULL; + data= NULL; + prev_record= &data; + return Protocol_simple::prepare_for_send(item_list); + } + bool send_fields(List<Item> *list, uint flag); + bool write(); +}; + void send_warning(THD *thd, uint sql_errno, const char *err=0); void net_printf(THD *thd,uint sql_errno, ...); void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc new file mode 100644 index 00000000000..19e3bb06d74 --- /dev/null +++ b/sql/protocol_cursor.cc @@ -0,0 +1,143 @@ +/* Copyright (C) 2000-2003 MySQL 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 */ + +/* + Low level functions for storing data to be send to the MySQL client + The actual communction is handled by the net_xxx functions in net_serv.cc +*/ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include <mysql.h> + +bool Protocol_cursor::send_fields(List<Item> *list, uint flag) +{ + List_iterator_fast<Item> it(*list); + Item *item; + MYSQL_FIELD *field, *client_field; + + DBUG_ENTER("send_fields"); + if (prepare_for_send(list)) + return FALSE; + + fields= (MYSQL_FIELD *)alloc_root(alloc, sizeof(MYSQL_FIELD) * field_count); + if (!fields) + goto err; + + client_field= fields; + while ((item= it++)) + { + Send_field server_field; + item->make_field(&server_field); + + client_field->db= strdup_root(alloc, server_field.db_name); + client_field->table= strdup_root(alloc, server_field.table_name); + client_field->name= strdup_root(alloc, server_field.col_name); + client_field->org_table= strdup_root(alloc, server_field.org_table_name); + client_field->org_name= strdup_root(alloc, server_field.org_col_name); + client_field->length= server_field.length; + client_field->type= server_field.type; + client_field->flags= server_field.flags; + client_field->decimals= server_field.decimals; + client_field->db_length= strlen(client_field->db); + client_field->table_length= strlen(client_field->table); + client_field->name_length= strlen(client_field->name); + client_field->org_name_length= strlen(client_field->org_name); + client_field->org_table_length= strlen(client_field->org_table); + client_field->charsetnr= server_field.charsetnr; + + if (INTERNAL_NUM_FIELD(client_field)) + client_field->flags|= NUM_FLAG; + + if (flag & 2) + { + char buff[80]; + String tmp(buff, sizeof(buff), default_charset_info), *res; + + if (!(res=item->val_str(&tmp))) + client_field->def= strdup_root(alloc, ""); + else + client_field->def= strdup_root(alloc, tmp.ptr()); + } + else + client_field->def=0; + client_field->max_length= 0; + ++client_field; + } + + DBUG_RETURN(FALSE); + err: + send_error(thd, ER_OUT_OF_RESOURCES); /* purecov: inspected */ + DBUG_RETURN(TRUE); /* purecov: inspected */ +} + +/* Get the length of next field. Change parameter to point at fieldstart */ +bool Protocol_cursor::write() +{ + byte *cp= (byte *)packet->ptr(); + byte *end_pos= (byte *)packet->ptr() + packet->length(); + ulong len; + MYSQL_FIELD *cur_field= fields; + MYSQL_FIELD *fields_end= fields + field_count; + MYSQL_ROWS *new_record; + byte **data; + byte *to; + + new_record= (MYSQL_ROWS *)alloc_root(alloc, + sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length()); + if (!new_record) + goto err; + data= (byte **)(new_record + 1); + new_record->data= (char **)data; + + to= (byte *)(fields + field_count + 1); + + for (; cur_field < fields_end; ++cur_field, ++data) + { + if ((len=net_field_length((uchar **)&cp))) + { + *data= 0; + } + else + { + if ((long)len > (end_pos - cp)) + { +// TODO error signal send_error(thd, CR_MALFORMED_PACKET); + return TRUE; + } + memcpy(to,(char*) cp,len); + to[len]=0; + to+=len+1; + cp+=len; + if (cur_field->max_length < len) + cur_field->max_length=len; + } + } + + *prev_record= new_record; + prev_record= &new_record->next; + new_record->next= NULL; + row_count++; + return FALSE; + err: +// TODO error signal send_error(thd, ER_OUT_OF_RESOURCES); + return TRUE; +} + + diff --git a/sql/records.cc b/sql/records.cc index 22c4d54550c..9d8627bc1fc 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -1,3 +1,4 @@ + /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify @@ -22,6 +23,8 @@ static int rr_quick(READ_RECORD *info); static int rr_sequential(READ_RECORD *info); static int rr_from_tempfile(READ_RECORD *info); +static int rr_unpack_from_tempfile(READ_RECORD *info); +static int rr_unpack_from_buffer(READ_RECORD *info); static int rr_from_pointers(READ_RECORD *info); static int rr_from_cache(READ_RECORD *info); static int init_rr_cache(READ_RECORD *info); @@ -41,8 +44,16 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, info->table=table; info->file= table->file; info->forms= &info->table; /* Only one table */ - info->record=table->record[0]; - info->ref_length=table->file->ref_length; + if (table->sort.addon_field) + { + info->rec_buf= table->sort.addon_buf; + info->ref_length= table->sort.addon_length; + } + else + { + info->record= table->record[0]; + info->ref_length= table->file->ref_length; + } info->select=select; info->print_error=print_error; info->ignore_not_found_rows= 0; @@ -51,11 +62,12 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (select && my_b_inited(&select->file)) tempfile= &select->file; else - tempfile= table->io_cache; + tempfile= table->sort.io_cache; if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used { DBUG_PRINT("info",("using rr_from_tempfile")); - info->read_record=rr_from_tempfile; + info->read_record= (table->sort.addon_field ? + rr_unpack_from_tempfile : rr_from_tempfile); info->io_cache=tempfile; reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0); info->ref_pos=table->file->ref; @@ -85,13 +97,15 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, DBUG_PRINT("info",("using rr_quick")); info->read_record=rr_quick; } - else if (table->record_pointers) + else if (table->sort.record_pointers) { DBUG_PRINT("info",("using record_pointers")); table->file->rnd_init(0); - info->cache_pos=table->record_pointers; - info->cache_end=info->cache_pos+ table->found_records*info->ref_length; - info->read_record= rr_from_pointers; + info->cache_pos=table->sort.record_pointers; + info->cache_end=info->cache_pos+ + table->sort.found_records*info->ref_length; + info->read_record= (table->sort.addon_field ? + rr_unpack_from_buffer : rr_from_pointers); } else { @@ -112,7 +126,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, void end_read_record(READ_RECORD *info) -{ /* free cache if used */ +{ /* free cache if used */ if (info->cache) { my_free_lock((char*) info->cache,MYF(0)); @@ -120,6 +134,7 @@ void end_read_record(READ_RECORD *info) } if (info->table) { + filesort_free_buffers(info->table); (void) info->file->extra(HA_EXTRA_NO_CACHE); (void) info->file->rnd_end(); info->table=0; @@ -200,6 +215,34 @@ tryNext: } /* rr_from_tempfile */ +/* + Read a result set record from a temporary file after sorting + + SYNOPSIS + rr_unpack_from_tempfile() + info Reference to the context including record descriptors + + DESCRIPTION + The function first reads the next sorted record from the temporary file. + into a buffer. If a success it calls a callback function that unpacks + the fields values use in the result set from this buffer into their + positions in the regular record buffer. + + RETURN + 0 - Record successfully read. + -1 - There is no record to be read anymore. +*/ + +static int rr_unpack_from_tempfile(READ_RECORD *info) +{ + if (my_b_read(info->io_cache, info->rec_buf, info->ref_length)) + return -1; + TABLE *table= info->table; + (*table->sort.unpack)(table->sort.addon_field, info->rec_buf); + + return 0; +} + static int rr_from_pointers(READ_RECORD *info) { int tmp; @@ -228,6 +271,34 @@ tryNext: return tmp; } +/* + Read a result set record from a buffer after sorting + + SYNOPSIS + rr_unpack_from_buffer() + info Reference to the context including record descriptors + + DESCRIPTION + The function first reads the next sorted record from the sort buffer. + If a success it calls a callback function that unpacks + the fields values use in the result set from this buffer into their + positions in the regular record buffer. + + RETURN + 0 - Record successfully read. + -1 - There is no record to be read anymore. +*/ + +static int rr_unpack_from_buffer(READ_RECORD *info) +{ + if (info->cache_pos == info->cache_end) + return -1; /* End of buffer */ + TABLE *table= info->table; + (*table->sort.unpack)(table->sort.addon_field, info->cache_pos); + info->cache_pos+= info->ref_length; + + return 0; +} /* cacheing of records from a database */ static int init_rr_cache(READ_RECORD *info) diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 46791c13219..58769827bed 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -873,7 +873,6 @@ int load_master_data(THD* thd) // don't hit the magic number if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE) active_mi->master_log_pos = BIN_LOG_HEADER_SIZE; - active_mi->rli.pending = 0; flush_master_info(active_mi); } mc_mysql_free_result(master_status_res); @@ -897,9 +896,13 @@ int load_master_data(THD* thd) return 1; } pthread_mutex_lock(&active_mi->rli.data_lock); - active_mi->rli.master_log_pos = active_mi->master_log_pos; - strmake(active_mi->rli.master_log_name,active_mi->master_log_name, - sizeof(active_mi->rli.master_log_name)-1); + active_mi->rli.group_master_log_pos = active_mi->master_log_pos; + strmake(active_mi->rli.group_master_log_name,active_mi->master_log_name, + sizeof(active_mi->rli.group_master_log_name)-1); + /* + No need to update rli.event* coordinates, they will be when the slave + threads start ; only rli.group* coordinates are necessary here. + */ flush_relay_log_info(&active_mi->rli); pthread_cond_broadcast(&active_mi->rli.data_cond); pthread_mutex_unlock(&active_mi->rli.data_lock); diff --git a/sql/set_var.cc b/sql/set_var.cc index d39a506c82d..e4adbb0a318 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -167,6 +167,8 @@ sys_var_thd_ulong sys_pseudo_thread_id("pseudo_thread_id", sys_var_thd_ha_rows sys_max_join_size("max_join_size", &SV::max_join_size, fix_max_join_size); +sys_var_thd_ulong sys_max_length_for_sort_data("max_length_for_sort_data", + &SV::max_length_for_sort_data); #ifndef TO_BE_DELETED /* Alias for max_join_size */ sys_var_thd_ha_rows sys_sql_max_join_size("sql_max_join_size", &SV::max_join_size, @@ -201,6 +203,10 @@ sys_var_thd_ulong sys_read_buff_size("read_buffer_size", &SV::read_buff_size); sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size", &SV::read_rnd_buff_size); +#ifdef HAVE_REPLICATION +sys_var_bool_ptr sys_relay_log_purge("relay_log_purge", + &relay_log_purge); +#endif sys_var_long_ptr sys_rpl_recovery_rank("rpl_recovery_rank", &rpl_recovery_rank); sys_var_long_ptr sys_query_cache_size("query_cache_size", @@ -381,6 +387,7 @@ sys_var *sys_variables[]= &sys_max_error_count, &sys_max_heap_table_size, &sys_max_join_size, + &sys_max_length_for_sort_data, &sys_max_prep_stmt_count, &sys_max_sort_length, &sys_max_tmp_tables, @@ -407,6 +414,9 @@ sys_var *sys_variables[]= &sys_rand_seed2, &sys_read_buff_size, &sys_read_rnd_buff_size, +#ifdef HAVE_REPLICATION + &sys_relay_log_purge, +#endif &sys_rpl_recovery_rank, &sys_safe_updates, &sys_select_limit, @@ -534,6 +544,9 @@ struct show_var_st init_vars[]= { {sys_max_delayed_threads.name,(char*) &sys_max_delayed_threads, SHOW_SYS}, {sys_max_heap_table_size.name,(char*) &sys_max_heap_table_size, SHOW_SYS}, {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS}, + {sys_max_length_for_sort_data.name, + (char*) &sys_max_length_for_sort_data, + SHOW_SYS}, {sys_max_prep_stmt_count.name,(char*) &sys_max_prep_stmt_count, SHOW_SYS}, {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS}, @@ -563,6 +576,9 @@ struct show_var_st init_vars[]= { {sys_pseudo_thread_id.name, (char*) &sys_pseudo_thread_id, SHOW_SYS}, {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS}, {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS}, +#ifdef HAVE_REPLICATION + {sys_relay_log_purge.name, (char*) &sys_relay_log_purge, SHOW_SYS}, +#endif {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, #ifdef HAVE_QUERY_CACHE {sys_query_cache_limit.name,(char*) &sys_query_cache_limit, SHOW_SYS}, diff --git a/sql/slave.cc b/sql/slave.cc index fa7ab180eaa..eae795ae760 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -217,11 +217,7 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len, - If not, open the 'log' binary file. TODO - - check proper initialization of master_log_name/master_log_pos - - We may always want to delete all logs before 'log'. - Currently if we are not calling this with 'log' as NULL or the first - log we will never delete relay logs. - If we want this we should not set skip_log_purge to 1. + - check proper initialization of group_master_log_name/group_master_log_pos RETURN VALUES 0 ok @@ -248,7 +244,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, rli->cur_log_fd = -1; } - rli->relay_log_pos = pos; + rli->group_relay_log_pos = rli->event_relay_log_pos = pos; /* Test to see if the previous run was with the skip of purging @@ -260,18 +256,15 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, goto err; } - if (log) // If not first log + if (log && rli->relay_log.find_log_pos(&rli->linfo, log, 1)) { - if (strcmp(log, rli->linfo.log_file_name)) - rli->skip_log_purge= 1; // Different name; Don't purge - if (rli->relay_log.find_log_pos(&rli->linfo, log, 1)) - { - *errmsg="Could not find target log during relay log initialization"; - goto err; - } + *errmsg="Could not find target log during relay log initialization"; + goto err; } - strmake(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)-1); + strmake(rli->group_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->group_relay_log_name)-1); + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); if (rli->relay_log.is_active(rli->linfo.log_file_name)) { /* @@ -302,7 +295,7 @@ err: If we don't purge, we can't honour relay_log_space_limit ; silently discard it */ - if (rli->skip_log_purge) + if (!relay_log_purge) rli->log_space_limit= 0; pthread_cond_broadcast(&rli->data_cond); if (need_data_lock) @@ -383,9 +376,8 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, to display fine in any case. */ - rli->master_log_name[0]= 0; - rli->master_log_pos= 0; - rli->pending= 0; + rli->group_master_log_name[0]= 0; + rli->group_master_log_pos= 0; if (!rli->inited) DBUG_RETURN(0); @@ -402,16 +394,18 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, goto err; } /* Save name of used relay log file */ - strmake(rli->relay_log_name, rli->relay_log.get_log_fname(), - sizeof(rli->relay_log_name)-1); + strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->group_relay_log_name)-1); + strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->event_relay_log_name)-1); // Just first log with magic number and nothing else rli->log_space_total= BIN_LOG_HEADER_SIZE; - rli->relay_log_pos= BIN_LOG_HEADER_SIZE; + rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; rli->relay_log.reset_bytes_written(); if (!just_reset) - error= init_relay_log_pos(rli, rli->relay_log_name, rli->relay_log_pos, - 0 /* do not need data lock */, errmsg); - + error= init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos, + 0 /* do not need data lock */, errmsg); + err: #ifndef DBUG_OFF char buf[22]; @@ -1238,13 +1232,11 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) fn_format(fname, info_fname, mysql_data_home, "", 4+32); pthread_mutex_lock(&rli->data_lock); info_fd = rli->info_fd; - rli->pending = 0; rli->cur_log_fd = -1; rli->slave_skip_counter=0; rli->abort_pos_wait=0; - rli->skip_log_purge=0; - rli->log_space_limit = relay_log_space_limit; - rli->log_space_total = 0; + rli->log_space_limit= relay_log_space_limit; + rli->log_space_total= 0; // TODO: make this work with multi-master if (!opt_relay_logname) @@ -1285,8 +1277,8 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, &msg)) goto err; - rli->master_log_name[0]= 0; - rli->master_log_pos= 0; + rli->group_master_log_name[0]= 0; + rli->group_master_log_pos= 0; rli->info_fd= info_fd; } else // file exists @@ -1307,31 +1299,33 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) rli->info_fd = info_fd; int relay_log_pos, master_log_pos; - if (init_strvar_from_file(rli->relay_log_name, - sizeof(rli->relay_log_name), &rli->info_file, + if (init_strvar_from_file(rli->group_relay_log_name, + sizeof(rli->group_relay_log_name), &rli->info_file, "") || init_intvar_from_file(&relay_log_pos, &rli->info_file, BIN_LOG_HEADER_SIZE) || - init_strvar_from_file(rli->master_log_name, - sizeof(rli->master_log_name), &rli->info_file, + init_strvar_from_file(rli->group_master_log_name, + sizeof(rli->group_master_log_name), &rli->info_file, "") || init_intvar_from_file(&master_log_pos, &rli->info_file, 0)) { msg="Error reading slave log configuration"; goto err; } - rli->relay_log_pos= relay_log_pos; - rli->master_log_pos= master_log_pos; + strmake(rli->event_relay_log_name,rli->group_relay_log_name, + sizeof(rli->event_relay_log_name)-1); + rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos; + rli->group_master_log_pos= master_log_pos; if (init_relay_log_pos(rli, - rli->relay_log_name, - rli->relay_log_pos, + rli->group_relay_log_name, + rli->group_relay_log_pos, 0 /* no data lock*/, &msg)) goto err; } - DBUG_ASSERT(rli->relay_log_pos >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); /* Now change the cache from READ to WRITE - must do this before flush_relay_log_info @@ -1407,7 +1401,7 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) { LOG_INFO linfo; DBUG_ENTER("count_relay_log_space"); - rli->log_space_total = 0; + rli->log_space_total= 0; if (rli->relay_log.find_log_pos(&linfo, NullS, 1)) { sql_print_error("Could not find first log while counting relay log space"); @@ -1631,10 +1625,10 @@ int show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((uint32) mi->connect_retry); protocol->store(mi->master_log_name, &my_charset_bin); protocol->store((ulonglong) mi->master_log_pos); - protocol->store(mi->rli.relay_log_name + - dirname_length(mi->rli.relay_log_name), &my_charset_bin); - protocol->store((ulonglong) mi->rli.relay_log_pos); - protocol->store(mi->rli.master_log_name, &my_charset_bin); + protocol->store(mi->rli.group_relay_log_name + + dirname_length(mi->rli.group_relay_log_name), &my_charset_bin); + protocol->store((ulonglong) mi->rli.group_relay_log_pos); + protocol->store(mi->rli.group_master_log_name, &my_charset_bin); protocol->store(mi->slave_running ? "Yes":"No", &my_charset_bin); protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin); protocol->store(&replicate_do_db); @@ -1642,7 +1636,7 @@ int show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((uint32) mi->rli.last_slave_errno); protocol->store(mi->rli.last_slave_error, &my_charset_bin); protocol->store((uint32) mi->rli.slave_skip_counter); - protocol->store((ulonglong) mi->rli.master_log_pos); + protocol->store((ulonglong) mi->rli.group_master_log_pos); protocol->store((ulonglong) mi->rli.log_space_total); pthread_mutex_unlock(&mi->rli.data_lock); pthread_mutex_unlock(&mi->data_lock); @@ -1673,17 +1667,15 @@ bool flush_master_info(MASTER_INFO* mi) st_relay_log_info::st_relay_log_info() - :info_fd(-1), cur_log_fd(-1), master_log_pos(0), save_temporary_tables(0), - cur_log_old_open_count(0), log_space_total(0), ignore_log_space_limit(0), - slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), - sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0), - slave_running(0), skip_log_purge(0), - inside_transaction(0) /* the default is autocommit=1 */ -{ - relay_log_name[0] = master_log_name[0] = 0; + :info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), + cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), + ignore_log_space_limit(0), slave_skip_counter(0), abort_pos_wait(0), + slave_run_id(0), sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0), + slave_running(0) +{ + group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; last_slave_error[0]=0; - bzero(&info_file,sizeof(info_file)); bzero(&cache_buf, sizeof(cache_buf)); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); @@ -1745,8 +1737,8 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, set_timespec(abstime,timeout); DBUG_ENTER("wait_for_pos"); - DBUG_PRINT("enter",("master_log_name: '%s' pos: %lu timeout: %ld", - master_log_name, (ulong) master_log_pos, + DBUG_PRINT("enter",("group_master_log_name: '%s' pos: %lu timeout: %ld", + group_master_log_name, (ulong) group_master_log_pos, (long) timeout)); pthread_mutex_lock(&data_lock); @@ -1796,10 +1788,10 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, { bool pos_reached; int cmp_result= 0; - DBUG_ASSERT(*master_log_name || master_log_pos == 0); - if (*master_log_name) + DBUG_ASSERT(*group_master_log_name || group_master_log_pos == 0); + if (*group_master_log_name) { - char *basename= master_log_name + dirname_length(master_log_name); + char *basename= group_master_log_name + dirname_length(group_master_log_name); /* First compare the parts before the extension. Find the dot in the master's log basename, @@ -1814,13 +1806,13 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, } // Now compare extensions. char *q_end; - ulong master_log_name_extension= strtoul(q, &q_end, 10); - if (master_log_name_extension < log_name_extension) + ulong group_master_log_name_extension= strtoul(q, &q_end, 10); + if (group_master_log_name_extension < log_name_extension) cmp_result = -1 ; else - cmp_result= (master_log_name_extension > log_name_extension) ? 1 : 0 ; + cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ; } - pos_reached = ((!cmp_result && master_log_pos >= (ulonglong)log_pos) || + pos_reached = ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) || cmp_result > 0); if (pos_reached || thd->killed) break; @@ -2127,7 +2119,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) (rli->slave_skip_counter && type_code != ROTATE_EVENT)) { /* TODO: I/O thread should not even log events with the same server id */ - rli->inc_pos(ev->get_event_len(), + rli->inc_group_relay_log_pos(ev->get_event_len(), type_code != STOP_EVENT ? ev->log_pos : LL(0), 1/* skip lock*/); flush_relay_log_info(rli); @@ -2497,15 +2489,13 @@ slave_begin: rli->abort_slave = 0; pthread_mutex_unlock(&rli->run_lock); pthread_cond_broadcast(&rli->start_cond); - // This should always be set to 0 when the slave thread is started - rli->pending = 0; //tell the I/O thread to take relay_log_space_limit into account from now on rli->ignore_log_space_limit= 0; if (init_relay_log_pos(rli, - rli->relay_log_name, - rli->relay_log_pos, + rli->group_relay_log_name, + rli->group_relay_log_pos, 1 /*need data lock*/, &errmsg)) { sql_print_error("Error initializing relay log position: %s", @@ -2513,18 +2503,18 @@ slave_begin: goto err; } THD_CHECK_SENTRY(thd); - DBUG_ASSERT(rli->relay_log_pos >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); DBUG_ASSERT(rli->sql_thd == thd); DBUG_PRINT("master_info",("log_file_name: %s position: %s", - rli->master_log_name, - llstr(rli->master_log_pos,llbuff))); + rli->group_master_log_name, + llstr(rli->group_master_log_pos,llbuff))); if (global_system_variables.log_warnings) sql_print_error("Slave SQL thread initialized, starting replication in \ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, - llstr(rli->master_log_pos,llbuff),rli->relay_log_name, - llstr(rli->relay_log_pos,llbuff1)); + llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name, + llstr(rli->group_relay_log_pos,llbuff1)); /* Read queries from the IO/THREAD until this thread is killed */ @@ -2541,7 +2531,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, Error running query, slave SQL thread aborted. Fix the problem, and restart \ the slave SQL thread with \"SLAVE START\". We stopped at log \ '%s' position %s", - RPL_LOG_NAME, llstr(rli->master_log_pos, llbuff)); + RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff)); goto err; } } @@ -2549,7 +2539,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ /* Thread stopped. Print the current replication position to the log */ sql_print_error("Slave SQL thread exiting, replication stopped in log \ '%s' at position %s", - RPL_LOG_NAME, llstr(rli->master_log_pos,llbuff)); + RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff)); err: VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -2699,7 +2689,7 @@ err: rev The rotate log event read from the binary log DESCRIPTION - Updates the master info and relay data with the place in the next binary + Updates the master info with the place in the next binary log where we should start reading. NOTES @@ -3073,18 +3063,14 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli) IO_CACHE *file = &rli->info_file; char buff[FN_REFLEN*2+22*2+4], *pos; - /* sql_thd is not set when calling from init_slave() */ - if ((rli->sql_thd && rli->sql_thd->options & OPTION_BEGIN)) - return 0; // Wait for COMMIT - my_b_seek(file, 0L); - pos=strmov(buff, rli->relay_log_name); + pos=strmov(buff, rli->group_relay_log_name); *pos++='\n'; - pos=longlong2str(rli->relay_log_pos, pos, 10); + pos=longlong2str(rli->group_relay_log_pos, pos, 10); *pos++='\n'; - pos=strmov(pos, rli->master_log_name); + pos=strmov(pos, rli->group_master_log_name); *pos++='\n'; - pos=longlong2str(rli->master_log_pos, pos, 10); + pos=longlong2str(rli->group_master_log_pos, pos, 10); *pos='\n'; if (my_b_write(file, (byte*) buff, (ulong) (pos-buff)+1)) error=1; @@ -3107,7 +3093,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) DBUG_ENTER("reopen_relay_log"); IO_CACHE *cur_log = rli->cur_log=&rli->cache_buf; - if ((rli->cur_log_fd=open_binlog(cur_log,rli->relay_log_name, + if ((rli->cur_log_fd=open_binlog(cur_log,rli->event_relay_log_name, errmsg)) <0) DBUG_RETURN(0); /* @@ -3115,7 +3101,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) relay_log_pos Current log pos pending Number of bytes already processed from the event */ - my_b_seek(cur_log,rli->relay_log_pos + rli->pending); + my_b_seek(cur_log,rli->event_relay_log_pos); DBUG_RETURN(cur_log); } @@ -3124,7 +3110,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) { Log_event* ev; IO_CACHE* cur_log = rli->cur_log; - pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); + pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); const char* errmsg=0; THD* thd = rli->sql_thd; DBUG_ENTER("next_event"); @@ -3173,7 +3159,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) } } DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(cur_log) == rli->relay_log_pos + rli->pending); + DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos); /* Relay log is always in new format - if the master is 3.23, the I/O thread will convert the format for us @@ -3240,8 +3226,8 @@ Log_event* next_event(RELAY_LOG_INFO* rli) // prevent the I/O thread from blocking next times rli->ignore_log_space_limit= 1; // If the I/O thread is blocked, unblock it - pthread_cond_broadcast(&rli->log_space_cond); pthread_mutex_unlock(&rli->log_space_lock); + pthread_cond_broadcast(&rli->log_space_cond); // Note that wait_for_update unlocks lock_log ! rli->relay_log.wait_for_update(rli->sql_thd); // re-acquire data lock since we released it earlier @@ -3258,16 +3244,25 @@ Log_event* next_event(RELAY_LOG_INFO* rli) my_close(rli->cur_log_fd, MYF(MY_WME)); rli->cur_log_fd = -1; - /* - TODO: make skip_log_purge a start-up option. At this point this - is not critical priority - */ - if (!rli->skip_log_purge) + if (relay_log_purge) { - // purge_first_log will properly set up relay log coordinates in rli - if (rli->relay_log.purge_first_log(rli)) + /* + purge_first_log will properly set up relay log coordinates in rli. + If the group's coordinates are equal to the event's coordinates + (i.e. the relay log was not rotated in the middle of a group), + we can purge this relay log too. + We do ulonglong and string comparisons, this may be slow but + - purging the last relay log is nice (it can save 1GB of disk), so we + like to detect the case where we can do it, and given this, + - I see no better detection method + - purge_first_log is not called that often + */ + if (rli->relay_log.purge_first_log + (rli, + rli->group_relay_log_pos == rli->event_relay_log_pos + && !strcmp(rli->group_relay_log_name,rli->event_relay_log_name))) { - errmsg = "Error purging processed log"; + errmsg = "Error purging processed logs"; goto err; } } @@ -3285,10 +3280,9 @@ Log_event* next_event(RELAY_LOG_INFO* rli) errmsg = "error switching to the next log"; goto err; } - rli->relay_log_pos = BIN_LOG_HEADER_SIZE; - rli->pending=0; - strmake(rli->relay_log_name,rli->linfo.log_file_name, - sizeof(rli->relay_log_name)-1); + rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); flush_relay_log_info(rli); } @@ -3336,7 +3330,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) event(errno: %d cur_log->error: %d)", my_errno,cur_log->error); // set read position to the beginning of the event - my_b_seek(cur_log,rli->relay_log_pos+rli->pending); + my_b_seek(cur_log,rli->event_relay_log_pos); /* otherwise, we have had a partial read */ errmsg = "Aborting slave SQL thread because of partial event read"; break; // To end of function diff --git a/sql/slave.h b/sql/slave.h index a4db7388be5..16ba7f80471 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -92,12 +92,6 @@ typedef struct st_relay_log_info cur_log_fd - file descriptor of the current read relay log */ File info_fd,cur_log_fd; - /* name of current read relay log */ - char relay_log_name[FN_REFLEN]; - /* master log name corresponding to current read position */ - char master_log_name[FN_REFLEN]; - /* original log position of last processed event */ - volatile my_off_t master_log_pos; /* Protected with internal locks. @@ -142,20 +136,36 @@ typedef struct st_relay_log_info uint32 cur_log_old_open_count; /* - relay_log_pos - Current offset in the relay log. - pending - In some cases we do not increment offset immediately - after processing an event, because the following event - needs to be processed atomically together with this one - such as: - - Intvar_event - sets auto_increment value - Rand_event - sets the random seed - - However, once both events have been processed, we need to - increment by the cumulative offset. 'pending' stores the - extra offset to be added to the position. + Let's call a group (of events) : + - a transaction + or + - an autocommiting query + its associated events (INSERT_ID, + TIMESTAMP...) + We need these rli coordinates : + - relay log name and position of the beginning of the group we currently are + executing. Needed to know where we have to restart when replication has + stopped in the middle of a group (which has been rolled back by the slave). + - relay log name and position just after the event we have just + executed. This event is part of the current group. + Formerly we only had the immediately above coordinates, plus a 'pending' + variable, but this dealt wrong with the case of a transaction starting on a + relay log and finishing (commiting) on another relay log. Case which can + happen when, for example, the relay log gets rotated because of + max_binlog_size. + */ + char group_relay_log_name[FN_REFLEN]; + ulonglong group_relay_log_pos; + char event_relay_log_name[FN_REFLEN]; + ulonglong event_relay_log_pos; + /* + Original log name and position of the group we're currently executing + (whose coordinates are group_relay_log_name/pos in the relay log) + in the master's binlog. These concern the *group*, because in the master's + binlog the log_pos that comes with each event is the position of the + beginning of the group. */ - ulonglong relay_log_pos, pending; + char group_master_log_name[FN_REFLEN]; + volatile my_off_t group_master_log_pos; /* Handling of the relay_log_space_limit optional constraint. @@ -193,38 +203,39 @@ typedef struct st_relay_log_info /* if not set, the value of other members of the structure are undefined */ bool inited; volatile bool abort_slave, slave_running; - bool skip_log_purge; - bool inside_transaction; st_relay_log_info(); ~st_relay_log_info(); - inline void inc_pending(ulonglong val) + + inline void inc_event_relay_log_pos(ulonglong val) { - pending += val; + event_relay_log_pos+= val; } - /* TODO: this probably needs to be fixed */ - inline void inc_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0) + + void inc_group_relay_log_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0) { if (!skip_lock) pthread_mutex_lock(&data_lock); - relay_log_pos += val+pending; - pending = 0; - if (log_pos) - master_log_pos = log_pos+ val; + inc_event_relay_log_pos(val); + group_relay_log_pos= event_relay_log_pos; + strmake(group_relay_log_name,event_relay_log_name, + sizeof(group_relay_log_name)-1); + /* + If the slave does not support transactions and replicates a transaction, + users should not trust group_master_log_pos (which they can display with + SHOW SLAVE STATUS or read from relay-log.info), because to compute + group_master_log_pos the slave relies on log_pos stored in the master's + binlog, but if we are in a master's transaction these positions are always + the BEGIN's one (excepted for the COMMIT), so group_master_log_pos does + not advance as it should on the non-transactional slave (it advances by + big leaps, whereas it should advance by small leaps). + */ + if (log_pos) // 3.23 binlogs don't have log_posx + group_master_log_pos= log_pos+ val; pthread_cond_broadcast(&data_cond); if (!skip_lock) pthread_mutex_unlock(&data_lock); } - /* - thread safe read of position - not needed if we are in the slave thread, - but required otherwise as var is a longlong - */ - inline void read_pos(ulonglong& var) - { - pthread_mutex_lock(&data_lock); - var = relay_log_pos; - pthread_mutex_unlock(&data_lock); - } int wait_for_pos(THD* thd, String* log_name, longlong log_pos, longlong timeout); @@ -334,7 +345,7 @@ typedef struct st_table_rule_ent #define TABLE_RULE_ARR_SIZE 16 #define MAX_SLAVE_ERRMSG 1024 -#define RPL_LOG_NAME (rli->master_log_name[0] ? rli->master_log_name :\ +#define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\ "FIRST") #define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\ "FIRST") diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4bed99489de..5ac52032a5f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1281,7 +1281,7 @@ static bool update_user_table(THD *thd, const char *host, const char *user, my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */ DBUG_RETURN(1); /* purecov: deadcode */ } - store_record(table,1); + store_record(table,record[1]); table->field[2]->store(new_password,(uint) strlen(new_password), &my_charset_latin1); if ((error=table->file->update_row(table->record[1],table->record[0]))) { @@ -1372,7 +1372,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, goto end; } old_row_exists = 0; - restore_record(table,2); // cp empty row from record[2] + restore_record(table,default_values); // cp empty row from default_values table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1); @@ -1380,7 +1380,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, else { old_row_exists = 1; - store_record(table,1); // Save copy for update + store_record(table,record[1]); // Save copy for update if (combo.password.str) // If password given table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1); } @@ -1455,7 +1455,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, We should NEVER delete from the user table, as a uses can still use mysqld even if he doesn't have any privileges in the user table! */ - if (cmp_record(table,1) && + if (cmp_record(table,record[1]) && (error=table->file->update_row(table->record[1],table->record[0]))) { // This should never happen table->file->print_error(error,MYF(0)); /* purecov: deadcode */ @@ -1539,7 +1539,7 @@ static int replace_db_table(TABLE *table, const char *db, goto abort; } old_row_exists = 0; - restore_record(table,2); // cp empty row from record[2] + restore_record(table,default_values); // cp empty row from default_values table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1); table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); @@ -1547,7 +1547,7 @@ static int replace_db_table(TABLE *table, const char *db, else { old_row_exists = 1; - store_record(table,1); + store_record(table,record[1]); } store_rights=get_rights_for_db(rights); @@ -1827,7 +1827,7 @@ static int replace_column_table(GRANT_TABLE *g_t, continue; /* purecov: inspected */ } old_row_exists = 0; - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record key_restore(table,key,0,key_length); table->field[4]->store(xx->column.ptr(),xx->column.length(), &my_charset_latin1); } @@ -1841,7 +1841,7 @@ static int replace_column_table(GRANT_TABLE *g_t, else privileges |= tmp; old_row_exists = 1; - store_record(table,1); // copy original row + store_record(table,record[1]); // copy original row } table->field[6]->store((longlong) get_rights_for_column(privileges)); @@ -1895,7 +1895,7 @@ static int replace_column_table(GRANT_TABLE *g_t, { ulong privileges = (ulong) table->field[6]->val_int(); privileges=fix_rights_for_column(privileges); - store_record(table,1); + store_record(table,record[1]); if (privileges & rights) // is in this record the priv to be revoked ?? { @@ -1970,12 +1970,12 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, DBUG_RETURN(-1); /* purecov: deadcode */ } - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1); table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->field[3]->store(table_name,(uint) strlen(table_name), &my_charset_latin1); - store_record(table,1); // store at pos 1 + store_record(table,record[1]); // store at pos 1 if (table->file->index_read_idx(table->record[0],0, (byte*) table->field[0]->ptr,0, @@ -1995,7 +1995,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, DBUG_RETURN(-1); /* purecov: deadcode */ } old_row_exists = 0; - restore_record(table,1); // Get saved record + restore_record(table,record[1]); // Get saved record } store_table_rights= get_rights_for_table(rights); @@ -2003,7 +2003,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, if (old_row_exists) { ulong j,k; - store_record(table,1); + store_record(table,record[1]); j = (ulong) table->field[6]->val_int(); k = (ulong) table->field[7]->val_int(); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d92b2ba16ff..e374cdb3696 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -245,16 +245,11 @@ static void free_cache_entry(TABLE *table) void free_io_cache(TABLE *table) { DBUG_ENTER("free_io_cache"); - if (table->io_cache) + if (table->sort.io_cache) { - close_cached_file(table->io_cache); - my_free((gptr) table->io_cache,MYF(0)); - table->io_cache=0; - } - if (table->record_pointers) - { - my_free((gptr) table->record_pointers,MYF(0)); - table->record_pointers=0; + close_cached_file(table->sort.io_cache); + my_free((gptr) table->sort.io_cache,MYF(0)); + table->sort.io_cache=0; } DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index d3cb843ad85..925afde2202 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -145,10 +145,12 @@ public: int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); - int update_log_index(LOG_INFO* linfo); - int purge_logs(THD* thd, const char* to_log); - int purge_logs_before_date(THD* thd, time_t purge_time); - int purge_first_log(struct st_relay_log_info* rli); + int update_log_index(LOG_INFO* linfo, bool need_update_threads); + int purge_logs(const char *to_log, bool included, + bool need_mutex, bool need_update_threads, + ulonglong *decrease_log_space); + int purge_logs_before_date(time_t purge_time); + int purge_first_log(struct st_relay_log_info* rli, bool included); bool reset_logs(THD* thd); // if we are exiting, we also want to close the index file void close(bool exiting = 0); @@ -352,6 +354,7 @@ struct system_variables ulong max_allowed_packet; ulong max_error_count; ulong max_heap_table_size; + ulong max_length_for_sort_data; ulong max_prep_stmt_count; ulong max_sort_length; ulong max_tmp_tables; @@ -815,11 +818,52 @@ public: void abort(); }; +#include <myisam.h> + +/* Param to create temporary tables when doing SELECT:s */ + +class TMP_TABLE_PARAM :public Sql_alloc +{ + public: + List<Item> copy_funcs; + List<Item> save_copy_funcs; + List_iterator_fast<Item> copy_funcs_it; + Copy_field *copy_field, *copy_field_end; + Copy_field *save_copy_field, *save_copy_field_end; + byte *group_buff; + Item **items_to_copy; /* Fields in tmp table */ + MI_COLUMNDEF *recinfo,*start_recinfo; + KEY *keyinfo; + ha_rows end_write_records; + uint field_count,sum_func_count,func_count; + uint hidden_field_count; + uint group_parts,group_length,group_null_parts; + uint quick_group; + bool using_indirect_summary_function; + + TMP_TABLE_PARAM() + :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), + group_length(0), group_null_parts(0) + {} + ~TMP_TABLE_PARAM() + { + cleanup(); + } + inline void cleanup(void) + { + if (copy_field) /* Fix for Intel compiler */ + { + delete [] copy_field; + copy_field=0; + } + } +}; + class select_union :public select_result { public: TABLE *table; COPY_INFO info; - TMP_TABLE_PARAM *tmp_table_param; + TMP_TABLE_PARAM tmp_table_param; bool not_describe; select_union(TABLE *table_par); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 05f84616a4c..6bb336a87a6 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -124,14 +124,17 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, bzero((char*) &tables,sizeof(tables)); tables.table = table; - table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); - if (setup_order(thd, 0, &tables, fields, all_fields, order) || - !(sortorder=make_unireg_sortorder(order, &length)) || - (table->found_records = filesort(thd, table, sortorder, length, - (SQL_SELECT *) 0, HA_POS_ERROR, - &examined_rows)) - == HA_POS_ERROR) + if (setup_ref_array(thd, &thd->lex.select_lex.ref_pointer_array, + all_fields.elements)|| + setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables, + fields, all_fields, order) || + !(sortorder=make_unireg_sortorder(order, &length)) || + (table->sort.found_records = filesort(thd, table, sortorder, length, + (SQL_SELECT *) 0, HA_POS_ERROR, + &examined_rows)) + == HA_POS_ERROR) { delete select; free_underlaid_joins(thd, &thd->lex.select_lex); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index ca761140955..7f555f37d40 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -151,7 +151,7 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, if ((derived_result=new select_union(table))) { - derived_result->tmp_table_param=&tmp_table_param; + derived_result->tmp_table_param=tmp_table_param; unit->offset_limit_cnt= select_cursor->offset_limit; unit->select_limit_cnt= select_cursor->select_limit+ select_cursor->offset_limit; @@ -194,7 +194,10 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, { // to fix a problem in EXPLAIN if (tables) - tables->table_list->table=tables->table; + { + for (TABLE_LIST *cursor= tables; cursor; cursor= cursor->next) + cursor->table_list->table=cursor->table; + } } else unit->exclude_level(); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index dcb39f8526f..7418140517c 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -184,6 +184,15 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, thd->proc_info="init"; thd->used_tables=0; values= its++; + + if (duplic == DUP_UPDATE && !table->insert_values) + { + /* it should be allocated before Item::fix_fields() */ + table->insert_values=alloc_root(&table->mem_root, table->rec_buff_length); + if (!table->insert_values) + goto abort; + } + if (check_insert_fields(thd,table,fields,*values,1) || setup_tables(insert_table_list) || setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) || @@ -248,7 +257,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, { if (fields.elements || !value_count) { - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record if (fill_record(fields,*values)|| thd->net.report_error || check_null_fields(thd,table)) { @@ -264,9 +273,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, else { if (thd->used_tables) // Column used in values() - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record else - table->record[0][0]=table->record[2][0]; // Fix delete marker + table->record[0][0]=table->default_values[0]; // Fix delete marker if (fill_record(table->field,*values) || thd->net.report_error) { if (values_list.elements != 1 && ! thd->net.report_error) @@ -333,7 +342,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, thd->insert_id(id); // For update log else if (table->next_number_field) id=table->next_number_field->val_int(); // Return auto_increment value - + transactional_table= table->file->has_transactions(); log_delayed= (transactional_table || table->tmp_table); if ((info.copied || info.deleted) && (error <= 0 || !transactional_table)) @@ -383,7 +392,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, char buff[160]; if (duplic == DUP_IGNORE) sprintf(buff,ER(ER_INSERT_INFO),info.records, - (lock_type == TL_WRITE_DELAYED) ? 0 : + (lock_type == TL_WRITE_DELAYED) ? 0 : info.records-info.copied, thd->cuted_fields); else @@ -392,12 +401,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ::send_ok(thd,info.copied+info.deleted,(ulonglong)id,buff); } free_underlaid_joins(thd, &thd->lex.select_lex); + table->insert_values=0; DBUG_RETURN(0); abort: if (lock_type == TL_WRITE_DELAYED) end_delayed_insert(thd); free_underlaid_joins(thd, &thd->lex.select_lex); + table->insert_values=0; DBUG_RETURN(-1); } @@ -482,7 +493,8 @@ int write_record(TABLE *table,COPY_INFO *info) that matches, is updated. If update causes a conflict again, an error is returned */ - restore_record(table,1); + store_record(table,insert_values); + restore_record(table,record[1]); if (fill_record(*info->update_fields,*info->update_values)) goto err; if ((error=table->file->update_row(table->record[1],table->record[0]))) @@ -1349,7 +1361,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) if (check_insert_fields(thd,table,*fields,values,1)) DBUG_RETURN(1); - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record table->next_number_field=table->found_next_number_field; thd->count_cuted_fields=1; // calc cuted fields thd->cuted_fields=0; @@ -1480,7 +1492,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) } table->next_number_field=table->found_next_number_field; - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record thd->count_cuted_fields=1; // count warnings thd->cuted_fields=0; if (info.handle_duplicates == DUP_IGNORE || diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d88908d9e0f..9bc4dfc74e7 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1402,6 +1402,17 @@ ulong st_select_lex::get_table_join_options() return table_join_options; } +st_select_lex::st_select_lex(struct st_lex *lex) +{ + select_number= ++lex->thd->select_number; + init_query(); + init_select(); + include_neighbour(lex->current_select); + include_global((st_select_lex_node**)&lex->all_selects_list); + lex->current_select= this; +} + + /* There are st_select_lex::add_table_to_list & st_select_lex::set_lock_for_tables in sql_parse.cc diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 75ec2f2d3fb..e03814bcd2f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -404,6 +404,8 @@ public: } friend void mysql_init_query(THD *thd); + st_select_lex(struct st_lex *lex); + st_select_lex() {} void make_empty_select(st_select_lex *last_select) { select_number=INT_MAX; @@ -486,7 +488,7 @@ typedef struct st_lex CHARSET_INFO *charset; char *help_arg; SQL_LIST *gorder_list; - + st_lex() {} inline void uncacheable() { safe_to_cache_query= 0; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 3c43c3d9e3a..a68eada0025 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -238,7 +238,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, lf_info.log_delayed= log_delayed; read_info.set_io_cache_arg((void*) &lf_info); } - restore_record(table,2); + restore_record(table,default_values); thd->count_cuted_fields=1; /* calc cuted fields */ thd->cuted_fields=0L; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d0a970c98b7..ba8a4af794a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3980,7 +3980,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) - mysql_bin_log.purge_logs_before_date(thd, purge_time); + mysql_bin_log.purge_logs_before_date(purge_time); } #endif mysql_slow_log.new_file(1); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 7e9b6aea7b5..0eb444b85c0 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -292,7 +292,7 @@ int purge_master_logs(THD* thd, const char* to_log) char search_file_name[FN_REFLEN]; mysql_bin_log.make_log_name(search_file_name, to_log); - int res = mysql_bin_log.purge_logs(thd, search_file_name); + int res = mysql_bin_log.purge_logs(search_file_name, 0, 1, 1, NULL); return purge_error_message(thd, res); } @@ -300,7 +300,7 @@ int purge_master_logs(THD* thd, const char* to_log) int purge_master_logs_before_date(THD* thd, time_t purge_time) { - int res = mysql_bin_log.purge_logs_before_date(thd, purge_time); + int res = mysql_bin_log.purge_logs_before_date(purge_time); return purge_error_message(thd ,res); } @@ -776,24 +776,25 @@ int reset_slave(THD *thd, MASTER_INFO* mi) error=1; goto err; } - //delete relay logs, clear relay log coordinates + // delete relay logs, clear relay log coordinates if ((error= purge_relay_logs(&mi->rli, thd, 1 /* just reset */, &errmsg))) goto err; - //Clear master's log coordinates (only for good display of SHOW SLAVE STATUS) + // Clear master's log coordinates (only for good display of SHOW SLAVE STATUS) mi->master_log_name[0]= 0; mi->master_log_pos= BIN_LOG_HEADER_SIZE; - //close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 + // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 end_master_info(mi); - //and delete these two files + // and delete these two files fn_format(fname, master_info_file, mysql_data_home, "", 4+32); if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) { error=1; goto err; } + // delete relay_log_info_file fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32); if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) { @@ -874,7 +875,6 @@ int change_master(THD* thd, MASTER_INFO* mi) // if we change host or port, we must reset the postion mi->master_log_name[0] = 0; mi->master_log_pos= BIN_LOG_HEADER_SIZE; - mi->rli.pending = 0; } if (lex_mi->log_file_name) @@ -883,7 +883,6 @@ int change_master(THD* thd, MASTER_INFO* mi) if (lex_mi->pos) { mi->master_log_pos= lex_mi->pos; - mi->rli.pending = 0; } DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); @@ -901,20 +900,22 @@ int change_master(THD* thd, MASTER_INFO* mi) if (lex_mi->relay_log_name) { need_relay_log_purge= 0; - strmake(mi->rli.relay_log_name,lex_mi->relay_log_name, - sizeof(mi->rli.relay_log_name)-1); + strmake(mi->rli.group_relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.group_relay_log_name)-1); + strmake(mi->rli.event_relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.event_relay_log_name)-1); } if (lex_mi->relay_log_pos) { need_relay_log_purge= 0; - mi->rli.relay_log_pos=lex_mi->relay_log_pos; + mi->rli.group_relay_log_pos= mi->rli.event_relay_log_pos= lex_mi->relay_log_pos; } flush_master_info(mi); if (need_relay_log_purge) { - mi->rli.skip_log_purge= 0; + relay_log_purge= 1; thd->proc_info="purging old relay logs"; if (purge_relay_logs(&mi->rli, thd, 0 /* not only reset, but also reinit */, @@ -928,11 +929,11 @@ int change_master(THD* thd, MASTER_INFO* mi) else { const char* msg; - mi->rli.skip_log_purge= 1; + relay_log_purge= 0; /* Relay log is already initialized */ if (init_relay_log_pos(&mi->rli, - mi->rli.relay_log_name, - mi->rli.relay_log_pos, + mi->rli.group_relay_log_name, + mi->rli.group_relay_log_pos, 0 /*no data lock*/, &msg)) { @@ -941,12 +942,12 @@ int change_master(THD* thd, MASTER_INFO* mi) DBUG_RETURN(1); } } - mi->rli.master_log_pos = mi->master_log_pos; + mi->rli.group_master_log_pos = mi->master_log_pos; DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); - strmake(mi->rli.master_log_name,mi->master_log_name, - sizeof(mi->rli.master_log_name)-1); - if (!mi->rli.master_log_name[0]) // uninitialized case - mi->rli.master_log_pos=0; + strmake(mi->rli.group_master_log_name,mi->master_log_name, + sizeof(mi->rli.group_master_log_name)-1); + if (!mi->rli.group_master_log_name[0]) // uninitialized case + mi->rli.group_master_log_pos=0; pthread_mutex_lock(&mi->rli.data_lock); mi->rli.abort_pos_wait++; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f2dc2b2afd6..f324950e3d5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -868,12 +868,14 @@ JOIN::reinit() exec_tmp_table1->file->extra(HA_EXTRA_RESET_STATE); exec_tmp_table1->file->delete_all_rows(); free_io_cache(exec_tmp_table1); + filesort_free_buffers(exec_tmp_table1); } if (exec_tmp_table2) { exec_tmp_table2->file->extra(HA_EXTRA_RESET_STATE); exec_tmp_table2->file->delete_all_rows(); free_io_cache(exec_tmp_table2); + filesort_free_buffers(exec_tmp_table2); } if (items0) memcpy(ref_pointer_array, items0, ref_pointer_array_size); @@ -1284,7 +1286,10 @@ JOIN::cleanup(THD *thd) { delete tab->select; delete tab->quick; + tab->select=0; + tab->quick=0; x_free(tab->cache.buff); + tab->cache.buff= 0; } } tmp_join->tmp_join= 0; @@ -2316,7 +2321,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, we don't make rec less than 100. */ if (keyuse->used_tables & - (map=(keyuse->used_tables & ~join->const_table_map))) + (map=(keyuse->used_tables & ~join->const_table_map & + ~OUTER_REF_TABLE_BIT))) { uint tablenr; for (tablenr=0 ; ! (map & 1) ; map>>=1, tablenr++) ; @@ -2628,7 +2634,7 @@ static double prev_record_reads(JOIN *join,table_map found_ref) { double found=1.0; - + found_ref&= ~OUTER_REF_TABLE_BIT; for (POSITION *pos=join->positions ; found_ref ; pos++) { if (pos->table->table->map & found_ref) @@ -2662,7 +2668,7 @@ get_best_combination(JOIN *join) join->full_join=0; - used_tables=0; + used_tables= OUTER_REF_TABLE_BIT; // Outer row is already read for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++) { TABLE *form; @@ -2933,7 +2939,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) DBUG_RETURN(1); // Impossible const condition } } - used_tables=(select->const_tables=join->const_table_map) | RAND_TABLE_BIT; + used_tables=((select->const_tables=join->const_table_map) | + OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); for (uint i=join->const_tables ; i < join->tables ; i++) { JOIN_TAB *tab=join->join_tab+i; @@ -2943,7 +2950,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5 */ if (i == join->tables-1) - current_map|= RAND_TABLE_BIT; + current_map|= OUTER_REF_TABLE_BIT | RAND_TABLE_BIT; bool use_quick_range=0; used_tables|=current_map; @@ -3262,7 +3269,10 @@ join_free(JOIN *join, bool full) first non const table in join->table */ if (join->tables > join->const_tables) // Test for not-const tables + { free_io_cache(join->table[join->const_tables]); + filesort_free_buffers(join->table[join->const_tables]); + } if (join->select_lex->dependent && !full) { for (tab=join->join_tab,end=tab+join->tables ; tab != end ; tab++) @@ -3286,6 +3296,8 @@ join_free(JOIN *join, bool full) { delete tab->select; delete tab->quick; + tab->select=0; + tab->quick=0; x_free(tab->cache.buff); tab->cache.buff= 0; if (tab->table) @@ -3445,7 +3457,8 @@ static void update_depend_map(JOIN *join, ORDER *order) table_map depend_map; order->item[0]->update_used_tables(); order->depend_map=depend_map=order->item[0]->used_tables(); - if (!(order->depend_map & RAND_TABLE_BIT)) // Not item_sum() or RAND() + // Not item_sum(), RAND() and no reference to table outside of sub select + if (!(order->depend_map & (OUTER_REF_TABLE_BIT | RAND_TABLE_BIT))) { for (JOIN_TAB **tab=join->map2table; depend_map ; @@ -3492,7 +3505,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) } else { - if (order_tables & RAND_TABLE_BIT) + if (order_tables & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT)) *simple_order=0; else { @@ -4010,7 +4023,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item_sum::AVG_FUNC: /* Place for sum & count */ if (group) return new Field_string(sizeof(double)+sizeof(longlong), - maybe_null, item->name,table,&my_charset_bin); + 0, item->name,table,&my_charset_bin); else return new Field_double(item_sum->max_length,maybe_null, item->name, table, item_sum->decimals); @@ -4018,7 +4031,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item_sum::STD_FUNC: if (group) return new Field_string(sizeof(double)*2+sizeof(longlong), - maybe_null, item->name,table,&my_charset_bin); + 0, item->name,table,&my_charset_bin); else return new Field_double(item_sum->max_length, maybe_null, item->name,table,item_sum->decimals); @@ -4371,7 +4384,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!(table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME)))) goto err; table->record[1]= table->record[0]+alloc_length; - table->record[2]= table->record[1]+alloc_length; + table->default_values= table->record[1]+alloc_length; } copy_func[0]=0; // End marker @@ -4445,7 +4458,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, param->copy_field_end=copy; param->recinfo=recinfo; - store_record(table,2); // Make empty default record + store_record(table,default_values); // Make empty default record if (thd->variables.tmp_table_size == ~(ulong) 0) // No limit table->max_rows= ~(ha_rows) 0; @@ -5048,7 +5061,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) if (!found && on_expr) { // OUTER JOIN - restore_record(join_tab->table,2); // Make empty record + restore_record(join_tab->table,default_values); // Make empty record mark_as_null_row(join_tab->table); // For group by without error if (!select_cond || select_cond->val_int()) { @@ -5194,10 +5207,10 @@ join_read_system(JOIN_TAB *tab) empty_record(table); // Make empty record return -1; } - store_record(table,1); + store_record(table,record[1]); } else if (!table->status) // Only happens with left join - restore_record(table,1); // restore old record + restore_record(table,record[1]); // restore old record table->null_row=0; return table->status ? -1 : 0; } @@ -5234,12 +5247,12 @@ join_read_const(JOIN_TAB *tab) } return -1; } - store_record(table,1); + store_record(table,record[1]); } else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join { table->status=0; - restore_record(table,1); // restore old record + restore_record(table,record[1]); // restore old record } table->null_row=0; return table->status ? -1 : 0; @@ -5622,11 +5635,11 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), TABLE *table=jt->table; join->select_options ^= OPTION_FOUND_ROWS; - if (table->record_pointers || - (table->io_cache && my_b_inited(table->io_cache))) + if (table->sort.record_pointers || + (table->sort.io_cache && my_b_inited(table->sort.io_cache))) { /* Using filesort */ - join->send_records= table->found_records; + join->send_records= table->sort.found_records; } else { @@ -5836,7 +5849,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), join->tmp_table_param.group_buff,0, HA_READ_KEY_EXACT)) { /* Update old record */ - restore_record(table,1); + restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); if ((error=table->file->update_row(table->record[1], table->record[0]))) @@ -5905,7 +5918,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(-1); /* purecov: inspected */ } - restore_record(table,1); + restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); if ((error=table->file->update_row(table->record[1], table->record[0]))) @@ -6461,8 +6474,8 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, if (!(sortorder=make_unireg_sortorder(order,&length))) goto err; /* purecov: inspected */ /* It's not fatal if the following alloc fails */ - table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_WME | MY_ZEROFILL)); + table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_WME | MY_ZEROFILL)); table->status=0; // May be wrong if quick_select // If table has a range, move it to select @@ -6491,9 +6504,9 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, } if (table->tmp_table) table->file->info(HA_STATUS_VARIABLE); // Get record count - table->found_records=filesort(thd, table,sortorder, length, - select, filesort_limit, &examined_rows); - tab->records=table->found_records; // For SQL_CALC_ROWS + table->sort.found_records=filesort(thd, table,sortorder, length, + select, filesort_limit, &examined_rows); + tab->records=table->sort.found_records; // For SQL_CALC_ROWS delete select; // filesort did select tab->select=0; tab->select_cond=0; @@ -6505,7 +6518,7 @@ create_sort_index(THD *thd, JOIN_TAB *tab, ORDER *order, table->key_read=0; table->file->extra(HA_EXTRA_NO_KEYREAD); } - DBUG_RETURN(table->found_records == HA_POS_ERROR); + DBUG_RETURN(table->sort.found_records == HA_POS_ERROR); err: DBUG_RETURN(-1); } @@ -7423,7 +7436,7 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) DBUG_RETURN(0); map|=a->item[0]->used_tables(); } - if (!map || (map & RAND_TABLE_BIT)) + if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT))) DBUG_RETURN(0); for (; !(map & tables->table->map) ; tables=tables->next) ; diff --git a/sql/sql_select.h b/sql/sql_select.h index ffc98548db4..7f3669f7478 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -113,45 +113,6 @@ typedef struct st_position { /* Used in find_best */ } POSITION; -/* Param to create temporary tables when doing SELECT:s */ - -class TMP_TABLE_PARAM :public Sql_alloc -{ - public: - List<Item> copy_funcs; - List<Item> save_copy_funcs; - List_iterator_fast<Item> copy_funcs_it; - Copy_field *copy_field, *copy_field_end; - Copy_field *save_copy_field, *save_copy_field_end; - byte *group_buff; - Item **items_to_copy; /* Fields in tmp table */ - MI_COLUMNDEF *recinfo,*start_recinfo; - KEY *keyinfo; - ha_rows end_write_records; - uint field_count,sum_func_count,func_count; - uint hidden_field_count; - uint group_parts,group_length,group_null_parts; - uint quick_group; - bool using_indirect_summary_function; - - TMP_TABLE_PARAM() - :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), - group_length(0), group_null_parts(0) - {} - ~TMP_TABLE_PARAM() - { - cleanup(); - } - inline void cleanup(void) - { - if (copy_field) /* Fix for Intel compiler */ - { - delete [] copy_field; - copy_field=0; - } - } -}; - class JOIN :public Sql_alloc { public: diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8d6eccae87a..2b8982f2484 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -694,7 +694,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, if (protocol->send_records_num(&field_list, (ulonglong)file->records) || protocol->send_fields(&field_list,0)) DBUG_RETURN(1); - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record Field **ptr,*field; String *packet= &thd->packet; @@ -959,7 +959,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) !wild_case_compare(system_charset_info, field->field_name,wild)) field_list.push_back(new Item_field(field)); } - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record if (thd->protocol->send_fields(&field_list,2)) DBUG_VOID_RETURN; net_flush(&thd->net); @@ -1041,7 +1041,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) DBUG_ENTER("store_create_info"); DBUG_PRINT("enter",("table: %s",table->real_name)); - restore_record(table,2); // Get empty record + restore_record(table,default_values); // Get empty record List<Item> field_list; char tmp[MAX_FIELD_WIDTH]; diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 14463a67a28..9f95ffa4884 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -19,6 +19,30 @@ #define MERGEBUFF 7 #define MERGEBUFF2 15 +/* + The structure SORT_ADDON_FIELD describes a fixed layout + for field values appended to sorted values in records to be sorted + in the sort buffer. + Only fixed layout is supported now. + Null bit maps for the appended values is placed before the values + themselves. Offsets are from the last sorted field, that is from the + record referefence, which is still last component of sorted records. + It is preserved for backward compatiblility. + The structure is used tp store values of the additional fields + in the sort buffer. It is used also when these values are read + from a temporary file/buffer. As the reading procedures are beyond the + scope of the 'filesort' code the values have to be retrieved via + the callback function 'unpack_addon_fields'. +*/ + +typedef struct st_sort_addon_field { /* Sort addon packed field */ + Field *field; /* Original field */ + uint offset; /* Offset from the last sorted field */ + uint null_offset; /* Offset to to null bit from the last sorted field */ + uint length; /* Length in the sort buffer */ + uint8 null_bit; /* Null bit mask for the field */ +} SORT_ADDON_FIELD; + typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ my_off_t file_pos; /* Where we are in the sort file */ uchar *base,*key; /* key pointers */ @@ -27,15 +51,18 @@ typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ ulong max_keys; /* Max keys in buffert */ } BUFFPEK; - typedef struct st_sort_param { - uint sort_length; /* Length of sort columns */ - uint keys; /* Max keys / buffert */ + uint rec_length; /* Length of sorted records */ + uint sort_length; /* Length of sorted columns */ uint ref_length; /* Length of record ref. */ + uint addon_length; /* Length of added packed fields */ + uint res_length; /* Length of records in final sorted file/buffer */ + uint keys; /* Max keys / buffer */ ha_rows max_rows,examined_rows; TABLE *sort_form; /* For quicker make_sortkey */ SORT_FIELD *local_sortorder; SORT_FIELD *end; + SORT_ADDON_FIELD *addon_field; /* Descriptors for companion fields */ uchar *unique_buff; bool not_killable; char* tmp_buffer; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f4bcd6bd684..9e8cefee223 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -486,6 +486,12 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, blob_columns++; break; case FIELD_TYPE_GEOMETRY: + if (!(file->table_flags() & HA_HAS_GEOMETRY)) + { + my_printf_error(ER_WRONG_USAGE,ER(ER_WRONG_USAGE),MYF(0), + "GEOMETRY FIELD TYPE","not supported by this storage engine "); + DBUG_RETURN(-1); + } sql_field->pack_flag=FIELDFLAG_GEOM | pack_length_to_packflag(sql_field->pack_length - portable_sizeof_char_ptr); @@ -1756,7 +1762,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* Full alter table */ - restore_record(table,2); // Empty record for DEFAULT + restore_record(table,default_values); // 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); @@ -2342,8 +2348,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (order) { - from->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_FAE | MY_ZEROFILL)); + from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_FAE | MY_ZEROFILL)); bzero((char*) &tables,sizeof(tables)); tables.table = from; tables.alias = tables.real_name= from->real_name; @@ -2355,9 +2361,9 @@ copy_data_between_tables(TABLE *from,TABLE *to, setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || - (from->found_records = filesort(thd, from, sortorder, length, - (SQL_SELECT *) 0, HA_POS_ERROR, - &examined_rows)) + (from->sort.found_records = filesort(thd, from, sortorder, length, + (SQL_SELECT *) 0, HA_POS_ERROR, + &examined_rows)) == HA_POS_ERROR) goto err; }; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 42f4dddea8d..d191550f396 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -429,7 +429,7 @@ int mysql_create_function(THD *thd,udf_func *udf) if (!(table = open_ltable(thd,&tables,TL_WRITE))) goto err; - restore_record(table,2); // Get default values for fields + restore_record(table,default_values); // Get default values for fields table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info); table->field[1]->store((longlong) u_d->returns); table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 88c461bc0f8..fe4ca49da14 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -82,7 +82,7 @@ bool select_union::send_data(List<Item> &values) if (thd->net.last_errno == ER_RECORD_FILE_FULL) { thd->clear_error(); // do not report user about table overflow - if (create_myisam_from_heap(thd, table, tmp_table_param, + if (create_myisam_from_heap(thd, table, &tmp_table_param, info.last_errno, 0)) return 1; } @@ -117,7 +117,6 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, { SELECT_LEX_NODE *lex_select_save= thd->lex.current_select; SELECT_LEX *select_cursor; - TMP_TABLE_PARAM tmp_table_param; DBUG_ENTER("st_select_lex_unit::prepare"); if (prepared) @@ -125,9 +124,11 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, prepared= 1; res= 0; found_rows_for_union= 0; + TMP_TABLE_PARAM tmp_table_param; result= sel_result; t_and_f= tables_and_fields_initied; - + + bzero((char *)&tmp_table_param,sizeof(TMP_TABLE_PARAM)); thd->lex.current_select= select_cursor= first_select_in_union(); /* Global option */ if (((void*)(global_parameters)) == ((void*)this)) @@ -167,7 +168,6 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, t_and_f= 1; } - bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); tmp_table_param.field_count=item_list.elements; if (!(table= create_tmp_table(thd, &tmp_table_param, item_list, (ORDER*) 0, !union_option, @@ -186,7 +186,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, goto err; union_result->not_describe=1; - union_result->tmp_table_param=&tmp_table_param; + union_result->tmp_table_param=tmp_table_param; /* The following piece of code is placed here solely for the purpose of @@ -251,11 +251,9 @@ int st_select_lex_unit::exec() { int do_print_slow= 0; SELECT_LEX_NODE *lex_select_save= thd->lex.current_select; - SELECT_LEX *select_cursor=first_select_in_union(), *last_select; + SELECT_LEX *select_cursor=first_select_in_union(); DBUG_ENTER("st_select_lex_unit::exec"); - LINT_INIT(last_select); - if (executed && !(dependent || uncacheable)) DBUG_RETURN(0); executed= 1; @@ -270,7 +268,6 @@ int st_select_lex_unit::exec() } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { - last_select=sl; if (optimized) res= sl->join->reinit(); else @@ -336,8 +333,7 @@ int st_select_lex_unit::exec() if (!thd->is_fatal_error) // Check if EOM { - SELECT_LEX *fake_select = new SELECT_LEX(); - fake_select->make_empty_select(last_select); + SELECT_LEX *fake_select = new SELECT_LEX(&thd->lex); offset_limit_cnt= (select_cursor->braces ? global_parameters->offset_limit : 0); select_limit_cnt= (select_cursor->braces ? diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 3e72f79da62..7c5983a0362 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -30,7 +30,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields); static bool compare_record(TABLE *table, ulong query_id) { if (!table->blob_fields) - return cmp_record(table,1); + return cmp_record(table,record[1]); /* Compare null bits */ if (memcmp(table->null_flags, table->null_flags+table->rec_buff_length, @@ -201,14 +201,14 @@ int mysql_update(THD *thd, bzero((char*) &tables,sizeof(tables)); tables.table = table; - table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); if (setup_ref_array(thd, &thd->lex.select_lex.ref_pointer_array, order_num)|| setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || - (table->found_records = filesort(thd, table, sortorder, length, + (table->sort.found_records = filesort(thd, table, sortorder, length, (SQL_SELECT *) 0, HA_POS_ERROR, &examined_rows)) == HA_POS_ERROR) @@ -288,7 +288,7 @@ int mysql_update(THD *thd, { if (!(select && select->skipp_record())) { - store_record(table,1); + store_record(table,record[1]); if (fill_record(fields,values) || thd->net.report_error) break; /* purecov: inspected */ found++; @@ -731,7 +731,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) if (table == table_to_update) { table->status|= STATUS_UPDATED; - store_record(table,1); + store_record(table,record[1]); if (fill_record(*fields_for_table[offset], *values_for_table[offset])) DBUG_RETURN(1); found++; @@ -863,7 +863,7 @@ int multi_update::do_updates(bool from_send_error) if ((local_error= table->file->rnd_pos(table->record[0], ref_pos))) goto err; table->status|= STATUS_UPDATED; - store_record(table,1); + store_record(table,record[1]); /* Copy data from temporary table to current table */ for (copy_field_ptr=copy_field; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 71035a75084..acbe07be332 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2174,12 +2174,12 @@ simple_expr: | '@' ident_or_text SET_VAR expr { $$= new Item_func_set_user_var($2,$4); - Lex->uncacheable();; + Lex->uncacheable(); } | '@' ident_or_text { $$= new Item_func_get_user_var($2); - Lex->uncacheable();; + Lex->uncacheable(); } | '@' '@' opt_var_ident_type ident_or_text { @@ -2227,6 +2227,8 @@ simple_expr: { $$= new Item_func_conv_charset3($3,$7,$5); } | DEFAULT '(' simple_ident ')' { $$= new Item_default_value($3); } + | VALUES '(' simple_ident ')' + { $$= new Item_insert_value($3); } | FUNC_ARG0 '(' ')' { $$= ((Item*(*)(void))($1.symbol->create_func))();} | FUNC_ARG1 '(' expr ')' @@ -2276,7 +2278,7 @@ simple_expr: | ENCRYPT '(' expr ')' { $$= new Item_func_encrypt($3); - Lex->uncacheable();; + Lex->uncacheable(); } | ENCRYPT '(' expr ',' expr ')' { $$= new Item_func_encrypt($3,$5); } | DECODE_SYM '(' expr ',' TEXT_STRING_literal ')' diff --git a/sql/structs.h b/sql/structs.h index 77c852673d5..05ebdba7a37 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -104,6 +104,7 @@ typedef struct st_read_record { /* Parameter to read_record */ uint index; byte *ref_pos; /* pointer to form->refpos */ byte *record; + byte *rec_buf; /* to read field values after filesort */ byte *cache,*cache_pos,*cache_end,*read_positions; IO_CACHE *io_cache; bool print_error, ignore_not_found_rows; diff --git a/sql/table.cc b/sql/table.cc index fda1fcb36e4..3aed75c7ac6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -267,6 +267,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, (ulong) (uint2korr(head+6)+uint2korr(head+14)), MYF(MY_NABP))) goto err_not_open; /* purecov: inspected */ + /* HACK: table->record[2] is used instead of table->default_values here */ for (i=0 ; i < records ; i++, record+=rec_buff_length) { outparam->record[i]=(byte*) record; @@ -276,11 +277,12 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (records == 2) { /* fix for select */ - outparam->record[2]=outparam->record[1]; + outparam->default_values=outparam->record[1]; if (db_stat & HA_READ_ONLY) outparam->record[1]=outparam->record[0]; /* purecov: inspected */ } - + outparam->insert_values=0; /* for INSERT ... UPDATE */ + VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0))); if (my_read(file,(byte*) head,288,MYF(MY_NABP))) goto err_not_open; if (crypted) diff --git a/sql/table.h b/sql/table.h index 33e2db98d5a..2aefe23cb2f 100644 --- a/sql/table.h +++ b/sql/table.h @@ -44,6 +44,17 @@ typedef struct st_grant_info enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2}; +typedef struct st_filesort_info +{ + IO_CACHE *io_cache; /* If sorted through filebyte */ + byte *addon_buf; /* Pointer to a buffer if sorted with fields */ + uint addon_length; /* Length of the buffer */ + struct st_sort_addon_field *addon_field; /* Pointer to the fields info */ + void (*unpack)(struct st_sort_addon_field *, byte *); /* To unpack back */ + byte *record_pointers; /* If sorted in memory */ + ha_rows found_records; /* How many records in sort */ +} FILESORT_INFO; + /* Table cache entry struct */ class Field_timestamp; @@ -54,7 +65,9 @@ struct st_table { Field **field; /* Pointer to fields */ Field_blob **blob_field; /* Pointer to blob fields */ HASH name_hash; /* hash of field names */ - byte *record[3]; /* Pointer to records */ + byte *record[2]; /* Pointer to records */ + byte *default_values; /* record with default values for INSERT */ + byte *insert_values; /* used by INSERT ... UPDATE */ uint fields; /* field count */ uint reclength; /* Recordlength */ uint rec_buff_length; @@ -120,9 +133,7 @@ struct st_table { table_map map; /* ID bit of table (1,2,4,8,16...) */ ulong version,flush_version; uchar *null_flags; - IO_CACHE *io_cache; /* If sorted trough filebyte */ - byte *record_pointers; /* If sorted in memory */ - ha_rows found_records; /* How many records in sort */ + FILESORT_INFO sort; ORDER *group; ha_rows quick_rows[MAX_KEY]; uint quick_key_parts[MAX_KEY]; @@ -135,10 +146,9 @@ struct st_table { struct st_table_list *pos_in_table_list; }; /* number of select if it is derived table */ - uint derived_select_number; + uint derived_select_number; THD *in_use; /* Which thread uses this */ struct st_table *next,*prev; - byte *default_values() { return record[2]; } }; diff --git a/sql/uniques.cc b/sql/uniques.cc index ed256a4b791..c6fb0f25643 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -95,12 +95,12 @@ bool Unique::flush() bool Unique::get(TABLE *table) { SORTPARAM sort_param; - table->found_records=elements+tree.elements_in_tree; + table->sort.found_records=elements+tree.elements_in_tree; if (my_b_tell(&file) == 0) { /* Whole tree is in memory; Don't use disk if you don't need to */ - if ((record_pointers=table->record_pointers= (byte*) + if ((record_pointers=table->sort.record_pointers= (byte*) my_malloc(tree.size_of_element * tree.elements_in_tree, MYF(0)))) { (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs, @@ -112,7 +112,7 @@ bool Unique::get(TABLE *table) if (flush()) return 1; - IO_CACHE *outfile=table->io_cache; + IO_CACHE *outfile=table->sort.io_cache; BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer; uint maxbuffer= file_ptrs.elements - 1; uchar *sort_buffer; @@ -120,8 +120,8 @@ bool Unique::get(TABLE *table) bool error=1; /* Open cached file if it isn't open */ - outfile=table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_ZEROFILL)); + outfile=table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_ZEROFILL)); if (!outfile || ! my_b_inited(outfile) && open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, diff --git a/sql/unireg.h b/sql/unireg.h index 9430329e67a..ff942f6748a 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -57,7 +57,8 @@ #endif #define MAX_FIELD_WIDTH 256 /* Max column width +1 */ -#define MAX_TABLES (sizeof(table_map)*8-1) /* Max tables in join */ +#define MAX_TABLES (sizeof(table_map)*8-2) /* Max tables in join */ +#define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2)) #define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1)) #define MAX_FIELDS 4096 /* Limit in the .frm file */ @@ -103,13 +104,13 @@ #define SPECIAL_SAFE_MODE 2048 /* Extern defines */ -#define store_record(A,B) bmove_allign((A)->record[B],(A)->record[0],(size_t) (A)->reclength) -#define restore_record(A,B) bmove_allign((A)->record[0],(A)->record[B],(size_t) (A)->reclength) -#define cmp_record(A,B) memcmp((A)->record[0],(A)->record[B],(size_t) (A)->reclength) +#define store_record(A,B) bmove_allign((A)->B,(A)->record[0],(size_t) (A)->reclength) +#define restore_record(A,B) bmove_allign((A)->record[0],(A)->B,(size_t) (A)->reclength) +#define cmp_record(A,B) memcmp((A)->record[0],(A)->B,(size_t) (A)->reclength) #define empty_record(A) { \ -bmove_allign((A)->record[0],(A)->record[2],(size_t) (A)->reclength); \ -bfill((A)->null_flags,(A)->null_bytes,255);\ -} + restore_record((A),default_values); \ + bfill((A)->null_flags,(A)->null_bytes,255);\ + } /* Defines for use with openfrm, openprt and openfrd */ |