diff options
author | Igor Babaev <igor@askmonty.org> | 2013-01-21 11:47:45 -0800 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2013-01-21 11:47:45 -0800 |
commit | fade3647ecb18a90d9c89a924a076d714ec45888 (patch) | |
tree | 1cdd64f9b996aa56df1993d3ea319075124ec5c4 | |
parent | 2e11ca36f28133c18b72351d176ee2fd7fcbc465 (diff) | |
download | mariadb-git-fade3647ecb18a90d9c89a924a076d714ec45888.tar.gz |
Fixed bug mdev-4063 (bug #56927).
This bug could result in returning 0 for the expressions of the form
<aggregate_function>(distinct field) when the system variable
max_heap_table_size was set to a small enough number.
It happened because the method Unique::walk() did not support
the case when more than one pass was needed to merge the trees
of distinct values saved in an external file.
Backported a fix in grant_lowercase.test from mariadb 5.5.
-rw-r--r-- | mysql-test/r/sum_distinct-big.result | 15 | ||||
-rw-r--r-- | mysql-test/t/grant_lowercase.test | 1 | ||||
-rw-r--r-- | mysql-test/t/sum_distinct-big.test | 17 | ||||
-rw-r--r-- | sql/item_sum.cc | 4 | ||||
-rw-r--r-- | sql/sql_class.h | 4 | ||||
-rw-r--r-- | sql/uniques.cc | 144 |
6 files changed, 131 insertions, 54 deletions
diff --git a/mysql-test/r/sum_distinct-big.result b/mysql-test/r/sum_distinct-big.result index 9b55d59ab91..d4933b31f80 100644 --- a/mysql-test/r/sum_distinct-big.result +++ b/mysql-test/r/sum_distinct-big.result @@ -103,5 +103,20 @@ sm 10323810 10325070 10326330 +# +# Bug mdev-4063: SUM(DISTINCT...) with small'max_heap_table_size +# (bug #56927) +# +SET max_heap_table_size=default; +INSERT INTO t1 SELECT id+16384 FROM t1; +DELETE FROM t2; +INSERT INTO t2 SELECT id FROM t1 ORDER BY id*rand(); +SELECT SUM(DISTINCT id) sm FROM t2; +sm +536887296 +SET max_heap_table_size=16384; +SELECT SUM(DISTINCT id) sm FROM t2; +sm +536887296 DROP TABLE t1; DROP TABLE t2; diff --git a/mysql-test/t/grant_lowercase.test b/mysql-test/t/grant_lowercase.test index 157e13449c2..b07cb88afd6 100644 --- a/mysql-test/t/grant_lowercase.test +++ b/mysql-test/t/grant_lowercase.test @@ -1,4 +1,5 @@ # test cases for strmov(tmp_db, db) -> strnmov replacement in sql_acl.cc +--source include/not_embedded.inc # # http://seclists.org/fulldisclosure/2012/Dec/4 diff --git a/mysql-test/t/sum_distinct-big.test b/mysql-test/t/sum_distinct-big.test index 0859f4b3d89..d3710056c9a 100644 --- a/mysql-test/t/sum_distinct-big.test +++ b/mysql-test/t/sum_distinct-big.test @@ -63,5 +63,22 @@ SELECT SUM(DISTINCT id) sm FROM t1; SELECT SUM(DISTINCT id) sm FROM t2; SELECT SUM(DISTINCT id) sm FROM t1 GROUP BY id % 13; +--echo # +--echo # Bug mdev-4063: SUM(DISTINCT...) with small'max_heap_table_size +--echo # (bug #56927) +--echo # + +SET max_heap_table_size=default; + +INSERT INTO t1 SELECT id+16384 FROM t1; +DELETE FROM t2; +INSERT INTO t2 SELECT id FROM t1 ORDER BY id*rand(); + +SELECT SUM(DISTINCT id) sm FROM t2; + +SET max_heap_table_size=16384; + +SELECT SUM(DISTINCT id) sm FROM t2; + DROP TABLE t1; DROP TABLE t2; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 92c2ba83f23..debba23438d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1084,7 +1084,7 @@ void Item_sum_distinct::calculate_val_and_count() if (tree) { table->field[0]->set_notnull(); - tree->walk(item_sum_distinct_walk, (void*) this); + tree->walk(table, item_sum_distinct_walk, (void*) this); } is_evaluated= TRUE; } @@ -2583,7 +2583,7 @@ longlong Item_sum_count_distinct::val_int() if (tree->elements == 0) return (longlong) tree->elements_in_tree(); // everything fits in memory count= 0; - tree->walk(count_distinct_walk, (void*) &count); + tree->walk(table, count_distinct_walk, (void*) &count); is_evaluated= TRUE; return (longlong) count; } diff --git a/sql/sql_class.h b/sql/sql_class.h index fb4e13ad9c6..2fd8e8cd04b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3008,6 +3008,8 @@ class Unique :public Sql_alloc bool flush(); uint size; + bool merge(TABLE *table, uchar *buff, bool without_last_merge); + public: ulong elements; Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg, @@ -3035,7 +3037,7 @@ public: } void reset(); - bool walk(tree_walk_action action, void *walk_action_arg); + bool walk(TABLE *table, tree_walk_action action, void *walk_action_arg); uint get_size() const { return size; } ulonglong get_max_in_memory_size() const { return max_in_memory_size; } diff --git a/sql/uniques.cc b/sql/uniques.cc index fd6056ae8a6..5788edc673e 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -538,6 +538,7 @@ end: SYNOPSIS Unique:walk() All params are 'IN': + table parameter for the call of the merge method action function-visitor, typed in include/my_tree.h function is called for each unique element arg argument for visitor, which is passed to it on each call @@ -546,66 +547,68 @@ end: <> 0 error */ -bool Unique::walk(tree_walk_action action, void *walk_action_arg) +bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg) { - int res; + int res= 0; uchar *merge_buffer; if (elements == 0) /* the whole tree is in memory */ return tree_walk(&tree, action, walk_action_arg, left_root_right); + table->sort.found_records=elements+tree.elements_in_tree; /* flush current tree to the file to have some memory for merge buffer */ if (flush()) return 1; if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0)) return 1; - if (!(merge_buffer= (uchar *) my_malloc((ulong) max_in_memory_size, MYF(0)))) + ulong buff_sz= (max_in_memory_size / size + 1) * size; + if (!(merge_buffer= (uchar *) my_malloc((ulong) buff_sz, MYF(0)))) return 1; - res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size, - (BUFFPEK *) file_ptrs.buffer, - (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements, - action, walk_action_arg, - tree.compare, tree.custom_arg, &file); + if (buff_sz < (ulong) (size * (file_ptrs.elements + 1))) + res= merge(table, merge_buffer, buff_sz >= size * MERGEBUFF2) ; + + if (!res) + { + res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size, + (BUFFPEK *) file_ptrs.buffer, + (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements, + action, walk_action_arg, + tree.compare, tree.custom_arg, &file); + } my_free((char*) merge_buffer, MYF(0)); return res; } + /* - Modify the TABLE element so that when one calls init_records() - the rows will be read in priority order. -*/ + DESCRIPTION + Perform multi-pass sort merge of the elements accessed through table->sort, + using the buffer buff as the merge buffer. The last pass is not performed + if without_last_merge is TRUE. + SYNOPSIS + Unique:merge() + All params are 'IN': + table the parameter to access sort context + buff merge buffer + without_last_merge TRUE <=> do not perform the last merge + RETURN VALUE + 0 OK + <> 0 error + */ -bool Unique::get(TABLE *table) +bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge) { SORTPARAM sort_param; - 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->sort.record_pointers= (uchar*) - my_malloc(size * tree.elements_in_tree, MYF(0)))) - { - (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs, - this, left_root_right); - return 0; - } - } - /* Not enough memory; Save the result to file && free memory used by tree */ - if (flush()) - return 1; - - IO_CACHE *outfile=table->sort.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; my_off_t save_pos; - bool error=1; - - /* Open cached file if it isn't open */ - outfile=table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_ZEROFILL)); + bool error= 1; + /* Open cached file if it isn't open */ + if (!outfile) + 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, @@ -615,42 +618,81 @@ bool Unique::get(TABLE *table) bzero((char*) &sort_param,sizeof(sort_param)); sort_param.max_rows= elements; - sort_param.sort_form=table; + sort_param.sort_form= table; sort_param.rec_length= sort_param.sort_length= sort_param.ref_length= size; sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length); - sort_param.not_killable=1; + sort_param.not_killable= 1; - if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) * - sort_param.sort_length, - MYF(0)))) - return 1; - sort_param.unique_buff= sort_buffer+(sort_param.keys* - sort_param.sort_length); + sort_param.unique_buff= buff + (sort_param.keys * sort_param.sort_length); sort_param.compare= (qsort2_cmp) buffpek_compare; sort_param.cmp_context.key_compare= tree.compare; sort_param.cmp_context.key_compare_arg= tree.custom_arg; /* Merge the buffers to one file, removing duplicates */ - if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file)) + if (merge_many_buff(&sort_param,buff,file_ptr,&maxbuffer,&file)) goto err; if (flush_io_cache(&file) || reinit_io_cache(&file,READ_CACHE,0L,0,0)) goto err; - if (merge_buffers(&sort_param, &file, outfile, sort_buffer, file_ptr, + if (without_last_merge) + { + file_ptrs.elements= maxbuffer+1; + return 0; + } + if (merge_buffers(&sort_param, &file, outfile, buff, file_ptr, file_ptr, file_ptr+maxbuffer,0)) goto err; - error=0; + error= 0; err: - x_free(sort_buffer); if (flush_io_cache(outfile)) - error=1; + error= 1; /* Setup io_cache for reading */ - save_pos=outfile->pos_in_file; + save_pos= outfile->pos_in_file; if (reinit_io_cache(outfile,READ_CACHE,0L,0,0)) - error=1; + error= 1; outfile->end_of_file=save_pos; return error; } + + +/* + Modify the TABLE element so that when one calls init_records() + the rows will be read in priority order. +*/ + +bool Unique::get(TABLE *table) +{ + bool rc= 1; + uchar *sort_buffer= NULL; + 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->sort.record_pointers= (uchar*) + my_malloc(size * tree.elements_in_tree, MYF(0)))) + { + (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs, + this, left_root_right); + return 0; + } + } + /* Not enough memory; Save the result to file && free memory used by tree */ + if (flush()) + return 1; + + ulong buff_sz= (max_in_memory_size / size + 1) * size; + if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(0)))) + return 1; + + if (merge(table, sort_buffer, FALSE)) + goto err; + rc= 0; + +err: + x_free(sort_buffer); + return rc; +} |