~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to drizzled/filesort.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-10-02 14:17:48 UTC
  • mfrom: (1.1.1 upstream)
  • mto: (2.1.17 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20101002141748-m6vbfbfjhrw1153e
Tags: 2010.09.1802-1
* New upstream release.
* Removed pid-file argument hack.
* Updated GPL-2 address to be new address.
* Directly copy in drizzledump.1 since debian doesn't have sphinx 1.0 yet.
* Link to jquery from libjs-jquery. Add it as a depend.
* Add drizzled.8 symlink to the install files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
56
56
                                             uint32_t count,
57
57
                                             unsigned char *buf);
58
58
 
59
 
static ha_rows find_all_keys(SORTPARAM *param,
 
59
static ha_rows find_all_keys(Session *session,
 
60
                             SORTPARAM *param,
60
61
                             optimizer::SqlSelect *select,
61
62
                             unsigned char * *sort_keys, 
62
63
                             internal::IO_CACHE *buffer_file,
75
76
static void register_used_fields(SORTPARAM *param);
76
77
static int merge_index(SORTPARAM *param,
77
78
                       unsigned char *sort_buffer,
78
 
                       BUFFPEK *buffpek,
 
79
                       buffpek_st *buffpek,
79
80
                       uint32_t maxbuffer,
80
81
                       internal::IO_CACHE *tempfile,
81
82
                       internal::IO_CACHE *outfile);
85
86
                       filesort_info_st *table_sort);
86
87
static uint32_t suffix_length(uint32_t string_length);
87
88
static uint32_t sortlength(Session *session,
88
 
                           SORT_FIELD *sortorder,
 
89
                           SortField *sortorder,
89
90
                           uint32_t s_length,
90
91
                           bool *multi_byte_charset);
91
 
static SORT_ADDON_FIELD *get_addon_fields(Session *session,
92
 
                                          Field **ptabfield,
93
 
                                          uint32_t sortlength,
94
 
                                          uint32_t *plength);
95
 
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
 
92
static sort_addon_field_st *get_addon_fields(Session *session,
 
93
                                             Field **ptabfield,
 
94
                                             uint32_t sortlength,
 
95
                                             uint32_t *plength);
 
96
static void unpack_addon_fields(sort_addon_field_st *addon_field,
96
97
                                unsigned char *buff);
97
98
/**
98
99
  Sort a table.
130
131
    examined_rows       will be set to number of examined rows
131
132
*/
132
133
 
133
 
ha_rows filesort(Session *session, Table *table, SORT_FIELD *sortorder, uint32_t s_length,
 
134
ha_rows filesort(Session *session, Table *table, SortField *sortorder, uint32_t s_length,
134
135
                 optimizer::SqlSelect *select, ha_rows max_rows,
135
136
                 bool sort_positions, ha_rows *examined_rows)
136
137
{
137
138
  int error;
138
139
  uint32_t memavl, min_sort_memory;
139
140
  uint32_t maxbuffer;
140
 
  BUFFPEK *buffpek;
 
141
  buffpek_st *buffpek;
141
142
  ha_rows records= HA_POS_ERROR;
142
143
  unsigned char **sort_keys= 0;
143
144
  internal::IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile;
148
149
  TableList *tab= table->pos_in_table_list;
149
150
  Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
150
151
 
151
 
  DRIZZLE_FILESORT_START(table->s->db.str, table->s->table_name.str);
 
152
  DRIZZLE_FILESORT_START(table->getShare()->getSchemaName(), table->getShare()->getTableName());
152
153
 
153
154
  /*
154
155
   Release InnoDB's adaptive hash index latch (if holding) before
180
181
      Get the descriptors of all fields whose values are appended
181
182
      to sorted fields and get its total length in param.spack_length.
182
183
    */
183
 
    param.addon_field= get_addon_fields(session, table->field,
 
184
    param.addon_field= get_addon_fields(session, table->getFields(),
184
185
                                        param.sort_length,
185
186
                                        &param.addon_length);
186
187
  }
209
210
 
210
211
  if (select && select->quick)
211
212
  {
212
 
    status_var_increment(session->status_var.filesort_range_count);
 
213
    session->status_var.filesort_range_count++;
213
214
  }
214
215
  else
215
216
  {
216
 
    status_var_increment(session->status_var.filesort_scan_count);
 
217
    session->status_var.filesort_scan_count++;
217
218
  }
218
219
#ifdef CAN_TRUST_RANGE
219
220
  if (select && select->quick && select->quick->records > 0L)
260
261
    my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG));
261
262
    goto err;
262
263
  }
263
 
  if (open_cached_file(&buffpek_pointers,drizzle_tmpdir,TEMP_PREFIX,
 
264
  if (open_cached_file(&buffpek_pointers,drizzle_tmpdir.c_str(),TEMP_PREFIX,
264
265
                       DISK_BUFFER_SIZE, MYF(MY_WME)))
265
266
    goto err;
266
267
 
267
268
  param.keys--;                         /* TODO: check why we do this */
268
269
  param.sort_form= table;
269
270
  param.end=(param.local_sortorder=sortorder)+s_length;
270
 
  if ((records=find_all_keys(&param,select,sort_keys, &buffpek_pointers,
 
271
  if ((records=find_all_keys(session, &param,select,sort_keys, &buffpek_pointers,
271
272
                             &tempfile, selected_records_file)) ==
272
273
      HA_POS_ERROR)
273
274
    goto err;
290
291
          (unsigned char *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
291
292
                                 table_sort.buffpek)))
292
293
      goto err;
293
 
    buffpek= (BUFFPEK *) table_sort.buffpek;
 
294
    buffpek= (buffpek_st *) table_sort.buffpek;
294
295
    table_sort.buffpek_len= maxbuffer;
295
296
    close_cached_file(&buffpek_pointers);
296
297
        /* Open cached file if it isn't open */
297
298
    if (! my_b_inited(outfile) &&
298
 
        open_cached_file(outfile,drizzle_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
 
299
        open_cached_file(outfile,drizzle_tmpdir.c_str(),TEMP_PREFIX,READ_RECORD_BUFFER,
299
300
                          MYF(MY_WME)))
300
301
      goto err;
301
302
    if (reinit_io_cache(outfile,internal::WRITE_CACHE,0L,0,0))
328
329
      free(param.tmp_buffer);
329
330
  if (!subselect || !subselect->is_uncacheable())
330
331
  {
331
 
    if ((unsigned char*) sort_keys)
332
 
      free((unsigned char*) sort_keys);
 
332
    free(sort_keys);
333
333
    table_sort.sort_keys= 0;
334
 
    if ((unsigned char*) buffpek)
335
 
      free((unsigned char*) buffpek);
 
334
    free(buffpek);
336
335
    table_sort.buffpek= 0;
337
336
    table_sort.buffpek_len= 0;
338
337
  }
351
350
    }
352
351
  }
353
352
  if (error)
 
353
  {
354
354
    my_message(ER_FILSORT_ABORT, ER(ER_FILSORT_ABORT),
355
355
               MYF(ME_ERROR+ME_WAITTANG));
 
356
  }
356
357
  else
357
 
    statistic_add(session->status_var.filesort_rows,
358
 
                  (uint32_t) records, &LOCK_status);
 
358
  {
 
359
    session->status_var.filesort_rows+= (uint32_t) records;
 
360
  }
359
361
  *examined_rows= param.examined_rows;
360
362
  memcpy(&table->sort, &table_sort, sizeof(filesort_info_st));
361
363
  DRIZZLE_FILESORT_DONE(error, records);
419
421
static unsigned char *read_buffpek_from_file(internal::IO_CACHE *buffpek_pointers, uint32_t count,
420
422
                                     unsigned char *buf)
421
423
{
422
 
  uint32_t length= sizeof(BUFFPEK)*count;
 
424
  uint32_t length= sizeof(buffpek_st)*count;
423
425
  unsigned char *tmp= buf;
424
 
  if (count > UINT_MAX/sizeof(BUFFPEK))
425
 
    return 0; /* sizeof(BUFFPEK)*count will overflow */
 
426
  if (count > UINT_MAX/sizeof(buffpek_st))
 
427
    return 0; /* sizeof(buffpek_st)*count will overflow */
426
428
  if (!tmp)
427
429
    tmp= (unsigned char *)malloc(length);
428
430
  if (tmp)
445
447
  @param param             Sorting parameter
446
448
  @param select            Use this to get source data
447
449
  @param sort_keys         Array of pointers to sort key + addon buffers.
448
 
  @param buffpek_pointers  File to write BUFFPEKs describing sorted segments
 
450
  @param buffpek_pointers  File to write buffpek_sts describing sorted segments
449
451
                           in tempfile.
450
452
  @param tempfile          File to write sorted sequences of sortkeys to.
451
453
  @param indexfile         If !NULL, use it for source data (contains rowids)
459
461
       {
460
462
         sort sort_keys buffer;
461
463
         dump sorted sequence to 'tempfile';
462
 
         dump BUFFPEK describing sequence location into 'buffpek_pointers';
 
464
         dump buffpek_st describing sequence location into 'buffpek_pointers';
463
465
       }
464
466
       put sort key into 'sort_keys';
465
467
     }
475
477
    HA_POS_ERROR on error.
476
478
*/
477
479
 
478
 
static ha_rows find_all_keys(SORTPARAM *param, 
 
480
static ha_rows find_all_keys(Session *session,
 
481
                             SORTPARAM *param, 
479
482
                             optimizer::SqlSelect *select,
480
483
                             unsigned char **sort_keys,
481
484
                             internal::IO_CACHE *buffpek_pointers,
486
489
  unsigned char *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
487
490
  internal::my_off_t record;
488
491
  Table *sort_form;
489
 
  Session *session= current_session;
490
492
  volatile Session::killed_state *killed= &session->killed;
491
493
  Cursor *file;
492
494
  MyBitmap *save_read_set, *save_write_set;
507
509
  if (! indexfile && ! quick_select)
508
510
  {
509
511
    next_pos=(unsigned char*) 0;                        /* Find records in sequence */
510
 
    file->ha_rnd_init(1);
 
512
    file->startTableScan(1);
511
513
    file->extra_opt(HA_EXTRA_CACHE,
512
 
                    current_session->variables.read_buff_size);
 
514
                    session->variables.read_buff_size);
513
515
  }
514
516
 
515
 
  READ_RECORD read_record_info;
 
517
  ReadRecord read_record_info;
516
518
  if (quick_select)
517
519
  {
518
520
    if (select->quick->reset())
519
521
      return(HA_POS_ERROR);
520
 
    init_read_record(&read_record_info, current_session, select->quick->head,
521
 
                     select, 1, 1);
 
522
 
 
523
    read_record_info.init_read_record(session, select->quick->head, select, 1, 1);
522
524
  }
523
525
 
524
526
  /* Remember original bitmaps */
543
545
        error= HA_ERR_END_OF_FILE;
544
546
        break;
545
547
      }
546
 
      file->position(sort_form->record[0]);
 
548
      file->position(sort_form->getInsertRecord());
547
549
    }
548
550
    else                                        /* Not quick-select */
549
551
    {
554
556
          error= errno ? errno : -1;            /* Abort */
555
557
          break;
556
558
        }
557
 
        error=file->rnd_pos(sort_form->record[0],next_pos);
 
559
        error=file->rnd_pos(sort_form->getInsertRecord(),next_pos);
558
560
      }
559
561
      else
560
562
      {
561
 
        error=file->rnd_next(sort_form->record[0]);
 
563
        error=file->rnd_next(sort_form->getInsertRecord());
562
564
 
563
565
        if (!flag)
564
566
        {
565
567
          internal::my_store_ptr(ref_pos,ref_length,record); // Position to row
566
 
          record+= sort_form->s->db_record_offset;
 
568
          record+= sort_form->getShare()->db_record_offset;
567
569
        }
568
570
        else if (!error)
569
 
          file->position(sort_form->record[0]);
 
571
          file->position(sort_form->getInsertRecord());
570
572
      }
571
573
      if (error && error != HA_ERR_RECORD_DELETED)
572
574
        break;
577
579
      if (!indexfile && !quick_select)
578
580
      {
579
581
        (void) file->extra(HA_EXTRA_NO_CACHE);
580
 
        file->ha_rnd_end();
 
582
        file->endTableScan();
581
583
      }
582
584
      return(HA_POS_ERROR);
583
585
    }
606
608
      index_merge quick select uses table->sort when retrieving rows, so free
607
609
      resoures it has allocated.
608
610
    */
609
 
    end_read_record(&read_record_info);
 
611
    read_record_info.end_read_record();
610
612
  }
611
613
  else
612
614
  {
613
615
    (void) file->extra(HA_EXTRA_NO_CACHE);      /* End cacheing of records */
614
616
    if (!next_pos)
615
 
      file->ha_rnd_end();
 
617
      file->endTableScan();
616
618
  }
617
619
 
618
620
  if (session->is_error())
639
641
  @details
640
642
  Sort the buffer and write:
641
643
  -# the sorted sequence to tempfile
642
 
  -# a BUFFPEK describing the sorted sequence position to buffpek_pointers
 
644
  -# a buffpek_st describing the sorted sequence position to buffpek_pointers
643
645
 
644
646
    (was: Skriver en buffert med nycklar till filen)
645
647
 
646
648
  @param param             Sort parameters
647
649
  @param sort_keys         Array of pointers to keys to sort
648
650
  @param count             Number of elements in sort_keys array
649
 
  @param buffpek_pointers  One 'BUFFPEK' struct will be written into this file.
650
 
                           The BUFFPEK::{file_pos, count} will indicate where
 
651
  @param buffpek_pointers  One 'buffpek_st' struct will be written into this file.
 
652
                           The buffpek_st::{file_pos, count} will indicate where
651
653
                           the sorted data was stored.
652
654
  @param tempfile          The sorted sequence will be written into this file.
653
655
 
663
665
{
664
666
  size_t sort_length, rec_length;
665
667
  unsigned char **end;
666
 
  BUFFPEK buffpek;
 
668
  buffpek_st buffpek;
667
669
 
668
670
  sort_length= param->sort_length;
669
671
  rec_length= param->rec_length;
670
672
  internal::my_string_ptr_sort((unsigned char*) sort_keys, (uint32_t) count, sort_length);
671
673
  if (!my_b_inited(tempfile) &&
672
 
      open_cached_file(tempfile, drizzle_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
 
674
      open_cached_file(tempfile, drizzle_tmpdir.c_str(), TEMP_PREFIX, DISK_BUFFER_SIZE,
673
675
                       MYF(MY_WME)))
674
676
    goto err;
675
677
  /* check we won't have more buffpeks than we can possibly keep in memory */
676
 
  if (my_b_tell(buffpek_pointers) + sizeof(BUFFPEK) > (uint64_t)UINT_MAX)
 
678
  if (my_b_tell(buffpek_pointers) + sizeof(buffpek_st) > (uint64_t)UINT_MAX)
677
679
    goto err;
678
680
  buffpek.file_pos= my_b_tell(tempfile);
679
681
  if ((ha_rows) count > param->max_rows)
719
721
static void make_sortkey(register SORTPARAM *param,
720
722
                         register unsigned char *to, unsigned char *ref_pos)
721
723
{
722
 
  register Field *field;
723
 
  register SORT_FIELD *sort_field;
 
724
  Field *field;
 
725
  SortField *sort_field;
724
726
  size_t length;
725
727
 
726
728
  for (sort_field=param->local_sortorder ;
908
910
      In this implementation we use fixed layout for field values -
909
911
      the same for all records.
910
912
    */
911
 
    SORT_ADDON_FIELD *addonf= param->addon_field;
 
913
    sort_addon_field_st *addonf= param->addon_field;
912
914
    unsigned char *nulls= to;
913
915
    assert(addonf != 0);
914
916
    memset(nulls, 0, addonf->offset);
952
954
 
953
955
static void register_used_fields(SORTPARAM *param)
954
956
{
955
 
  register SORT_FIELD *sort_field;
 
957
  SortField *sort_field;
956
958
  Table *table=param->sort_form;
957
959
 
958
960
  for (sort_field= param->local_sortorder ;
962
964
    Field *field;
963
965
    if ((field= sort_field->field))
964
966
    {
965
 
      if (field->table == table)
 
967
      if (field->getTable() == table)
966
968
        table->setReadSet(field->field_index);
967
969
    }
968
970
    else
974
976
 
975
977
  if (param->addon_field)
976
978
  {
977
 
    SORT_ADDON_FIELD *addonf= param->addon_field;
 
979
    sort_addon_field_st *addonf= param->addon_field;
978
980
    Field *field;
979
981
    for ( ; (field= addonf->field) ; addonf++)
980
982
      table->setReadSet(field->field_index);
1013
1015
/** Merge buffers to make < MERGEBUFF2 buffers. */
1014
1016
 
1015
1017
int merge_many_buff(SORTPARAM *param, unsigned char *sort_buffer,
1016
 
                    BUFFPEK *buffpek, uint32_t *maxbuffer, internal::IO_CACHE *t_file)
 
1018
                    buffpek_st *buffpek, uint32_t *maxbuffer, internal::IO_CACHE *t_file)
1017
1019
{
1018
1020
  register uint32_t i;
1019
1021
  internal::IO_CACHE t_file2,*from_file,*to_file,*temp;
1020
 
  BUFFPEK *lastbuff;
 
1022
  buffpek_st *lastbuff;
1021
1023
 
1022
1024
  if (*maxbuffer < MERGEBUFF2)
1023
1025
    return(0);
1024
1026
  if (flush_io_cache(t_file) ||
1025
 
      open_cached_file(&t_file2,drizzle_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE,
 
1027
      open_cached_file(&t_file2,drizzle_tmpdir.c_str(),TEMP_PREFIX,DISK_BUFFER_SIZE,
1026
1028
                        MYF(MY_WME)))
1027
1029
    return(1);
1028
1030
 
1069
1071
    (uint32_t)-1 if something goes wrong
1070
1072
*/
1071
1073
 
1072
 
uint32_t read_to_buffer(internal::IO_CACHE *fromfile, BUFFPEK *buffpek,
 
1074
uint32_t read_to_buffer(internal::IO_CACHE *fromfile, buffpek_st *buffpek,
1073
1075
                        uint32_t rec_length)
1074
1076
{
1075
1077
  register uint32_t count;
1096
1098
  public:
1097
1099
  compare_functor(qsort2_cmp in_key_compare, void *in_compare_arg)
1098
1100
    : key_compare(in_key_compare), key_compare_arg(in_compare_arg) { }
1099
 
  inline bool operator()(const BUFFPEK *i, const BUFFPEK *j) const
 
1101
  inline bool operator()(const buffpek_st *i, const buffpek_st *j) const
1100
1102
  {
1101
1103
    int val= key_compare(key_compare_arg,
1102
1104
                      &i->key, &j->key);
1109
1111
  Merge buffers to one buffer.
1110
1112
 
1111
1113
  @param param        Sort parameter
1112
 
  @param from_file    File with source data (BUFFPEKs point to this file)
 
1114
  @param from_file    File with source data (buffpek_sts point to this file)
1113
1115
  @param to_file      File to write the sorted result data.
1114
1116
  @param sort_buffer  Buffer for data to store up to MERGEBUFF2 sort keys.
1115
 
  @param lastbuff     OUT Store here BUFFPEK describing data written to to_file
1116
 
  @param Fb           First element in source BUFFPEKs array
1117
 
  @param Tb           Last element in source BUFFPEKs array
 
1117
  @param lastbuff     OUT Store here buffpek_st describing data written to to_file
 
1118
  @param Fb           First element in source buffpek_sts array
 
1119
  @param Tb           Last element in source buffpek_sts array
1118
1120
  @param flag
1119
1121
 
1120
1122
  @retval
1125
1127
 
1126
1128
int merge_buffers(SORTPARAM *param, internal::IO_CACHE *from_file,
1127
1129
                  internal::IO_CACHE *to_file, unsigned char *sort_buffer,
1128
 
                  BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
 
1130
                  buffpek_st *lastbuff, buffpek_st *Fb, buffpek_st *Tb,
1129
1131
                  int flag)
1130
1132
{
1131
1133
  int error;
1135
1137
  ha_rows max_rows,org_max_rows;
1136
1138
  internal::my_off_t to_start_filepos;
1137
1139
  unsigned char *strpos;
1138
 
  BUFFPEK *buffpek;
 
1140
  buffpek_st *buffpek;
1139
1141
  qsort2_cmp cmp;
1140
1142
  void *first_cmp_arg;
1141
1143
  volatile Session::killed_state *killed= &current_session->killed;
1142
1144
  Session::killed_state not_killable;
1143
1145
 
1144
 
  status_var_increment(current_session->status_var.filesort_merge_passes);
 
1146
  current_session->status_var.filesort_merge_passes++;
1145
1147
  if (param->not_killable)
1146
1148
  {
1147
1149
    killed= &not_killable;
1171
1173
    cmp= internal::get_ptr_compare(sort_length);
1172
1174
    first_cmp_arg= (void*) &sort_length;
1173
1175
  }
1174
 
  priority_queue<BUFFPEK *, vector<BUFFPEK *>, compare_functor > 
 
1176
  priority_queue<buffpek_st *, vector<buffpek_st *>, compare_functor > 
1175
1177
    queue(compare_functor(cmp, first_cmp_arg));
1176
1178
  for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
1177
1179
  {
1331
1333
        /* Do a merge to output-file (save only positions) */
1332
1334
 
1333
1335
static int merge_index(SORTPARAM *param, unsigned char *sort_buffer,
1334
 
                       BUFFPEK *buffpek, uint32_t maxbuffer,
 
1336
                       buffpek_st *buffpek, uint32_t maxbuffer,
1335
1337
                       internal::IO_CACHE *tempfile, internal::IO_CACHE *outfile)
1336
1338
{
1337
1339
  if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
1373
1375
*/
1374
1376
 
1375
1377
static uint32_t
1376
 
sortlength(Session *session, SORT_FIELD *sortorder, uint32_t s_length,
 
1378
sortlength(Session *session, SortField *sortorder, uint32_t s_length,
1377
1379
           bool *multi_byte_charset)
1378
1380
{
1379
1381
  register uint32_t length;
1479
1481
    NULL   if we do not store field values with sort data.
1480
1482
*/
1481
1483
 
1482
 
static SORT_ADDON_FIELD *
 
1484
static sort_addon_field_st *
1483
1485
get_addon_fields(Session *session, Field **ptabfield, uint32_t sortlength, uint32_t *plength)
1484
1486
{
1485
1487
  Field **pfield;
1486
1488
  Field *field;
1487
 
  SORT_ADDON_FIELD *addonf;
 
1489
  sort_addon_field_st *addonf;
1488
1490
  uint32_t length= 0;
1489
1491
  uint32_t fields= 0;
1490
1492
  uint32_t null_fields= 0;
1516
1518
  length+= (null_fields+7)/8;
1517
1519
 
1518
1520
  if (length+sortlength > session->variables.max_length_for_sort_data ||
1519
 
      !(addonf= (SORT_ADDON_FIELD *) malloc(sizeof(SORT_ADDON_FIELD)*
 
1521
      !(addonf= (sort_addon_field_st *) malloc(sizeof(sort_addon_field_st)*
1520
1522
                                            (fields+1))))
1521
1523
    return 0;
1522
1524
 
1566
1568
*/
1567
1569
 
1568
1570
static void
1569
 
unpack_addon_fields(struct st_sort_addon_field *addon_field, unsigned char *buff)
 
1571
unpack_addon_fields(struct sort_addon_field_st *addon_field, unsigned char *buff)
1570
1572
{
1571
1573
  Field *field;
1572
 
  SORT_ADDON_FIELD *addonf= addon_field;
 
1574
  sort_addon_field_st *addonf= addon_field;
1573
1575
 
1574
1576
  for ( ; (field= addonf->field) ; addonf++)
1575
1577
  {