diff options
Diffstat (limited to 'sql/filesort.cc')
-rw-r--r-- | sql/filesort.cc | 289 |
1 files changed, 129 insertions, 160 deletions
diff --git a/sql/filesort.cc b/sql/filesort.cc index 9289d712cbc..54a79421d2e 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -50,26 +50,27 @@ if (my_b_write((file),(uchar*) (from),param->ref_length)) \ static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count, uchar *buf); static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, - Filesort_info *fs_info, + SORT_INFO *fs_info, IO_CACHE *buffer_file, IO_CACHE *tempfile, Bounded_queue<uchar, uchar> *pq, ha_rows *found_rows); -static bool write_keys(Sort_param *param, Filesort_info *fs_info, +static bool write_keys(Sort_param *param, SORT_INFO *fs_info, uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile); static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos); static void register_used_fields(Sort_param *param); static bool save_index(Sort_param *param, uint count, - Filesort_info *table_sort); + SORT_INFO *table_sort); static uint suffix_length(ulong string_length); static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset); static SORT_ADDON_FIELD *get_addon_fields(ulong max_length_for_sort_data, Field **ptabfield, - uint sortlength, uint *plength); + uint sortlength, + LEX_STRING *addon_buf); static void unpack_addon_fields(struct st_sort_addon_field *addon_field, uchar *buff, uchar *buff_end); -static bool check_if_pq_applicable(Sort_param *param, Filesort_info *info, +static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *info, TABLE *table, ha_rows records, ulong memory_available); @@ -78,6 +79,8 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, ulong max_length_for_sort_data, ha_rows maxrows, bool sort_positions) { + DBUG_ASSERT(addon_field == 0 && addon_buf.length == 0); + sort_length= sortlen; ref_length= table->file->ref_length; if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) && @@ -85,13 +88,13 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, { /* Get the descriptors of all fields whose values are appended - to sorted fields and get its total length in addon_length. + to sorted fields and get its total length in addon_buf.length */ addon_field= get_addon_fields(max_length_for_sort_data, - table->field, sort_length, &addon_length); + table->field, sort_length, &addon_buf); } if (addon_field) - res_length= addon_length; + res_length= addon_buf.length; else { res_length= ref_length; @@ -101,7 +104,7 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, */ sort_length+= ref_length; } - rec_length= sort_length + addon_length; + rec_length= sort_length + addon_buf.length; max_rows= maxrows; } @@ -115,8 +118,9 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, Before calling filesort, one must have done table->file->info(HA_STATUS_VARIABLE) - The result set is stored in table->io_cache or - table->record_pointers. + The result set is stored in + filesort_info->io_cache or + filesort_info->record_pointers. @param thd Current thread @param table Table to sort @@ -124,28 +128,24 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, @param s_length Number of elements in sortorder @param select Condition to apply to the rows @param max_rows Return only this many rows - @param sort_positions Set to TRUE if we want to force sorting by position + @param sort_positions Set to TRUE if we want to force sorting by + position (Needed by UPDATE/INSERT or ALTER TABLE or when rowids are required by executor) - @param[out] examined_rows Store number of examined rows here - @param[out] found_rows Store the number of found rows here - @note If we sort by position (like if sort_positions is 1) filesort() will call table->prepare_for_position(). @retval - HA_POS_ERROR Error - @retval - \# Number of rows + 0 Error + # SORT_INFO */ -ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, - SQL_SELECT *select, ha_rows max_rows, - bool sort_positions, - ha_rows *examined_rows, - ha_rows *found_rows, - Filesort_tracker* tracker) +SORT_INFO *filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, + uint s_length, + SQL_SELECT *select, ha_rows max_rows, + bool sort_positions, + Filesort_tracker* tracker) { int error; size_t memory_available= thd->variables.sortbuff_size; @@ -162,33 +162,37 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, #ifdef SKIP_DBUG_IN_FILESORT DBUG_PUSH(""); /* No DBUG here */ #endif - Filesort_info table_sort= table->sort; + SORT_INFO *sort; TABLE_LIST *tab= table->pos_in_table_list; Item_subselect *subselect= tab ? tab->containing_subselect() : 0; - MYSQL_FILESORT_START(table->s->db.str, table->s->table_name.str); DEBUG_SYNC(thd, "filesort_start"); + if (!(sort= new SORT_INFO)) + return 0; + + if (subselect && subselect->filesort_buffer.is_allocated()) + { + /* Reuse cache from last call */ + sort->filesort_buffer= subselect->filesort_buffer; + sort->buffpek= subselect->sortbuffer; + subselect->filesort_buffer.reset(); + subselect->sortbuffer.str=0; + } + + outfile= &sort->io_cache; + /* Release InnoDB's adaptive hash index latch (if holding) before running a sort. */ ha_release_temporary_latches(thd); - /* - Don't use table->sort in filesort as it is also used by - QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end - when index_merge select has finished with it. - */ - table->sort.io_cache= NULL; - DBUG_ASSERT(table_sort.record_pointers == NULL); - - outfile= table_sort.io_cache; my_b_clear(&tempfile); my_b_clear(&buffpek_pointers); buffpek=0; error= 1; - *found_rows= HA_POS_ERROR; + sort->found_rows= HA_POS_ERROR; param.init_for_filesort(sortlength(thd, sortorder, s_length, &multi_byte_charset), @@ -196,14 +200,12 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, thd->variables.max_length_for_sort_data, max_rows, sort_positions); - 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 && - !(table_sort.addon_buf= - (uchar *) my_malloc(param.addon_length, MYF(MY_WME | - MY_THREAD_SPECIFIC)))) + sort->addon_buf= param.addon_buf; + sort->addon_field= param.addon_field; + sort->unpack= unpack_addon_fields; + if (multi_byte_charset && + !(param.tmp_buffer= (char*) my_malloc(param.sort_length, + MYF(MY_WME | MY_THREAD_SPECIFIC)))) goto err; if (select && select->quick) @@ -216,12 +218,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, // If number of rows is not known, use as much of sort buffer as possible. num_rows= table->file->estimate_rows_upper_bound(); - if (multi_byte_charset && - !(param.tmp_buffer= (char*) my_malloc(param.sort_length, - MYF(MY_WME | MY_THREAD_SPECIFIC)))) - goto err; - - if (check_if_pq_applicable(¶m, &table_sort, + if (check_if_pq_applicable(¶m, sort, table, num_rows, memory_available)) { DBUG_PRINT("info", ("filesort PQ is applicable")); @@ -233,45 +230,31 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, true, // max_at_top NULL, // compare_function compare_length, - &make_sortkey, ¶m, table_sort.get_sort_keys())) + &make_sortkey, ¶m, sort->get_sort_keys())) { /* If we fail to init pq, we have to give up: out of memory means my_malloc() will call my_error(). */ DBUG_PRINT("info", ("failed to allocate PQ")); - table_sort.free_sort_buffer(); DBUG_ASSERT(thd->is_error()); goto err; } // For PQ queries (with limit) we initialize all pointers. - table_sort.init_record_pointers(); + sort->init_record_pointers(); } else { DBUG_PRINT("info", ("filesort PQ is not applicable")); - size_t min_sort_memory= MY_MAX(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2); + size_t min_sort_memory= MY_MAX(MIN_SORT_MEMORY, + param.sort_length*MERGEBUFF2); set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2); while (memory_available >= min_sort_memory) { ulonglong keys= memory_available / (param.rec_length + sizeof(char*)); param.max_keys_per_buffer= (uint) MY_MIN(num_rows, keys); - if (table_sort.get_sort_keys()) - { - // If we have already allocated a buffer, it better have same size! - if (!table_sort.check_sort_buffer_properties(param.max_keys_per_buffer, - param.rec_length)) - { - /* - table->sort will still have a pointer to the same buffer, - but that will be overwritten by the assignment below. - */ - table_sort.free_sort_buffer(); - } - } - table_sort.alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length); - if (table_sort.get_sort_keys()) + if (sort->alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length)) break; size_t old_memory_available= memory_available; memory_available= memory_available/4*3; @@ -284,7 +267,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR)); goto err; } - tracker->report_sort_buffer_size(table_sort.sort_buffer_size()); + tracker->report_sort_buffer_size(sort->sort_buffer_size()); } if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX, @@ -294,21 +277,21 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, param.sort_form= table; param.end=(param.local_sortorder=sortorder)+s_length; num_rows= find_all_keys(thd, ¶m, select, - &table_sort, + sort, &buffpek_pointers, &tempfile, pq.is_initialized() ? &pq : NULL, - found_rows); + &sort->found_rows); if (num_rows == HA_POS_ERROR) goto err; maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek)); tracker->report_merge_passes_at_start(thd->query_plan_fsort_passes); - tracker->report_row_numbers(param.examined_rows, *found_rows, num_rows); + tracker->report_row_numbers(param.examined_rows, sort->found_rows, num_rows); if (maxbuffer == 0) // The whole set is in memory { - if (save_index(¶m, (uint) num_rows, &table_sort)) + if (save_index(¶m, (uint) num_rows, sort)) goto err; } else @@ -316,17 +299,17 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, /* filesort cannot handle zero-length records during merge. */ DBUG_ASSERT(param.sort_length != 0); - if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer) + if (sort->buffpek.str && sort->buffpek.length < maxbuffer) { - my_free(table_sort.buffpek); - table_sort.buffpek= 0; + my_free(sort->buffpek.str); + sort->buffpek.str= 0; } - if (!(table_sort.buffpek= - (uchar *) read_buffpek_from_file(&buffpek_pointers, maxbuffer, - table_sort.buffpek))) + if (!(sort->buffpek.str= + (char *) read_buffpek_from_file(&buffpek_pointers, maxbuffer, + (uchar*) sort->buffpek.str))) goto err; - buffpek= (BUFFPEK *) table_sort.buffpek; - table_sort.buffpek_len= maxbuffer; + sort->buffpek.length= maxbuffer; + buffpek= (BUFFPEK *) sort->buffpek.str; close_cached_file(&buffpek_pointers); /* Open cached file if it isn't open */ if (! my_b_inited(outfile) && @@ -345,7 +328,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, param.rec_length - 1); maxbuffer--; // Offset from 0 if (merge_many_buff(¶m, - (uchar*) table_sort.get_sort_keys(), + (uchar*) sort->get_sort_keys(), buffpek,&maxbuffer, &tempfile)) goto err; @@ -353,7 +336,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, reinit_io_cache(&tempfile,READ_CACHE,0L,0,0)) goto err; if (merge_index(¶m, - (uchar*) table_sort.get_sort_keys(), + (uchar*) sort->get_sort_keys(), buffpek, maxbuffer, &tempfile, @@ -372,11 +355,18 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, my_free(param.tmp_buffer); if (!subselect || !subselect->is_uncacheable()) { - table_sort.free_sort_buffer(); - my_free(buffpek); - table_sort.buffpek= 0; - table_sort.buffpek_len= 0; + sort->free_sort_buffer(); + my_free(sort->buffpek.str); } + else + { + /* Remember sort buffers for next subquery call */ + subselect->filesort_buffer= sort->filesort_buffer; + subselect->sortbuffer= sort->buffpek; + sort->filesort_buffer.reset(); // Don't free this + } + sort->buffpek.str= 0; + close_cached_file(&tempfile); close_cached_file(&buffpek_pointers); if (my_b_inited(outfile)) @@ -397,13 +387,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, int kill_errno= thd->killed_errno(); DBUG_ASSERT(thd->is_error() || kill_errno || thd->killed == ABORT_QUERY); - /* - We replace the table->sort at the end. - Hence calling free_io_cache to make sure table->sort.io_cache - used for QUICK_INDEX_MERGE_SELECT is free. - */ - free_io_cache(table); - my_printf_error(ER_FILSORT_ABORT, "%s: %s", MYF(0), @@ -424,50 +407,26 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, } else thd->inc_status_sort_rows(num_rows); - *examined_rows= param.examined_rows; + + sort->examined_rows= param.examined_rows; + sort->return_rows= num_rows; #ifdef SKIP_DBUG_IN_FILESORT DBUG_POP(); /* Ok to DBUG */ #endif - /* table->sort.io_cache should be free by this time */ - DBUG_ASSERT(NULL == table->sort.io_cache); - - // Assign the copy back! - table->sort= table_sort; - DBUG_PRINT("exit", - ("num_rows: %ld examined_rows: %ld found_rows: %ld", - (long) num_rows, (long) *examined_rows, (long) *found_rows)); + ("num_rows: %lld examined_rows: %lld found_rows: %lld", + (longlong) sort->return_rows, (longlong) sort->examined_rows, + (longlong) sort->found_rows)); MYSQL_FILESORT_DONE(error, num_rows); - DBUG_RETURN(error ? HA_POS_ERROR : num_rows); -} /* filesort */ - - -void filesort_free_buffers(TABLE *table, bool full) -{ - DBUG_ENTER("filesort_free_buffers"); - - my_free(table->sort.record_pointers); - table->sort.record_pointers= NULL; - - if (unlikely(full)) - { - table->sort.free_sort_buffer(); - my_free(table->sort.buffpek); - table->sort.buffpek= NULL; - table->sort.buffpek_len= 0; - } - /* addon_buf is only allocated if addon_field is set */ - if (unlikely(table->sort.addon_field)) + if (error) { - my_free(table->sort.addon_field); - my_free(table->sort.addon_buf); - table->sort.addon_buf= NULL; - table->sort.addon_field= NULL; + delete sort; + sort= 0; } - DBUG_VOID_RETURN; -} + DBUG_RETURN(sort); +} /* filesort */ /** Read 'count' number of buffer pointers into memory. */ @@ -672,7 +631,7 @@ static void dbug_print_record(TABLE *table, bool print_rowid) */ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, - Filesort_info *fs_info, + SORT_INFO *fs_info, IO_CACHE *buffpek_pointers, IO_CACHE *tempfile, Bounded_queue<uchar, uchar> *pq, @@ -877,7 +836,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, const ha_rows retval= my_b_inited(tempfile) ? (ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx; - DBUG_PRINT("info", ("find_all_keys return %u", (uint) retval)); + DBUG_PRINT("info", ("find_all_keys return %llu", (ulonglong) retval)); DBUG_RETURN(retval); } /* find_all_keys */ @@ -905,7 +864,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, */ static bool -write_keys(Sort_param *param, Filesort_info *fs_info, uint count, +write_keys(Sort_param *param, SORT_INFO *fs_info, uint count, IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) { size_t rec_length; @@ -1274,11 +1233,13 @@ static void register_used_fields(Sort_param *param) } -static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort) +static bool save_index(Sort_param *param, uint count, + SORT_INFO *table_sort) { uint offset,res_length; uchar *to; DBUG_ENTER("save_index"); + DBUG_ASSERT(table_sort->record_pointers == 0); table_sort->sort_buffer(param, count); res_length= param->res_length; @@ -1327,7 +1288,7 @@ static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort) */ bool check_if_pq_applicable(Sort_param *param, - Filesort_info *filesort_info, + SORT_INFO *filesort_info, TABLE *table, ha_rows num_rows, ulong memory_available) { @@ -1361,9 +1322,8 @@ bool check_if_pq_applicable(Sort_param *param, // The whole source set fits into memory. if (param->max_rows < num_rows/PQ_slowness ) { - filesort_info->alloc_sort_buffer(param->max_keys_per_buffer, - param->rec_length); - DBUG_RETURN(filesort_info->get_sort_keys() != NULL); + DBUG_RETURN(filesort_info->alloc_sort_buffer(param->max_keys_per_buffer, + param->rec_length) != NULL); } else { @@ -1375,9 +1335,8 @@ bool check_if_pq_applicable(Sort_param *param, // Do we have space for LIMIT rows in memory? if (param->max_keys_per_buffer < num_available_keys) { - filesort_info->alloc_sort_buffer(param->max_keys_per_buffer, - param->rec_length); - DBUG_RETURN(filesort_info->get_sort_keys() != NULL); + DBUG_RETURN(filesort_info->alloc_sort_buffer(param->max_keys_per_buffer, + param->rec_length) != NULL); } // Try to strip off addon fields. @@ -1413,17 +1372,14 @@ bool check_if_pq_applicable(Sort_param *param, if (sort_merge_cost < pq_cost) DBUG_RETURN(false); - filesort_info->alloc_sort_buffer(param->max_keys_per_buffer, - param->sort_length + param->ref_length); - if (filesort_info->get_sort_keys()) + if (filesort_info->alloc_sort_buffer(param->max_keys_per_buffer, + param->sort_length + + param->ref_length)) { - // Make attached data to be references instead of fields. - my_free(filesort_info->addon_buf); + /* Make attached data to be references instead of fields. */ my_free(filesort_info->addon_field); - filesort_info->addon_buf= NULL; filesort_info->addon_field= NULL; param->addon_field= NULL; - param->addon_length= 0; param->res_length= param->ref_length; param->sort_length+= param->ref_length; @@ -1993,7 +1949,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, @param thd Current thread @param ptabfield Array of references to the table fields @param sortlength Total length of sorted fields - @param[out] plength Total length of appended fields + @param [out] addon_buf Buffer to us for appended fields @note The null bits for the appended values are supposed to be put together @@ -2007,7 +1963,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, static SORT_ADDON_FIELD * get_addon_fields(ulong max_length_for_sort_data, - Field **ptabfield, uint sortlength, uint *plength) + Field **ptabfield, uint sortlength, LEX_STRING *addon_buf) { Field **pfield; Field *field; @@ -2016,6 +1972,7 @@ get_addon_fields(ulong max_length_for_sort_data, uint fields= 0; uint null_fields= 0; MY_BITMAP *read_set= (*ptabfield)->table->read_set; + DBUG_ENTER("get_addon_fields"); /* If there is a reference to a field in the query add it @@ -2027,31 +1984,33 @@ get_addon_fields(ulong max_length_for_sort_data, the values directly from sorted fields. But beware the case when item->cmp_type() != item->result_type() */ - *plength= 0; + addon_buf->str= 0; + addon_buf->length= 0; for (pfield= ptabfield; (field= *pfield) ; pfield++) { if (!bitmap_is_set(read_set, field->field_index)) continue; if (field->flags & BLOB_FLAG) - return 0; + DBUG_RETURN(0); length+= field->max_packed_col_length(field->pack_length()); if (field->maybe_null()) null_fields++; fields++; } if (!fields) - return 0; + DBUG_RETURN(0); length+= (null_fields+7)/8; if (length+sortlength > max_length_for_sort_data || - !(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)* - (fields+1), - MYF(MY_WME | - MY_THREAD_SPECIFIC)))) - return 0; + !my_multi_malloc(MYF(MY_WME | MY_THREAD_SPECIFIC), + &addonf, sizeof(SORT_ADDON_FIELD) * (fields+1), + &addon_buf->str, length, + NullS)) - *plength= length; + DBUG_RETURN(0); + + addon_buf->length= length; length= (null_fields+7)/8; null_fields= 0; for (pfield= ptabfield; (field= *pfield) ; pfield++) @@ -2078,7 +2037,7 @@ get_addon_fields(ulong max_length_for_sort_data, addonf->field= 0; // Put end marker DBUG_PRINT("info",("addon_length: %d",length)); - return (addonf-fields); + DBUG_RETURN(addonf-fields); } @@ -2164,3 +2123,13 @@ void change_double_for_sort(double nr,uchar *to) } } +/** + Free SORT_INFO +*/ + +SORT_INFO::~SORT_INFO() +{ + DBUG_ENTER("~SORT_INFO::SORT_INFO()"); + free_data(); + DBUG_VOID_RETURN; +} |