diff options
author | Varun Gupta <varun.gupta@mariadb.com> | 2020-03-28 12:31:22 +0530 |
---|---|---|
committer | Varun Gupta <varun.gupta@mariadb.com> | 2020-06-29 14:04:07 +0530 |
commit | 077b71e4e5e5ad4aa2b4a7f6a1b49d457f5ceb55 (patch) | |
tree | c4a09ebb32e662a1243c23601ab6bd65a321668f /sql/uniques.cc | |
parent | ead98fe5d34912578445d42e620c8ed95df4c433 (diff) | |
download | mariadb-git-10.5-varun2.tar.gz |
MDEV-21829: Use packed sort keys in Unique objects10.5-varun2
The task deals with packing the values stored in the Unique tree for each record.
The changes brought by this feature is:
1) Unique tree can have dynamic length keys
2) Format of keys looks like
<key_length> <packed_value1> <packed_value2> ....... <packed_valueN>
Unique class is currently used in
1) agg_func(DISTINCT col)
Here most aggregate functions like SUM, AVG accept only fixed size arguments
so it is not beneficial to use packing for these. Packing is done for
COUNT and GROUP_CONCAT (or JSON_ARRAYAGG) aggregate function as these are meaningful
2) index-merge stores row-ids
index merge stores row-ids which are of fixed size, so packing is not required
3) Engine Independent Table statistics
Packing is done here for variable length data types
This task is an extension to MDEV-21580.
Diffstat (limited to 'sql/uniques.cc')
-rw-r--r-- | sql/uniques.cc | 179 |
1 files changed, 167 insertions, 12 deletions
diff --git a/sql/uniques.cc b/sql/uniques.cc index a0cebe3e4dd..d8772f366fb 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -48,12 +48,14 @@ int unique_write_to_file(uchar* key, element_count count, Unique *unique) when tree implementation chooses to store pointer to key in TREE_ELEMENT (instead of storing the element itself there) */ - return my_b_write(&unique->file, key, unique->size) ? 1 : 0; + return (unique->is_packed() ? + my_b_write(&unique->file, key, Unique::read_packed_length(key)) : + my_b_write(&unique->file, key, unique->size)) ? 1 : 0; } int unique_write_to_file_with_count(uchar* key, element_count count, Unique *unique) { - return my_b_write(&unique->file, key, unique->size) || + return unique_write_to_file(key, count, unique) || my_b_write(&unique->file, (uchar*)&count, sizeof(element_count)) ? 1 : 0; } @@ -79,10 +81,14 @@ int unique_intersect_write_to_ptrs(uchar* key, element_count count, Unique *uniq Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, uint size_arg, size_t max_in_memory_size_arg, - uint min_dupl_count_arg) + uint min_dupl_count_arg, bool packed_arg) :max_in_memory_size(max_in_memory_size_arg), size(size_arg), + packed(packed_arg), + memory_used(0), packed_rec_ptr(NULL), + sortorder(NULL), sort_keys(NULL), elements(0) + { my_b_clear(&file); min_dupl_count= min_dupl_count_arg; @@ -90,18 +96,32 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, if (min_dupl_count_arg) full_size+= sizeof(element_count); with_counters= MY_TEST(min_dupl_count_arg); - init_tree(&tree, (max_in_memory_size / 16), 0, size, comp_func, + init_tree(&tree, (max_in_memory_size / 16), 0, 0, comp_func, NULL, comp_func_fixed_arg, MYF(MY_THREAD_SPECIFIC)); /* If the following fail's the next add will also fail */ my_init_dynamic_array(PSI_INSTRUMENT_ME, &file_ptrs, sizeof(Merge_chunk), 16, 16, MYF(MY_THREAD_SPECIFIC)); + + if (packed) + { + packed_rec_ptr= (uchar *)my_malloc(PSI_INSTRUMENT_ME, + size, + MYF(MY_WME | MY_THREAD_SPECIFIC)); + } /* If you change the following, change it in get_max_elements function, too. */ max_elements= (ulong) (max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size)); if (!max_elements) + { max_elements= 1; + /* + Need to ensure that we have memory to store atleast one record + in the Unique tree + */ + max_in_memory_size= sizeof(TREE_ELEMENT) + size; + } (void) open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME)); @@ -371,6 +391,7 @@ Unique::~Unique() close_cached_file(&file); delete_tree(&tree, 0); delete_dynamic(&file_ptrs); + my_free(packed_rec_ptr); } @@ -389,6 +410,10 @@ bool Unique::flush() (void*) this, left_root_right) || insert_dynamic(&file_ptrs, (uchar*) &file_ptr)) return 1; + /** + the tree gets reset so make sure the memory used is reset too + */ + memory_used= 0; delete_tree(&tree, 0); return 0; } @@ -495,7 +520,8 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size, uint key_length, Merge_chunk *begin, Merge_chunk *end, tree_walk_action walk_action, void *walk_action_arg, qsort_cmp2 compare, void *compare_arg, - IO_CACHE *file, bool with_counters) + IO_CACHE *file, bool with_counters, + uint min_dupl_count, bool packed) { BUFFPEK_COMPARE_CONTEXT compare_context = { compare, compare_arg }; QUEUE queue; @@ -521,7 +547,12 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size, // read_to_buffer() needs only rec_length. Sort_param sort_param; sort_param.rec_length= key_length; + sort_param.sort_length= key_length; + sort_param.min_dupl_count= min_dupl_count; + DBUG_ASSERT(sort_param.res_length == 0); DBUG_ASSERT(!sort_param.using_addon_fields()); + sort_param.set_using_packed_keys(packed); + uint size_of_dupl_count= min_dupl_count ? sizeof(element_count) : 0; /* Invariant: queue must contain top element from each tree, until a tree @@ -534,7 +565,7 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size, top->set_buffer(merge_buffer + (top - begin) * piece_size, merge_buffer + (top - begin) * piece_size + piece_size); top->set_max_keys(max_key_count_per_piece); - bytes_read= read_to_buffer(file, top, &sort_param, false); + bytes_read= read_to_buffer(file, top, &sort_param, packed); if (unlikely(bytes_read == (ulong) -1)) goto end; DBUG_ASSERT(bytes_read); @@ -555,6 +586,10 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size, read next key from the cache or from the file and push it to the queue; this gives new top. */ + key_length= sort_param.get_record_length_for_unique((uchar*)old_key, + size_of_dupl_count); + + cnt_ofs= key_length - (with_counters ? sizeof(element_count) : 0); top->advance_current_key(key_length); top->decrement_mem_count(); if (top->mem_count()) @@ -564,7 +599,7 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size, /* save old_key not to overwrite it in read_to_buffer */ memcpy(save_key_buff, old_key, key_length); old_key= save_key_buff; - bytes_read= read_to_buffer(file, top, &sort_param, false); + bytes_read= read_to_buffer(file, top, &sort_param, packed); if (unlikely(bytes_read == (ulong) -1)) goto end; else if (bytes_read) /* top->key, top->mem_count are reset */ @@ -604,15 +639,18 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size, { do { - + key_length= sort_param.get_record_length_for_unique(top->current_key(), + size_of_dupl_count); + cnt_ofs= key_length - (with_counters ? sizeof(element_count) : 0); cnt= with_counters ? get_counter_from_merged_element(top->current_key(), cnt_ofs) : 1; if (walk_action(top->current_key(), cnt, walk_action_arg)) goto end; + top->advance_current_key(key_length); } while (top->decrement_mem_count()); - bytes_read= read_to_buffer(file, top, &sort_param, false); + bytes_read= read_to_buffer(file, top, &sort_param, packed); if (unlikely(bytes_read == (ulong) -1)) goto end; } @@ -678,7 +716,8 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg) (Merge_chunk *) file_ptrs.buffer, (Merge_chunk *) file_ptrs.buffer + file_ptrs.elements, action, walk_action_arg, - tree.compare, tree.custom_arg, &file, with_counters); + tree.compare, tree.custom_arg, &file, with_counters, + min_dupl_count, is_packed()); } my_free(merge_buffer); return res; @@ -730,9 +769,11 @@ bool Unique::merge(TABLE *table, uchar *buff, size_t buff_size, sort_param.max_keys_per_buffer= (uint) MY_MAX((max_in_memory_size / sort_param.sort_length), MERGEBUFF2); sort_param.not_killable= 1; + sort_param.set_using_packed_keys(is_packed()); + sort_param.set_packed_format(is_packed()); sort_param.unique_buff= buff +(sort_param.max_keys_per_buffer * - sort_param.sort_length); + sort_param.sort_length); sort_param.compare= (qsort2_cmp) buffpek_compare; sort_param.cmp_context.key_compare= tree.compare; @@ -760,7 +801,8 @@ bool Unique::merge(TABLE *table, uchar *buff, size_t buff_size, file_ptrs.elements= maxbuffer+1; return 0; } - if (merge_index(&sort_param, Bounds_checked_array<uchar>(buff, buff_size), + if (merge_index(&sort_param, + Bounds_checked_array<uchar>(buff, buff_size), file_ptr, maxbuffer, &file, outfile)) goto err; error= 0; @@ -789,6 +831,8 @@ bool Unique::get(TABLE *table) sort.return_rows= elements+tree.elements_in_tree; DBUG_ENTER("Unique::get"); + DBUG_ASSERT(packed == FALSE); + if (my_b_tell(&file) == 0) { /* Whole tree is in memory; Don't use disk if you don't need to */ @@ -831,3 +875,114 @@ err: my_free(sort_buffer); DBUG_RETURN(rc); } + + +/* + @brief + Setup the structures that are used when Unique stores packed values + + @param thd thread structure + @param item item of aggregate function + @param non_const_args number of non constant arguments + @param arg_count total number of arguments + @param exclude_nulls TRUE: NULLS should be excluded + FALSE: otherwise + + @note + This implementation is used by GROUP_CONCAT as it can have more than + one arguments in the argument list. + + @retval + TRUE error + FALSE setup successful +*/ + +bool +Unique::setup(THD *thd, Item_sum *item, uint non_const_args, + uint arg_count, bool exclude_nulls) +{ + if (!packed) // no packing so don't create the sortorder list + return false; + SORT_FIELD *sort,*pos; + if (sortorder) + return false; + DBUG_ASSERT(sort_keys == NULL); + sortorder= (SORT_FIELD*) thd->alloc(sizeof(SORT_FIELD) * non_const_args); + pos= sort= sortorder; + if (!pos) + return true; + sort_keys= new Sort_keys(sortorder, non_const_args); + if (!sort_keys) + return true; + sort=pos= sortorder; + for (uint i= 0; i < arg_count; i++) + { + Item *arg= item->get_arg(i); + if (arg->const_item()) + continue; + Field *field= arg->get_tmp_table_field(); + if (!field) + continue; + + pos->setup(field, exclude_nulls, false); + pos++; + } + return false; +} + + +/* + @brief + Setup the structures that are used when Unique stores packed values + + @param thd thread structure + @param field field structure + + @retval + TRUE error + FALSE setup successful +*/ + +bool Unique::setup(THD *thd, Field *field) +{ + if (!packed) // no packing so don't create the sortorder list + return false; + + SORT_FIELD *sort,*pos; + if (sortorder) + return false; + + DBUG_ASSERT(sort_keys == NULL); + sortorder= (SORT_FIELD*) thd->alloc(sizeof(SORT_FIELD)); + pos= sort= sortorder; + if (!pos) + return true; + sort_keys= new Sort_keys(sortorder, 1); + if (!sort_keys) + return true; + sort=pos= sortorder; + pos->setup(field, true, false); // Nulls are always excluded + + return false; +} + + +/* + @brief + Compare two packed keys inside the Unique tree + + @param a_ptr packed sort key + @param b_ptr packed sort key + + @retval + >0 key a_ptr greater than b_ptr + =0 key a_ptr equal to b_ptr + <0 key a_ptr less than b_ptr + +*/ + +int Unique::compare_packed_keys(uchar *a_ptr, uchar *b_ptr) +{ + return sort_keys->compare_keys(a_ptr + Unique::size_of_length_field, + b_ptr + Unique::size_of_length_field); +} |