Drizzled Public API Documentation

sort.cc
00001 /* Copyright (C) 2000-2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 /*
00017   Creates a index for a database by reading keys, sorting them and outputing
00018   them in sorted order through SORT_INFO functions.
00019 */
00020 
00021 #include "myisam_priv.h"
00022 #include <stddef.h>
00023 #include <queue>
00024 #include <algorithm>
00025 
00026 /* static variables */
00027 
00028 #undef MIN_SORT_MEMORY
00029 #undef MYF_RW
00030 #undef DISK_BUFFER_SIZE
00031 
00032 #define MERGEBUFF 15
00033 #define MERGEBUFF2 31
00034 #define MIN_SORT_MEMORY (4096-MALLOC_OVERHEAD)
00035 #define MYF_RW  MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL)
00036 #define DISK_BUFFER_SIZE (IO_SIZE*16)
00037 
00038 using namespace std;
00039 using namespace drizzled;
00040 
00041 
00042 /*
00043  Pointers of functions for store and read keys from temp file
00044 */
00045 
00046 extern void print_error(const char *fmt,...);
00047 
00048 /* Functions defined in this file */
00049 
00050 static ha_rows  find_all_keys(MI_SORT_PARAM *info,uint32_t keys,
00051             unsigned char **sort_keys,
00052             DYNAMIC_ARRAY *buffpek,
00053             size_t *maxbuffer,
00054             internal::io_cache_st *tempfile,
00055             internal::io_cache_st *tempfile_for_exceptions);
00056 static int  write_keys(MI_SORT_PARAM *info,unsigned char **sort_keys,
00057                              uint32_t count, BUFFPEK *buffpek,internal::io_cache_st *tempfile);
00058 static int  write_key(MI_SORT_PARAM *info, unsigned char *key,
00059           internal::io_cache_st *tempfile);
00060 static int  write_index(MI_SORT_PARAM *info,unsigned char * *sort_keys,
00061                               uint32_t count);
00062 static int  merge_many_buff(MI_SORT_PARAM *info,uint32_t keys,
00063           unsigned char * *sort_keys,
00064           BUFFPEK *buffpek,size_t *maxbuffer,
00065           internal::io_cache_st *t_file);
00066 static uint32_t  read_to_buffer(internal::io_cache_st *fromfile,BUFFPEK *buffpek,
00067                                   uint32_t sort_length);
00068 static int  merge_buffers(MI_SORT_PARAM *info,uint32_t keys,
00069                                 internal::io_cache_st *from_file, internal::io_cache_st *to_file,
00070                                 unsigned char * *sort_keys, BUFFPEK *lastbuff,
00071                                 BUFFPEK *Fb, BUFFPEK *Tb);
00072 static int  merge_index(MI_SORT_PARAM *,uint,unsigned char **,BUFFPEK *, int,
00073                               internal::io_cache_st *);
00074 static int  write_keys_varlen(MI_SORT_PARAM *info,unsigned char **sort_keys,
00075                        uint32_t count, BUFFPEK *buffpek,
00076                        internal::io_cache_st *tempfile);
00077 static uint32_t  read_to_buffer_varlen(internal::io_cache_st *fromfile,BUFFPEK *buffpek,
00078                                 uint32_t sort_length);
00079 static int  write_merge_key(MI_SORT_PARAM *info, internal::io_cache_st *to_file,
00080                      unsigned char *key, uint32_t sort_length, uint32_t count);
00081 static int  write_merge_key_varlen(MI_SORT_PARAM *info,
00082                             internal::io_cache_st *to_file,
00083                             unsigned char* key, uint32_t sort_length,
00084                             uint32_t count);
00085 
00086 inline int
00087 my_var_write(MI_SORT_PARAM *info, internal::io_cache_st *to_file, unsigned char *bufs);
00088 
00089 /*
00090   Creates a index of sorted keys
00091 
00092   SYNOPSIS
00093     _create_index_by_sort()
00094     info    Sort parameters
00095     no_messages   Set to 1 if no output
00096     sortbuff_size Size if sortbuffer to allocate
00097 
00098   RESULT
00099     0 ok
00100    <> 0 Error
00101 */
00102 
00103 int _create_index_by_sort(MI_SORT_PARAM *info,bool no_messages,
00104         size_t sortbuff_size)
00105 {
00106   size_t skr;
00107   uint32_t memavl,keys;
00108   DYNAMIC_ARRAY buffpek;
00109   internal::io_cache_st tempfile, tempfile_for_exceptions;
00110 
00111   if (info->keyinfo->flag & HA_VAR_LENGTH_KEY)
00112   {
00113     info->write_keys=write_keys_varlen;
00114     info->read_to_buffer=read_to_buffer_varlen;
00115     info->write_key= write_merge_key_varlen;
00116   }
00117   else
00118   {
00119     info->write_keys=write_keys;
00120     info->read_to_buffer=read_to_buffer;
00121     info->write_key=write_merge_key;
00122   }
00123 
00124   tempfile.clear();
00125   tempfile_for_exceptions.clear();
00126   memset(&buffpek, 0, sizeof(buffpek));
00127   unsigned char** sort_keys= (unsigned char **) NULL; 
00128   int error= 1;
00129   size_t maxbuffer=1;
00130 
00131   memavl=max(sortbuff_size,(size_t)MIN_SORT_MEMORY);
00132   ha_rows records= info->sort_info->max_records;
00133   uint32_t sort_length= info->key_length;
00134 
00135   while (memavl >= MIN_SORT_MEMORY)
00136   {
00137     if ((records < UINT32_MAX) &&
00138        ((internal::my_off_t) (records + 1) *
00139         (sort_length + sizeof(char*)) <= (internal::my_off_t) memavl))
00140       keys= (uint)records+1;
00141     else
00142       do
00143       {
00144         skr=maxbuffer;
00145         if (memavl < sizeof(BUFFPEK)* maxbuffer ||
00146           (keys=(memavl-sizeof(BUFFPEK)* maxbuffer) / (sort_length+sizeof(char*))) <= 1 ||
00147           keys < maxbuffer)
00148         {
00149           mi_check_print_error(info->sort_info->param,
00150             "myisam_sort_buffer_size is too small");
00151           goto err;
00152         }
00153       }
00154       while ((maxbuffer= (size_t)(records/(keys-1)+1)) != skr);
00155 
00156     sort_keys=(unsigned char **)malloc(keys*(sort_length+sizeof(char*)));
00157     buffpek.init(sizeof(BUFFPEK), maxbuffer, maxbuffer/2);
00158     break;
00159   }
00160   if (memavl < MIN_SORT_MEMORY)
00161   {
00162     mi_check_print_error(info->sort_info->param,"MyISAM sort buffer too small");
00163     goto err;
00164   }
00165   (*info->lock_in_memory)(info->sort_info->param);/* Everything is allocated */
00166 
00167   if (!no_messages)
00168     printf("  - Searching for keys, allocating buffer for %d keys\n",keys);
00169 
00170   if ((records=find_all_keys(info,keys,sort_keys,&buffpek,&maxbuffer,
00171            &tempfile,&tempfile_for_exceptions))
00172       == HA_POS_ERROR)
00173     goto err;
00174   if (maxbuffer == 0)
00175   {
00176     if (!no_messages)
00177       printf("  - Dumping %u keys\n", (uint32_t) records);
00178     if (write_index(info,sort_keys, (uint) records))
00179       goto err;
00180   }
00181   else
00182   {
00183     keys=(keys*(sort_length+sizeof(char*)))/sort_length;
00184     if (maxbuffer >= MERGEBUFF2)
00185     {
00186       if (!no_messages)
00187   printf("  - Merging %u keys\n", (uint32_t) records);
00188       if (merge_many_buff(info,keys,sort_keys, (BUFFPEK*)buffpek.buffer, &maxbuffer, &tempfile))
00189   goto err;
00190     }
00191     if (tempfile.flush() ||
00192   tempfile.reinit_io_cache(internal::READ_CACHE,0L,0,0))
00193       goto err;
00194     if (!no_messages)
00195       printf("  - Last merge and dumping keys\n");
00196     if (merge_index(info,keys,sort_keys, (BUFFPEK*)buffpek.buffer, maxbuffer, &tempfile))
00197       goto err;
00198   }
00199 
00200   if (flush_pending_blocks(info))
00201     goto err;
00202 
00203   if (tempfile_for_exceptions.inited())
00204   {
00205     MI_INFO *idx=info->sort_info->info;
00206     uint32_t     keyno=info->key;
00207     uint32_t     key_length, ref_length=idx->s->rec_reflength;
00208 
00209     if (not no_messages)
00210       printf("  - Adding exceptions\n");
00211 
00212     if (tempfile_for_exceptions.flush() || tempfile_for_exceptions.reinit_io_cache(internal::READ_CACHE,0L,0,0))
00213     {
00214       goto err;
00215     }
00216 
00217     while (not tempfile_for_exceptions.read(&key_length, sizeof(key_length))
00218         && not tempfile_for_exceptions.read(sort_keys, key_length))
00219     {
00220       if (_mi_ck_write(idx,keyno,(unsigned char*) sort_keys,key_length-ref_length))
00221         goto err;
00222     }
00223   }
00224 
00225   error =0;
00226 
00227 err:
00228   free(sort_keys);
00229   buffpek.free();
00230   tempfile.close_cached_file();
00231   tempfile_for_exceptions.close_cached_file();
00232 
00233   return error ? -1 : 0;
00234 } /* _create_index_by_sort */
00235 
00236 
00237 /* Search after all keys and place them in a temp. file */
00238 
00239 static ha_rows  find_all_keys(MI_SORT_PARAM *info, uint32_t keys,
00240             unsigned char **sort_keys,
00241             DYNAMIC_ARRAY *buffpek,
00242             size_t *maxbuffer, internal::io_cache_st *tempfile,
00243             internal::io_cache_st *tempfile_for_exceptions)
00244 {
00245   int error;
00246   uint32_t idx;
00247 
00248   idx=error=0;
00249   sort_keys[0]=(unsigned char*) (sort_keys+keys);
00250 
00251   while (!(error=(*info->key_read)(info,sort_keys[idx])))
00252   {
00253     if (info->real_key_length > info->key_length)
00254     {
00255       if (write_key(info,sort_keys[idx],tempfile_for_exceptions))
00256         return(HA_POS_ERROR);
00257       continue;
00258     }
00259 
00260     if (++idx == keys)
00261     {
00262       if (info->write_keys(info,sort_keys,idx-1,(BUFFPEK *)buffpek->alloc(), tempfile))
00263         return HA_POS_ERROR;
00264 
00265       sort_keys[0]=(unsigned char*) (sort_keys+keys);
00266       memcpy(sort_keys[0],sort_keys[idx-1],(size_t) info->key_length);
00267       idx=1;
00268     }
00269     sort_keys[idx]=sort_keys[idx-1]+info->key_length;
00270   }
00271   if (error > 0)
00272     return(HA_POS_ERROR);
00273   if (buffpek->size())
00274   {
00275     if (info->write_keys(info,sort_keys,idx,(BUFFPEK *)buffpek->alloc(), tempfile))
00276       return HA_POS_ERROR;
00277     *maxbuffer=buffpek->size() - 1;
00278   }
00279   else
00280     *maxbuffer=0;
00281 
00282   return((*maxbuffer)*(keys-1)+idx);
00283 } /* find_all_keys */
00284 
00285 
00286 int thr_write_keys(MI_SORT_PARAM *sort_param)
00287 {
00288   SORT_INFO *sort_info= sort_param->sort_info;
00289   MI_CHECK *param= sort_info->param;
00290   uint32_t length= 0, keys;
00291   ulong *rec_per_key_part= param->rec_per_key_part;
00292   int got_error=sort_info->got_error;
00293   uint32_t i;
00294   MI_INFO *info=sort_info->info;
00295   MYISAM_SHARE *share=info->s;
00296   MI_SORT_PARAM *sinfo;
00297   unsigned char *mergebuf=0;
00298 
00299   for (i= 0, sinfo= sort_param ;
00300        i < sort_info->total_keys ;
00301        i++, rec_per_key_part+=sinfo->keyinfo->keysegs, sinfo++)
00302   {
00303     if (!sinfo->sort_keys)
00304     {
00305       got_error=1;
00306       void * rec_buff_ptr= mi_get_rec_buff_ptr(info, sinfo->rec_buff);
00307       free(rec_buff_ptr);
00308       continue;
00309     }
00310     if (!got_error)
00311     {
00312       mi_set_key_active(share->state.key_map, sinfo->key);
00313       if (!sinfo->buffpek.size())
00314       {
00315         if (param->testflag & T_VERBOSE)
00316         {
00317           printf("Key %d  - Dumping %u keys\n",sinfo->key+1, sinfo->keys);
00318           fflush(stdout);
00319         }
00320         if (write_index(sinfo, sinfo->sort_keys, sinfo->keys) || flush_pending_blocks(sinfo))
00321           got_error=1;
00322       }
00323       if (!got_error && param->testflag & T_STATISTICS)
00324         update_key_parts(sinfo->keyinfo, rec_per_key_part, sinfo->unique,
00325                          param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
00326                          sinfo->notnull: NULL,
00327                          (uint64_t) info->state->records);
00328     }
00329     free((unsigned char*) sinfo->sort_keys);
00330     void * rec_buff_ptr= mi_get_rec_buff_ptr(info, sinfo->rec_buff);
00331     free(rec_buff_ptr);
00332     sinfo->sort_keys=0;
00333   }
00334 
00335   for (i= 0, sinfo= sort_param; i < sort_info->total_keys; i++,
00336    sinfo->buffpek.free(),
00337    sinfo->tempfile.close_cached_file(),
00338    sinfo->tempfile_for_exceptions.close_cached_file(),
00339    sinfo++)
00340   {
00341     if (got_error)
00342       continue;
00343     if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY)
00344     {
00345       sinfo->write_keys=write_keys_varlen;
00346       sinfo->read_to_buffer=read_to_buffer_varlen;
00347       sinfo->write_key=write_merge_key_varlen;
00348     }
00349     else
00350     {
00351       sinfo->write_keys=write_keys;
00352       sinfo->read_to_buffer=read_to_buffer;
00353       sinfo->write_key=write_merge_key;
00354     }
00355     if (sinfo->buffpek.size())
00356     {
00357       size_t maxbuffer=sinfo->buffpek.size() - 1;
00358       if (!mergebuf)
00359       {
00360         length=param->sort_buffer_length;
00361         while (length >= MIN_SORT_MEMORY)
00362         {
00363           if ((mergebuf= (unsigned char *)malloc(length)))
00364               break;
00365           length=length*3/4;
00366         }
00367         if (!mergebuf)
00368         {
00369           got_error=1;
00370           continue;
00371         }
00372       }
00373       keys=length/sinfo->key_length;
00374       if (maxbuffer >= MERGEBUFF2)
00375       {
00376         if (param->testflag & T_VERBOSE)
00377           printf("Key %d  - Merging %u keys\n",sinfo->key+1, sinfo->keys);
00378         if (merge_many_buff(sinfo, keys, (unsigned char **)mergebuf, (BUFFPEK*)sinfo->buffpek.buffer,
00379           &maxbuffer, &sinfo->tempfile))
00380         {
00381           got_error=1;
00382           continue;
00383         }
00384       }
00385       if (sinfo->tempfile.flush() || sinfo->tempfile.reinit_io_cache(internal::READ_CACHE,0L,0,0))
00386       {
00387         got_error=1;
00388         continue;
00389       }
00390       if (param->testflag & T_VERBOSE)
00391         printf("Key %d  - Last merge and dumping keys\n", sinfo->key+1);
00392       if (merge_index(sinfo, keys, (unsigned char **)mergebuf, (BUFFPEK*)sinfo->buffpek.buffer,
00393                       maxbuffer,&sinfo->tempfile) ||
00394     flush_pending_blocks(sinfo))
00395       {
00396         got_error=1;
00397         continue;
00398       }
00399     }
00400     if (sinfo->tempfile_for_exceptions.inited())
00401     {
00402       uint32_t key_length;
00403 
00404       if (param->testflag & T_VERBOSE)
00405         printf("Key %d  - Dumping 'long' keys\n", sinfo->key+1);
00406 
00407       if (sinfo->tempfile_for_exceptions.flush() || sinfo->tempfile_for_exceptions.reinit_io_cache(internal::READ_CACHE,0L,0,0))
00408       {
00409         got_error=1;
00410         continue;
00411       }
00412 
00413       while (!got_error && not sinfo->tempfile_for_exceptions.read(&key_length, sizeof(key_length)))
00414       {
00415         unsigned char ft_buf[10];
00416         if (key_length > sizeof(ft_buf) || sinfo->tempfile_for_exceptions.read(ft_buf, (uint)key_length) 
00417           || _mi_ck_write(info, sinfo->key, (unsigned char*)ft_buf, key_length - info->s->rec_reflength))
00418           got_error= 1;
00419       }
00420     }
00421   }
00422   free((unsigned char*) mergebuf);
00423   return(got_error);
00424 }
00425 
00426         /* Write all keys in memory to file for later merge */
00427 
00428 static int  write_keys(MI_SORT_PARAM *info, register unsigned char **sort_keys,
00429                              uint32_t count, BUFFPEK *buffpek, internal::io_cache_st *tempfile)
00430 {
00431   unsigned char **end;
00432   uint32_t sort_length=info->key_length;
00433 
00434   internal::my_qsort2((unsigned char*) sort_keys,count,sizeof(unsigned char*),(qsort2_cmp) info->key_cmp, info);
00435   if (not tempfile->inited() && tempfile->open_cached_file(P_tmpdir, "ST", DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
00436     return(1);
00437 
00438   buffpek->file_pos= tempfile->tell();
00439   buffpek->count=count;
00440 
00441   for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
00442   {
00443     if (tempfile->write(*sort_keys, sort_length))
00444       return(1);
00445   }
00446   return(0);
00447 } /* write_keys */
00448 
00449 
00450 inline int
00451 my_var_write(MI_SORT_PARAM *info, internal::io_cache_st *to_file, unsigned char *bufs)
00452 {
00453   int err;
00454   uint16_t len = _mi_keylength(info->keyinfo, (unsigned char*) bufs);
00455 
00456   /* The following is safe as this is a local file */
00457   if ((err= to_file->write(&len, sizeof(len))))
00458     return (err);
00459   if ((err= to_file->write(bufs, len)))
00460     return (err);
00461   return (0);
00462 }
00463 
00464 
00465 static int  write_keys_varlen(MI_SORT_PARAM *info,
00466             register unsigned char **sort_keys,
00467                                     uint32_t count, BUFFPEK *buffpek,
00468             internal::io_cache_st *tempfile)
00469 {
00470   unsigned char **end;
00471   int err;
00472 
00473   internal::my_qsort2((unsigned char*) sort_keys,count,sizeof(unsigned char*),(qsort2_cmp) info->key_cmp, info);
00474   if (not tempfile->inited() && tempfile->open_cached_file(P_tmpdir, "ST", DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
00475     return(1);
00476 
00477   buffpek->file_pos= tempfile->tell();
00478   buffpek->count=count;
00479   for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
00480   {
00481     if ((err= my_var_write(info,tempfile, (unsigned char*) *sort_keys)))
00482       return(err);
00483   }
00484   return(0);
00485 } /* write_keys_varlen */
00486 
00487 
00488 static int  write_key(MI_SORT_PARAM *info, unsigned char *key,
00489           internal::io_cache_st *tempfile)
00490 {
00491   uint32_t key_length=info->real_key_length;
00492 
00493   if (not tempfile->inited() && tempfile->open_cached_file(P_tmpdir, "ST", DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
00494     return(1);
00495 
00496   if (tempfile->write(&key_length, sizeof(key_length)) ||
00497       tempfile->write(key, key_length))
00498     return(1);
00499   return(0);
00500 } /* write_key */
00501 
00502 
00503 /* Write index */
00504 
00505 static int  write_index(MI_SORT_PARAM *info, register unsigned char **sort_keys,
00506                               register uint32_t count)
00507 {
00508   internal::my_qsort2((unsigned char*) sort_keys,(size_t) count,sizeof(unsigned char*),
00509            (qsort2_cmp) info->key_cmp,info);
00510   while (count--)
00511   {
00512     if ((*info->key_write)(info,*sort_keys++))
00513       return(-1);
00514   }
00515   return(0);
00516 } /* write_index */
00517 
00518 
00519         /* Merge buffers to make < MERGEBUFF2 buffers */
00520 
00521 static int  merge_many_buff(MI_SORT_PARAM *info, uint32_t keys,
00522           unsigned char **sort_keys, BUFFPEK *buffpek,
00523           size_t *maxbuffer, internal::io_cache_st *t_file)
00524 {
00525   uint32_t i;
00526   internal::io_cache_st t_file2, *from_file, *to_file, *temp;
00527   BUFFPEK *lastbuff;
00528 
00529   if (*maxbuffer < MERGEBUFF2)
00530     return(0);
00531   if (t_file->flush() || t_file2.open_cached_file(P_tmpdir, "ST", DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
00532     return(1);
00533 
00534   from_file= t_file ; to_file= &t_file2;
00535   while (*maxbuffer >= MERGEBUFF2)
00536   {
00537     from_file->reinit_io_cache(internal::READ_CACHE,0L,0,0);
00538     to_file->reinit_io_cache(internal::WRITE_CACHE,0L,0,0);
00539     lastbuff=buffpek;
00540     for (i=0 ; i <= *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
00541     {
00542       if (merge_buffers(info,keys,from_file,to_file,sort_keys,lastbuff++,
00543                         buffpek+i,buffpek+i+MERGEBUFF-1))
00544         goto cleanup;
00545     }
00546     if (merge_buffers(info,keys,from_file,to_file,sort_keys,lastbuff++,
00547                       buffpek+i,buffpek+ *maxbuffer))
00548       break;
00549     if (to_file->flush())
00550       break;
00551     temp=from_file; from_file=to_file; to_file=temp;
00552     *maxbuffer= (int) (lastbuff-buffpek)-1;
00553   }
00554 cleanup:
00555   to_file->close_cached_file();                   /* This holds old result */
00556   if (to_file == t_file)
00557     *t_file=t_file2;                            /* Copy result file */
00558 
00559   return(*maxbuffer >= MERGEBUFF2);        /* Return 1 if interrupted */
00560 } /* merge_many_buff */
00561 
00562 
00563 /*
00564    Read data to buffer
00565 
00566   SYNOPSIS
00567     read_to_buffer()
00568     fromfile    File to read from
00569     buffpek   Where to read from
00570     sort_length   max length to read
00571   RESULT
00572     > 0 Ammount of bytes read
00573     -1  Error
00574 */
00575 
00576 static uint32_t  read_to_buffer(internal::io_cache_st *fromfile, BUFFPEK *buffpek,
00577                                   uint32_t sort_length)
00578 {
00579   register uint32_t count;
00580   uint32_t length;
00581 
00582   if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
00583   {
00584     if (my_pread(fromfile->file,(unsigned char*) buffpek->base,
00585                  (length= sort_length*count),buffpek->file_pos,MYF_RW))
00586       return((uint) -1);
00587     buffpek->key=buffpek->base;
00588     buffpek->file_pos+= length;                 /* New filepos */
00589     buffpek->count-=    count;
00590     buffpek->mem_count= count;
00591   }
00592   return (count*sort_length);
00593 } /* read_to_buffer */
00594 
00595 static uint32_t  read_to_buffer_varlen(internal::io_cache_st *fromfile, BUFFPEK *buffpek,
00596                                          uint32_t sort_length)
00597 {
00598   register uint32_t count;
00599   uint16_t length_of_key = 0;
00600   uint32_t idx;
00601   unsigned char *buffp;
00602 
00603   if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
00604   {
00605     buffp = buffpek->base;
00606 
00607     for (idx=1;idx<=count;idx++)
00608     {
00609       if (my_pread(fromfile->file,(unsigned char*)&length_of_key,sizeof(length_of_key),
00610                    buffpek->file_pos,MYF_RW))
00611         return((uint) -1);
00612       buffpek->file_pos+=sizeof(length_of_key);
00613       if (my_pread(fromfile->file,(unsigned char*) buffp,length_of_key,
00614                    buffpek->file_pos,MYF_RW))
00615         return((uint) -1);
00616       buffpek->file_pos+=length_of_key;
00617       buffp = buffp + sort_length;
00618     }
00619     buffpek->key=buffpek->base;
00620     buffpek->count-=    count;
00621     buffpek->mem_count= count;
00622   }
00623   return (count*sort_length);
00624 } /* read_to_buffer_varlen */
00625 
00626 
00627 static int  write_merge_key_varlen(MI_SORT_PARAM *info,
00628            internal::io_cache_st *to_file, unsigned char* key,
00629                                          uint32_t sort_length, uint32_t count)
00630 {
00631   uint32_t idx;
00632   unsigned char *bufs = key;
00633 
00634   for (idx=1;idx<=count;idx++)
00635   {
00636     int err;
00637     if ((err= my_var_write(info, to_file, bufs)))
00638       return (err);
00639     bufs=bufs+sort_length;
00640   }
00641   return(0);
00642 }
00643 
00644 
00645 static int  write_merge_key(MI_SORT_PARAM*,
00646           internal::io_cache_st *to_file, unsigned char *key,
00647           uint32_t sort_length, uint32_t count)
00648 {
00649   return to_file->write(key, sort_length*count);
00650 }
00651 
00652 /*
00653  * Function object to be used as the comparison function
00654  * for the priority queue in the merge_buffers method.
00655  */
00656 class compare_functor
00657 {
00658   qsort2_cmp key_compare;
00659   void *key_compare_arg;
00660   public:
00661   compare_functor(qsort2_cmp in_key_compare, void *in_compare_arg)
00662     : key_compare(in_key_compare), key_compare_arg(in_compare_arg) { }
00663   inline bool operator()(const BUFFPEK *i, const BUFFPEK *j) const
00664   {
00665     int val= key_compare(key_compare_arg,
00666                       &i->key, &j->key);
00667     return (val >= 0);
00668   }
00669 };
00670 
00671 /*
00672   Merge buffers to one buffer
00673   If to_file == 0 then use info->key_write
00674 */
00675 
00676 static int
00677 merge_buffers(MI_SORT_PARAM *info, uint32_t keys, internal::io_cache_st *from_file,
00678               internal::io_cache_st *to_file, unsigned char **sort_keys, BUFFPEK *lastbuff,
00679               BUFFPEK *Fb, BUFFPEK *Tb)
00680 {
00681   int error;
00682   uint32_t sort_length,maxcount;
00683   ha_rows count;
00684   internal::my_off_t to_start_filepos= 0;
00685   unsigned char *strpos;
00686   BUFFPEK *buffpek;
00687   priority_queue<BUFFPEK *, vector<BUFFPEK *>, compare_functor > 
00688     queue(compare_functor((qsort2_cmp) info->key_cmp, static_cast<void *>(info)));
00689   volatile int *killed= killed_ptr(info->sort_info->param);
00690 
00691   count=error=0;
00692   maxcount=keys/((uint) (Tb-Fb) +1);
00693   assert(maxcount > 0);
00694   if (to_file)
00695     to_start_filepos= to_file->tell();
00696   strpos=(unsigned char*) sort_keys;
00697   sort_length=info->key_length;
00698 
00699   for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
00700   {
00701     count+= buffpek->count;
00702     buffpek->base= strpos;
00703     buffpek->max_keys=maxcount;
00704     strpos+= (uint) (error=(int) info->read_to_buffer(from_file,buffpek,
00705                                                       sort_length));
00706     if (error == -1)
00707       goto err;
00708     queue.push(buffpek);
00709   }
00710 
00711   while (queue.size() > 1)
00712   {
00713     for (;;)
00714     {
00715       if (*killed)
00716       {
00717         error=1; goto err;
00718       }
00719       buffpek= queue.top();
00720       if (to_file)
00721       {
00722         if (info->write_key(info,to_file,(unsigned char*) buffpek->key,
00723                             (uint) sort_length,1))
00724         {
00725           error=1; goto err;
00726         }
00727       }
00728       else
00729       {
00730         if ((*info->key_write)(info,(void*) buffpek->key))
00731         {
00732           error=1; goto err;
00733         }
00734       }
00735       buffpek->key+=sort_length;
00736       if (! --buffpek->mem_count)
00737       {
00738         if (!(error=(int) info->read_to_buffer(from_file,buffpek,sort_length)))
00739         {
00740           queue.pop();
00741           break;                /* One buffer have been removed */
00742         }
00743       }
00744       else if (error == -1)
00745         goto err;
00746       /* Top element has been replaced */
00747       queue.pop();
00748       queue.push(buffpek);
00749     }
00750   }
00751   buffpek= queue.top();
00752   buffpek->base=(unsigned char *) sort_keys;
00753   buffpek->max_keys=keys;
00754   do
00755   {
00756     if (to_file)
00757     {
00758       if (info->write_key(info,to_file,(unsigned char*) buffpek->key,
00759                          sort_length,buffpek->mem_count))
00760       {
00761         error=1; goto err;
00762       }
00763     }
00764     else
00765     {
00766       register unsigned char *end;
00767       strpos= buffpek->key;
00768       for (end=strpos+buffpek->mem_count*sort_length;
00769            strpos != end ;
00770            strpos+=sort_length)
00771       {
00772         if ((*info->key_write)(info,(void*) strpos))
00773         {
00774           error=1; goto err;
00775         }
00776       }
00777     }
00778   }
00779   while ((error=(int) info->read_to_buffer(from_file,buffpek,sort_length)) != -1 &&
00780          error != 0);
00781 
00782   lastbuff->count=count;
00783   if (to_file)
00784     lastbuff->file_pos=to_start_filepos;
00785 err:
00786   return(error);
00787 } /* merge_buffers */
00788 
00789 
00790         /* Do a merge to output-file (save only positions) */
00791 
00792 static int
00793 merge_index(MI_SORT_PARAM *info, uint32_t keys, unsigned char **sort_keys,
00794             BUFFPEK *buffpek, int maxbuffer, internal::io_cache_st *tempfile)
00795 {
00796   if (merge_buffers(info,keys,tempfile,(internal::io_cache_st*) 0,sort_keys,buffpek,buffpek,
00797                     buffpek+maxbuffer))
00798     return(1);
00799   return(0);
00800 } /* merge_index */
00801