filesort.cc 45.9 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB  
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
unknown's avatar
unknown committed
12

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20 21 22 23 24
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


/* Sorts a database */

#include "mysql_priv.h"
#ifdef HAVE_STDDEF_H
#include <stddef.h>			/* for macro offsetof */
#endif
#include <m_ctype.h>
25 26
#include "sql_sort.h"

unknown's avatar
unknown committed
27
#ifndef THREAD
unknown's avatar
unknown committed
28
#define SKIP_DBUG_IN_FILESORT
unknown's avatar
unknown committed
29 30 31 32 33 34 35 36 37 38 39
#endif

	/* How to write record_ref. */

#define WRITE_REF(file,from) \
if (my_b_write((file),(byte*) (from),param->ref_length)) \
  DBUG_RETURN(1);

	/* functions defined in this file */

static char **make_char_array(register uint fields, uint length, myf my_flag);
40
static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffer_file, uint count);
unknown's avatar
unknown committed
41
static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
42
			     uchar * *sort_keys, IO_CACHE *buffer_file,
unknown's avatar
unknown committed
43 44
			     IO_CACHE *tempfile,IO_CACHE *indexfile);
static int write_keys(SORTPARAM *param,uchar * *sort_keys,
45 46
		      uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
static void make_sortkey(SORTPARAM *param,uchar *to, byte *ref_pos);
47
static void register_used_fields(SORTPARAM *param);
48
static int merge_index(SORTPARAM *param,uchar *sort_buffer,
unknown's avatar
unknown committed
49 50 51
		       BUFFPEK *buffpek,
		       uint maxbuffer,IO_CACHE *tempfile,
		       IO_CACHE *outfile);
52 53
static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count, 
                       FILESORT_INFO *table_sort);
54 55
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
56
		       bool *multi_byte_charset);
unknown's avatar
unknown committed
57 58 59 60
static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield,
                                          uint sortlength, uint *plength);
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
                                byte *buff);
unknown's avatar
unknown committed
61

62 63 64 65 66 67 68 69
/*
  Sort a table

  SYNOPSIS
    filesort()
    table		Table to sort
    sortorder		How to sort the table
    s_length		Number of elements in sortorder	
70 71 72 73 74
    select		Condition to apply to the rows
    ha_maxrows		Return only this many rows
    sort_positions	Set to 1 if we want to force sorting by position
			(Needed by UPDATE/INSERT or ALTER TABLE)
    examined_rows	Store number of examined rows here
75 76 77 78 79 80 81 82 83 84

  IMPLEMENTATION
    Creates a set of pointers that can be used to read the rows
    in sorted order. This should be done with the functions
    in records.cc
  
  REQUIREMENTS
    Before calling filesort, one must have done
    table->file->info(HA_STATUS_VARIABLE)

85 86 87 88
  NOTES
    If we sort by position (like if sort_positions is 1) filesort() will
    call table->prepare_for_position().

89 90 91
  RETURN
    HA_POS_ERROR	Error
    #			Number of rows
unknown's avatar
unknown committed
92

93
    examined_rows	will be set to number of examined rows
94

95 96 97
    The result set is stored in table->io_cache or
    table->record_pointers
*/
98

99
ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
100 101
		 SQL_SELECT *select, ha_rows max_rows,
                 bool sort_positions, ha_rows *examined_rows)
unknown's avatar
unknown committed
102 103
{
  int error;
unknown's avatar
unknown committed
104
  ulong memavl, min_sort_memory;
unknown's avatar
unknown committed
105
  uint maxbuffer;
unknown's avatar
unknown committed
106
  BUFFPEK *buffpek;
unknown's avatar
unknown committed
107
  ha_rows records= HA_POS_ERROR;
unknown's avatar
unknown committed
108
  uchar **sort_keys;
109
  IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile; 
unknown's avatar
unknown committed
110
  SORTPARAM param;
111
  bool multi_byte_charset;
112 113
  DBUG_ENTER("filesort");
  DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length););
unknown's avatar
unknown committed
114
#ifdef SKIP_DBUG_IN_FILESORT
unknown's avatar
unknown committed
115 116
  DBUG_PUSH("");		/* No DBUG here */
#endif
117
  FILESORT_INFO table_sort;
unknown's avatar
unknown committed
118 119
  TABLE_LIST *tab= table->pos_in_table_list;
  Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
120
  /* 
121 122 123
    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.
124 125 126
  */
  memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
  table->sort.io_cache= NULL;
127
  
128
  outfile= table_sort.io_cache;
unknown's avatar
unknown committed
129
  my_b_clear(&tempfile);
130 131 132
  my_b_clear(&buffpek_pointers);
  buffpek=0;
  error= 1;
133
  bzero((char*) &param,sizeof(param));
134
  param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset);
135
  param.ref_length= table->file->ref_length;
unknown's avatar
unknown committed
136 137
  param.addon_field= 0;
  param.addon_length= 0;
138 139
  if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
      !table->fulltext_searched && !sort_positions)
unknown's avatar
unknown committed
140 141 142 143 144 145 146 147 148
  {
    /* 
      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);
  }
149 150 151 152 153

  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;
unknown's avatar
unknown committed
154 155 156
  if (param.addon_field)
  {
    param.res_length= param.addon_length;
157
    if (!(table_sort.addon_buf= (byte *) my_malloc(param.addon_length,
unknown's avatar
unknown committed
158 159 160 161 162 163 164 165 166 167 168 169 170
                                                    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;
unknown's avatar
unknown committed
171
  param.max_rows= max_rows;
unknown's avatar
unknown committed
172

173 174
  if (select && select->quick)
  {
175
    statistic_increment(thd->status_var.filesort_range_count, &LOCK_status);
176 177 178
  }
  else
  {
179
    statistic_increment(thd->status_var.filesort_scan_count, &LOCK_status);
180
  }
unknown's avatar
unknown committed
181
#ifdef CAN_TRUST_RANGE
182
  if (select && select->quick && select->quick->records > 0L)
unknown's avatar
unknown committed
183 184
  {
    records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2),
185
		table->file->stats.records)+EXTRA_RECORDS;
unknown's avatar
unknown committed
186 187 188
    selected_records_file=0;
  }
  else
189
#endif
unknown's avatar
unknown committed
190
  {
unknown's avatar
unknown committed
191 192 193 194 195 196 197
    records= table->file->estimate_rows_upper_bound();
    /*
      If number of records is not known, use as much of sort buffer 
      as possible. 
    */
    if (records == HA_POS_ERROR)
      records--;  // we use 'records+1' below.
unknown's avatar
unknown committed
198 199 200
    selected_records_file= 0;
  }

201
  if (multi_byte_charset &&
unknown's avatar
unknown committed
202 203 204
      !(param.tmp_buffer=my_malloc(param.sort_length,MYF(MY_WME))))
    goto err;

205
  memavl= thd->variables.sortbuff_size;
unknown's avatar
unknown committed
206 207
  min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
  while (memavl >= min_sort_memory)
unknown's avatar
unknown committed
208
  {
209
    ulong old_memavl;
unknown's avatar
unknown committed
210
    ulong keys= memavl/(param.rec_length+sizeof(char*));
211
    param.keys=(uint) min(records+1, keys);
unknown's avatar
unknown committed
212 213 214
    if (table_sort.sort_keys ||
        (table_sort.sort_keys= (uchar **) make_char_array(param.keys, param.rec_length,
                                               MYF(0))))
215
      break;
unknown's avatar
unknown committed
216
    old_memavl=memavl;
unknown's avatar
unknown committed
217
    if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory)
unknown's avatar
unknown committed
218
      memavl= min_sort_memory;
unknown's avatar
unknown committed
219
  }
unknown's avatar
unknown committed
220
  sort_keys= table_sort.sort_keys;
unknown's avatar
unknown committed
221
  if (memavl < min_sort_memory)
unknown's avatar
unknown committed
222
  {
223 224
    my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),
	     thd->variables.sortbuff_size);
unknown's avatar
unknown committed
225 226
    goto err;
  }
227 228 229 230
  if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
		       DISK_BUFFER_SIZE, MYF(MY_WME)))
    goto err;

231
  param.keys--;  			/* TODO: check why we do this */
232
  param.sort_form= table;
unknown's avatar
unknown committed
233
  param.end=(param.local_sortorder=sortorder)+s_length;
234
  if ((records=find_all_keys(&param,select,sort_keys, &buffpek_pointers,
unknown's avatar
unknown committed
235 236 237
			     &tempfile, selected_records_file)) ==
      HA_POS_ERROR)
    goto err;
unknown's avatar
unknown committed
238
  maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
239

240
  if (maxbuffer == 0)			// The whole set is in memory
unknown's avatar
unknown committed
241
  {
242
    if (save_index(&param,sort_keys,(uint) records, &table_sort))
unknown's avatar
unknown committed
243 244 245 246
      goto err;
  }
  else
  {
unknown's avatar
unknown committed
247 248 249
    if (!table_sort.buffpek && table_sort.buffpek_len < maxbuffer &&
        !(table_sort.buffpek=
          (byte *) read_buffpek_from_file(&buffpek_pointers, maxbuffer)))
250
      goto err;
unknown's avatar
unknown committed
251 252
    buffpek= (BUFFPEK *) table_sort.buffpek;
    table_sort.buffpek_len= maxbuffer;
253
    close_cached_file(&buffpek_pointers);
unknown's avatar
unknown committed
254 255 256 257 258 259 260
	/* 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(MY_WME)))
      goto err;
    reinit_io_cache(outfile,WRITE_CACHE,0L,0,0);

261 262 263 264
    /*
      Use also the space previously used by string pointers in sort_buffer
      for temporary key storage.
    */
unknown's avatar
unknown committed
265 266
    param.keys=((param.keys*(param.rec_length+sizeof(char*))) /
		param.rec_length-1);
267
    maxbuffer--;				// Offset from 0
268 269
    if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
			&tempfile))
unknown's avatar
unknown committed
270 271 272 273
      goto err;
    if (flush_io_cache(&tempfile) ||
	reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
      goto err;
274 275
    if (merge_index(&param,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile,
		    outfile))
unknown's avatar
unknown committed
276 277 278 279 280 281 282
      goto err;
  }
  if (records > param.max_rows)
    records=param.max_rows;
  error =0;

 err:
unknown's avatar
unknown committed
283
  if (param.tmp_buffer)
unknown's avatar
unknown committed
284
    x_free(param.tmp_buffer);
unknown's avatar
unknown committed
285 286 287 288 289 290 291 292
  if (!subselect || !subselect->is_uncacheable())
  {
    x_free((gptr) sort_keys);
    table_sort.sort_keys= 0;
    x_free((gptr) buffpek);
    table_sort.buffpek= 0;
    table_sort.buffpek_len= 0;
  }
unknown's avatar
unknown committed
293
  close_cached_file(&tempfile);
294
  close_cached_file(&buffpek_pointers);
unknown's avatar
unknown committed
295 296 297 298 299 300 301 302 303 304 305 306 307
  if (my_b_inited(outfile))
  {
    if (flush_io_cache(outfile))
      error=1;
    {
      my_off_t save_pos=outfile->pos_in_file;
      /* For following reads */
      if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
	error=1;
      outfile->end_of_file=save_pos;
    }
  }
  if (error)
unknown's avatar
unknown committed
308 309
    my_message(ER_FILSORT_ABORT, ER(ER_FILSORT_ABORT),
               MYF(ME_ERROR+ME_WAITTANG));
310
  else
311 312
    statistic_add(thd->status_var.filesort_rows,
		  (ulong) records, &LOCK_status);
313
  *examined_rows= param.examined_rows;
unknown's avatar
unknown committed
314
#ifdef SKIP_DBUG_IN_FILESORT
unknown's avatar
unknown committed
315 316
  DBUG_POP();			/* Ok to DBUG */
#endif
317
  memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
unknown's avatar
unknown committed
318 319 320 321 322
  DBUG_PRINT("exit",("records: %ld",records));
  DBUG_RETURN(error ? HA_POS_ERROR : records);
} /* filesort */


unknown's avatar
unknown committed
323
void filesort_free_buffers(TABLE *table, bool full)
unknown's avatar
unknown committed
324 325 326 327 328 329
{
  if (table->sort.record_pointers)
  {
    my_free((gptr) table->sort.record_pointers,MYF(0));
    table->sort.record_pointers=0;
  }
unknown's avatar
unknown committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343
  if (full)
  {
    if (table->sort.sort_keys )
    {
      x_free((gptr) table->sort.sort_keys);
      table->sort.sort_keys= 0;
    }
    if (table->sort.buffpek)
    {
      x_free((gptr) table->sort.buffpek);
      table->sort.buffpek= 0;
      table->sort.buffpek_len= 0;
    }
  }
unknown's avatar
unknown committed
344 345 346 347 348 349 350 351 352
  if (table->sort.addon_buf)
  {
    my_free((char *) table->sort.addon_buf, MYF(0));
    my_free((char *) table->sort.addon_field, MYF(MY_ALLOW_ZERO_PTR));
    table->sort.addon_buf=0;
    table->sort.addon_field=0;
  }
}

unknown's avatar
unknown committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	/* Make a array of string pointers */

static char **make_char_array(register uint fields, uint length, myf my_flag)
{
  register char **pos;
  char **old_pos,*char_pos;
  DBUG_ENTER("make_char_array");

  if ((old_pos= (char**) my_malloc((uint) fields*(length+sizeof(char*)),
				    my_flag)))
  {
    pos=old_pos; char_pos=((char*) (pos+fields)) -length;
    while (fields--) *(pos++) = (char_pos+= length);
  }

  DBUG_RETURN(old_pos);
} /* make_char_array */


unknown's avatar
unknown committed
372
/* Read 'count' number of buffer pointers into memory */
373 374 375 376 377 378 379 380 381 382

static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count)
{
  ulong length;
  BUFFPEK *tmp;
  DBUG_ENTER("read_buffpek_from_file");
  tmp=(BUFFPEK*) my_malloc(length=sizeof(BUFFPEK)*count, MYF(MY_WME));
  if (tmp)
  {
    if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
unknown's avatar
unknown committed
383
	my_b_read(buffpek_pointers, (byte*) tmp, length))
384 385 386
    {
      my_free((char*) tmp, MYF(0));
      tmp=0;
unknown's avatar
unknown committed
387
    }
388 389
  }
  DBUG_RETURN(tmp);
unknown's avatar
unknown committed
390
}
391 392


unknown's avatar
unknown committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
/* 
  Search after sort_keys and write them into tempfile.
  SYNOPSIS
    find_all_keys()
      param             Sorting parameter
      select            Use this to get source data
      sort_keys         Array of pointers to sort key + addon buffers.
      buffpek_pointers  File to write BUFFPEKs describing sorted segments
                        in tempfile.
      tempfile          File to write sorted sequences of sortkeys to.
      indexfile         If !NULL, use it for source data (contains rowids)
  
  NOTE
    Basic idea:
      while (get_next_sortkey())
      {
        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';
      }
      if (sort_keys has some elements && dumped at least once)
        sort-dump-dump as above;
      else
        don't sort, leave sort_keys array to be sorted by caller.
    
     All produced sequences are guaranteed to be non-empty.
  RETURN
    Number of records written on success.
    HA_POS_ERROR on error.
*/
unknown's avatar
unknown committed
427 428 429

static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
			     uchar **sort_keys,
430
			     IO_CACHE *buffpek_pointers,
unknown's avatar
unknown committed
431 432 433 434 435 436 437
			     IO_CACHE *tempfile, IO_CACHE *indexfile)
{
  int error,flag,quick_select;
  uint idx,indexpos,ref_length;
  byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
  my_off_t record;
  TABLE *sort_form;
unknown's avatar
SCRUM  
unknown committed
438
  volatile THD::killed_state *killed= &current_thd->killed;
unknown's avatar
unknown committed
439
  handler *file;
440
  MY_BITMAP *save_read_set, *save_write_set;
unknown's avatar
unknown committed
441
  DBUG_ENTER("find_all_keys");
442 443 444
  DBUG_PRINT("info",("using: %s",
                     (select ? select->quick ? "ranges" : "where":
                      "every row")));
unknown's avatar
unknown committed
445 446 447 448 449 450 451 452 453

  idx=indexpos=0;
  error=quick_select=0;
  sort_form=param->sort_form;
  file=sort_form->file;
  ref_length=param->ref_length;
  ref_pos= ref_buff;
  quick_select=select && select->quick;
  record=0;
454
  flag= ((!indexfile && file->ha_table_flags() & HA_REC_NOT_IN_SEQ)
unknown's avatar
unknown committed
455 456 457 458 459 460 461
	 || quick_select);
  if (indexfile || flag)
    ref_pos= &file->ref[0];
  next_pos=ref_pos;
  if (! indexfile && ! quick_select)
  {
    next_pos=(byte*) 0;			/* Find records in sequence */
462
    file->ha_rnd_init(1);
unknown's avatar
unknown committed
463 464
    file->extra_opt(HA_EXTRA_CACHE,
		    current_thd->variables.read_buff_size);
unknown's avatar
unknown committed
465 466
  }

467 468 469 470 471 472 473 474 475
  READ_RECORD read_record_info;
  if (quick_select)
  {
    if (select->quick->reset())
      DBUG_RETURN(HA_POS_ERROR);
    init_read_record(&read_record_info, current_thd, select->quick->head,
                     select, 1, 1);
  }

476 477 478 479 480 481 482 483
  /* Remember original bitmaps */
  save_read_set=  sort_form->read_set;
  save_write_set= sort_form->write_set;
  /* Set up temporary column read map for columns used by sort */
  bitmap_clear_all(&sort_form->tmp_set);
  /* Temporary set for register_used_fields and register_field_in_read_map */
  sort_form->read_set= &sort_form->tmp_set;
  register_used_fields(param);
484
  if (select && select->cond)
485 486 487 488
    select->cond->walk(&Item::register_field_in_read_map, 1,
                       (byte*) sort_form);
  sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set);

unknown's avatar
unknown committed
489 490 491 492
  for (;;)
  {
    if (quick_select)
    {
493 494 495 496 497
      if ((error= read_record_info.read_record(&read_record_info)))
      {
        error= HA_ERR_END_OF_FILE;
        break;
      }
unknown's avatar
unknown committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
      file->position(sort_form->record[0]);
    }
    else					/* Not quick-select */
    {
      if (indexfile)
      {
	if (my_b_read(indexfile,(byte*) ref_pos,ref_length)) /* purecov: deadcode */
	{
	  error= my_errno ? my_errno : -1;		/* Abort */
	  break;
	}
	error=file->rnd_pos(sort_form->record[0],next_pos);
      }
      else
      {
	error=file->rnd_next(sort_form->record[0]);
	if (!flag)
	{
unknown's avatar
unknown committed
516
	  my_store_ptr(ref_pos,ref_length,record); // Position to row
517
	  record+= sort_form->s->db_record_offset;
unknown's avatar
unknown committed
518
	}
unknown's avatar
unknown committed
519
	else if (!error)
unknown's avatar
unknown committed
520
	  file->position(sort_form->record[0]);
unknown's avatar
unknown committed
521 522 523 524
      }
      if (error && error != HA_ERR_RECORD_DELETED)
	break;
    }
525

unknown's avatar
unknown committed
526 527
    if (*killed)
    {
528
      DBUG_PRINT("info",("Sort killed by user"));
529 530 531 532 533
      if (!indexfile && !quick_select)
      {
        (void) file->extra(HA_EXTRA_NO_CACHE);
        file->ha_rnd_end();
      }
unknown's avatar
unknown committed
534 535
      DBUG_RETURN(HA_POS_ERROR);		/* purecov: inspected */
    }
536 537
    if (error == 0)
      param->examined_rows++;
538
    if (error == 0 && (!select || select->skip_record() == 0))
unknown's avatar
unknown committed
539 540 541
    {
      if (idx == param->keys)
      {
542 543
	if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
	  DBUG_RETURN(HA_POS_ERROR);
544 545
	idx=0;
	indexpos++;
unknown's avatar
unknown committed
546 547 548
      }
      make_sortkey(param,sort_keys[idx++],ref_pos);
    }
unknown's avatar
unknown committed
549 550
    else
      file->unlock_row();
unknown's avatar
unknown committed
551
  }
552
  if (quick_select)
553 554
  {
    /*
555
      index_merge quick select uses table->sort when retrieving rows, so free
556 557
      resoures it has allocated.
    */
558
    end_read_record(&read_record_info);
559
  }
560 561 562
  else
  {
    (void) file->extra(HA_EXTRA_NO_CACHE);	/* End cacheing of records */
unknown's avatar
unknown committed
563 564
    if (!next_pos)
      file->ha_rnd_end();
565 566
  }

567 568 569
  /* Signal we should use orignal column read and write maps */
  sort_form->column_bitmaps_set(save_read_set, save_write_set);

unknown's avatar
unknown committed
570 571 572 573 574 575
  DBUG_PRINT("test",("error: %d  indexpos: %d",error,indexpos));
  if (error != HA_ERR_END_OF_FILE)
  {
    file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); /* purecov: inspected */
    DBUG_RETURN(HA_POS_ERROR);			/* purecov: inspected */
  }
576
  if (indexpos && idx &&
577 578
      write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
    DBUG_RETURN(HA_POS_ERROR);			/* purecov: inspected */
unknown's avatar
unknown committed
579
  DBUG_RETURN(my_b_inited(tempfile) ?
unknown's avatar
unknown committed
580
	      (ha_rows) (my_b_tell(tempfile)/param->rec_length) :
unknown's avatar
unknown committed
581 582 583 584
	      idx);
} /* find_all_keys */


unknown's avatar
unknown committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
/*
  Sort the buffer and write:
    1) the sorted sequence to tempfile
    2) a BUFFPEK describing the sorted sequence position to buffpek_pointers
  (was: Skriver en buffert med nycklar till filen)
  SYNOPSIS
    write_keys()
      param             Sort parameters
      sort_keys         Array of pointers to keys to sort
      count             Number of elements in sort_keys array 
      buffpek_pointers  One 'BUFFPEK' struct will be written into this file.
                        The BUFFPEK::{file_pos, count} will indicate where 
                        the sorted data was stored.
      tempfile          The sorted sequence will be written into this file.
    
  RETURN
    0 OK
    1 Error
*/
unknown's avatar
unknown committed
604

unknown's avatar
unknown committed
605 606
static int
write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
unknown's avatar
unknown committed
607
           IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
unknown's avatar
unknown committed
608
{
unknown's avatar
unknown committed
609
  uint sort_length, rec_length;
unknown's avatar
unknown committed
610
  uchar **end;
611
  BUFFPEK buffpek;
unknown's avatar
unknown committed
612 613
  DBUG_ENTER("write_keys");

unknown's avatar
unknown committed
614 615
  sort_length= param->sort_length;
  rec_length= param->rec_length;
unknown's avatar
unknown committed
616 617 618
#ifdef MC68000
  quicksort(sort_keys,count,sort_length);
#else
unknown's avatar
unknown committed
619
  my_string_ptr_sort((gptr) sort_keys, (uint) count, sort_length);
unknown's avatar
unknown committed
620 621
#endif
  if (!my_b_inited(tempfile) &&
unknown's avatar
unknown committed
622 623
      open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
                       MYF(MY_WME)))
624
    goto err;                                   /* purecov: inspected */
unknown's avatar
unknown committed
625
  buffpek.file_pos= my_b_tell(tempfile);
unknown's avatar
unknown committed
626
  if ((ha_rows) count > param->max_rows)
627
    count=(uint) param->max_rows;               /* purecov: inspected */
628
  buffpek.count=(ha_rows) count;
unknown's avatar
unknown committed
629
  for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
unknown's avatar
unknown committed
630
    if (my_b_write(tempfile, (byte*) *sort_keys, (uint) rec_length))
631
      goto err;
unknown's avatar
unknown committed
632
  if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek)))
633
    goto err;
unknown's avatar
unknown committed
634
  DBUG_RETURN(0);
635 636 637

err:
  DBUG_RETURN(1);
unknown's avatar
unknown committed
638 639 640
} /* write_keys */


641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
/*
  Store length as suffix in high-byte-first order
*/

static inline void store_length(uchar *to, uint length, uint pack_length)
{
  switch (pack_length) {
  case 1:
    *to= (uchar) length;
    break;
  case 2:
    mi_int2store(to, length);
    break;
  case 3:
    mi_int3store(to, length);
656
    break;
657 658 659 660 661 662 663
  default:
    mi_int4store(to, length);
    break;
  }
}


unknown's avatar
unknown committed
664 665 666 667 668 669 670 671 672 673 674 675 676
	/* makes a sort-key from record */

static void make_sortkey(register SORTPARAM *param,
			 register uchar *to, byte *ref_pos)
{
  reg3 Field *field;
  reg1 SORT_FIELD *sort_field;
  reg5 uint length;

  for (sort_field=param->local_sortorder ;
       sort_field != param->end ;
       sort_field++)
  {
unknown's avatar
unknown committed
677
    bool maybe_null=0;
unknown's avatar
unknown committed
678 679 680 681 682 683
    if ((field=sort_field->field))
    {						// Field
      if (field->maybe_null())
      {
	if (field->is_null())
	{
unknown's avatar
unknown committed
684 685 686 687
	  if (sort_field->reverse)
	    bfill(to,sort_field->length+1,(char) 255);
	  else
	    bzero((char*) to,sort_field->length+1);
unknown's avatar
unknown committed
688 689 690 691 692 693 694 695 696 697 698
	  to+= sort_field->length+1;
	  continue;
	}
	else
	  *to++=1;
      }
      field->sort_string((char*) to,sort_field->length);
    }
    else
    {						// Item
      Item *item=sort_field->item;
699
      maybe_null= item->maybe_null;
unknown's avatar
unknown committed
700 701
      switch (sort_field->result_type) {
      case STRING_RESULT:
702
      {
unknown's avatar
unknown committed
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
        CHARSET_INFO *cs=item->collation.collation;
        char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
        int diff;
        uint sort_field_length;

        if (maybe_null)
          *to++=1;
        /* All item->str() to use some extra byte for end null.. */
        String tmp((char*) to,sort_field->length+4,cs);
        String *res= item->str_result(&tmp);
        if (!res)
        {
          if (maybe_null)
            bzero((char*) to-1,sort_field->length+1);
          else
unknown's avatar
unknown committed
718
          {
719 720 721 722 723 724 725
            /* purecov: begin deadcode */
            /*
              This should only happen during extreme conditions if we run out
              of memory or have an item marked not null when it can be null.
              This code is here mainly to avoid a hard crash in this case.
            */
            DBUG_ASSERT(0);
unknown's avatar
unknown committed
726 727 728
            DBUG_PRINT("warning",
                       ("Got null on something that shouldn't be null"));
            bzero((char*) to,sort_field->length);	// Avoid crash
729
            /* purecov: end */
unknown's avatar
unknown committed
730
          }
unknown's avatar
unknown committed
731 732 733 734 735 736 737
          break;
        }
        length= res->length();
        sort_field_length= sort_field->length - sort_field->suffix_length;
        diff=(int) (sort_field_length - length);
        if (diff < 0)
        {
738
          diff=0;
unknown's avatar
unknown committed
739 740 741 742 743 744 745 746 747 748 749 750 751
          length= sort_field_length;
        }
        if (sort_field->suffix_length)
        {
          /* Store length last in result_string */
          store_length(to + sort_field_length, length,
                       sort_field->suffix_length);
        }
        if (sort_field->need_strxnfrm)
        {
          char *from=(char*) res->ptr();
          uint tmp_length;
          if ((uchar*) from == to)
unknown's avatar
unknown committed
752
          {
unknown's avatar
unknown committed
753 754 755
            set_if_smaller(length,sort_field->length);
            memcpy(param->tmp_buffer,from,length);
            from=param->tmp_buffer;
unknown's avatar
unknown committed
756
          }
unknown's avatar
unknown committed
757 758 759 760 761 762 763 764 765 766
          tmp_length= my_strnxfrm(cs,to,sort_field->length,
                                  (uchar*) from, length);
          DBUG_ASSERT(tmp_length == sort_field->length);
        }
        else
        {
          my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length);
          cs->cset->fill(cs, (char *)to+length,diff,fill_char);
        }
        break;
767
      }
unknown's avatar
unknown committed
768 769
      case INT_RESULT:
	{
770 771
          longlong value= item->val_int_result();
          if (maybe_null)
772
          {
unknown's avatar
unknown committed
773
	    *to++=1;				/* purecov: inspected */
774 775 776 777 778 779 780 781 782 783 784 785 786
            if (item->null_value)
            {
              if (maybe_null)
                bzero((char*) to-1,sort_field->length+1);
              else
              {
                DBUG_PRINT("warning",
                           ("Got null on something that shouldn't be null"));
                bzero((char*) to,sort_field->length);
              }
              break;
            }
          }
unknown's avatar
unknown committed
787 788 789 790 791 792 793 794
#if SIZEOF_LONG_LONG > 4
	  to[7]= (uchar) value;
	  to[6]= (uchar) (value >> 8);
	  to[5]= (uchar) (value >> 16);
	  to[4]= (uchar) (value >> 24);
	  to[3]= (uchar) (value >> 32);
	  to[2]= (uchar) (value >> 40);
	  to[1]= (uchar) (value >> 48);
unknown's avatar
unknown committed
795 796 797 798
          if (item->unsigned_flag)                    /* Fix sign */
            to[0]= (uchar) (value >> 56);
          else
            to[0]= (uchar) (value >> 56) ^ 128;	/* Reverse signbit */
unknown's avatar
unknown committed
799 800 801 802
#else
	  to[3]= (uchar) value;
	  to[2]= (uchar) (value >> 8);
	  to[1]= (uchar) (value >> 16);
unknown's avatar
unknown committed
803 804 805 806
          if (item->unsigned_flag)                    /* Fix sign */
            to[0]= (uchar) (value >> 24);
          else
            to[0]= (uchar) (value >> 24) ^ 128;	/* Reverse signbit */
unknown's avatar
unknown committed
807 808 809
#endif
	  break;
	}
unknown's avatar
unknown committed
810 811
      case DECIMAL_RESULT:
        {
812
          my_decimal dec_buf, *dec_val= item->val_decimal_result(&dec_buf);
813 814 815 816 817 818 819 820
          if (maybe_null)
          {
            if (item->null_value)
            { 
              bzero((char*)to, sort_field->length+1);
              to++;
              break;
            }
unknown's avatar
unknown committed
821
            *to++=1;
822
          }
823
          my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, (char*)to,
unknown's avatar
unknown committed
824 825 826 827
                            item->max_length - (item->decimals ? 1:0),
                            item->decimals);
         break;
        }
unknown's avatar
unknown committed
828 829
      case REAL_RESULT:
	{
830
          double value= item->val_result();
831 832 833 834 835 836 837 838
	  if (maybe_null)
          {
            if (item->null_value)
            {
              bzero((char*) to,sort_field->length+1);
              to++;
              break;
            }
unknown's avatar
unknown committed
839
	    *to++=1;
840
          }
unknown's avatar
unknown committed
841 842 843
	  change_double_for_sort(value,(byte*) to);
	  break;
	}
844
      case ROW_RESULT:
unknown's avatar
unknown committed
845
      default: 
unknown's avatar
unknown committed
846 847 848
	// This case should never be choosen
	DBUG_ASSERT(0);
	break;
unknown's avatar
unknown committed
849 850 851 852
      }
    }
    if (sort_field->reverse)
    {							/* Revers key */
unknown's avatar
unknown committed
853 854
      if (maybe_null)
        to[-1]= ~to[-1];
unknown's avatar
unknown committed
855 856 857 858 859 860 861 862 863 864
      length=sort_field->length;
      while (length--)
      {
	*to = (uchar) (~ *to);
	to++;
      }
    }
    else
      to+= sort_field->length;
  }
unknown's avatar
unknown committed
865 866 867 868 869 870 871 872 873 874 875

  if (param->addon_field)
  {
    /* 
      Save field values appended to sorted fields.
      First null bit indicators are appended then field values follow.
      In this implementation we use fixed layout for field values -
      the same for all records.
    */
    SORT_ADDON_FIELD *addonf= param->addon_field;
    uchar *nulls= to;
876
    DBUG_ASSERT(addonf != 0);
unknown's avatar
unknown committed
877 878 879 880 881
    bzero((char *) nulls, addonf->offset);
    to+= addonf->offset;
    for ( ; (field= addonf->field) ; addonf++)
    {
      if (addonf->null_bit && field->is_null())
882
      {
unknown's avatar
unknown committed
883
        nulls[addonf->null_offset]|= addonf->null_bit;
884 885 886 887
#ifdef HAVE_purify
	bzero(to, addonf->length);
#endif
      }
unknown's avatar
unknown committed
888
      else
889 890 891 892 893 894 895 896 897
      {
        uchar *end= (uchar*) field->pack((char *) to, field->ptr);
#ifdef HAVE_purify
	uint length= (uint) ((to + addonf->length) - end);
	DBUG_ASSERT((int) length >= 0);
	if (length)
	  bzero(end, length);
#endif
      }
unknown's avatar
unknown committed
898 899 900 901 902 903 904 905
      to+= addonf->length;
    }
  }
  else
  {
    /* Save filepos last */
    memcpy((byte*) to, ref_pos, (size_s) param->ref_length);
  }
unknown's avatar
unknown committed
906 907 908
  return;
}

909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951

/*
  Register fields used by sorting in the sorted table's read set
*/

static void register_used_fields(SORTPARAM *param)
{
  reg1 SORT_FIELD *sort_field;
  TABLE *table=param->sort_form;
  MY_BITMAP *bitmap= table->read_set;

  for (sort_field= param->local_sortorder ;
       sort_field != param->end ;
       sort_field++)
  {
    Field *field;
    if ((field= sort_field->field))
    {
      if (field->table == table)
      bitmap_set_bit(bitmap, field->field_index);
    }
    else
    {						// Item
      sort_field->item->walk(&Item::register_field_in_read_map, 1,
                             (byte *) table);
    }
  }

  if (param->addon_field)
  {
    SORT_ADDON_FIELD *addonf= param->addon_field;
    Field *field;
    for ( ; (field= addonf->field) ; addonf++)
      bitmap_set_bit(bitmap, field->field_index);
  }
  else
  {
    /* Save filepos last */
    table->prepare_for_position();
  }
}


952 953
static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count, 
                       FILESORT_INFO *table_sort)
unknown's avatar
unknown committed
954
{
unknown's avatar
unknown committed
955
  uint offset,res_length;
unknown's avatar
unknown committed
956 957 958
  byte *to;
  DBUG_ENTER("save_index");

unknown's avatar
unknown committed
959 960 961
  my_string_ptr_sort((gptr) sort_keys, (uint) count, param->sort_length);
  res_length= param->res_length;
  offset= param->rec_length-res_length;
unknown's avatar
unknown committed
962 963
  if ((ha_rows) count > param->max_rows)
    count=(uint) param->max_rows;
964
  if (!(to= table_sort->record_pointers= 
unknown's avatar
unknown committed
965 966 967
        (byte*) my_malloc(res_length*count, MYF(MY_WME))))
    DBUG_RETURN(1);                 /* purecov: inspected */
  for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
unknown's avatar
unknown committed
968
  {
unknown's avatar
unknown committed
969 970
    memcpy(to, *sort_keys+offset, res_length);
    to+= res_length;
unknown's avatar
unknown committed
971 972 973 974 975 976 977
  }
  DBUG_RETURN(0);
}


	/* Merge buffers to make < MERGEBUFF2 buffers */

978 979
int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
		    BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
unknown's avatar
unknown committed
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
{
  register int i;
  IO_CACHE t_file2,*from_file,*to_file,*temp;
  BUFFPEK *lastbuff;
  DBUG_ENTER("merge_many_buff");

  if (*maxbuffer < MERGEBUFF2)
    DBUG_RETURN(0);				/* purecov: inspected */
  if (flush_io_cache(t_file) ||
      open_cached_file(&t_file2,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE,
			MYF(MY_WME)))
    DBUG_RETURN(1);				/* purecov: inspected */

  from_file= t_file ; to_file= &t_file2;
  while (*maxbuffer >= MERGEBUFF2)
  {
    reinit_io_cache(from_file,READ_CACHE,0L,0,0);
    reinit_io_cache(to_file,WRITE_CACHE,0L,0,0);
    lastbuff=buffpek;
    for (i=0 ; i <= (int) *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
    {
1001
      if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
unknown's avatar
unknown committed
1002 1003 1004
			buffpek+i,buffpek+i+MERGEBUFF-1,0))
	break;					/* purecov: inspected */
    }
1005
    if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
unknown's avatar
unknown committed
1006 1007 1008 1009 1010
		      buffpek+i,buffpek+ *maxbuffer,0))
      break;					/* purecov: inspected */
    if (flush_io_cache(to_file))
      break;					/* purecov: inspected */
    temp=from_file; from_file=to_file; to_file=temp;
1011 1012
    setup_io_cache(from_file);
    setup_io_cache(to_file);
unknown's avatar
unknown committed
1013 1014 1015 1016
    *maxbuffer= (uint) (lastbuff-buffpek)-1;
  }
  close_cached_file(to_file);			// This holds old result
  if (to_file == t_file)
unknown's avatar
unknown committed
1017
  {
unknown's avatar
unknown committed
1018
    *t_file=t_file2;				// Copy result file
unknown's avatar
unknown committed
1019 1020
    setup_io_cache(t_file);
  }
unknown's avatar
unknown committed
1021 1022 1023 1024 1025 1026 1027 1028

  DBUG_RETURN(*maxbuffer >= MERGEBUFF2);	/* Return 1 if interrupted */
} /* merge_many_buff */


	/* Read data to buffer */
	/* This returns (uint) -1 if something goes wrong */

1029
uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
unknown's avatar
unknown committed
1030
		    uint rec_length)
unknown's avatar
unknown committed
1031 1032 1033 1034 1035 1036 1037
{
  register uint count;
  uint length;

  if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
  {
    if (my_pread(fromfile->file,(byte*) buffpek->base,
unknown's avatar
unknown committed
1038
		 (length= rec_length*count),buffpek->file_pos,MYF_RW))
unknown's avatar
unknown committed
1039 1040 1041 1042 1043 1044
      return((uint) -1);			/* purecov: inspected */
    buffpek->key=buffpek->base;
    buffpek->file_pos+= length;			/* New filepos */
    buffpek->count-=	count;
    buffpek->mem_count= count;
  }
unknown's avatar
unknown committed
1045
  return (count*rec_length);
unknown's avatar
unknown committed
1046 1047 1048
} /* read_to_buffer */


1049 1050 1051 1052
/*
    Put all room used by freed buffer to use in adjacent buffer.  Note, that
    we can't simply distribute memory evenly between all buffers, because
    new areas must not overlap with old ones.
unknown's avatar
unknown committed
1053
  SYNOPSIS
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
    reuse_freed_buff()
    queue      IN  list of non-empty buffers, without freed buffer
    reuse      IN  empty buffer
    key_length IN  key length
*/

void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
{
  uchar *reuse_end= reuse->base + reuse->max_keys * key_length;
  for (uint i= 0; i < queue->elements; ++i)
  {
    BUFFPEK *bp= (BUFFPEK *) queue_element(queue, i);
    if (bp->base + bp->max_keys * key_length == reuse->base)
    {
      bp->max_keys+= reuse->max_keys;
      return;
    }
    else if (bp->base == reuse_end)
    {
      bp->base= reuse->base;
      bp->max_keys+= reuse->max_keys;
      return;
    }
  }
  DBUG_ASSERT(0);
}


unknown's avatar
unknown committed
1082
/* 
unknown's avatar
unknown committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
  Merge buffers to one buffer
  SYNOPSIS
    merge_buffers()
      param        Sort parameter
      from_file    File with source data (BUFFPEKs point to this file)
      to_file      File to write the sorted result data.
      sort_buffer  Buffer for data to store up to MERGEBUFF2 sort keys.
      lastbuff     OUT Store here BUFFPEK describing data written to to_file                   
      Fb           First element in source BUFFPEKs array
      Tb           Last element in source BUFFPEKs array
      flag

  RETURN
    0     - OK
    other - error
unknown's avatar
unknown committed
1098
*/
unknown's avatar
unknown committed
1099

1100
int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
unknown's avatar
unknown committed
1101 1102 1103
                  IO_CACHE *to_file, uchar *sort_buffer,
                  BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
                  int flag)
unknown's avatar
unknown committed
1104 1105
{
  int error;
unknown's avatar
unknown committed
1106
  uint rec_length,sort_length,res_length,offset;
unknown's avatar
unknown committed
1107
  ulong maxcount;
unknown's avatar
unknown committed
1108
  ha_rows max_rows,org_max_rows;
unknown's avatar
unknown committed
1109 1110
  my_off_t to_start_filepos;
  uchar *strpos;
unknown's avatar
unknown committed
1111
  BUFFPEK *buffpek;
unknown's avatar
unknown committed
1112
  QUEUE queue;
unknown's avatar
unknown committed
1113
  qsort2_cmp cmp;
unknown's avatar
SCRUM  
unknown committed
1114 1115
  volatile THD::killed_state *killed= &current_thd->killed;
  THD::killed_state not_killable;
unknown's avatar
unknown committed
1116 1117
  DBUG_ENTER("merge_buffers");

1118 1119
  statistic_increment(current_thd->status_var.filesort_merge_passes,
		      &LOCK_status);
1120 1121 1122
  if (param->not_killable)
  {
    killed= &not_killable;
unknown's avatar
unknown committed
1123
    not_killable= THD::NOT_KILLED;
1124
  }
1125

unknown's avatar
unknown committed
1126
  error=0;
unknown's avatar
unknown committed
1127 1128 1129 1130 1131 1132 1133 1134 1135
  rec_length= param->rec_length;
  res_length= param->res_length;
  sort_length= param->sort_length;
  offset= rec_length-res_length;
  maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
  to_start_filepos= my_b_tell(to_file);
  strpos= (uchar*) sort_buffer;
  org_max_rows=max_rows= param->max_rows;

unknown's avatar
unknown committed
1136 1137 1138
  /* The following will fire if there is not enough space in sort_buffer */
  DBUG_ASSERT(maxcount!=0);
  
unknown's avatar
unknown committed
1139 1140 1141 1142
  if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
                 (queue_compare) (cmp= get_ptr_compare(sort_length)),
                 (void*) &sort_length))
    DBUG_RETURN(1);                                /* purecov: inspected */
unknown's avatar
unknown committed
1143 1144 1145
  for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
  {
    buffpek->base= strpos;
unknown's avatar
unknown committed
1146 1147 1148
    buffpek->max_keys= maxcount;
    strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek,
                                                                         rec_length));
unknown's avatar
unknown committed
1149 1150
    if (error == -1)
      goto err;					/* purecov: inspected */
unknown's avatar
unknown committed
1151
    buffpek->max_keys= buffpek->mem_count;	// If less data in buffers than expected
unknown's avatar
unknown committed
1152
    queue_insert(&queue, (byte*) buffpek);
unknown's avatar
unknown committed
1153 1154
  }

1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
  if (param->unique_buff)
  {
    /* 
       Called by Unique::get()
       Copy the first argument to param->unique_buff for unique removal.
       Store it also in 'to_file'.

       This is safe as we know that there is always more than one element
       in each block to merge (This is guaranteed by the Unique:: algorithm
    */
unknown's avatar
unknown committed
1165 1166 1167
    buffpek= (BUFFPEK*) queue_top(&queue);
    memcpy(param->unique_buff, buffpek->key, rec_length);
    if (my_b_write(to_file, (byte*) buffpek->key, rec_length))
1168
    {
unknown's avatar
unknown committed
1169
      error=1; goto err;                        /* purecov: inspected */
1170
    }
unknown's avatar
unknown committed
1171
    buffpek->key+= rec_length;
unknown's avatar
unknown committed
1172
    buffpek->mem_count--;
1173 1174
    if (!--max_rows)
    {
unknown's avatar
unknown committed
1175 1176
      error= 0;                                       /* purecov: inspected */
      goto end;                                       /* purecov: inspected */
1177
    }
unknown's avatar
unknown committed
1178
    queue_replaced(&queue);                        // Top element has been used
1179 1180
  }
  else
unknown's avatar
unknown committed
1181
    cmp= 0;                                        // Not unique
1182

unknown's avatar
unknown committed
1183 1184
  while (queue.elements > 1)
  {
1185 1186
    if (*killed)
    {
unknown's avatar
unknown committed
1187
      error= 1; goto err;                        /* purecov: inspected */
1188
    }
unknown's avatar
unknown committed
1189 1190
    for (;;)
    {
unknown's avatar
unknown committed
1191 1192
      buffpek= (BUFFPEK*) queue_top(&queue);
      if (cmp)                                        // Remove duplicates
1193
      {
unknown's avatar
unknown committed
1194 1195 1196 1197
        if (!(*cmp)(&sort_length, &(param->unique_buff),
                    (uchar**) &buffpek->key))
              goto skip_duplicate;
            memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length);
1198
      }
unknown's avatar
unknown committed
1199 1200
      if (flag == 0)
      {
unknown's avatar
unknown committed
1201 1202 1203 1204
        if (my_b_write(to_file,(byte*) buffpek->key, rec_length))
        {
          error=1; goto err;                        /* purecov: inspected */
        }
unknown's avatar
unknown committed
1205 1206 1207
      }
      else
      {
unknown's avatar
unknown committed
1208 1209 1210 1211
        if (my_b_write(to_file, (byte*) buffpek->key+offset, res_length))
        {
          error=1; goto err;                        /* purecov: inspected */
        }
unknown's avatar
unknown committed
1212 1213 1214
      }
      if (!--max_rows)
      {
unknown's avatar
unknown committed
1215 1216
        error= 0;                               /* purecov: inspected */
        goto end;                               /* purecov: inspected */
unknown's avatar
unknown committed
1217
      }
1218 1219

    skip_duplicate:
unknown's avatar
unknown committed
1220
      buffpek->key+= rec_length;
unknown's avatar
unknown committed
1221 1222
      if (! --buffpek->mem_count)
      {
unknown's avatar
unknown committed
1223 1224 1225 1226
        if (!(error= (int) read_to_buffer(from_file,buffpek,
                                          rec_length)))
        {
          VOID(queue_remove(&queue,0));
1227
          reuse_freed_buff(&queue, buffpek, rec_length);
unknown's avatar
unknown committed
1228 1229 1230 1231
          break;                        /* One buffer have been removed */
        }
        else if (error == -1)
          goto err;                        /* purecov: inspected */
unknown's avatar
unknown committed
1232
      }
unknown's avatar
unknown committed
1233
      queue_replaced(&queue);              /* Top element has been replaced */
unknown's avatar
unknown committed
1234 1235
    }
  }
unknown's avatar
unknown committed
1236
  buffpek= (BUFFPEK*) queue_top(&queue);
1237
  buffpek->base= sort_buffer;
unknown's avatar
unknown committed
1238
  buffpek->max_keys= param->keys;
unknown's avatar
unknown committed
1239 1240 1241 1242 1243 1244 1245 1246 1247

  /*
    As we know all entries in the buffer are unique, we only have to
    check if the first one is the same as the last one we wrote
  */
  if (cmp)
  {
    if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key))
    {
unknown's avatar
unknown committed
1248
      buffpek->key+= rec_length;         // Remove duplicate
unknown's avatar
unknown committed
1249 1250 1251 1252
      --buffpek->mem_count;
    }
  }

unknown's avatar
unknown committed
1253 1254 1255
  do
  {
    if ((ha_rows) buffpek->mem_count > max_rows)
unknown's avatar
unknown committed
1256 1257 1258
    {                                        /* Don't write too many records */
      buffpek->mem_count= (uint) max_rows;
      buffpek->count= 0;                        /* Don't read more */
unknown's avatar
unknown committed
1259
    }
unknown's avatar
unknown committed
1260
    max_rows-= buffpek->mem_count;
unknown's avatar
unknown committed
1261 1262 1263
    if (flag == 0)
    {
      if (my_b_write(to_file,(byte*) buffpek->key,
unknown's avatar
unknown committed
1264
                     (rec_length*buffpek->mem_count)))
unknown's avatar
unknown committed
1265
      {
unknown's avatar
unknown committed
1266
        error= 1; goto err;                        /* purecov: inspected */
unknown's avatar
unknown committed
1267 1268 1269 1270 1271 1272
      }
    }
    else
    {
      register uchar *end;
      strpos= buffpek->key+offset;
unknown's avatar
unknown committed
1273 1274 1275 1276 1277 1278 1279 1280
      for (end= strpos+buffpek->mem_count*rec_length ;
           strpos != end ;
           strpos+= rec_length)
      {     
        if (my_b_write(to_file, (byte *) strpos, res_length))
        {
          error=1; goto err;                        
        }
unknown's avatar
unknown committed
1281 1282 1283
      }
    }
  }
unknown's avatar
unknown committed
1284 1285
  while ((error=(int) read_to_buffer(from_file,buffpek, rec_length))
         != -1 && error != 0);
unknown's avatar
unknown committed
1286 1287

end:
unknown's avatar
unknown committed
1288 1289
  lastbuff->count= min(org_max_rows-max_rows, param->max_rows);
  lastbuff->file_pos= to_start_filepos;
unknown's avatar
unknown committed
1290 1291 1292 1293 1294 1295 1296 1297
err:
  delete_queue(&queue);
  DBUG_RETURN(error);
} /* merge_buffers */


	/* Do a merge to output-file (save only positions) */

1298
static int merge_index(SORTPARAM *param, uchar *sort_buffer,
unknown's avatar
unknown committed
1299 1300 1301 1302
		       BUFFPEK *buffpek, uint maxbuffer,
		       IO_CACHE *tempfile, IO_CACHE *outfile)
{
  DBUG_ENTER("merge_index");
1303
  if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
unknown's avatar
unknown committed
1304 1305 1306 1307 1308 1309
		    buffpek+maxbuffer,1))
    DBUG_RETURN(1);				/* purecov: inspected */
  DBUG_RETURN(0);
} /* merge_index */


1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
static uint suffix_length(ulong string_length)
{
  if (string_length < 256)
    return 1;
  if (string_length < 256L*256L)
    return 2;
  if (string_length < 256L*256L*256L)
    return 3;
  return 4;                                     // Can't sort longer than 4G
}



1323 1324 1325 1326 1327
/*
  Calculate length of sort key

  SYNOPSIS
    sortlength()
1328
    thd			Thread handler
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
    sortorder		Order of items to sort
    uint s_length	Number of items to sort
    multi_byte_charset  (out)
			Set to 1 if we are using multi-byte charset
			(In which case we have to use strxnfrm())

  NOTES
    sortorder->length is updated for each sort item
    sortorder->need_strxnfrm is set 1 if we have to use strxnfrm

  RETURN
    Total length of sort buffer in bytes
*/
unknown's avatar
unknown committed
1342 1343

static uint
1344 1345
sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
           bool *multi_byte_charset)
unknown's avatar
unknown committed
1346 1347
{
  reg2 uint length;
1348
  CHARSET_INFO *cs;
1349
  *multi_byte_charset= 0;
unknown's avatar
unknown committed
1350 1351 1352 1353

  length=0;
  for (; s_length-- ; sortorder++)
  {
1354
    sortorder->need_strxnfrm= 0;
1355
    sortorder->suffix_length= 0;
unknown's avatar
unknown committed
1356 1357
    if (sortorder->field)
    {
1358 1359 1360 1361
      cs= sortorder->field->sort_charset();
      sortorder->length= sortorder->field->sort_length();

      if (use_strnxfrm((cs=sortorder->field->sort_charset())))
unknown's avatar
unknown committed
1362
      {
1363 1364 1365
        sortorder->need_strxnfrm= 1;
        *multi_byte_charset= 1;
        sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
unknown's avatar
unknown committed
1366 1367 1368 1369 1370 1371 1372 1373 1374
      }
      if (sortorder->field->maybe_null())
	length++;				// Place for NULL marker
    }
    else
    {
      switch ((sortorder->result_type=sortorder->item->result_type())) {
      case STRING_RESULT:
	sortorder->length=sortorder->item->max_length;
1375
        set_if_smaller(sortorder->length, thd->variables.max_sort_length);
1376
	if (use_strnxfrm((cs=sortorder->item->collation.collation)))
unknown's avatar
unknown committed
1377
	{ 
1378
          sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
1379 1380
	  sortorder->need_strxnfrm= 1;
	  *multi_byte_charset= 1;
1381
	}
1382 1383 1384 1385 1386 1387
        else if (cs == &my_charset_bin)
        {
          /* Store length last to be able to sort blob/varbinary */
          sortorder->suffix_length= suffix_length(sortorder->length);
          sortorder->length+= sortorder->suffix_length;
        }
unknown's avatar
unknown committed
1388 1389 1390 1391 1392 1393 1394 1395
	break;
      case INT_RESULT:
#if SIZEOF_LONG_LONG > 4
	sortorder->length=8;			// Size of intern longlong
#else
	sortorder->length=4;
#endif
	break;
unknown's avatar
unknown committed
1396 1397 1398 1399 1400 1401
      case DECIMAL_RESULT:
        sortorder->length=
          my_decimal_get_binary_size(sortorder->item->max_length - 
                                     (sortorder->item->decimals ? 1 : 0),
                                     sortorder->item->decimals);
        break;
unknown's avatar
unknown committed
1402 1403 1404
      case REAL_RESULT:
	sortorder->length=sizeof(double);
	break;
1405
      case ROW_RESULT:
unknown's avatar
unknown committed
1406
      default: 
unknown's avatar
unknown committed
1407 1408
	// This case should never be choosen
	DBUG_ASSERT(0);
unknown's avatar
unknown committed
1409
	break;
unknown's avatar
unknown committed
1410 1411 1412 1413
      }
      if (sortorder->item->maybe_null)
	length++;				// Place for NULL marker
    }
unknown's avatar
unknown committed
1414
    set_if_smaller(sortorder->length, thd->variables.max_sort_length);
unknown's avatar
unknown committed
1415 1416 1417 1418 1419 1420 1421 1422
    length+=sortorder->length;
  }
  sortorder->field= (Field*) 0;			// end marker
  DBUG_PRINT("info",("sort_length: %d",length));
  return length;
}


unknown's avatar
unknown committed
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
/*
  Get descriptors of fields appended to sorted fields and
  calculate its total length

  SYNOPSIS
    get_addon_fields()
    thd                 Current thread
    ptabfields          Array of references to the table fields
    sortlength          Total length of sorted fields
    plength    out:     Total length of appended fields

  DESCRIPTION
    The function first finds out what fields are used in the result set.
    Then it calculates the length of the buffer to store the values of
    these fields together with the value of sort values. 
    If the calculated length is not greater than max_length_for_sort_data
    the function allocates memory for an array of descriptors containing
    layouts for the values of the non-sorted fields in the buffer and
    fills them.

  NOTES
    The null bits for the appended values are supposed to be put together
    and stored the buffer just ahead of the value of the first field.

  RETURN
    Pointer to the layout descriptors for the appended fields, if any
    NULL - if we do not store field values with sort data.
*/

static SORT_ADDON_FIELD *
get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
{
  Field **pfield;
  Field *field;
  SORT_ADDON_FIELD *addonf;
  uint length= 0;
  uint fields= 0;
  uint null_fields= 0;
1461 1462
  MY_BITMAP *read_set= (*ptabfield)->table->read_set;

1463 1464 1465 1466 1467 1468 1469 1470
  /*
    If there is a reference to a field in the query add it
    to the the set of appended fields.
    Note for future refinement:
    This this a too strong condition.
    Actually we need only the fields referred in the
    result set. And for some of them it makes sense to use 
    the values directly from sorted fields.
unknown's avatar
unknown committed
1471 1472
  */
  *plength= 0;
1473

unknown's avatar
unknown committed
1474 1475
  for (pfield= ptabfield; (field= *pfield) ; pfield++)
  {
1476
    if (!bitmap_is_set(read_set, field->field_index))
unknown's avatar
unknown committed
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498
      continue;
    if (field->flags & BLOB_FLAG)
      return 0;
    length+= field->max_packed_col_length(field->pack_length());
    if (field->maybe_null())
      null_fields++;
    fields++;
  } 
  if (!fields)
    return 0;
  length+= (null_fields+7)/8;

  if (length+sortlength > thd->variables.max_length_for_sort_data ||
      !(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)*
                                               (fields+1), MYF(MY_WME))))
    return 0;

  *plength= length;
  length= (null_fields+7)/8;
  null_fields= 0;
  for (pfield= ptabfield; (field= *pfield) ; pfield++)
  {
1499
    if (!bitmap_is_set(read_set, field->field_index))
unknown's avatar
unknown committed
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
      continue;
    addonf->field= field;
    addonf->offset= length;
    if (field->maybe_null())
    {
      addonf->null_offset= null_fields/8;
      addonf->null_bit= 1<<(null_fields & 7);
      null_fields++;
    }
    else
    {
      addonf->null_offset= 0;
      addonf->null_bit= 0;
    }
    addonf->length= field->max_packed_col_length(field->pack_length());
    length+= addonf->length;
    addonf++;
  }
  addonf->field= 0;     // Put end marker
  
  DBUG_PRINT("info",("addon_length: %d",length));
  return (addonf-fields);
}


/*
  Copy (unpack) values appended to sorted fields from a buffer back to 
  their regular positions specified by the Field::ptr pointers.

  SYNOPSIS
    unpack_addon_fields()
    addon_field     Array of descriptors for appended fields
    buff            Buffer which to unpack the value from

  NOTES
    The function is supposed to be used only as a callback function
    when getting field values for the sorted result set.

  RETURN
    void.
*/

static void 
unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff)
{
  Field *field;
  SORT_ADDON_FIELD *addonf= addon_field;

  for ( ; (field= addonf->field) ; addonf++)
  {
    if (addonf->null_bit && (addonf->null_bit & buff[addonf->null_offset]))
    {
      field->set_null();
      continue;
    }
    field->set_notnull();
    field->unpack(field->ptr, (char *) buff+addonf->offset);
  }
}

unknown's avatar
unknown committed
1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581
/*
** functions to change a double or float to a sortable string
** The following should work for IEEE
*/

#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)

void change_double_for_sort(double nr,byte *to)
{
  uchar *tmp=(uchar*) to;
  if (nr == 0.0)
  {						/* Change to zero string */
    tmp[0]=(uchar) 128;
    bzero((char*) tmp+1,sizeof(nr)-1);
  }
  else
  {
#ifdef WORDS_BIGENDIAN
    memcpy_fixed(tmp,&nr,sizeof(nr));
#else
    {
      uchar *ptr= (uchar*) &nr;
1582
#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
unknown's avatar
unknown committed
1583 1584 1585
      tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
      tmp[4]= ptr[7]; tmp[5]=ptr[6]; tmp[6]= ptr[5]; tmp[7]=ptr[4];
#else
unknown's avatar
unknown committed
1586 1587
      tmp[0]= ptr[7]; tmp[1]=ptr[6]; tmp[2]= ptr[5]; tmp[3]=ptr[4];
      tmp[4]= ptr[3]; tmp[5]=ptr[2]; tmp[6]= ptr[1]; tmp[7]=ptr[0];
unknown's avatar
unknown committed
1588
#endif
unknown's avatar
unknown committed
1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
    }
#endif
    if (tmp[0] & 128)				/* Negative */
    {						/* make complement */
      uint i;
      for (i=0 ; i < sizeof(nr); i++)
	tmp[i]=tmp[i] ^ (uchar) 255;
    }
    else
    {					/* Set high and move exponent one up */
      ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
		       (ushort) 32768);
      exp_part+= (ushort) 1 << (16-1-DBL_EXP_DIG);
      tmp[0]= (uchar) (exp_part >> 8);
      tmp[1]= (uchar) exp_part;
    }
  }
}