summaryrefslogtreecommitdiff
path: root/sql/filesort.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/filesort.cc')
-rw-r--r--sql/filesort.cc574
1 files changed, 400 insertions, 174 deletions
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 7ee4dc52557..103d3600559 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -34,6 +34,9 @@
#include "sql_base.h" // update_virtual_fields
#include "sql_test.h" // TEST_filesort
#include "opt_range.h" // SQL_SELECT
+#include "bounded_queue.h"
+#include "filesort_utils.h"
+#include "sql_select.h"
#include "log_slow.h"
#include "debug_sync.h"
@@ -42,26 +45,72 @@
if (my_b_write((file),(uchar*) (from),param->ref_length)) \
DBUG_RETURN(1);
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+template class Bounded_queue<uchar, uchar>;
+#endif
+
/* functions defined in this file */
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
uchar *buf);
-static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
- uchar * *sort_keys, uchar *sort_keys_buf,
- IO_CACHE *buffer_file, IO_CACHE *tempfile);
-static int write_keys(SORTPARAM *param,uchar * *sort_keys,
- uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
-static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
-static void register_used_fields(SORTPARAM *param);
-static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
- FILESORT_INFO *table_sort);
+static ha_rows find_all_keys(Sort_param *param,SQL_SELECT *select,
+ Filesort_info *fs_info,
+ IO_CACHE *buffer_file,
+ IO_CACHE *tempfile,
+ Bounded_queue<uchar, uchar> *pq,
+ ha_rows *found_rows);
+static int write_keys(Sort_param *param, Filesort_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);
+static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
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(THD *thd, Field **ptabfield,
+static SORT_ADDON_FIELD *get_addon_fields(ulong max_length_for_sort_data,
+ Field **ptabfield,
uint sortlength, uint *plength);
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,
+ TABLE *table,
+ ha_rows records, ulong memory_available);
+
+
+void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
+ ulong max_length_for_sort_data,
+ ha_rows maxrows, bool sort_positions)
+{
+ sort_length= sortlen;
+ ref_length= table->file->ref_length;
+ if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
+ !table->fulltext_searched && !sort_positions)
+ {
+ /*
+ Get the descriptors of all fields whose values are appended
+ to sorted fields and get its total length in addon_length.
+ */
+ addon_field= get_addon_fields(max_length_for_sort_data,
+ table->field, sort_length, &addon_length);
+ }
+ if (addon_field)
+ res_length= addon_length;
+ else
+ {
+ res_length= ref_length;
+ /*
+ The reference to the record is considered
+ as an additional sorted field
+ */
+ sort_length+= ref_length;
+ }
+ rec_length= sort_length + addon_length;
+ max_rows= maxrows;
+}
+
+
/**
Sort a table.
Creates a set of pointers that can be used to read the rows
@@ -74,15 +123,17 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
The result set is stored in table->io_cache or
table->record_pointers.
- @param thd Current thread
- @param table Table to sort
- @param sortorder How to sort the 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 1 if we want to force sorting by position
- (Needed by UPDATE/INSERT or ALTER TABLE)
- @param examined_rows Store number of examined rows here
+ @param thd Current thread
+ @param table Table to sort
+ @param sortorder How to sort the 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
+ (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
@@ -92,30 +143,30 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
HA_POS_ERROR Error
@retval
\# Number of rows
- @retval
- examined_rows will be set to number of examined rows
*/
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)
+ bool sort_positions,
+ ha_rows *examined_rows,
+ ha_rows *found_rows)
{
int error;
ulong memory_available= thd->variables.sortbuff_size;
- ulong min_sort_memory;
uint maxbuffer;
BUFFPEK *buffpek;
ha_rows num_rows= HA_POS_ERROR;
- uchar **sort_keys= 0;
IO_CACHE tempfile, buffpek_pointers, *outfile;
- SORTPARAM param;
+ Sort_param param;
bool multi_byte_charset;
+ Bounded_queue<uchar, uchar> pq;
+
DBUG_ENTER("filesort");
DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length););
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_PUSH(""); /* No DBUG here */
#endif
- FILESORT_INFO table_sort;
+ Filesort_info table_sort= table->sort;
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
@@ -133,7 +184,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
when index_merge select has finished with it.
*/
- memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
table->sort.io_cache= NULL;
outfile= table_sort.io_cache;
@@ -141,43 +191,21 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
my_b_clear(&buffpek_pointers);
buffpek=0;
error= 1;
- bzero((char*) &param,sizeof(param));
- param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset);
- param.ref_length= table->file->ref_length;
- if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
- !table->fulltext_searched && !sort_positions)
- {
- /*
- 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,
- &param.addon_length);
- }
+
+ param.init_for_filesort(sortlength(thd, sortorder, s_length,
+ &multi_byte_charset),
+ table,
+ 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)
- {
- param.res_length= param.addon_length;
- if (!(table_sort.addon_buf= (uchar *) 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 (param.addon_field &&
+ !(table_sort.addon_buf=
+ (uchar *) my_malloc(param.addon_length, MYF(MY_WME))))
+ goto err;
if (select && select->quick)
thd->inc_status_sort_range();
@@ -192,20 +220,54 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
!(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
goto err;
- min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length * MERGEBUFF2);
- if (!table_sort.sort_keys)
+ if (check_if_pq_applicable(&param, &table_sort,
+ table, num_rows, memory_available))
+ {
+ DBUG_PRINT("info", ("filesort PQ is applicable"));
+ const size_t compare_length= param.sort_length;
+ if (pq.init(param.max_rows,
+ true, // max_at_top
+ NULL, // compare_function
+ compare_length,
+ &make_sortkey, &param, table_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();
+ }
+ else
{
+ DBUG_PRINT("info", ("filesort PQ is not applicable"));
+
+ ulong min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
+ set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
while (memory_available >= min_sort_memory)
{
ulong keys= memory_available / (param.rec_length + sizeof(char*));
- table_sort.keys= (uint) min(num_rows, keys);
-
- DBUG_EXECUTE_IF("make_sort_keys_alloc_fail",
- DBUG_SET("+d,simulate_out_of_memory"););
-
- if ((table_sort.sort_keys=
- (uchar**) my_malloc(table_sort.keys*(param.rec_length+sizeof(char*)),
- MYF(0))))
+ param.max_keys_per_buffer= (uint) 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())
break;
ulong old_memory_available= memory_available;
memory_available= memory_available/4*3;
@@ -213,40 +275,37 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
old_memory_available > min_sort_memory)
memory_available= min_sort_memory;
}
+ if (memory_available < min_sort_memory)
+ {
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
+ goto err;
+ }
}
- sort_keys= table_sort.sort_keys;
- param.keys= table_sort.keys;
- if (memory_available < min_sort_memory)
- {
- my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
- goto err;
- }
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
- DISK_BUFFER_SIZE, MYF(ME_ERROR | MY_WME)))
+ DISK_BUFFER_SIZE, MYF(MY_WME)))
goto err;
param.sort_form= table;
param.end=(param.local_sortorder=sortorder)+s_length;
- num_rows= find_all_keys(&param,
- select,
- sort_keys,
- (uchar *)(sort_keys+param.keys),
+ num_rows= find_all_keys(&param, select,
+ &table_sort,
&buffpek_pointers,
- &tempfile);
+ &tempfile,
+ pq.is_initialized() ? &pq : NULL,
+ found_rows);
if (num_rows == HA_POS_ERROR)
goto err;
+
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
if (maxbuffer == 0) // The whole set is in memory
{
- if (save_index(&param,sort_keys,(uint) num_rows, &table_sort))
+ if (save_index(&param, (uint) num_rows, &table_sort))
goto err;
}
else
{
- thd->query_plan_flags|= QPLAN_FILESORT_DISK;
-
/* filesort cannot handle zero-length records during merge. */
DBUG_ASSERT(param.sort_length != 0);
@@ -265,7 +324,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
/* Open cached file if it isn't open */
if (! my_b_inited(outfile) &&
open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
- MYF(ME_ERROR | MY_WME)))
+ MYF(MY_WME)))
goto err;
if (reinit_io_cache(outfile,WRITE_CACHE,0L,0,0))
goto err;
@@ -274,18 +333,20 @@ 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.rec_length+sizeof(char*))) /
- param.rec_length - 1);
+ param.max_keys_per_buffer=((param.max_keys_per_buffer *
+ (param.rec_length + sizeof(char*))) /
+ param.rec_length - 1);
maxbuffer--; // Offset from 0
- if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
+ if (merge_many_buff(&param,
+ (uchar*) table_sort.get_sort_keys(),
+ buffpek,&maxbuffer,
&tempfile))
goto err;
if (flush_io_cache(&tempfile) ||
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
goto err;
if (merge_index(&param,
- (uchar*) sort_keys,
+ (uchar*) table_sort.get_sort_keys(),
buffpek,
maxbuffer,
&tempfile,
@@ -300,12 +361,11 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
}
error= 0;
- err:
+ err:
my_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- my_free(sort_keys);
- table_sort.sort_keys= 0;
+ table_sort.free_sort_buffer();
my_free(buffpek);
table_sort.buffpek= 0;
table_sort.buffpek_len= 0;
@@ -336,7 +396,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
thd->killed == ABORT_QUERY ? "" : thd->stmt_da->message());
if (global_system_variables.log_warnings > 1)
- {
+ {
sql_print_warning("%s, host: %s, user: %s, thread: %lu, query: %-.4096s",
ER_THD(thd, ER_FILSORT_ABORT),
thd->security_ctx->host_or_ip,
@@ -351,8 +411,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP(); /* Ok to DBUG */
#endif
- memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
- DBUG_PRINT("exit",("num_rows: %ld", (long) num_rows));
+
+ // 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));
MYSQL_FILESORT_DONE(error, num_rows);
DBUG_RETURN(error ? HA_POS_ERROR : num_rows);
} /* filesort */
@@ -360,13 +425,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
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 (full)
{
- my_free(table->sort.sort_keys);
- table->sort.sort_keys= NULL;
+ table->sort.free_sort_buffer();
my_free(table->sort.buffpek);
table->sort.buffpek= NULL;
table->sort.buffpek_len= 0;
@@ -376,6 +441,7 @@ void filesort_free_buffers(TABLE *table, bool full)
my_free(table->sort.addon_field);
table->sort.addon_buf= NULL;
table->sort.addon_field= NULL;
+ DBUG_VOID_RETURN;
}
@@ -454,8 +520,10 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
}
#endif
+
/**
- Search after sort_keys and write them into tempfile.
+ Search after sort_keys, and write them into tempfile
+ (if we run out of space in the sort_keys buffer).
All produced sequences are guaranteed to be non-empty.
@param param Sorting parameter
@@ -464,19 +532,28 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
@param buffpek_pointers File to write BUFFPEKs describing sorted segments
in tempfile.
@param tempfile File to write sorted sequences of sortkeys to.
+ @param pq If !NULL, use it for keeping top N elements
+ @param [out] found_rows The number of FOUND_ROWS().
+ For a query with LIMIT, this value will typically
+ be larger than the function return value.
@note
Basic idea:
@verbatim
while (get_next_sortkey())
{
- if (no free space in sort_keys buffers)
+ if (using priority queue)
+ push sort key into queue
+ else
{
- sort sort_keys buffer;
- dump sorted sequence to 'tempfile';
- dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ if (no free space in sort_keys buffers)
+ {
+ sort sort_keys buffer;
+ dump sorted sequence to 'tempfile';
+ dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ }
+ put sort key into 'sort_keys';
}
- put sort key into 'sort_keys';
}
if (sort_keys has some elements && dumped at least once)
sort-dump-dump as above;
@@ -490,10 +567,12 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
HA_POS_ERROR on error.
*/
-static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
- uchar **sort_keys, uchar *sort_keys_buf,
+static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
+ Filesort_info *fs_info,
IO_CACHE *buffpek_pointers,
- IO_CACHE *tempfile)
+ IO_CACHE *tempfile,
+ Bounded_queue<uchar, uchar> *pq,
+ ha_rows *found_rows)
{
int error,flag,quick_select;
uint idx,indexpos,ref_length;
@@ -501,10 +580,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
my_off_t record;
TABLE *sort_form;
THD *thd= current_thd;
- volatile killed_state *killed= &thd->killed;
handler *file;
MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
- uchar *next_sort_key= sort_keys_buf;
+
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",
(select ? select->quick ? "ranges" : "where":
@@ -518,10 +596,16 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
ref_pos= ref_buff;
quick_select=select && select->quick;
record=0;
+ *found_rows= 0;
flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
if (flag)
ref_pos= &file->ref[0];
next_pos=ref_pos;
+
+ DBUG_EXECUTE_IF("show_explain_in_find_all_keys",
+ dbug_serve_apcs(thd, 1);
+ );
+
if (!quick_select)
{
next_pos=(uchar*) 0; /* Find records in sequence */
@@ -531,7 +615,6 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
current_thd->variables.read_buff_size);
}
-
/* Remember original bitmaps */
save_read_set= sort_form->read_set;
save_write_set= sort_form->write_set;
@@ -585,7 +668,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
break;
}
- if (*killed)
+ if (thd->check_killed())
{
DBUG_PRINT("info",("Sort killed by user"));
if (!quick_select)
@@ -630,18 +713,23 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (write_record)
{
- if (idx == param->keys)
+ ++(*found_rows);
+ if (pq)
{
- if (write_keys(param, sort_keys,
- idx, buffpek_pointers, tempfile))
- DBUG_RETURN(HA_POS_ERROR);
- idx= 0;
- next_sort_key= sort_keys_buf;
- indexpos++;
+ pq->push(ref_pos);
+ idx= pq->num_elements();
+ }
+ else
+ {
+ if (idx == param->max_keys_per_buffer)
+ {
+ if (write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
+ DBUG_RETURN(HA_POS_ERROR);
+ idx= 0;
+ indexpos++;
+ }
+ make_sortkey(param, fs_info->get_record_buffer(idx++), ref_pos);
}
- sort_keys[idx++]= next_sort_key;
- make_sortkey(param, next_sort_key, ref_pos);
- next_sort_key+= param->rec_length;
}
else
file->unlock_row();
@@ -670,12 +758,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
}
if (indexpos && idx &&
- write_keys(param, sort_keys,
- idx, buffpek_pointers, tempfile))
+ write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
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_RETURN(retval);
} /* find_all_keys */
@@ -703,21 +791,19 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
*/
static int
-write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
+write_keys(Sort_param *param, Filesort_info *fs_info, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
- size_t sort_length, rec_length;
+ size_t rec_length;
uchar **end;
BUFFPEK buffpek;
DBUG_ENTER("write_keys");
- sort_length= param->sort_length;
rec_length= param->rec_length;
-#ifdef MC68000
- quicksort(sort_keys,count,sort_length);
-#else
- my_string_ptr_sort((uchar*) sort_keys, (uint) count, sort_length);
-#endif
+ uchar **sort_keys= fs_info->get_sort_keys();
+
+ fs_info->sort_buffer(param, count);
+
if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
MYF(MY_WME)))
@@ -766,8 +852,8 @@ static inline void store_length(uchar *to, uint length, uint pack_length)
/** Make a sort-key from record. */
-static void make_sortkey(register SORTPARAM *param,
- register uchar *to, uchar *ref_pos)
+static void make_sortkey(register Sort_param *param,
+ register uchar *to, uchar *ref_pos)
{
reg3 Field *field;
reg1 SORT_FIELD *sort_field;
@@ -785,9 +871,9 @@ static void make_sortkey(register SORTPARAM *param,
if (field->is_null())
{
if (sort_field->reverse)
- bfill(to,sort_field->length+1,(char) 255);
+ memset(to, 255, sort_field->length+1);
else
- bzero((char*) to,sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
to+= sort_field->length+1;
continue;
}
@@ -803,7 +889,7 @@ static void make_sortkey(register SORTPARAM *param,
switch (sort_field->result_type) {
case STRING_RESULT:
{
- CHARSET_INFO *cs=item->collation.collation;
+ const CHARSET_INFO *cs=item->collation.collation;
char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
int diff;
uint sort_field_length;
@@ -816,7 +902,7 @@ static void make_sortkey(register SORTPARAM *param,
if (!res)
{
if (maybe_null)
- bzero((char*) to-1,sort_field->length+1);
+ memset(to-1, 0, sort_field->length+1);
else
{
/* purecov: begin deadcode */
@@ -828,7 +914,7 @@ static void make_sortkey(register SORTPARAM *param,
DBUG_ASSERT(0);
DBUG_PRINT("warning",
("Got null on something that shouldn't be null"));
- bzero((char*) to,sort_field->length); // Avoid crash
+ memset(to, 0, sort_field->length); // Avoid crash
/* purecov: end */
}
break;
@@ -884,12 +970,19 @@ static void make_sortkey(register SORTPARAM *param,
}
if (maybe_null)
{
+ *to++=1; /* purecov: inspected */
if (item->null_value)
{
- bzero((char*) to++, sort_field->length+1);
+ if (maybe_null)
+ memset(to-1, 0, sort_field->length+1);
+ else
+ {
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ memset(to, 0, sort_field->length);
+ }
break;
}
- *to++=1; /* purecov: inspected */
}
to[7]= (uchar) value;
to[6]= (uchar) (value >> 8);
@@ -911,7 +1004,8 @@ static void make_sortkey(register SORTPARAM *param,
{
if (item->null_value)
{
- bzero((char*) to++, sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
+ to++;
break;
}
*to++=1;
@@ -928,7 +1022,7 @@ static void make_sortkey(register SORTPARAM *param,
{
if (item->null_value)
{
- bzero((char*) to,sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
to++;
break;
}
@@ -970,7 +1064,7 @@ static void make_sortkey(register SORTPARAM *param,
SORT_ADDON_FIELD *addonf= param->addon_field;
uchar *nulls= to;
DBUG_ASSERT(addonf != 0);
- bzero((char *) nulls, addonf->offset);
+ memset(nulls, 0, addonf->offset);
to+= addonf->offset;
for ( ; (field= addonf->field) ; addonf++)
{
@@ -1009,7 +1103,7 @@ static void make_sortkey(register SORTPARAM *param,
Register fields used by sorting in the sorted table's read set
*/
-static void register_used_fields(SORTPARAM *param)
+static void register_used_fields(Sort_param *param)
{
reg1 SORT_FIELD *sort_field;
TABLE *table=param->sort_form;
@@ -1054,21 +1148,19 @@ static void register_used_fields(SORTPARAM *param)
}
-static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
- FILESORT_INFO *table_sort)
+static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort)
{
uint offset,res_length;
uchar *to;
DBUG_ENTER("save_index");
- my_string_ptr_sort((uchar*) sort_keys, (uint) count, param->sort_length);
+ table_sort->sort_buffer(param, count);
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= table_sort->record_pointers=
(uchar*) my_malloc(res_length*count, MYF(MY_WME))))
DBUG_RETURN(1); /* purecov: inspected */
+ uchar **sort_keys= table_sort->get_sort_keys();
for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
{
memcpy(to, *sort_keys+offset, res_length);
@@ -1078,10 +1170,150 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
}
+/**
+ Test whether priority queue is worth using to get top elements of an
+ ordered result set. If it is, then allocates buffer for required amount of
+ records
+
+ @param param Sort parameters.
+ @param filesort_info Filesort information.
+ @param table Table to sort.
+ @param num_rows Estimate of number of rows in source record set.
+ @param memory_available Memory available for sorting.
+
+ DESCRIPTION
+ Given a query like this:
+ SELECT ... FROM t ORDER BY a1,...,an LIMIT max_rows;
+ This function tests whether a priority queue should be used to keep
+ the result. Necessary conditions are:
+ - estimate that it is actually cheaper than merge-sort
+ - enough memory to store the <max_rows> records.
+
+ If we don't have space for <max_rows> records, but we *do* have
+ space for <max_rows> keys, we may rewrite 'table' to sort with
+ references to records instead of additional data.
+ (again, based on estimates that it will actually be cheaper).
+
+ @retval
+ true - if it's ok to use PQ
+ false - PQ will be slower than merge-sort, or there is not enough memory.
+*/
+
+bool check_if_pq_applicable(Sort_param *param,
+ Filesort_info *filesort_info,
+ TABLE *table, ha_rows num_rows,
+ ulong memory_available)
+{
+ DBUG_ENTER("check_if_pq_applicable");
+
+ /*
+ How much Priority Queue sort is slower than qsort.
+ Measurements (see unit test) indicate that PQ is roughly 3 times slower.
+ */
+ const double PQ_slowness= 3.0;
+
+ if (param->max_rows == HA_POS_ERROR)
+ {
+ DBUG_PRINT("info", ("No LIMIT"));
+ DBUG_RETURN(NULL);
+ }
+
+ if (param->max_rows + 2 >= UINT_MAX)
+ {
+ DBUG_PRINT("info", ("Too large LIMIT"));
+ DBUG_RETURN(NULL);
+ }
+
+ ulong num_available_keys=
+ memory_available / (param->rec_length + sizeof(char*));
+ // We need 1 extra record in the buffer, when using PQ.
+ param->max_keys_per_buffer= (uint) param->max_rows + 1;
+
+ if (num_rows < num_available_keys)
+ {
+ // 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);
+ }
+ else
+ {
+ // PQ will be slower.
+ DBUG_RETURN(false);
+ }
+ }
+
+ // 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);
+ }
+
+ // Try to strip off addon fields.
+ if (param->addon_field)
+ {
+ const ulong row_length=
+ param->sort_length + param->ref_length + sizeof(char*);
+ num_available_keys= memory_available / row_length;
+
+ // Can we fit all the keys in memory?
+ if (param->max_keys_per_buffer < num_available_keys)
+ {
+ const double sort_merge_cost=
+ get_merge_many_buffs_cost_fast(num_rows,
+ num_available_keys,
+ row_length);
+ /*
+ PQ has cost:
+ (insert + qsort) * log(queue size) / TIME_FOR_COMPARE_ROWID +
+ cost of file lookup afterwards.
+ The lookup cost is a bit pessimistic: we take scan_time and assume
+ that on average we find the row after scanning half of the file.
+ A better estimate would be lookup cost, but note that we are doing
+ random lookups here, rather than sequential scan.
+ */
+ const double pq_cpu_cost=
+ (PQ_slowness * num_rows + param->max_keys_per_buffer) *
+ log((double) param->max_keys_per_buffer) / TIME_FOR_COMPARE_ROWID;
+ const double pq_io_cost=
+ param->max_rows * table->file->scan_time() / 2.0;
+ const double pq_cost= pq_cpu_cost + pq_io_cost;
+
+ 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())
+ {
+ // Make attached data to be references instead of fields.
+ my_free(filesort_info->addon_buf);
+ 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;
+ param->rec_length= param->sort_length;
+
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
/** Merge buffers to make < MERGEBUFF2 buffers. */
-int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
+int merge_many_buff(Sort_param *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
{
register uint i;
IO_CACHE t_file2,*from_file,*to_file,*temp;
@@ -1212,12 +1444,11 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
other error
*/
-int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
+int merge_buffers(Sort_param *param, IO_CACHE *from_file,
IO_CACHE *to_file, uchar *sort_buffer,
BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
int flag)
{
- THD *thd= current_thd;
int error;
uint rec_length,res_length,offset;
size_t sort_length;
@@ -1231,18 +1462,13 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
void *first_cmp_arg;
element_count dupl_count= 0;
uchar *src;
- killed_state not_killable;
uchar *unique_buff= param->unique_buff;
- volatile killed_state *killed= &thd->killed;
+ const bool killable= !param->not_killable;
+ THD* const thd=current_thd;
DBUG_ENTER("merge_buffers");
thd->inc_status_sort_merge_passes();
thd->query_plan_fsort_passes++;
- if (param->not_killable)
- {
- killed= &not_killable;
- not_killable= NOT_KILLED;
- }
error=0;
rec_length= param->rec_length;
@@ -1255,13 +1481,12 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
(flag && min_dupl_count ? sizeof(dupl_count) : 0)-res_length);
uint wr_len= flag ? res_length : rec_length;
uint wr_offset= flag ? offset : 0;
- maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
+ maxcount= (ulong) (param->max_keys_per_buffer/((uint) (Tb-Fb) +1));
to_start_filepos= my_b_tell(to_file);
strpos= sort_buffer;
org_max_rows=max_rows= param->max_rows;
-
- /* The following will fire if there is not enough space in sort_buffer */
- DBUG_ASSERT(maxcount!=0);
+
+ set_if_bigger(maxcount, 1);
if (unique_buff)
{
@@ -1320,7 +1545,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
while (queue.elements > 1)
{
- if (*killed)
+ if (killable && thd->check_killed())
{
error= 1; goto err; /* purecov: inspected */
}
@@ -1396,7 +1621,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
}
buffpek= (BUFFPEK*) queue_top(&queue);
buffpek->base= (uchar*) sort_buffer;
- buffpek->max_keys= param->keys;
+ buffpek->max_keys= param->max_keys_per_buffer;
/*
As we know all entries in the buffer are unique, we only have to
@@ -1486,9 +1711,9 @@ err:
/* Do a merge to output-file (save only positions) */
-int merge_index(SORTPARAM *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint maxbuffer,
- IO_CACHE *tempfile, IO_CACHE *outfile)
+int merge_index(Sort_param *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint maxbuffer,
+ IO_CACHE *tempfile, IO_CACHE *outfile)
{
DBUG_ENTER("merge_index");
if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
@@ -1534,7 +1759,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset)
{
reg2 uint length;
- CHARSET_INFO *cs;
+ const CHARSET_INFO *cs;
*multi_byte_charset= 0;
length=0;
@@ -1635,7 +1860,8 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
*/
static SORT_ADDON_FIELD *
-get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
+get_addon_fields(ulong max_length_for_sort_data,
+ Field **ptabfield, uint sortlength, uint *plength)
{
Field **pfield;
Field *field;
@@ -1672,7 +1898,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
return 0;
length+= (null_fields+7)/8;
- if (length+sortlength > thd->variables.max_length_for_sort_data ||
+ if (length+sortlength > max_length_for_sort_data ||
!(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)*
(fields+1), MYF(MY_WME))))
return 0;
@@ -1755,7 +1981,7 @@ void change_double_for_sort(double nr,uchar *to)
if (nr == 0.0)
{ /* Change to zero string */
tmp[0]=(uchar) 128;
- bzero((char*) tmp+1,sizeof(nr)-1);
+ memset(tmp+1, 0, sizeof(nr)-1);
}
else
{