1
/* Copyright (C) 2000-2006 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24
#include "mysql_priv.h"
26
#include <stddef.h> /* for macro offsetof */
32
#define SKIP_DBUG_IN_FILESORT
35
/// How to write record_ref.
36
#define WRITE_REF(file,from) \
37
if (my_b_write((file),(uchar*) (from),param->ref_length)) \
40
/* functions defined in this file */
42
static char **make_char_array(char **old_pos, register uint fields,
43
uint length, myf my_flag);
44
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
46
static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
47
uchar * *sort_keys, IO_CACHE *buffer_file,
48
IO_CACHE *tempfile,IO_CACHE *indexfile);
49
static int write_keys(SORTPARAM *param,uchar * *sort_keys,
50
uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
51
static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
52
static void register_used_fields(SORTPARAM *param);
53
static int merge_index(SORTPARAM *param,uchar *sort_buffer,
55
uint maxbuffer,IO_CACHE *tempfile,
57
static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
58
FILESORT_INFO *table_sort);
59
static uint suffix_length(ulong string_length);
60
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
61
bool *multi_byte_charset);
62
static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield,
63
uint sortlength, uint *plength);
64
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
68
Creates a set of pointers that can be used to read the rows
69
in sorted order. This should be done with the functions
72
Before calling filesort, one must have done
73
table->file->info(HA_STATUS_VARIABLE)
75
The result set is stored in table->io_cache or
76
table->record_pointers.
78
@param thd Current thread
79
@param table Table to sort
80
@param sortorder How to sort the table
81
@param s_length Number of elements in sortorder
82
@param select condition to apply to the rows
83
@param max_rows Return only this many rows
84
@param sort_positions Set to 1 if we want to force sorting by position
85
(Needed by UPDATE/INSERT or ALTER TABLE)
86
@param examined_rows Store number of examined rows here
89
check why we do this (param.keys--)
91
If we sort by position (like if sort_positions is 1) filesort() will
92
call table->prepare_for_position().
99
examined_rows will be set to number of examined rows
102
ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
103
SQL_SELECT *select, ha_rows max_rows,
104
bool sort_positions, ha_rows *examined_rows)
107
ulong memavl, min_sort_memory;
110
ha_rows records= HA_POS_ERROR;
111
uchar **sort_keys= 0;
112
IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile;
114
bool multi_byte_charset;
115
DBUG_ENTER("filesort");
116
DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length););
117
#ifdef SKIP_DBUG_IN_FILESORT
118
DBUG_PUSH(""); /* No DBUG here */
120
FILESORT_INFO table_sort;
121
TABLE_LIST *tab= table->pos_in_table_list;
122
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
124
MYSQL_FILESORT_START();
127
Release InnoDB's adaptive hash index latch (if holding) before
130
ha_release_temporary_latches(thd);
133
Don't use table->sort in filesort as it is also used by
134
QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
135
when index_merge select has finished with it.
137
memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
138
table->sort.io_cache= NULL;
140
outfile= table_sort.io_cache;
141
my_b_clear(&tempfile);
142
my_b_clear(&buffpek_pointers);
145
bzero((char*) ¶m,sizeof(param));
146
param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset);
147
param.ref_length= table->file->ref_length;
148
param.addon_field= 0;
149
param.addon_length= 0;
150
if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
151
!table->fulltext_searched && !sort_positions)
154
Get the descriptors of all fields whose values are appended
155
to sorted fields and get its total length in param.spack_length.
157
param.addon_field= get_addon_fields(thd, table->field,
159
¶m.addon_length);
162
table_sort.addon_buf= 0;
163
table_sort.addon_length= param.addon_length;
164
table_sort.addon_field= param.addon_field;
165
table_sort.unpack= unpack_addon_fields;
166
if (param.addon_field)
168
param.res_length= param.addon_length;
169
if (!(table_sort.addon_buf= (uchar *) my_malloc(param.addon_length,
175
param.res_length= param.ref_length;
177
The reference to the record is considered
178
as an additional sorted field
180
param.sort_length+= param.ref_length;
182
param.rec_length= param.sort_length+param.addon_length;
183
param.max_rows= max_rows;
185
if (select && select->quick)
187
status_var_increment(thd->status_var.filesort_range_count);
191
status_var_increment(thd->status_var.filesort_scan_count);
193
#ifdef CAN_TRUST_RANGE
194
if (select && select->quick && select->quick->records > 0L)
196
records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2),
197
table->file->stats.records)+EXTRA_RECORDS;
198
selected_records_file=0;
203
records= table->file->estimate_rows_upper_bound();
205
If number of records is not known, use as much of sort buffer
208
if (records == HA_POS_ERROR)
209
records--; // we use 'records+1' below.
210
selected_records_file= 0;
213
if (multi_byte_charset &&
214
!(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
217
memavl= thd->variables.sortbuff_size;
218
min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
219
while (memavl >= min_sort_memory)
222
ulong keys= memavl/(param.rec_length+sizeof(char*));
223
param.keys=(uint) min(records+1, keys);
224
if ((table_sort.sort_keys=
225
(uchar **) make_char_array((char **) table_sort.sort_keys,
226
param.keys, param.rec_length, MYF(0))))
229
if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory)
230
memavl= min_sort_memory;
232
sort_keys= table_sort.sort_keys;
233
if (memavl < min_sort_memory)
235
my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG));
238
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
239
DISK_BUFFER_SIZE, MYF(MY_WME)))
242
param.keys--; /* TODO: check why we do this */
243
param.sort_form= table;
244
param.end=(param.local_sortorder=sortorder)+s_length;
245
if ((records=find_all_keys(¶m,select,sort_keys, &buffpek_pointers,
246
&tempfile, selected_records_file)) ==
249
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
251
if (maxbuffer == 0) // The whole set is in memory
253
if (save_index(¶m,sort_keys,(uint) records, &table_sort))
258
if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer)
260
x_free(table_sort.buffpek);
261
table_sort.buffpek= 0;
263
if (!(table_sort.buffpek=
264
(uchar *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
265
table_sort.buffpek)))
267
buffpek= (BUFFPEK *) table_sort.buffpek;
268
table_sort.buffpek_len= maxbuffer;
269
close_cached_file(&buffpek_pointers);
270
/* Open cached file if it isn't open */
271
if (! my_b_inited(outfile) &&
272
open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
275
if (reinit_io_cache(outfile,WRITE_CACHE,0L,0,0))
279
Use also the space previously used by string pointers in sort_buffer
280
for temporary key storage.
282
param.keys=((param.keys*(param.rec_length+sizeof(char*))) /
284
maxbuffer--; // Offset from 0
285
if (merge_many_buff(¶m,(uchar*) sort_keys,buffpek,&maxbuffer,
288
if (flush_io_cache(&tempfile) ||
289
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
291
if (merge_index(¶m,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile,
295
if (records > param.max_rows)
296
records=param.max_rows;
300
if (param.tmp_buffer)
301
x_free(param.tmp_buffer);
302
if (!subselect || !subselect->is_uncacheable())
304
x_free((uchar*) sort_keys);
305
table_sort.sort_keys= 0;
306
x_free((uchar*) buffpek);
307
table_sort.buffpek= 0;
308
table_sort.buffpek_len= 0;
310
close_cached_file(&tempfile);
311
close_cached_file(&buffpek_pointers);
312
if (my_b_inited(outfile))
314
if (flush_io_cache(outfile))
317
my_off_t save_pos=outfile->pos_in_file;
318
/* For following reads */
319
if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
321
outfile->end_of_file=save_pos;
325
my_message(ER_FILSORT_ABORT, ER(ER_FILSORT_ABORT),
326
MYF(ME_ERROR+ME_WAITTANG));
328
statistic_add(thd->status_var.filesort_rows,
329
(ulong) records, &LOCK_status);
330
*examined_rows= param.examined_rows;
331
#ifdef SKIP_DBUG_IN_FILESORT
332
DBUG_POP(); /* Ok to DBUG */
334
memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
335
DBUG_PRINT("exit",("records: %ld", (long) records));
336
MYSQL_FILESORT_END();
337
DBUG_RETURN(error ? HA_POS_ERROR : records);
341
void filesort_free_buffers(TABLE *table, bool full)
343
if (table->sort.record_pointers)
345
my_free((uchar*) table->sort.record_pointers,MYF(0));
346
table->sort.record_pointers=0;
350
if (table->sort.sort_keys )
352
x_free((uchar*) table->sort.sort_keys);
353
table->sort.sort_keys= 0;
355
if (table->sort.buffpek)
357
x_free((uchar*) table->sort.buffpek);
358
table->sort.buffpek= 0;
359
table->sort.buffpek_len= 0;
362
if (table->sort.addon_buf)
364
my_free((char *) table->sort.addon_buf, MYF(0));
365
my_free((char *) table->sort.addon_field, MYF(MY_ALLOW_ZERO_PTR));
366
table->sort.addon_buf=0;
367
table->sort.addon_field=0;
371
/** Make a array of string pointers. */
373
static char **make_char_array(char **old_pos, register uint fields,
374
uint length, myf my_flag)
378
DBUG_ENTER("make_char_array");
381
(old_pos= (char**) my_malloc((uint) fields*(length+sizeof(char*)),
384
pos=old_pos; char_pos=((char*) (pos+fields)) -length;
385
while (fields--) *(pos++) = (char_pos+= length);
388
DBUG_RETURN(old_pos);
389
} /* make_char_array */
392
/** Read 'count' number of buffer pointers into memory. */
394
static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
397
ulong length= sizeof(BUFFPEK)*count;
399
DBUG_ENTER("read_buffpek_from_file");
400
if (count > UINT_MAX/sizeof(BUFFPEK))
401
return 0; /* sizeof(BUFFPEK)*count will overflow */
403
tmp= (uchar *)my_malloc(length, MYF(MY_WME));
406
if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
407
my_b_read(buffpek_pointers, (uchar*) tmp, length))
409
my_free((char*) tmp, MYF(0));
418
Search after sort_keys and write them into tempfile.
419
All produced sequences are guaranteed to be non-empty.
421
@param param Sorting parameter
422
@param select Use this to get source data
423
@param sort_keys Array of pointers to sort key + addon buffers.
424
@param buffpek_pointers File to write BUFFPEKs describing sorted segments
426
@param tempfile File to write sorted sequences of sortkeys to.
427
@param indexfile If !NULL, use it for source data (contains rowids)
432
while (get_next_sortkey())
434
if (no free space in sort_keys buffers)
436
sort sort_keys buffer;
437
dump sorted sequence to 'tempfile';
438
dump BUFFPEK describing sequence location into 'buffpek_pointers';
440
put sort key into 'sort_keys';
442
if (sort_keys has some elements && dumped at least once)
443
sort-dump-dump as above;
445
don't sort, leave sort_keys array to be sorted by caller.
449
Number of records written on success.
451
HA_POS_ERROR on error.
454
static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
456
IO_CACHE *buffpek_pointers,
457
IO_CACHE *tempfile, IO_CACHE *indexfile)
459
int error,flag,quick_select;
460
uint idx,indexpos,ref_length;
461
uchar *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
464
THD *thd= current_thd;
465
volatile THD::killed_state *killed= &thd->killed;
467
MY_BITMAP *save_read_set, *save_write_set;
468
DBUG_ENTER("find_all_keys");
469
DBUG_PRINT("info",("using: %s",
470
(select ? select->quick ? "ranges" : "where":
474
error=quick_select=0;
475
sort_form=param->sort_form;
476
file=sort_form->file;
477
ref_length=param->ref_length;
479
quick_select=select && select->quick;
481
flag= ((!indexfile && file->ha_table_flags() & HA_REC_NOT_IN_SEQ)
483
if (indexfile || flag)
484
ref_pos= &file->ref[0];
486
if (! indexfile && ! quick_select)
488
next_pos=(uchar*) 0; /* Find records in sequence */
489
file->ha_rnd_init(1);
490
file->extra_opt(HA_EXTRA_CACHE,
491
current_thd->variables.read_buff_size);
494
READ_RECORD read_record_info;
497
if (select->quick->reset())
498
DBUG_RETURN(HA_POS_ERROR);
499
init_read_record(&read_record_info, current_thd, select->quick->head,
503
/* Remember original bitmaps */
504
save_read_set= sort_form->read_set;
505
save_write_set= sort_form->write_set;
506
/* Set up temporary column read map for columns used by sort */
507
bitmap_clear_all(&sort_form->tmp_set);
508
/* Temporary set for register_used_fields and register_field_in_read_map */
509
sort_form->read_set= &sort_form->tmp_set;
510
register_used_fields(param);
511
if (select && select->cond)
512
select->cond->walk(&Item::register_field_in_read_map, 1,
514
sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set);
520
if ((error= read_record_info.read_record(&read_record_info)))
522
error= HA_ERR_END_OF_FILE;
525
file->position(sort_form->record[0]);
527
else /* Not quick-select */
531
if (my_b_read(indexfile,(uchar*) ref_pos,ref_length)) /* purecov: deadcode */
533
error= my_errno ? my_errno : -1; /* Abort */
536
error=file->rnd_pos(sort_form->record[0],next_pos);
540
error=file->rnd_next(sort_form->record[0]);
543
my_store_ptr(ref_pos,ref_length,record); // Position to row
544
record+= sort_form->s->db_record_offset;
547
file->position(sort_form->record[0]);
549
if (error && error != HA_ERR_RECORD_DELETED)
555
DBUG_PRINT("info",("Sort killed by user"));
556
if (!indexfile && !quick_select)
558
(void) file->extra(HA_EXTRA_NO_CACHE);
561
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
564
param->examined_rows++;
565
if (error == 0 && (!select || select->skip_record() == 0))
567
if (idx == param->keys)
569
if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
570
DBUG_RETURN(HA_POS_ERROR);
574
make_sortkey(param,sort_keys[idx++],ref_pos);
578
/* It does not make sense to read more keys in case of a fatal error */
585
index_merge quick select uses table->sort when retrieving rows, so free
586
resoures it has allocated.
588
end_read_record(&read_record_info);
592
(void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */
598
DBUG_RETURN(HA_POS_ERROR);
600
/* Signal we should use orignal column read and write maps */
601
sort_form->column_bitmaps_set(save_read_set, save_write_set);
603
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
604
if (error != HA_ERR_END_OF_FILE)
606
file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); /* purecov: inspected */
607
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
609
if (indexpos && idx &&
610
write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
611
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
612
DBUG_RETURN(my_b_inited(tempfile) ?
613
(ha_rows) (my_b_tell(tempfile)/param->rec_length) :
615
} /* find_all_keys */
620
Sort the buffer and write:
621
-# the sorted sequence to tempfile
622
-# a BUFFPEK describing the sorted sequence position to buffpek_pointers
624
(was: Skriver en buffert med nycklar till filen)
626
@param param Sort parameters
627
@param sort_keys Array of pointers to keys to sort
628
@param count Number of elements in sort_keys array
629
@param buffpek_pointers One 'BUFFPEK' struct will be written into this file.
630
The BUFFPEK::{file_pos, count} will indicate where
631
the sorted data was stored.
632
@param tempfile The sorted sequence will be written into this file.
641
write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
642
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
644
size_t sort_length, rec_length;
647
DBUG_ENTER("write_keys");
649
sort_length= param->sort_length;
650
rec_length= param->rec_length;
652
quicksort(sort_keys,count,sort_length);
654
my_string_ptr_sort((uchar*) sort_keys, (uint) count, sort_length);
656
if (!my_b_inited(tempfile) &&
657
open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
659
goto err; /* purecov: inspected */
660
/* check we won't have more buffpeks than we can possibly keep in memory */
661
if (my_b_tell(buffpek_pointers) + sizeof(BUFFPEK) > (ulonglong)UINT_MAX)
663
buffpek.file_pos= my_b_tell(tempfile);
664
if ((ha_rows) count > param->max_rows)
665
count=(uint) param->max_rows; /* purecov: inspected */
666
buffpek.count=(ha_rows) count;
667
for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
668
if (my_b_write(tempfile, (uchar*) *sort_keys, (uint) rec_length))
670
if (my_b_write(buffpek_pointers, (uchar*) &buffpek, sizeof(buffpek)))
680
Store length as suffix in high-byte-first order.
683
static inline void store_length(uchar *to, uint length, uint pack_length)
685
switch (pack_length) {
690
mi_int2store(to, length);
693
mi_int3store(to, length);
696
mi_int4store(to, length);
702
/** Make a sort-key from record. */
704
static void make_sortkey(register SORTPARAM *param,
705
register uchar *to, uchar *ref_pos)
707
register Field *field;
708
register SORT_FIELD *sort_field;
709
register uint length;
711
for (sort_field=param->local_sortorder ;
712
sort_field != param->end ;
716
if ((field=sort_field->field))
718
if (field->maybe_null())
720
if (field->is_null())
722
if (sort_field->reverse)
723
bfill(to,sort_field->length+1,(char) 255);
725
bzero((char*) to,sort_field->length+1);
726
to+= sort_field->length+1;
732
field->sort_string(to, sort_field->length);
736
Item *item=sort_field->item;
737
maybe_null= item->maybe_null;
738
switch (sort_field->result_type) {
741
CHARSET_INFO *cs=item->collation.collation;
742
char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
744
uint sort_field_length;
748
/* All item->str() to use some extra byte for end null.. */
749
String tmp((char*) to,sort_field->length+4,cs);
750
String *res= item->str_result(&tmp);
754
bzero((char*) to-1,sort_field->length+1);
757
/* purecov: begin deadcode */
759
This should only happen during extreme conditions if we run out
760
of memory or have an item marked not null when it can be null.
761
This code is here mainly to avoid a hard crash in this case.
764
DBUG_PRINT("warning",
765
("Got null on something that shouldn't be null"));
766
bzero((char*) to,sort_field->length); // Avoid crash
771
length= res->length();
772
sort_field_length= sort_field->length - sort_field->suffix_length;
773
diff=(int) (sort_field_length - length);
777
length= sort_field_length;
779
if (sort_field->suffix_length)
781
/* Store length last in result_string */
782
store_length(to + sort_field_length, length,
783
sort_field->suffix_length);
785
if (sort_field->need_strxnfrm)
787
char *from=(char*) res->ptr();
789
if ((uchar*) from == to)
791
set_if_smaller(length,sort_field->length);
792
memcpy(param->tmp_buffer,from,length);
793
from=param->tmp_buffer;
795
tmp_length= my_strnxfrm(cs,to,sort_field->length,
796
(uchar*) from, length);
797
DBUG_ASSERT(tmp_length == sort_field->length);
801
my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length);
802
cs->cset->fill(cs, (char *)to+length,diff,fill_char);
808
longlong value= item->val_int_result();
811
*to++=1; /* purecov: inspected */
812
if (item->null_value)
815
bzero((char*) to-1,sort_field->length+1);
818
DBUG_PRINT("warning",
819
("Got null on something that shouldn't be null"));
820
bzero((char*) to,sort_field->length);
825
#if SIZEOF_LONG_LONG > 4
826
to[7]= (uchar) value;
827
to[6]= (uchar) (value >> 8);
828
to[5]= (uchar) (value >> 16);
829
to[4]= (uchar) (value >> 24);
830
to[3]= (uchar) (value >> 32);
831
to[2]= (uchar) (value >> 40);
832
to[1]= (uchar) (value >> 48);
833
if (item->unsigned_flag) /* Fix sign */
834
to[0]= (uchar) (value >> 56);
836
to[0]= (uchar) (value >> 56) ^ 128; /* Reverse signbit */
838
to[3]= (uchar) value;
839
to[2]= (uchar) (value >> 8);
840
to[1]= (uchar) (value >> 16);
841
if (item->unsigned_flag) /* Fix sign */
842
to[0]= (uchar) (value >> 24);
844
to[0]= (uchar) (value >> 24) ^ 128; /* Reverse signbit */
850
my_decimal dec_buf, *dec_val= item->val_decimal_result(&dec_buf);
853
if (item->null_value)
855
bzero((char*)to, sort_field->length+1);
861
my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, to,
862
item->max_length - (item->decimals ? 1:0),
868
double value= item->val_result();
871
if (item->null_value)
873
bzero((char*) to,sort_field->length+1);
879
change_double_for_sort(value,(uchar*) to);
884
// This case should never be choosen
889
if (sort_field->reverse)
893
length=sort_field->length;
896
*to = (uchar) (~ *to);
901
to+= sort_field->length;
904
if (param->addon_field)
907
Save field values appended to sorted fields.
908
First null bit indicators are appended then field values follow.
909
In this implementation we use fixed layout for field values -
910
the same for all records.
912
SORT_ADDON_FIELD *addonf= param->addon_field;
914
DBUG_ASSERT(addonf != 0);
915
bzero((char *) nulls, addonf->offset);
917
for ( ; (field= addonf->field) ; addonf++)
919
if (addonf->null_bit && field->is_null())
921
nulls[addonf->null_offset]|= addonf->null_bit;
923
bzero(to, addonf->length);
929
uchar *end= field->pack(to, field->ptr);
930
uint length= (uint) ((to + addonf->length) - end);
931
DBUG_ASSERT((int) length >= 0);
935
(void) field->pack(to, field->ptr);
943
/* Save filepos last */
944
memcpy((uchar*) to, ref_pos, (size_t) param->ref_length);
951
Register fields used by sorting in the sorted table's read set
954
static void register_used_fields(SORTPARAM *param)
956
register SORT_FIELD *sort_field;
957
TABLE *table=param->sort_form;
958
MY_BITMAP *bitmap= table->read_set;
960
for (sort_field= param->local_sortorder ;
961
sort_field != param->end ;
965
if ((field= sort_field->field))
967
if (field->table == table)
968
bitmap_set_bit(bitmap, field->field_index);
972
sort_field->item->walk(&Item::register_field_in_read_map, 1,
977
if (param->addon_field)
979
SORT_ADDON_FIELD *addonf= param->addon_field;
981
for ( ; (field= addonf->field) ; addonf++)
982
bitmap_set_bit(bitmap, field->field_index);
986
/* Save filepos last */
987
table->prepare_for_position();
992
static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
993
FILESORT_INFO *table_sort)
995
uint offset,res_length;
997
DBUG_ENTER("save_index");
999
my_string_ptr_sort((uchar*) sort_keys, (uint) count, param->sort_length);
1000
res_length= param->res_length;
1001
offset= param->rec_length-res_length;
1002
if ((ha_rows) count > param->max_rows)
1003
count=(uint) param->max_rows;
1004
if (!(to= table_sort->record_pointers=
1005
(uchar*) my_malloc(res_length*count, MYF(MY_WME))))
1006
DBUG_RETURN(1); /* purecov: inspected */
1007
for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
1009
memcpy(to, *sort_keys+offset, res_length);
1016
/** Merge buffers to make < MERGEBUFF2 buffers. */
1018
int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
1019
BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
1022
IO_CACHE t_file2,*from_file,*to_file,*temp;
1024
DBUG_ENTER("merge_many_buff");
1026
if (*maxbuffer < MERGEBUFF2)
1027
DBUG_RETURN(0); /* purecov: inspected */
1028
if (flush_io_cache(t_file) ||
1029
open_cached_file(&t_file2,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE,
1031
DBUG_RETURN(1); /* purecov: inspected */
1033
from_file= t_file ; to_file= &t_file2;
1034
while (*maxbuffer >= MERGEBUFF2)
1036
if (reinit_io_cache(from_file,READ_CACHE,0L,0,0))
1038
if (reinit_io_cache(to_file,WRITE_CACHE,0L,0,0))
1041
for (i=0 ; i <= *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
1043
if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
1044
buffpek+i,buffpek+i+MERGEBUFF-1,0))
1047
if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
1048
buffpek+i,buffpek+ *maxbuffer,0))
1049
break; /* purecov: inspected */
1050
if (flush_io_cache(to_file))
1051
break; /* purecov: inspected */
1052
temp=from_file; from_file=to_file; to_file=temp;
1053
setup_io_cache(from_file);
1054
setup_io_cache(to_file);
1055
*maxbuffer= (uint) (lastbuff-buffpek)-1;
1058
close_cached_file(to_file); // This holds old result
1059
if (to_file == t_file)
1061
*t_file=t_file2; // Copy result file
1062
setup_io_cache(t_file);
1065
DBUG_RETURN(*maxbuffer >= MERGEBUFF2); /* Return 1 if interrupted */
1066
} /* merge_many_buff */
1070
Read data to buffer.
1073
(uint)-1 if something goes wrong
1076
uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
1079
register uint count;
1082
if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
1084
if (my_pread(fromfile->file,(uchar*) buffpek->base,
1085
(length= rec_length*count),buffpek->file_pos,MYF_RW))
1086
return((uint) -1); /* purecov: inspected */
1087
buffpek->key=buffpek->base;
1088
buffpek->file_pos+= length; /* New filepos */
1089
buffpek->count-= count;
1090
buffpek->mem_count= count;
1092
return (count*rec_length);
1093
} /* read_to_buffer */
1097
Put all room used by freed buffer to use in adjacent buffer.
1099
Note, that we can't simply distribute memory evenly between all buffers,
1100
because new areas must not overlap with old ones.
1102
@param[in] queue list of non-empty buffers, without freed buffer
1103
@param[in] reuse empty buffer
1104
@param[in] key_length key length
1107
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
1109
uchar *reuse_end= reuse->base + reuse->max_keys * key_length;
1110
for (uint i= 0; i < queue->elements; ++i)
1112
BUFFPEK *bp= (BUFFPEK *) queue_element(queue, i);
1113
if (bp->base + bp->max_keys * key_length == reuse->base)
1115
bp->max_keys+= reuse->max_keys;
1118
else if (bp->base == reuse_end)
1120
bp->base= reuse->base;
1121
bp->max_keys+= reuse->max_keys;
1130
Merge buffers to one buffer.
1132
@param param Sort parameter
1133
@param from_file File with source data (BUFFPEKs point to this file)
1134
@param to_file File to write the sorted result data.
1135
@param sort_buffer Buffer for data to store up to MERGEBUFF2 sort keys.
1136
@param lastbuff OUT Store here BUFFPEK describing data written to to_file
1137
@param Fb First element in source BUFFPEKs array
1138
@param Tb Last element in source BUFFPEKs array
1147
int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
1148
IO_CACHE *to_file, uchar *sort_buffer,
1149
BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
1153
uint rec_length,res_length,offset;
1156
ha_rows max_rows,org_max_rows;
1157
my_off_t to_start_filepos;
1162
void *first_cmp_arg;
1163
volatile THD::killed_state *killed= ¤t_thd->killed;
1164
THD::killed_state not_killable;
1165
DBUG_ENTER("merge_buffers");
1167
status_var_increment(current_thd->status_var.filesort_merge_passes);
1168
if (param->not_killable)
1170
killed= ¬_killable;
1171
not_killable= THD::NOT_KILLED;
1175
rec_length= param->rec_length;
1176
res_length= param->res_length;
1177
sort_length= param->sort_length;
1178
offset= rec_length-res_length;
1179
maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
1180
to_start_filepos= my_b_tell(to_file);
1181
strpos= (uchar*) sort_buffer;
1182
org_max_rows=max_rows= param->max_rows;
1184
/* The following will fire if there is not enough space in sort_buffer */
1185
DBUG_ASSERT(maxcount!=0);
1187
if (param->unique_buff)
1189
cmp= param->compare;
1190
first_cmp_arg= (void *) ¶m->cmp_context;
1194
cmp= get_ptr_compare(sort_length);
1195
first_cmp_arg= (void*) &sort_length;
1197
if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
1198
(queue_compare) cmp, first_cmp_arg))
1199
DBUG_RETURN(1); /* purecov: inspected */
1200
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
1202
buffpek->base= strpos;
1203
buffpek->max_keys= maxcount;
1204
strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek,
1207
goto err; /* purecov: inspected */
1208
buffpek->max_keys= buffpek->mem_count; // If less data in buffers than expected
1209
queue_insert(&queue, (uchar*) buffpek);
1212
if (param->unique_buff)
1215
Called by Unique::get()
1216
Copy the first argument to param->unique_buff for unique removal.
1217
Store it also in 'to_file'.
1219
This is safe as we know that there is always more than one element
1220
in each block to merge (This is guaranteed by the Unique:: algorithm
1222
buffpek= (BUFFPEK*) queue_top(&queue);
1223
memcpy(param->unique_buff, buffpek->key, rec_length);
1224
if (my_b_write(to_file, (uchar*) buffpek->key, rec_length))
1226
error=1; goto err; /* purecov: inspected */
1228
buffpek->key+= rec_length;
1229
buffpek->mem_count--;
1232
error= 0; /* purecov: inspected */
1233
goto end; /* purecov: inspected */
1235
queue_replaced(&queue); // Top element has been used
1238
cmp= 0; // Not unique
1240
while (queue.elements > 1)
1244
error= 1; goto err; /* purecov: inspected */
1248
buffpek= (BUFFPEK*) queue_top(&queue);
1249
if (cmp) // Remove duplicates
1251
if (!(*cmp)(first_cmp_arg, &(param->unique_buff),
1252
(uchar**) &buffpek->key))
1253
goto skip_duplicate;
1254
memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length);
1258
if (my_b_write(to_file,(uchar*) buffpek->key, rec_length))
1260
error=1; goto err; /* purecov: inspected */
1265
if (my_b_write(to_file, (uchar*) buffpek->key+offset, res_length))
1267
error=1; goto err; /* purecov: inspected */
1272
error= 0; /* purecov: inspected */
1273
goto end; /* purecov: inspected */
1277
buffpek->key+= rec_length;
1278
if (! --buffpek->mem_count)
1280
if (!(error= (int) read_to_buffer(from_file,buffpek,
1283
VOID(queue_remove(&queue,0));
1284
reuse_freed_buff(&queue, buffpek, rec_length);
1285
break; /* One buffer have been removed */
1287
else if (error == -1)
1288
goto err; /* purecov: inspected */
1290
queue_replaced(&queue); /* Top element has been replaced */
1293
buffpek= (BUFFPEK*) queue_top(&queue);
1294
buffpek->base= sort_buffer;
1295
buffpek->max_keys= param->keys;
1298
As we know all entries in the buffer are unique, we only have to
1299
check if the first one is the same as the last one we wrote
1303
if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key))
1305
buffpek->key+= rec_length; // Remove duplicate
1306
--buffpek->mem_count;
1312
if ((ha_rows) buffpek->mem_count > max_rows)
1313
{ /* Don't write too many records */
1314
buffpek->mem_count= (uint) max_rows;
1315
buffpek->count= 0; /* Don't read more */
1317
max_rows-= buffpek->mem_count;
1320
if (my_b_write(to_file,(uchar*) buffpek->key,
1321
(rec_length*buffpek->mem_count)))
1323
error= 1; goto err; /* purecov: inspected */
1328
register uchar *end;
1329
strpos= buffpek->key+offset;
1330
for (end= strpos+buffpek->mem_count*rec_length ;
1332
strpos+= rec_length)
1334
if (my_b_write(to_file, (uchar *) strpos, res_length))
1341
while ((error=(int) read_to_buffer(from_file,buffpek, rec_length))
1342
!= -1 && error != 0);
1345
lastbuff->count= min(org_max_rows-max_rows, param->max_rows);
1346
lastbuff->file_pos= to_start_filepos;
1348
delete_queue(&queue);
1350
} /* merge_buffers */
1353
/* Do a merge to output-file (save only positions) */
1355
static int merge_index(SORTPARAM *param, uchar *sort_buffer,
1356
BUFFPEK *buffpek, uint maxbuffer,
1357
IO_CACHE *tempfile, IO_CACHE *outfile)
1359
DBUG_ENTER("merge_index");
1360
if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
1361
buffpek+maxbuffer,1))
1362
DBUG_RETURN(1); /* purecov: inspected */
1367
static uint suffix_length(ulong string_length)
1369
if (string_length < 256)
1371
if (string_length < 256L*256L)
1373
if (string_length < 256L*256L*256L)
1375
return 4; // Can't sort longer than 4G
1381
Calculate length of sort key.
1383
@param thd Thread handler
1384
@param sortorder Order of items to sort
1385
@param s_length Number of items to sort
1386
@param[out] multi_byte_charset Set to 1 if we are using multi-byte charset
1387
(In which case we have to use strxnfrm())
1390
sortorder->length is updated for each sort item.
1392
sortorder->need_strxnfrm is set 1 if we have to use strxnfrm
1395
Total length of sort buffer in bytes
1399
sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
1400
bool *multi_byte_charset)
1402
register uint length;
1404
*multi_byte_charset= 0;
1407
for (; s_length-- ; sortorder++)
1409
sortorder->need_strxnfrm= 0;
1410
sortorder->suffix_length= 0;
1411
if (sortorder->field)
1413
cs= sortorder->field->sort_charset();
1414
sortorder->length= sortorder->field->sort_length();
1416
if (use_strnxfrm((cs=sortorder->field->sort_charset())))
1418
sortorder->need_strxnfrm= 1;
1419
*multi_byte_charset= 1;
1420
sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
1422
if (sortorder->field->maybe_null())
1423
length++; // Place for NULL marker
1427
sortorder->result_type= sortorder->item->result_type();
1428
if (sortorder->item->result_as_longlong())
1429
sortorder->result_type= INT_RESULT;
1430
switch (sortorder->result_type) {
1432
sortorder->length=sortorder->item->max_length;
1433
set_if_smaller(sortorder->length, thd->variables.max_sort_length);
1434
if (use_strnxfrm((cs=sortorder->item->collation.collation)))
1436
sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
1437
sortorder->need_strxnfrm= 1;
1438
*multi_byte_charset= 1;
1440
else if (cs == &my_charset_bin)
1442
/* Store length last to be able to sort blob/varbinary */
1443
sortorder->suffix_length= suffix_length(sortorder->length);
1444
sortorder->length+= sortorder->suffix_length;
1448
#if SIZEOF_LONG_LONG > 4
1449
sortorder->length=8; // Size of intern longlong
1451
sortorder->length=4;
1454
case DECIMAL_RESULT:
1456
my_decimal_get_binary_size(sortorder->item->max_length -
1457
(sortorder->item->decimals ? 1 : 0),
1458
sortorder->item->decimals);
1461
sortorder->length=sizeof(double);
1465
// This case should never be choosen
1469
if (sortorder->item->maybe_null)
1470
length++; // Place for NULL marker
1472
set_if_smaller(sortorder->length, thd->variables.max_sort_length);
1473
length+=sortorder->length;
1475
sortorder->field= (Field*) 0; // end marker
1476
DBUG_PRINT("info",("sort_length: %d",length));
1482
Get descriptors of fields appended to sorted fields and
1483
calculate its total length.
1485
The function first finds out what fields are used in the result set.
1486
Then it calculates the length of the buffer to store the values of
1487
these fields together with the value of sort values.
1488
If the calculated length is not greater than max_length_for_sort_data
1489
the function allocates memory for an array of descriptors containing
1490
layouts for the values of the non-sorted fields in the buffer and
1493
@param thd Current thread
1494
@param ptabfield Array of references to the table fields
1495
@param sortlength Total length of sorted fields
1496
@param[out] plength Total length of appended fields
1499
The null bits for the appended values are supposed to be put together
1500
and stored the buffer just ahead of the value of the first field.
1503
Pointer to the layout descriptors for the appended fields, if any
1505
NULL if we do not store field values with sort data.
1508
static SORT_ADDON_FIELD *
1509
get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
1513
SORT_ADDON_FIELD *addonf;
1516
uint null_fields= 0;
1517
MY_BITMAP *read_set= (*ptabfield)->table->read_set;
1520
If there is a reference to a field in the query add it
1521
to the the set of appended fields.
1522
Note for future refinement:
1523
This this a too strong condition.
1524
Actually we need only the fields referred in the
1525
result set. And for some of them it makes sense to use
1526
the values directly from sorted fields.
1530
for (pfield= ptabfield; (field= *pfield) ; pfield++)
1532
if (!bitmap_is_set(read_set, field->field_index))
1534
if (field->flags & BLOB_FLAG)
1536
length+= field->max_packed_col_length(field->pack_length());
1537
if (field->maybe_null())
1543
length+= (null_fields+7)/8;
1545
if (length+sortlength > thd->variables.max_length_for_sort_data ||
1546
!(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)*
1547
(fields+1), MYF(MY_WME))))
1551
length= (null_fields+7)/8;
1553
for (pfield= ptabfield; (field= *pfield) ; pfield++)
1555
if (!bitmap_is_set(read_set, field->field_index))
1557
addonf->field= field;
1558
addonf->offset= length;
1559
if (field->maybe_null())
1561
addonf->null_offset= null_fields/8;
1562
addonf->null_bit= 1<<(null_fields & 7);
1567
addonf->null_offset= 0;
1568
addonf->null_bit= 0;
1570
addonf->length= field->max_packed_col_length(field->pack_length());
1571
length+= addonf->length;
1574
addonf->field= 0; // Put end marker
1576
DBUG_PRINT("info",("addon_length: %d",length));
1577
return (addonf-fields);
1582
Copy (unpack) values appended to sorted fields from a buffer back to
1583
their regular positions specified by the Field::ptr pointers.
1585
@param addon_field Array of descriptors for appended fields
1586
@param buff Buffer which to unpack the value from
1589
The function is supposed to be used only as a callback function
1590
when getting field values for the sorted result set.
1597
unpack_addon_fields(struct st_sort_addon_field *addon_field, uchar *buff)
1600
SORT_ADDON_FIELD *addonf= addon_field;
1602
for ( ; (field= addonf->field) ; addonf++)
1604
if (addonf->null_bit && (addonf->null_bit & buff[addonf->null_offset]))
1609
field->set_notnull();
1610
field->unpack(field->ptr, buff + addonf->offset);
1615
** functions to change a double or float to a sortable string
1616
** The following should work for IEEE
1619
#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)
1621
void change_double_for_sort(double nr,uchar *to)
1623
uchar *tmp=(uchar*) to;
1625
{ /* Change to zero string */
1627
bzero((char*) tmp+1,sizeof(nr)-1);
1631
#ifdef WORDS_BIGENDIAN
1632
memcpy_fixed(tmp,&nr,sizeof(nr));
1635
uchar *ptr= (uchar*) &nr;
1636
#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
1637
tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
1638
tmp[4]= ptr[7]; tmp[5]=ptr[6]; tmp[6]= ptr[5]; tmp[7]=ptr[4];
1640
tmp[0]= ptr[7]; tmp[1]=ptr[6]; tmp[2]= ptr[5]; tmp[3]=ptr[4];
1641
tmp[4]= ptr[3]; tmp[5]=ptr[2]; tmp[6]= ptr[1]; tmp[7]=ptr[0];
1645
if (tmp[0] & 128) /* Negative */
1646
{ /* make complement */
1648
for (i=0 ; i < sizeof(nr); i++)
1649
tmp[i]=tmp[i] ^ (uchar) 255;
1652
{ /* Set high and move exponent one up */
1653
ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
1655
exp_part+= (ushort) 1 << (16-1-DBL_EXP_DIG);
1656
tmp[0]= (uchar) (exp_part >> 8);
1657
tmp[1]= (uchar) exp_part;