summaryrefslogtreecommitdiff
path: root/sql/filesort.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/filesort.cc')
-rw-r--r--sql/filesort.cc289
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(&param, &table_sort,
+ if (check_if_pq_applicable(&param, 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, &param, table_sort.get_sort_keys()))
+ &make_sortkey, &param, 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, &param, 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(&param, (uint) num_rows, &table_sort))
+ if (save_index(&param, (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(&param,
- (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(&param,
- (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;
+}