filesort.cc 47.2 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2006 MySQL AB  
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4
   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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown committed
7 8 9 10
   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
11

unknown's avatar
unknown committed
12 13 14 15 16
   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 */


unknown's avatar
unknown committed
17 18 19 20 21 22
/**
  @file

  @brief
  Sorts a database
*/
unknown's avatar
unknown committed
23 24 25 26 27 28

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

unknown's avatar
unknown committed
31
#ifndef THREAD
unknown's avatar
unknown committed
32
#define SKIP_DBUG_IN_FILESORT
unknown's avatar
unknown committed
33 34
#endif

unknown's avatar
unknown committed
35
/// How to write record_ref.
unknown's avatar
unknown committed
36
#define WRITE_REF(file,from) \
37
if (my_b_write((file),(uchar*) (from),param->ref_length)) \
unknown's avatar
unknown committed
38 39 40 41
  DBUG_RETURN(1);

	/* functions defined in this file */

42 43
static char **make_char_array(char **old_pos, register uint fields,
                              uint length, myf my_flag);
44 45
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
                                     uchar *buf);
unknown's avatar
unknown committed
46
static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
47
			     uchar * *sort_keys, IO_CACHE *buffer_file,
unknown's avatar
unknown committed
48 49
			     IO_CACHE *tempfile,IO_CACHE *indexfile);
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,
unknown's avatar
unknown committed
54 55 56
		       BUFFPEK *buffpek,
		       uint maxbuffer,IO_CACHE *tempfile,
		       IO_CACHE *outfile);
57 58
static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count, 
                       FILESORT_INFO *table_sort);
59 60
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
61
		       bool *multi_byte_charset);
unknown's avatar
unknown committed
62 63 64
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,
65
                                uchar *buff);
unknown's avatar
unknown committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
/**
  Sort a table.
  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.

  Before calling filesort, one must have done
  table->file->info(HA_STATUS_VARIABLE)

  The result set is stored in table->io_cache or
  table->record_pointers.

  @param thd           Current thread
  @param table		Table to sort
  @param sortorder	How to sort the table
  @param s_length	Number of elements in sortorder
  @param select		condition to apply to the rows
  @param max_rows	Return only this many rows
  @param sort_positions	Set to 1 if we want to force sorting by position
85
			(Needed by UPDATE/INSERT or ALTER TABLE)
unknown's avatar
unknown committed
86
  @param examined_rows	Store number of examined rows here
87

unknown's avatar
unknown committed
88 89 90
  @todo
    check why we do this (param.keys--)
  @note
91 92 93
    If we sort by position (like if sort_positions is 1) filesort() will
    call table->prepare_for_position().

unknown's avatar
unknown committed
94
  @retval
95
    HA_POS_ERROR	Error
unknown's avatar
unknown committed
96 97 98
  @retval
    \#			Number of rows
  @retval
99 100
    examined_rows	will be set to number of examined rows
*/
101

102
ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
103 104
		 SQL_SELECT *select, ha_rows max_rows,
                 bool sort_positions, ha_rows *examined_rows)
unknown's avatar
unknown committed
105 106
{
  int error;
unknown's avatar
unknown committed
107
  ulong memavl, min_sort_memory;
unknown's avatar
unknown committed
108
  uint maxbuffer;
unknown's avatar
unknown committed
109
  BUFFPEK *buffpek;
unknown's avatar
unknown committed
110
  ha_rows records= HA_POS_ERROR;
unknown's avatar
unknown committed
111
  uchar **sort_keys= 0;
112
  IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile; 
unknown's avatar
unknown committed
113
  SORTPARAM param;
114
  bool multi_byte_charset;
115 116
  DBUG_ENTER("filesort");
  DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length););
unknown's avatar
unknown committed
117
#ifdef SKIP_DBUG_IN_FILESORT
unknown's avatar
unknown committed
118 119
  DBUG_PUSH("");		/* No DBUG here */
#endif
120
  FILESORT_INFO table_sort;
unknown's avatar
unknown committed
121 122
  TABLE_LIST *tab= table->pos_in_table_list;
  Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
123 124 125 126 127 128 129

  /*
   Release InnoDB's adaptive hash index latch (if holding) before
   running a sort.
  */
  ha_release_temporary_latches(thd);

130
  /* 
131 132 133
    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.
134 135 136
  */
  memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
  table->sort.io_cache= NULL;
137
  
138
  outfile= table_sort.io_cache;
unknown's avatar
unknown committed
139
  my_b_clear(&tempfile);
140 141 142
  my_b_clear(&buffpek_pointers);
  buffpek=0;
  error= 1;
143
  bzero((char*) &param,sizeof(param));
144
  param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset);
145
  param.ref_length= table->file->ref_length;
unknown's avatar
unknown committed
146 147
  param.addon_field= 0;
  param.addon_length= 0;
148 149
  if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
      !table->fulltext_searched && !sort_positions)
unknown's avatar
unknown committed
150 151 152 153 154 155 156 157 158
  {
    /* 
      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);
  }
159 160 161 162 163

  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
164 165 166
  if (param.addon_field)
  {
    param.res_length= param.addon_length;
167
    if (!(table_sort.addon_buf= (uchar *) my_malloc(param.addon_length,
unknown's avatar
unknown committed
168 169 170 171 172 173 174 175 176 177 178 179 180
                                                    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
181
  param.max_rows= max_rows;
unknown's avatar
unknown committed
182

183 184
  if (select && select->quick)
  {
185
    status_var_increment(thd->status_var.filesort_range_count);
186 187 188
  }
  else
  {
189
    status_var_increment(thd->status_var.filesort_scan_count);
190
  }
unknown's avatar
unknown committed
191
#ifdef CAN_TRUST_RANGE
192
  if (select && select->quick && select->quick->records > 0L)
unknown's avatar
unknown committed
193 194
  {
    records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2),
195
		table->file->stats.records)+EXTRA_RECORDS;
unknown's avatar
unknown committed
196 197 198
    selected_records_file=0;
  }
  else
199
#endif
unknown's avatar
unknown committed
200
  {
unknown's avatar
unknown committed
201 202 203 204 205 206 207
    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
208 209 210
    selected_records_file= 0;
  }

211
  if (multi_byte_charset &&
212
      !(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
unknown's avatar
unknown committed
213 214
    goto err;

215
  memavl= thd->variables.sortbuff_size;
unknown's avatar
unknown committed
216 217
  min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
  while (memavl >= min_sort_memory)
unknown's avatar
unknown committed
218
  {
219
    ulong old_memavl;
unknown's avatar
unknown committed
220
    ulong keys= memavl/(param.rec_length+sizeof(char*));
221
    param.keys=(uint) min(records+1, keys);
222 223 224
    if ((table_sort.sort_keys=
	 (uchar **) make_char_array((char **) table_sort.sort_keys,
                                    param.keys, param.rec_length, MYF(0))))
225
      break;
unknown's avatar
unknown committed
226
    old_memavl=memavl;
unknown's avatar
unknown committed
227
    if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory)
unknown's avatar
unknown committed
228
      memavl= min_sort_memory;
unknown's avatar
unknown committed
229
  }
unknown's avatar
unknown committed
230
  sort_keys= table_sort.sort_keys;
unknown's avatar
unknown committed
231
  if (memavl < min_sort_memory)
unknown's avatar
unknown committed
232
  {
233 234
    my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),
	     thd->variables.sortbuff_size);
unknown's avatar
unknown committed
235 236
    goto err;
  }
237 238 239 240
  if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
		       DISK_BUFFER_SIZE, MYF(MY_WME)))
    goto err;

241
  param.keys--;  			/* TODO: check why we do this */
242
  param.sort_form= table;
unknown's avatar
unknown committed
243
  param.end=(param.local_sortorder=sortorder)+s_length;
244
  if ((records=find_all_keys(&param,select,sort_keys, &buffpek_pointers,
unknown's avatar
unknown committed
245 246 247
			     &tempfile, selected_records_file)) ==
      HA_POS_ERROR)
    goto err;
unknown's avatar
unknown committed
248
  maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
249

250
  if (maxbuffer == 0)			// The whole set is in memory
unknown's avatar
unknown committed
251
  {
252
    if (save_index(&param,sort_keys,(uint) records, &table_sort))
unknown's avatar
unknown committed
253 254 255 256
      goto err;
  }
  else
  {
unknown's avatar
unknown committed
257 258 259 260 261
    if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer)
    {
      x_free(table_sort.buffpek);
      table_sort.buffpek= 0;
    }
262
    if (!(table_sort.buffpek=
263
          (uchar *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
unknown's avatar
unknown committed
264
                                 table_sort.buffpek)))
265
      goto err;
unknown's avatar
unknown committed
266 267
    buffpek= (BUFFPEK *) table_sort.buffpek;
    table_sort.buffpek_len= maxbuffer;
268
    close_cached_file(&buffpek_pointers);
unknown's avatar
unknown committed
269 270 271 272 273
	/* 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;
274 275
    if (reinit_io_cache(outfile,WRITE_CACHE,0L,0,0))
      goto err;
unknown's avatar
unknown committed
276

277 278 279 280
    /*
      Use also the space previously used by string pointers in sort_buffer
      for temporary key storage.
    */
unknown's avatar
unknown committed
281 282
    param.keys=((param.keys*(param.rec_length+sizeof(char*))) /
		param.rec_length-1);
283
    maxbuffer--;				// Offset from 0
284 285
    if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
			&tempfile))
unknown's avatar
unknown committed
286 287 288 289
      goto err;
    if (flush_io_cache(&tempfile) ||
	reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
      goto err;
290 291
    if (merge_index(&param,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile,
		    outfile))
unknown's avatar
unknown committed
292 293 294 295 296 297 298
      goto err;
  }
  if (records > param.max_rows)
    records=param.max_rows;
  error =0;

 err:
unknown's avatar
unknown committed
299
  if (param.tmp_buffer)
unknown's avatar
unknown committed
300
    x_free(param.tmp_buffer);
unknown's avatar
unknown committed
301 302
  if (!subselect || !subselect->is_uncacheable())
  {
303
    x_free((uchar*) sort_keys);
unknown's avatar
unknown committed
304
    table_sort.sort_keys= 0;
305
    x_free((uchar*) buffpek);
unknown's avatar
unknown committed
306 307 308
    table_sort.buffpek= 0;
    table_sort.buffpek_len= 0;
  }
unknown's avatar
unknown committed
309
  close_cached_file(&tempfile);
310
  close_cached_file(&buffpek_pointers);
unknown's avatar
unknown committed
311 312 313 314 315 316 317 318 319 320 321 322 323
  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
324 325
    my_message(ER_FILSORT_ABORT, ER(ER_FILSORT_ABORT),
               MYF(ME_ERROR+ME_WAITTANG));
326
  else
327 328
    statistic_add(thd->status_var.filesort_rows,
		  (ulong) records, &LOCK_status);
329
  *examined_rows= param.examined_rows;
unknown's avatar
unknown committed
330
#ifdef SKIP_DBUG_IN_FILESORT
unknown's avatar
unknown committed
331 332
  DBUG_POP();			/* Ok to DBUG */
#endif
333
  memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
unknown's avatar
unknown committed
334
  DBUG_PRINT("exit",("records: %ld", (long) records));
unknown's avatar
unknown committed
335 336 337 338
  DBUG_RETURN(error ? HA_POS_ERROR : records);
} /* filesort */


unknown's avatar
unknown committed
339
void filesort_free_buffers(TABLE *table, bool full)
unknown's avatar
unknown committed
340 341 342
{
  if (table->sort.record_pointers)
  {
343
    my_free((uchar*) table->sort.record_pointers,MYF(0));
unknown's avatar
unknown committed
344 345
    table->sort.record_pointers=0;
  }
unknown's avatar
unknown committed
346 347 348 349
  if (full)
  {
    if (table->sort.sort_keys )
    {
350
      x_free((uchar*) table->sort.sort_keys);
unknown's avatar
unknown committed
351 352 353 354
      table->sort.sort_keys= 0;
    }
    if (table->sort.buffpek)
    {
355
      x_free((uchar*) table->sort.buffpek);
unknown's avatar
unknown committed
356 357 358 359
      table->sort.buffpek= 0;
      table->sort.buffpek_len= 0;
    }
  }
unknown's avatar
unknown committed
360 361 362 363 364 365 366 367 368
  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
369
/** Make a array of string pointers. */
unknown's avatar
unknown committed
370

371 372
static char **make_char_array(char **old_pos, register uint fields,
                              uint length, myf my_flag)
unknown's avatar
unknown committed
373 374
{
  register char **pos;
375
  char *char_pos;
unknown's avatar
unknown committed
376 377
  DBUG_ENTER("make_char_array");

378 379 380
  if (old_pos ||
      (old_pos= (char**) my_malloc((uint) fields*(length+sizeof(char*)),
				   my_flag)))
unknown's avatar
unknown committed
381 382 383 384 385 386 387 388 389
  {
    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
390
/** Read 'count' number of buffer pointers into memory. */
391

392 393
static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
                                     uchar *buf)
394
{
395
  ulong length= sizeof(BUFFPEK)*count;
396
  uchar *tmp= buf;
397
  DBUG_ENTER("read_buffpek_from_file");
398
  if (count > UINT_MAX/sizeof(BUFFPEK))
399
    return 0; /* sizeof(BUFFPEK)*count will overflow */
400
  if (!tmp)
401
    tmp= (uchar *)my_malloc(length, MYF(MY_WME));
402 403 404
  if (tmp)
  {
    if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
405
	my_b_read(buffpek_pointers, (uchar*) tmp, length))
406 407 408
    {
      my_free((char*) tmp, MYF(0));
      tmp=0;
unknown's avatar
unknown committed
409
    }
410 411
  }
  DBUG_RETURN(tmp);
unknown's avatar
unknown committed
412
}
413 414


unknown's avatar
unknown committed
415
/**
unknown's avatar
unknown committed
416
  Search after sort_keys and write them into tempfile.
unknown's avatar
unknown committed
417 418 419 420 421 422 423 424 425 426 427
  All produced sequences are guaranteed to be non-empty.

  @param param             Sorting parameter
  @param select            Use this to get source data
  @param sort_keys         Array of pointers to sort key + addon buffers.
  @param buffpek_pointers  File to write BUFFPEKs describing sorted segments
                           in tempfile.
  @param tempfile          File to write sorted sequences of sortkeys to.
  @param indexfile         If !NULL, use it for source data (contains rowids)

  @note
unknown's avatar
unknown committed
428
    Basic idea:
unknown's avatar
unknown committed
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    @verbatim
     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.
  @endverbatim

  @retval
unknown's avatar
unknown committed
447
    Number of records written on success.
unknown's avatar
unknown committed
448
  @retval
unknown's avatar
unknown committed
449 450
    HA_POS_ERROR on error.
*/
unknown's avatar
unknown committed
451 452 453

static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
			     uchar **sort_keys,
454
			     IO_CACHE *buffpek_pointers,
unknown's avatar
unknown committed
455 456 457 458
			     IO_CACHE *tempfile, IO_CACHE *indexfile)
{
  int error,flag,quick_select;
  uint idx,indexpos,ref_length;
459
  uchar *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
unknown's avatar
unknown committed
460 461
  my_off_t record;
  TABLE *sort_form;
unknown's avatar
unknown committed
462
  THD *thd= current_thd;
463
  volatile THD::killed_state *killed= &thd->killed;
unknown's avatar
unknown committed
464
  handler *file;
465
  MY_BITMAP *save_read_set, *save_write_set;
unknown's avatar
unknown committed
466
  DBUG_ENTER("find_all_keys");
467 468 469
  DBUG_PRINT("info",("using: %s",
                     (select ? select->quick ? "ranges" : "where":
                      "every row")));
unknown's avatar
unknown committed
470 471 472 473 474 475 476 477 478

  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;
479
  flag= ((!indexfile && file->ha_table_flags() & HA_REC_NOT_IN_SEQ)
unknown's avatar
unknown committed
480 481 482 483 484 485
	 || quick_select);
  if (indexfile || flag)
    ref_pos= &file->ref[0];
  next_pos=ref_pos;
  if (! indexfile && ! quick_select)
  {
486
    next_pos=(uchar*) 0;			/* Find records in sequence */
487
    file->ha_rnd_init(1);
unknown's avatar
unknown committed
488 489
    file->extra_opt(HA_EXTRA_CACHE,
		    current_thd->variables.read_buff_size);
unknown's avatar
unknown committed
490 491
  }

492 493 494 495 496 497 498 499 500
  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);
  }

501 502 503 504 505 506 507 508
  /* 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);
509
  if (select && select->cond)
510
    select->cond->walk(&Item::register_field_in_read_map, 1,
511
                       (uchar*) sort_form);
512 513
  sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set);

unknown's avatar
unknown committed
514 515 516 517
  for (;;)
  {
    if (quick_select)
    {
518 519 520 521 522
      if ((error= read_record_info.read_record(&read_record_info)))
      {
        error= HA_ERR_END_OF_FILE;
        break;
      }
unknown's avatar
unknown committed
523 524 525 526 527 528
      file->position(sort_form->record[0]);
    }
    else					/* Not quick-select */
    {
      if (indexfile)
      {
529
	if (my_b_read(indexfile,(uchar*) ref_pos,ref_length)) /* purecov: deadcode */
unknown's avatar
unknown committed
530 531 532 533 534 535 536 537 538 539 540
	{
	  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
541
	  my_store_ptr(ref_pos,ref_length,record); // Position to row
542
	  record+= sort_form->s->db_record_offset;
unknown's avatar
unknown committed
543
	}
unknown's avatar
unknown committed
544
	else if (!error)
unknown's avatar
unknown committed
545
	  file->position(sort_form->record[0]);
unknown's avatar
unknown committed
546 547 548 549
      }
      if (error && error != HA_ERR_RECORD_DELETED)
	break;
    }
550

unknown's avatar
unknown committed
551 552
    if (*killed)
    {
553
      DBUG_PRINT("info",("Sort killed by user"));
554 555 556 557 558
      if (!indexfile && !quick_select)
      {
        (void) file->extra(HA_EXTRA_NO_CACHE);
        file->ha_rnd_end();
      }
unknown's avatar
unknown committed
559 560
      DBUG_RETURN(HA_POS_ERROR);		/* purecov: inspected */
    }
561 562
    if (error == 0)
      param->examined_rows++;
563
    if (error == 0 && (!select || select->skip_record() == 0))
unknown's avatar
unknown committed
564 565 566
    {
      if (idx == param->keys)
      {
567 568
	if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
	  DBUG_RETURN(HA_POS_ERROR);
569 570
	idx=0;
	indexpos++;
unknown's avatar
unknown committed
571 572 573
      }
      make_sortkey(param,sort_keys[idx++],ref_pos);
    }
unknown's avatar
unknown committed
574 575
    else
      file->unlock_row();
unknown's avatar
unknown committed
576
    /* It does not make sense to read more keys in case of a fatal error */
577
    if (thd->is_error())
578
      break;
unknown's avatar
unknown committed
579
  }
580
  if (quick_select)
581 582
  {
    /*
583
      index_merge quick select uses table->sort when retrieving rows, so free
584 585
      resoures it has allocated.
    */
586
    end_read_record(&read_record_info);
587
  }
588 589 590
  else
  {
    (void) file->extra(HA_EXTRA_NO_CACHE);	/* End cacheing of records */
unknown's avatar
unknown committed
591 592
    if (!next_pos)
      file->ha_rnd_end();
593 594
  }

595
  if (thd->is_error())
596 597
    DBUG_RETURN(HA_POS_ERROR);
  
598 599 600
  /* 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
601 602 603 604 605 606
  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 */
  }
607
  if (indexpos && idx &&
608 609
      write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
    DBUG_RETURN(HA_POS_ERROR);			/* purecov: inspected */
unknown's avatar
unknown committed
610
  DBUG_RETURN(my_b_inited(tempfile) ?
unknown's avatar
unknown committed
611
	      (ha_rows) (my_b_tell(tempfile)/param->rec_length) :
unknown's avatar
unknown committed
612 613 614 615
	      idx);
} /* find_all_keys */


unknown's avatar
unknown committed
616 617
/**
  @details
unknown's avatar
unknown committed
618
  Sort the buffer and write:
unknown's avatar
unknown committed
619 620 621 622 623 624 625 626 627 628 629 630 631 632
  -# the sorted sequence to tempfile
  -# a BUFFPEK describing the sorted sequence position to buffpek_pointers

    (was: Skriver en buffert med nycklar till filen)

  @param param             Sort parameters
  @param sort_keys         Array of pointers to keys to sort
  @param count             Number of elements in sort_keys array
  @param buffpek_pointers  One 'BUFFPEK' struct will be written into this file.
                           The BUFFPEK::{file_pos, count} will indicate where
                           the sorted data was stored.
  @param tempfile          The sorted sequence will be written into this file.

  @retval
unknown's avatar
unknown committed
633
    0 OK
unknown's avatar
unknown committed
634
  @retval
unknown's avatar
unknown committed
635 636
    1 Error
*/
unknown's avatar
unknown committed
637

unknown's avatar
unknown committed
638 639
static int
write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
unknown's avatar
unknown committed
640
           IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
unknown's avatar
unknown committed
641
{
642
  size_t sort_length, rec_length;
unknown's avatar
unknown committed
643
  uchar **end;
644
  BUFFPEK buffpek;
unknown's avatar
unknown committed
645 646
  DBUG_ENTER("write_keys");

unknown's avatar
unknown committed
647 648
  sort_length= param->sort_length;
  rec_length= param->rec_length;
unknown's avatar
unknown committed
649 650 651
#ifdef MC68000
  quicksort(sort_keys,count,sort_length);
#else
652
  my_string_ptr_sort((uchar*) sort_keys, (uint) count, sort_length);
unknown's avatar
unknown committed
653 654
#endif
  if (!my_b_inited(tempfile) &&
unknown's avatar
unknown committed
655 656
      open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
                       MYF(MY_WME)))
657
    goto err;                                   /* purecov: inspected */
658
  /* check we won't have more buffpeks than we can possibly keep in memory */
659
  if (my_b_tell(buffpek_pointers) + sizeof(BUFFPEK) > (ulonglong)UINT_MAX)
660
    goto err;
unknown's avatar
unknown committed
661
  buffpek.file_pos= my_b_tell(tempfile);
unknown's avatar
unknown committed
662
  if ((ha_rows) count > param->max_rows)
663
    count=(uint) param->max_rows;               /* purecov: inspected */
664
  buffpek.count=(ha_rows) count;
unknown's avatar
unknown committed
665
  for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
666
    if (my_b_write(tempfile, (uchar*) *sort_keys, (uint) rec_length))
667
      goto err;
668
  if (my_b_write(buffpek_pointers, (uchar*) &buffpek, sizeof(buffpek)))
669
    goto err;
unknown's avatar
unknown committed
670
  DBUG_RETURN(0);
671 672 673

err:
  DBUG_RETURN(1);
unknown's avatar
unknown committed
674 675 676
} /* write_keys */


unknown's avatar
unknown committed
677 678
/**
  Store length as suffix in high-byte-first order.
679 680 681 682 683 684 685 686 687 688 689 690 691
*/

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);
692
    break;
693 694 695 696 697 698 699
  default:
    mi_int4store(to, length);
    break;
  }
}


unknown's avatar
unknown committed
700
/** Make a sort-key from record. */
unknown's avatar
unknown committed
701 702

static void make_sortkey(register SORTPARAM *param,
703
			 register uchar *to, uchar *ref_pos)
unknown's avatar
unknown committed
704 705 706 707 708 709 710 711 712
{
  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
713
    bool maybe_null=0;
unknown's avatar
unknown committed
714 715 716 717 718 719
    if ((field=sort_field->field))
    {						// Field
      if (field->maybe_null())
      {
	if (field->is_null())
	{
unknown's avatar
unknown committed
720 721 722 723
	  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
724 725 726 727 728 729
	  to+= sort_field->length+1;
	  continue;
	}
	else
	  *to++=1;
      }
730
      field->sort_string(to, sort_field->length);
unknown's avatar
unknown committed
731 732 733 734
    }
    else
    {						// Item
      Item *item=sort_field->item;
735
      maybe_null= item->maybe_null;
unknown's avatar
unknown committed
736 737
      switch (sort_field->result_type) {
      case STRING_RESULT:
738
      {
unknown's avatar
unknown committed
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
        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
754
          {
755 756 757 758 759 760 761
            /* 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
762 763 764
            DBUG_PRINT("warning",
                       ("Got null on something that shouldn't be null"));
            bzero((char*) to,sort_field->length);	// Avoid crash
765
            /* purecov: end */
unknown's avatar
unknown committed
766
          }
unknown's avatar
unknown committed
767 768 769 770 771 772 773
          break;
        }
        length= res->length();
        sort_field_length= sort_field->length - sort_field->suffix_length;
        diff=(int) (sort_field_length - length);
        if (diff < 0)
        {
774
          diff=0;
unknown's avatar
unknown committed
775 776 777 778 779 780 781 782 783 784 785 786 787
          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
788
          {
unknown's avatar
unknown committed
789 790 791
            set_if_smaller(length,sort_field->length);
            memcpy(param->tmp_buffer,from,length);
            from=param->tmp_buffer;
unknown's avatar
unknown committed
792
          }
unknown's avatar
unknown committed
793 794 795 796 797 798 799 800 801 802
          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;
803
      }
unknown's avatar
unknown committed
804 805
      case INT_RESULT:
	{
806 807
          longlong value= item->val_int_result();
          if (maybe_null)
808
          {
unknown's avatar
unknown committed
809
	    *to++=1;				/* purecov: inspected */
810 811 812 813 814 815 816 817 818 819 820 821 822
            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
823 824 825 826 827 828 829 830
#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
831 832 833 834
          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
835 836 837 838
#else
	  to[3]= (uchar) value;
	  to[2]= (uchar) (value >> 8);
	  to[1]= (uchar) (value >> 16);
unknown's avatar
unknown committed
839 840 841 842
          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
843 844 845
#endif
	  break;
	}
unknown's avatar
unknown committed
846 847
      case DECIMAL_RESULT:
        {
848
          my_decimal dec_buf, *dec_val= item->val_decimal_result(&dec_buf);
849 850 851 852 853 854 855 856
          if (maybe_null)
          {
            if (item->null_value)
            { 
              bzero((char*)to, sort_field->length+1);
              to++;
              break;
            }
unknown's avatar
unknown committed
857
            *to++=1;
858
          }
859
          my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, to,
unknown's avatar
unknown committed
860 861 862 863
                            item->max_length - (item->decimals ? 1:0),
                            item->decimals);
         break;
        }
unknown's avatar
unknown committed
864 865
      case REAL_RESULT:
	{
866
          double value= item->val_result();
867 868 869 870 871 872 873 874
	  if (maybe_null)
          {
            if (item->null_value)
            {
              bzero((char*) to,sort_field->length+1);
              to++;
              break;
            }
unknown's avatar
unknown committed
875
	    *to++=1;
876
          }
877
	  change_double_for_sort(value,(uchar*) to);
unknown's avatar
unknown committed
878 879
	  break;
	}
880
      case ROW_RESULT:
unknown's avatar
unknown committed
881
      default: 
unknown's avatar
unknown committed
882 883 884
	// This case should never be choosen
	DBUG_ASSERT(0);
	break;
unknown's avatar
unknown committed
885 886 887 888
      }
    }
    if (sort_field->reverse)
    {							/* Revers key */
unknown's avatar
unknown committed
889 890
      if (maybe_null)
        to[-1]= ~to[-1];
unknown's avatar
unknown committed
891 892 893 894 895 896 897 898 899 900
      length=sort_field->length;
      while (length--)
      {
	*to = (uchar) (~ *to);
	to++;
      }
    }
    else
      to+= sort_field->length;
  }
unknown's avatar
unknown committed
901 902 903 904 905 906 907 908 909 910 911

  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;
912
    DBUG_ASSERT(addonf != 0);
unknown's avatar
unknown committed
913 914 915 916 917
    bzero((char *) nulls, addonf->offset);
    to+= addonf->offset;
    for ( ; (field= addonf->field) ; addonf++)
    {
      if (addonf->null_bit && field->is_null())
918
      {
unknown's avatar
unknown committed
919
        nulls[addonf->null_offset]|= addonf->null_bit;
920 921 922 923
#ifdef HAVE_purify
	bzero(to, addonf->length);
#endif
      }
unknown's avatar
unknown committed
924
      else
925 926
      {
#ifdef HAVE_purify
927
        uchar *end= field->pack(to, field->ptr);
928 929 930 931
	uint length= (uint) ((to + addonf->length) - end);
	DBUG_ASSERT((int) length >= 0);
	if (length)
	  bzero(end, length);
932
#else
933
        (void) field->pack(to, field->ptr);
934 935
#endif
      }
unknown's avatar
unknown committed
936 937 938 939 940 941
      to+= addonf->length;
    }
  }
  else
  {
    /* Save filepos last */
942
    memcpy((uchar*) to, ref_pos, (size_t) param->ref_length);
unknown's avatar
unknown committed
943
  }
unknown's avatar
unknown committed
944 945 946
  return;
}

947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970

/*
  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,
971
                             (uchar *) table);
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
    }
  }

  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();
  }
}


990 991
static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count, 
                       FILESORT_INFO *table_sort)
unknown's avatar
unknown committed
992
{
unknown's avatar
unknown committed
993
  uint offset,res_length;
994
  uchar *to;
unknown's avatar
unknown committed
995 996
  DBUG_ENTER("save_index");

997
  my_string_ptr_sort((uchar*) sort_keys, (uint) count, param->sort_length);
unknown's avatar
unknown committed
998 999
  res_length= param->res_length;
  offset= param->rec_length-res_length;
unknown's avatar
unknown committed
1000 1001
  if ((ha_rows) count > param->max_rows)
    count=(uint) param->max_rows;
1002
  if (!(to= table_sort->record_pointers= 
1003
        (uchar*) my_malloc(res_length*count, MYF(MY_WME))))
unknown's avatar
unknown committed
1004 1005
    DBUG_RETURN(1);                 /* purecov: inspected */
  for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
unknown's avatar
unknown committed
1006
  {
unknown's avatar
unknown committed
1007 1008
    memcpy(to, *sort_keys+offset, res_length);
    to+= res_length;
unknown's avatar
unknown committed
1009 1010 1011 1012 1013
  }
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
1014
/** Merge buffers to make < MERGEBUFF2 buffers. */
unknown's avatar
unknown committed
1015

1016 1017
int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
		    BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
unknown's avatar
unknown committed
1018
{
1019
  register uint i;
unknown's avatar
unknown committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
  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)
  {
1034 1035 1036 1037
    if (reinit_io_cache(from_file,READ_CACHE,0L,0,0))
      goto cleanup;
    if (reinit_io_cache(to_file,WRITE_CACHE,0L,0,0))
      goto cleanup;
unknown's avatar
unknown committed
1038
    lastbuff=buffpek;
1039
    for (i=0 ; i <= *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
unknown's avatar
unknown committed
1040
    {
1041
      if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
unknown's avatar
unknown committed
1042
			buffpek+i,buffpek+i+MERGEBUFF-1,0))
1043
      goto cleanup;
unknown's avatar
unknown committed
1044
    }
1045
    if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
unknown's avatar
unknown committed
1046 1047 1048 1049 1050
		      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;
1051 1052
    setup_io_cache(from_file);
    setup_io_cache(to_file);
unknown's avatar
unknown committed
1053 1054
    *maxbuffer= (uint) (lastbuff-buffpek)-1;
  }
1055
cleanup:
unknown's avatar
unknown committed
1056 1057
  close_cached_file(to_file);			// This holds old result
  if (to_file == t_file)
1058
  {
unknown's avatar
unknown committed
1059
    *t_file=t_file2;				// Copy result file
1060 1061
    setup_io_cache(t_file);
  }
unknown's avatar
unknown committed
1062 1063 1064 1065 1066

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


unknown's avatar
unknown committed
1067 1068 1069 1070 1071 1072
/**
  Read data to buffer.

  @retval
    (uint)-1 if something goes wrong
*/
unknown's avatar
unknown committed
1073

1074
uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
unknown's avatar
unknown committed
1075
		    uint rec_length)
unknown's avatar
unknown committed
1076 1077 1078 1079 1080 1081
{
  register uint count;
  uint length;

  if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
  {
1082
    if (my_pread(fromfile->file,(uchar*) buffpek->base,
unknown's avatar
unknown committed
1083
		 (length= rec_length*count),buffpek->file_pos,MYF_RW))
unknown's avatar
unknown committed
1084 1085 1086 1087 1088 1089
      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
1090
  return (count*rec_length);
unknown's avatar
unknown committed
1091 1092 1093
} /* read_to_buffer */


unknown's avatar
unknown committed
1094 1095 1096 1097 1098 1099 1100 1101 1102
/**
  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.

  @param[in] queue      list of non-empty buffers, without freed buffer
  @param[in] reuse      empty buffer
  @param[in] key_length key length
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
*/

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
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
/**
  Merge buffers to one buffer.

  @param param        Sort parameter
  @param from_file    File with source data (BUFFPEKs point to this file)
  @param to_file      File to write the sorted result data.
  @param sort_buffer  Buffer for data to store up to MERGEBUFF2 sort keys.
  @param lastbuff     OUT Store here BUFFPEK describing data written to to_file
  @param Fb           First element in source BUFFPEKs array
  @param Tb           Last element in source BUFFPEKs array
  @param flag

  @retval
    0      OK
  @retval
    other  error
unknown's avatar
unknown committed
1143
*/
unknown's avatar
unknown committed
1144

1145
int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
unknown's avatar
unknown committed
1146 1147 1148
                  IO_CACHE *to_file, uchar *sort_buffer,
                  BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
                  int flag)
unknown's avatar
unknown committed
1149 1150
{
  int error;
1151 1152
  uint rec_length,res_length,offset;
  size_t sort_length;
unknown's avatar
unknown committed
1153
  ulong maxcount;
1154
  ha_rows max_rows,org_max_rows;
unknown's avatar
unknown committed
1155 1156
  my_off_t to_start_filepos;
  uchar *strpos;
unknown's avatar
unknown committed
1157
  BUFFPEK *buffpek;
unknown's avatar
unknown committed
1158
  QUEUE queue;
unknown's avatar
unknown committed
1159
  qsort2_cmp cmp;
unknown's avatar
unknown committed
1160
  void *first_cmp_arg;
unknown's avatar
SCRUM  
unknown committed
1161 1162
  volatile THD::killed_state *killed= &current_thd->killed;
  THD::killed_state not_killable;
unknown's avatar
unknown committed
1163 1164
  DBUG_ENTER("merge_buffers");

1165
  status_var_increment(current_thd->status_var.filesort_merge_passes);
1166 1167 1168
  if (param->not_killable)
  {
    killed= &not_killable;
unknown's avatar
unknown committed
1169
    not_killable= THD::NOT_KILLED;
1170
  }
1171

1172
  error=0;
unknown's avatar
unknown committed
1173 1174 1175 1176 1177 1178 1179 1180 1181
  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
1182 1183 1184
  /* The following will fire if there is not enough space in sort_buffer */
  DBUG_ASSERT(maxcount!=0);
  
unknown's avatar
unknown committed
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
  if (param->unique_buff)
  {
    cmp= param->compare;
    first_cmp_arg= (void *) &param->cmp_context;
  }
  else
  {
    cmp= get_ptr_compare(sort_length);
    first_cmp_arg= (void*) &sort_length;
  }
unknown's avatar
unknown committed
1195
  if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
unknown's avatar
unknown committed
1196
                 (queue_compare) cmp, first_cmp_arg))
unknown's avatar
unknown committed
1197
    DBUG_RETURN(1);                                /* purecov: inspected */
unknown's avatar
unknown committed
1198 1199 1200
  for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
  {
    buffpek->base= strpos;
unknown's avatar
unknown committed
1201 1202 1203
    buffpek->max_keys= maxcount;
    strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek,
                                                                         rec_length));
unknown's avatar
unknown committed
1204 1205
    if (error == -1)
      goto err;					/* purecov: inspected */
unknown's avatar
unknown committed
1206
    buffpek->max_keys= buffpek->mem_count;	// If less data in buffers than expected
1207
    queue_insert(&queue, (uchar*) buffpek);
unknown's avatar
unknown committed
1208 1209
  }

1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
  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
1220 1221
    buffpek= (BUFFPEK*) queue_top(&queue);
    memcpy(param->unique_buff, buffpek->key, rec_length);
1222
    if (my_b_write(to_file, (uchar*) buffpek->key, rec_length))
1223
    {
unknown's avatar
unknown committed
1224
      error=1; goto err;                        /* purecov: inspected */
1225
    }
unknown's avatar
unknown committed
1226
    buffpek->key+= rec_length;
1227
    buffpek->mem_count--;
1228 1229
    if (!--max_rows)
    {
unknown's avatar
unknown committed
1230 1231
      error= 0;                                       /* purecov: inspected */
      goto end;                                       /* purecov: inspected */
1232
    }
unknown's avatar
unknown committed
1233
    queue_replaced(&queue);                        // Top element has been used
1234 1235
  }
  else
unknown's avatar
unknown committed
1236
    cmp= 0;                                        // Not unique
1237

unknown's avatar
unknown committed
1238 1239
  while (queue.elements > 1)
  {
1240 1241
    if (*killed)
    {
unknown's avatar
unknown committed
1242
      error= 1; goto err;                        /* purecov: inspected */
1243
    }
unknown's avatar
unknown committed
1244 1245
    for (;;)
    {
unknown's avatar
unknown committed
1246 1247
      buffpek= (BUFFPEK*) queue_top(&queue);
      if (cmp)                                        // Remove duplicates
1248
      {
unknown's avatar
unknown committed
1249
        if (!(*cmp)(first_cmp_arg, &(param->unique_buff),
unknown's avatar
unknown committed
1250 1251 1252
                    (uchar**) &buffpek->key))
              goto skip_duplicate;
            memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length);
1253
      }
unknown's avatar
unknown committed
1254 1255
      if (flag == 0)
      {
1256
        if (my_b_write(to_file,(uchar*) buffpek->key, rec_length))
unknown's avatar
unknown committed
1257 1258 1259
        {
          error=1; goto err;                        /* purecov: inspected */
        }
unknown's avatar
unknown committed
1260 1261 1262
      }
      else
      {
1263
        if (my_b_write(to_file, (uchar*) buffpek->key+offset, res_length))
unknown's avatar
unknown committed
1264 1265 1266
        {
          error=1; goto err;                        /* purecov: inspected */
        }
unknown's avatar
unknown committed
1267 1268 1269
      }
      if (!--max_rows)
      {
unknown's avatar
unknown committed
1270 1271
        error= 0;                               /* purecov: inspected */
        goto end;                               /* purecov: inspected */
unknown's avatar
unknown committed
1272
      }
1273 1274

    skip_duplicate:
unknown's avatar
unknown committed
1275
      buffpek->key+= rec_length;
unknown's avatar
unknown committed
1276 1277
      if (! --buffpek->mem_count)
      {
unknown's avatar
unknown committed
1278 1279 1280 1281
        if (!(error= (int) read_to_buffer(from_file,buffpek,
                                          rec_length)))
        {
          VOID(queue_remove(&queue,0));
1282
          reuse_freed_buff(&queue, buffpek, rec_length);
unknown's avatar
unknown committed
1283 1284 1285 1286
          break;                        /* One buffer have been removed */
        }
        else if (error == -1)
          goto err;                        /* purecov: inspected */
unknown's avatar
unknown committed
1287
      }
unknown's avatar
unknown committed
1288
      queue_replaced(&queue);              /* Top element has been replaced */
unknown's avatar
unknown committed
1289 1290
    }
  }
unknown's avatar
unknown committed
1291
  buffpek= (BUFFPEK*) queue_top(&queue);
1292
  buffpek->base= sort_buffer;
unknown's avatar
unknown committed
1293
  buffpek->max_keys= param->keys;
1294 1295 1296 1297 1298 1299 1300

  /*
    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)
  {
unknown's avatar
unknown committed
1301
    if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key))
1302
    {
unknown's avatar
unknown committed
1303
      buffpek->key+= rec_length;         // Remove duplicate
1304 1305 1306 1307
      --buffpek->mem_count;
    }
  }

unknown's avatar
unknown committed
1308 1309 1310
  do
  {
    if ((ha_rows) buffpek->mem_count > max_rows)
unknown's avatar
unknown committed
1311 1312 1313
    {                                        /* Don't write too many records */
      buffpek->mem_count= (uint) max_rows;
      buffpek->count= 0;                        /* Don't read more */
unknown's avatar
unknown committed
1314
    }
unknown's avatar
unknown committed
1315
    max_rows-= buffpek->mem_count;
unknown's avatar
unknown committed
1316 1317
    if (flag == 0)
    {
1318
      if (my_b_write(to_file,(uchar*) buffpek->key,
unknown's avatar
unknown committed
1319
                     (rec_length*buffpek->mem_count)))
unknown's avatar
unknown committed
1320
      {
unknown's avatar
unknown committed
1321
        error= 1; goto err;                        /* purecov: inspected */
unknown's avatar
unknown committed
1322 1323 1324 1325 1326 1327
      }
    }
    else
    {
      register uchar *end;
      strpos= buffpek->key+offset;
unknown's avatar
unknown committed
1328 1329 1330 1331
      for (end= strpos+buffpek->mem_count*rec_length ;
           strpos != end ;
           strpos+= rec_length)
      {     
1332
        if (my_b_write(to_file, (uchar *) strpos, res_length))
unknown's avatar
unknown committed
1333 1334 1335
        {
          error=1; goto err;                        
        }
unknown's avatar
unknown committed
1336 1337 1338
      }
    }
  }
unknown's avatar
unknown committed
1339 1340
  while ((error=(int) read_to_buffer(from_file,buffpek, rec_length))
         != -1 && error != 0);
unknown's avatar
unknown committed
1341 1342

end:
unknown's avatar
unknown committed
1343 1344
  lastbuff->count= min(org_max_rows-max_rows, param->max_rows);
  lastbuff->file_pos= to_start_filepos;
unknown's avatar
unknown committed
1345 1346 1347 1348 1349 1350 1351 1352
err:
  delete_queue(&queue);
  DBUG_RETURN(error);
} /* merge_buffers */


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

1353
static int merge_index(SORTPARAM *param, uchar *sort_buffer,
unknown's avatar
unknown committed
1354 1355 1356 1357
		       BUFFPEK *buffpek, uint maxbuffer,
		       IO_CACHE *tempfile, IO_CACHE *outfile)
{
  DBUG_ENTER("merge_index");
1358
  if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
unknown's avatar
unknown committed
1359 1360 1361 1362 1363 1364
		    buffpek+maxbuffer,1))
    DBUG_RETURN(1);				/* purecov: inspected */
  DBUG_RETURN(0);
} /* merge_index */


1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
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
}



unknown's avatar
unknown committed
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
/**
  Calculate length of sort key.

  @param thd			  Thread handler
  @param sortorder		  Order of items to sort
  @param s_length	          Number of items to sort
  @param[out] multi_byte_charset Set to 1 if we are using multi-byte charset
                                 (In which case we have to use strxnfrm())

  @note
    sortorder->length is updated for each sort item.
  @n
1390 1391
    sortorder->need_strxnfrm is set 1 if we have to use strxnfrm

unknown's avatar
unknown committed
1392
  @return
1393 1394
    Total length of sort buffer in bytes
*/
unknown's avatar
unknown committed
1395 1396

static uint
1397 1398
sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
           bool *multi_byte_charset)
unknown's avatar
unknown committed
1399 1400
{
  reg2 uint length;
1401
  CHARSET_INFO *cs;
1402
  *multi_byte_charset= 0;
unknown's avatar
unknown committed
1403 1404 1405 1406

  length=0;
  for (; s_length-- ; sortorder++)
  {
1407
    sortorder->need_strxnfrm= 0;
1408
    sortorder->suffix_length= 0;
unknown's avatar
unknown committed
1409 1410
    if (sortorder->field)
    {
1411 1412 1413 1414
      cs= sortorder->field->sort_charset();
      sortorder->length= sortorder->field->sort_length();

      if (use_strnxfrm((cs=sortorder->field->sort_charset())))
unknown's avatar
unknown committed
1415
      {
1416 1417 1418
        sortorder->need_strxnfrm= 1;
        *multi_byte_charset= 1;
        sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
unknown's avatar
unknown committed
1419 1420 1421 1422 1423 1424
      }
      if (sortorder->field->maybe_null())
	length++;				// Place for NULL marker
    }
    else
    {
unknown's avatar
unknown committed
1425 1426 1427 1428
      sortorder->result_type= sortorder->item->result_type();
      if (sortorder->item->result_as_longlong())
        sortorder->result_type= INT_RESULT;
      switch (sortorder->result_type) {
unknown's avatar
unknown committed
1429 1430
      case STRING_RESULT:
	sortorder->length=sortorder->item->max_length;
1431
        set_if_smaller(sortorder->length, thd->variables.max_sort_length);
1432
	if (use_strnxfrm((cs=sortorder->item->collation.collation)))
unknown's avatar
unknown committed
1433
	{ 
1434
          sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length);
1435 1436
	  sortorder->need_strxnfrm= 1;
	  *multi_byte_charset= 1;
1437
	}
1438 1439 1440 1441 1442 1443
        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
1444 1445 1446 1447 1448 1449 1450 1451
	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
1452 1453 1454 1455 1456 1457
      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
1458 1459 1460
      case REAL_RESULT:
	sortorder->length=sizeof(double);
	break;
1461
      case ROW_RESULT:
unknown's avatar
unknown committed
1462
      default: 
unknown's avatar
unknown committed
1463 1464
	// This case should never be choosen
	DBUG_ASSERT(0);
unknown's avatar
unknown committed
1465
	break;
unknown's avatar
unknown committed
1466 1467 1468 1469
      }
      if (sortorder->item->maybe_null)
	length++;				// Place for NULL marker
    }
unknown's avatar
unknown committed
1470
    set_if_smaller(sortorder->length, thd->variables.max_sort_length);
unknown's avatar
unknown committed
1471 1472 1473 1474 1475 1476 1477 1478
    length+=sortorder->length;
  }
  sortorder->field= (Field*) 0;			// end marker
  DBUG_PRINT("info",("sort_length: %d",length));
  return length;
}


unknown's avatar
unknown committed
1479
/**
unknown's avatar
unknown committed
1480
  Get descriptors of fields appended to sorted fields and
unknown's avatar
unknown committed
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
  calculate its total length.

  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.

  @param thd                 Current thread
  @param ptabfield           Array of references to the table fields
  @param sortlength          Total length of sorted fields
  @param[out] plength        Total length of appended fields

  @note
unknown's avatar
unknown committed
1497 1498 1499
    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.

unknown's avatar
unknown committed
1500
  @return
unknown's avatar
unknown committed
1501
    Pointer to the layout descriptors for the appended fields, if any
unknown's avatar
unknown committed
1502 1503
  @retval
    NULL   if we do not store field values with sort data.
unknown's avatar
unknown committed
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514
*/

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;
1515 1516
  MY_BITMAP *read_set= (*ptabfield)->table->read_set;

1517 1518 1519 1520 1521 1522 1523 1524
  /*
    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
1525 1526
  */
  *plength= 0;
1527

unknown's avatar
unknown committed
1528 1529
  for (pfield= ptabfield; (field= *pfield) ; pfield++)
  {
1530
    if (!bitmap_is_set(read_set, field->field_index))
unknown's avatar
unknown committed
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552
      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++)
  {
1553
    if (!bitmap_is_set(read_set, field->field_index))
unknown's avatar
unknown committed
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
      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);
}


unknown's avatar
unknown committed
1579 1580
/**
  Copy (unpack) values appended to sorted fields from a buffer back to
unknown's avatar
unknown committed
1581 1582
  their regular positions specified by the Field::ptr pointers.

unknown's avatar
unknown committed
1583 1584
  @param addon_field     Array of descriptors for appended fields
  @param buff            Buffer which to unpack the value from
unknown's avatar
unknown committed
1585

unknown's avatar
unknown committed
1586
  @note
unknown's avatar
unknown committed
1587 1588 1589
    The function is supposed to be used only as a callback function
    when getting field values for the sorted result set.

unknown's avatar
unknown committed
1590
  @return
unknown's avatar
unknown committed
1591 1592 1593 1594
    void.
*/

static void 
1595
unpack_addon_fields(struct st_sort_addon_field *addon_field, uchar *buff)
unknown's avatar
unknown committed
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607
{
  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();
1608
    field->unpack(field->ptr, buff + addonf->offset);
unknown's avatar
unknown committed
1609 1610 1611
  }
}

unknown's avatar
unknown committed
1612 1613 1614 1615 1616 1617 1618
/*
** 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)

1619
void change_double_for_sort(double nr,uchar *to)
unknown's avatar
unknown committed
1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
{
  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;
1634
#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
unknown's avatar
unknown committed
1635 1636 1637
      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
1638 1639
      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
1640
#endif
unknown's avatar
unknown committed
1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658
    }
#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;
    }
  }
}