~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2001 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
/*
17
  Function to handle quick removal of duplicates
18
  This code is used when doing multi-table deletes to find the rows in
19
  reference tables that needs to be deleted.
20
21
  The basic idea is as follows:
22
23
  Store first all strings in a binary tree, ignoring duplicates.
24
  When the tree uses more memory than 'max_heap_table_size',
25
  write the tree (in sorted order) out to disk and start with a new tree.
26
  When all data has been generated, merge the trees (removing any found
27
  duplicates).
28
29
  The unique entries will be returned in sort order, to ensure that we do the
30
  deletes in disk order.
31
*/
32
243.1.17 by Jay Pipes
FINAL PHASE removal of mysql_priv.h (Bye, bye my friend.)
33
#include <drizzled/server_includes.h>
572.1.4 by Monty Taylor
Removed a bunch of unusued tests and defines from autoconf.
34
#include <drizzled/sql_sort.h>
584.1.15 by Monty Taylor
The mega-patch from hell. Renamed sql_class to session (since that's what it is) and removed it and field and table from common_includes.
35
#include <drizzled/session.h>
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
36
#include <queue>
572.1.4 by Monty Taylor
Removed a bunch of unusued tests and defines from autoconf.
37
#include CMATH_H
38
39
#if defined(CMATH_NAMESPACE)
40
using namespace CMATH_NAMESPACE;
41
#endif
1 by brian
clean slate
42
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
43
using namespace std;
44
1 by brian
clean slate
45
654 by Brian Aker
Remove unused (yet more)
46
int unique_write_to_file(unsigned char* key, element_count,
77.1.45 by Monty Taylor
Warning fixes.
47
                         Unique *unique)
1 by brian
clean slate
48
{
49
  /*
50
    Use unique->size (size of element stored in the tree) and not
51
    unique->tree.size_of_element. The latter is different from unique->size
52
    when tree implementation chooses to store pointer to key in TREE_ELEMENT
53
    (instead of storing the element itself there)
54
  */
55
  return my_b_write(&unique->file, key, unique->size) ? 1 : 0;
56
}
57
481 by Brian Aker
Remove all of uchar.
58
int unique_write_to_ptrs(unsigned char* key,
654 by Brian Aker
Remove unused (yet more)
59
                         element_count, Unique *unique)
1 by brian
clean slate
60
{
61
  memcpy(unique->record_pointers, key, unique->size);
62
  unique->record_pointers+=unique->size;
63
  return 0;
64
}
65
66
Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
641.3.8 by Monty Taylor
Removed my_malloc from drizzled.
67
	       uint32_t size_arg, size_t max_in_memory_size_arg)
1 by brian
clean slate
68
  :max_in_memory_size(max_in_memory_size_arg), size(size_arg), elements(0)
69
{
70
  my_b_clear(&file);
71
  init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func, 0,
72
            NULL, comp_func_fixed_arg);
73
  /* If the following fail's the next add will also fail */
74
  my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16);
75
  /*
76
    If you change the following, change it in get_max_elements function, too.
77
  */
78
  max_elements= (ulong) (max_in_memory_size /
79
                         ALIGN_SIZE(sizeof(TREE_ELEMENT)+size));
575.4.3 by ysano
Rename mysql to drizzle.
80
  open_cached_file(&file, drizzle_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE,
398.1.10 by Monty Taylor
Actually removed VOID() this time.
81
                   MYF(MY_WME));
1 by brian
clean slate
82
}
83
84
85
/*
86
  Calculate log2(n!)
87
88
  NOTES
89
    Stirling's approximate formula is used:
90
91
      n! ~= sqrt(2*M_PI*n) * (n/M_E)^n
92
93
    Derivation of formula used for calculations is as follows:
94
95
    log2(n!) = log(n!)/log(2) = log(sqrt(2*M_PI*n)*(n/M_E)^n) / log(2) =
96
97
      = (log(2*M_PI*n)/2 + n*log(n/M_E)) / log(2).
98
*/
99
100
inline double log2_n_fact(double x)
101
{
102
  return (log(2*M_PI*x)/2 + x*log(x/M_E)) / M_LN2;
103
}
104
105
106
/*
107
  Calculate cost of merge_buffers function call for given sequence of
108
  input stream lengths and store the number of rows in result stream in *last.
109
110
  SYNOPSIS
111
    get_merge_buffers_cost()
112
      buff_elems  Array of #s of elements in buffers
113
      elem_size   Size of element stored in buffer
114
      first       Pointer to first merged element size
115
      last        Pointer to last merged element size
116
117
  RETURN
118
    Cost of merge_buffers operation in disk seeks.
119
120
  NOTES
121
    It is assumed that no rows are eliminated during merge.
122
    The cost is calculated as
123
124
      cost(read_and_write) + cost(merge_comparisons).
125
126
    All bytes in the sequences is read and written back during merge so cost
127
    of disk io is 2*elem_size*total_buf_elems/IO_SIZE (2 is for read + write)
128
129
    For comparisons cost calculations we assume that all merged sequences have
130
    the same length, so each of total_buf_size elements will be added to a sort
131
    heap with (n_buffers-1) elements. This gives the comparison cost:
132
133
      total_buf_elems* log2(n_buffers) / TIME_FOR_COMPARE_ROWID;
134
*/
135
654 by Brian Aker
Remove unused (yet more)
136
static double get_merge_buffers_cost(uint32_t *, uint32_t elem_size,
482 by Brian Aker
Remove uint.
137
                                     uint32_t *first, uint32_t *last)
1 by brian
clean slate
138
{
482 by Brian Aker
Remove uint.
139
  uint32_t total_buf_elems= 0;
140
  for (uint32_t *pbuf= first; pbuf <= last; pbuf++)
1 by brian
clean slate
141
    total_buf_elems+= *pbuf;
142
  *last= total_buf_elems;
143
144
  int n_buffers= last - first + 1;
145
146
  /* Using log2(n)=log(n)/log(2) formula */
147
  return 2*((double)total_buf_elems*elem_size) / IO_SIZE +
148
     total_buf_elems*log((double) n_buffers) / (TIME_FOR_COMPARE_ROWID * M_LN2);
149
}
150
151
152
/*
153
  Calculate cost of merging buffers into one in Unique::get, i.e. calculate
154
  how long (in terms of disk seeks) the two calls
155
    merge_many_buffs(...);
156
    merge_buffers(...);
157
  will take.
158
159
  SYNOPSIS
160
    get_merge_many_buffs_cost()
161
      buffer        buffer space for temporary data, at least
162
                    Unique::get_cost_calc_buff_size bytes
163
      maxbuffer     # of full buffers
164
      max_n_elems   # of elements in first maxbuffer buffers
165
      last_n_elems  # of elements in last buffer
166
      elem_size     size of buffer element
167
168
  NOTES
169
    maxbuffer+1 buffers are merged, where first maxbuffer buffers contain
170
    max_n_elems elements each and last buffer contains last_n_elems elements.
171
172
    The current implementation does a dumb simulation of merge_many_buffs
173
    function actions.
174
175
  RETURN
176
    Cost of merge in disk seeks.
177
*/
178
482 by Brian Aker
Remove uint.
179
static double get_merge_many_buffs_cost(uint32_t *buffer,
180
                                        uint32_t maxbuffer, uint32_t max_n_elems,
181
                                        uint32_t last_n_elems, int elem_size)
1 by brian
clean slate
182
{
183
  register int i;
184
  double total_cost= 0.0;
482 by Brian Aker
Remove uint.
185
  uint32_t *buff_elems= buffer; /* #s of elements in each of merged sequences */
1 by brian
clean slate
186
187
  /*
188
    Set initial state: first maxbuffer sequences contain max_n_elems elements
189
    each, last sequence contains last_n_elems elements.
190
  */
191
  for (i = 0; i < (int)maxbuffer; i++)
192
    buff_elems[i]= max_n_elems;
193
  buff_elems[maxbuffer]= last_n_elems;
194
195
  /*
196
    Do it exactly as merge_many_buff function does, calling
197
    get_merge_buffers_cost to get cost of merge_buffers.
198
  */
199
  if (maxbuffer >= MERGEBUFF2)
200
  {
201
    while (maxbuffer >= MERGEBUFF2)
202
    {
482 by Brian Aker
Remove uint.
203
      uint32_t lastbuff= 0;
1 by brian
clean slate
204
      for (i = 0; i <= (int) maxbuffer - MERGEBUFF*3/2; i += MERGEBUFF)
205
      {
206
        total_cost+=get_merge_buffers_cost(buff_elems, elem_size,
207
                                           buff_elems + i,
208
                                           buff_elems + i + MERGEBUFF-1);
209
	lastbuff++;
210
      }
211
      total_cost+=get_merge_buffers_cost(buff_elems, elem_size,
212
                                         buff_elems + i,
213
                                         buff_elems + maxbuffer);
214
      maxbuffer= lastbuff;
215
    }
216
  }
217
218
  /* Simulate final merge_buff call. */
219
  total_cost += get_merge_buffers_cost(buff_elems, elem_size,
220
                                       buff_elems, buff_elems + maxbuffer);
221
  return total_cost;
222
}
223
224
225
/*
226
  Calculate cost of using Unique for processing nkeys elements of size
227
  key_size using max_in_memory_size memory.
228
229
  SYNOPSIS
230
    Unique::get_use_cost()
231
      buffer    space for temporary data, use Unique::get_cost_calc_buff_size
232
                to get # bytes needed.
233
      nkeys     #of elements in Unique
234
      key_size  size of each elements in bytes
235
      max_in_memory_size amount of memory Unique will be allowed to use
236
237
  RETURN
238
    Cost in disk seeks.
239
240
  NOTES
241
    cost(using_unqiue) =
242
      cost(create_trees) +  (see #1)
243
      cost(merge) +         (see #2)
244
      cost(read_result)     (see #3)
245
246
    1. Cost of trees creation
247
      For each Unique::put operation there will be 2*log2(n+1) elements
248
      comparisons, where n runs from 1 tree_size (we assume that all added
249
      elements are different). Together this gives:
250
251
      n_compares = 2*(log2(2) + log2(3) + ... + log2(N+1)) = 2*log2((N+1)!)
252
253
      then cost(tree_creation) = n_compares*ROWID_COMPARE_COST;
254
255
      Total cost of creating trees:
256
      (n_trees - 1)*max_size_tree_cost + non_max_size_tree_cost.
257
258
      Approximate value of log2(N!) is calculated by log2_n_fact function.
259
260
    2. Cost of merging.
261
      If only one tree is created by Unique no merging will be necessary.
262
      Otherwise, we model execution of merge_many_buff function and count
263
      #of merges. (The reason behind this is that number of buffers is small,
264
      while size of buffers is big and we don't want to loose precision with
265
      O(x)-style formula)
266
267
    3. If only one tree is created by Unique no disk io will happen.
268
      Otherwise, ceil(key_len*n_keys) disk seeks are necessary. We assume
269
      these will be random seeks.
270
*/
271
482 by Brian Aker
Remove uint.
272
double Unique::get_use_cost(uint32_t *buffer, uint32_t nkeys, uint32_t key_size,
641.3.8 by Monty Taylor
Removed my_malloc from drizzled.
273
                            size_t max_in_memory_size)
1 by brian
clean slate
274
{
275
  ulong max_elements_in_tree;
276
  ulong last_tree_elems;
277
  int   n_full_trees; /* number of trees in unique - 1 */
278
  double result;
279
280
  max_elements_in_tree= ((ulong) max_in_memory_size /
281
                         ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size));
282
283
  n_full_trees=    nkeys / max_elements_in_tree;
284
  last_tree_elems= nkeys % max_elements_in_tree;
285
286
  /* Calculate cost of creating trees */
287
  result= 2*log2_n_fact(last_tree_elems + 1.0);
288
  if (n_full_trees)
289
    result+= n_full_trees * log2_n_fact(max_elements_in_tree + 1.0);
290
  result /= TIME_FOR_COMPARE_ROWID;
291
292
293
  if (!n_full_trees)
294
    return result;
295
296
  /*
297
    There is more then one tree and merging is necessary.
298
    First, add cost of writing all trees to disk, assuming that all disk
299
    writes are sequential.
300
  */
301
  result += DISK_SEEK_BASE_COST * n_full_trees *
302
              ceil(((double) key_size)*max_elements_in_tree / IO_SIZE);
303
  result += DISK_SEEK_BASE_COST * ceil(((double) key_size)*last_tree_elems / IO_SIZE);
304
305
  /* Cost of merge */
306
  double merge_cost= get_merge_many_buffs_cost(buffer, n_full_trees,
307
                                               max_elements_in_tree,
308
                                               last_tree_elems, key_size);
309
  if (merge_cost < 0.0)
310
    return merge_cost;
311
312
  result += merge_cost;
313
  /*
314
    Add cost of reading the resulting sequence, assuming there were no
315
    duplicate elements.
316
  */
317
  result += ceil((double)key_size*nkeys/IO_SIZE);
318
319
  return result;
320
}
321
322
Unique::~Unique()
323
{
324
  close_cached_file(&file);
325
  delete_tree(&tree);
326
  delete_dynamic(&file_ptrs);
327
}
328
329
330
    /* Write tree to disk; clear tree */
331
bool Unique::flush()
332
{
333
  BUFFPEK file_ptr;
334
  elements+= tree.elements_in_tree;
335
  file_ptr.count=tree.elements_in_tree;
336
  file_ptr.file_pos=my_b_tell(&file);
337
338
  if (tree_walk(&tree, (tree_walk_action) unique_write_to_file,
339
		(void*) this, left_root_right) ||
481 by Brian Aker
Remove all of uchar.
340
      insert_dynamic(&file_ptrs, (unsigned char*) &file_ptr))
1 by brian
clean slate
341
    return 1;
342
  delete_tree(&tree);
343
  return 0;
344
}
345
346
347
/*
348
  Clear the tree and the file.
349
  You must call reset() if you want to reuse Unique after walk().
350
*/
351
352
void
353
Unique::reset()
354
{
355
  reset_tree(&tree);
356
  /*
357
    If elements != 0, some trees were stored in the file (see how
358
    flush() works). Note, that we can not count on my_b_tell(&file) == 0
359
    here, because it can return 0 right after walk(), and walk() does not
360
    reset any Unique member.
361
  */
362
  if (elements)
363
  {
364
    reset_dynamic(&file_ptrs);
365
    reinit_io_cache(&file, WRITE_CACHE, 0L, 0, 1);
366
  }
367
  elements= 0;
368
}
369
370
/*
371
  The comparison function, passed to queue_init() in merge_walk() and in
372
  merge_buffers() when the latter is called from Uniques::get() must
373
  use comparison function of Uniques::tree, but compare members of struct
374
  BUFFPEK.
375
*/
376
398.1.9 by Monty Taylor
Cleaned up stuff out of global.h.
377
#ifdef __cplusplus
378
extern "C" {
379
#endif
1 by brian
clean slate
380
481 by Brian Aker
Remove all of uchar.
381
static int buffpek_compare(void *arg, unsigned char *key_ptr1, unsigned char *key_ptr2)
1 by brian
clean slate
382
{
383
  BUFFPEK_COMPARE_CONTEXT *ctx= (BUFFPEK_COMPARE_CONTEXT *) arg;
384
  return ctx->key_compare(ctx->key_compare_arg,
481 by Brian Aker
Remove all of uchar.
385
                          *((unsigned char **) key_ptr1), *((unsigned char **)key_ptr2));
1 by brian
clean slate
386
}
387
398.1.9 by Monty Taylor
Cleaned up stuff out of global.h.
388
#ifdef __cplusplus
389
}
390
#endif
391
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
392
/*
393
 The comparison function object, passed to a priority_queue in merge_walk()
394
 as its sort function parameter.
395
*/
1 by brian
clean slate
396
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
397
class buffpek_compare_functor
398
{
399
  qsort_cmp2 key_compare;
400
  void *key_compare_arg;
401
  public:
402
  buffpek_compare_functor(qsort_cmp2 in_key_compare, void *in_compare_arg)
403
    : key_compare(in_key_compare), key_compare_arg(in_compare_arg) { }
404
  inline bool operator()(BUFFPEK *i, BUFFPEK *j)
405
  {
406
    return key_compare(key_compare_arg,
407
                    i->key, j->key);
408
  }
409
};
1 by brian
clean slate
410
411
/*
412
  DESCRIPTION
413
414
    Function is very similar to merge_buffers, but instead of writing sorted
415
    unique keys to the output file, it invokes walk_action for each key.
416
    This saves I/O if you need to pass through all unique keys only once.
417
418
  SYNOPSIS
419
    merge_walk()
420
  All params are 'IN' (but see comment for begin, end):
421
    merge_buffer       buffer to perform cached piece-by-piece loading
422
                       of trees; initially the buffer is empty
423
    merge_buffer_size  size of merge_buffer. Must be aligned with
424
                       key_length
425
    key_length         size of tree element; key_length * (end - begin)
426
                       must be less or equal than merge_buffer_size.
427
    begin              pointer to BUFFPEK struct for the first tree.
428
    end                pointer to BUFFPEK struct for the last tree;
429
                       end > begin and [begin, end) form a consecutive
430
                       range. BUFFPEKs structs in that range are used and
431
                       overwritten in merge_walk().
432
    walk_action        element visitor. Action is called for each unique
433
                       key.
434
    walk_action_arg    argument to walk action. Passed to it on each call.
435
    compare            elements comparison function
436
    compare_arg        comparison function argument
437
    file               file with all trees dumped. Trees in the file
438
                       must contain sorted unique values. Cache must be
439
                       initialized in read mode.
440
  RETURN VALUE
441
    0     ok
442
    <> 0  error
443
*/
444
481 by Brian Aker
Remove all of uchar.
445
static bool merge_walk(unsigned char *merge_buffer, ulong merge_buffer_size,
482 by Brian Aker
Remove uint.
446
                       uint32_t key_length, BUFFPEK *begin, BUFFPEK *end,
1 by brian
clean slate
447
                       tree_walk_action walk_action, void *walk_action_arg,
448
                       qsort_cmp2 compare, void *compare_arg,
449
                       IO_CACHE *file)
450
{
451
  if (end <= begin ||
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
452
      merge_buffer_size < (ulong) (key_length * (end - begin + 1))) 
1 by brian
clean slate
453
    return 1;
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
454
  priority_queue<BUFFPEK *, vector<BUFFPEK *>, buffpek_compare_functor >
455
    queue(buffpek_compare_functor(compare, compare_arg));
1 by brian
clean slate
456
  /* we need space for one key when a piece of merge buffer is re-read */
457
  merge_buffer_size-= key_length;
481 by Brian Aker
Remove all of uchar.
458
  unsigned char *save_key_buff= merge_buffer + merge_buffer_size;
895 by Brian Aker
Completion (?) of uint conversion.
459
  uint32_t max_key_count_per_piece= (uint32_t) (merge_buffer_size/(end-begin) /
1 by brian
clean slate
460
                                        key_length);
461
  /* if piece_size is aligned reuse_freed_buffer will always hit */
482 by Brian Aker
Remove uint.
462
  uint32_t piece_size= max_key_count_per_piece * key_length;
463
  uint32_t bytes_read;               /* to hold return value of read_to_buffer */
1 by brian
clean slate
464
  BUFFPEK *top;
465
  int res= 1;
466
  /*
467
    Invariant: queue must contain top element from each tree, until a tree
468
    is not completely walked through.
469
    Here we're forcing the invariant, inserting one element from each tree
470
    to the queue.
471
  */
472
  for (top= begin; top != end; ++top)
473
  {
474
    top->base= merge_buffer + (top - begin) * piece_size;
475
    top->max_keys= max_key_count_per_piece;
476
    bytes_read= read_to_buffer(file, top, key_length);
895 by Brian Aker
Completion (?) of uint conversion.
477
    if (bytes_read == (uint32_t) (-1))
1 by brian
clean slate
478
      goto end;
51.1.73 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
479
    assert(bytes_read);
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
480
    queue.push(top);
1 by brian
clean slate
481
  }
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
482
  top= queue.top();
483
  while (queue.size() > 1)
1 by brian
clean slate
484
  {
485
    /*
486
      Every iteration one element is removed from the queue, and one is
487
      inserted by the rules of the invariant. If two adjacent elements on
488
      the top of the queue are not equal, biggest one is unique, because all
489
      elements in each tree are unique. Action is applied only to unique
490
      elements.
491
    */
492
    void *old_key= top->key;
493
    /*
494
      read next key from the cache or from the file and push it to the
495
      queue; this gives new top.
496
    */
497
    top->key+= key_length;
498
    if (--top->mem_count)
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
499
    {
500
      queue.pop();
501
      queue.push(top);
502
    }
1 by brian
clean slate
503
    else /* next piece should be read */
504
    {
505
      /* save old_key not to overwrite it in read_to_buffer */
506
      memcpy(save_key_buff, old_key, key_length);
507
      old_key= save_key_buff;
508
      bytes_read= read_to_buffer(file, top, key_length);
895 by Brian Aker
Completion (?) of uint conversion.
509
      if (bytes_read == (uint32_t) (-1))
1 by brian
clean slate
510
        goto end;
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
511
      else if (bytes_read > 0) /* top->key, top->mem_count are reset */
512
      {                        /* in read_to_buffer */
513
        queue.pop();
514
        queue.push(top);
515
      }
1 by brian
clean slate
516
      else
517
      {
518
        /*
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
519
          Tree for old 'top' element is empty: remove it from the queue. 
1 by brian
clean slate
520
        */
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
521
        queue.pop();
1 by brian
clean slate
522
      }
523
    }
897.1.1 by Padraig
Replaced the instance of QUEUE in the merge_walk() function with
524
    top= queue.top();
1 by brian
clean slate
525
    /* new top has been obtained; if old top is unique, apply the action */
526
    if (compare(compare_arg, old_key, top->key))
527
    {
528
      if (walk_action(old_key, 1, walk_action_arg))
529
        goto end;
530
    }
531
  }
532
  /*
533
    Applying walk_action to the tail of the last tree: this is safe because
534
    either we had only one tree in the beginning, either we work with the
535
    last tree in the queue.
536
  */
537
  do
538
  {
539
    do
540
    {
541
      if (walk_action(top->key, 1, walk_action_arg))
542
        goto end;
543
      top->key+= key_length;
544
    }
545
    while (--top->mem_count);
546
    bytes_read= read_to_buffer(file, top, key_length);
895 by Brian Aker
Completion (?) of uint conversion.
547
    if (bytes_read == (uint32_t) (-1))
1 by brian
clean slate
548
      goto end;
549
  }
550
  while (bytes_read);
551
  res= 0;
552
end:
553
  return res;
554
}
555
556
557
/*
558
  DESCRIPTION
559
    Walks consecutively through all unique elements:
560
    if all elements are in memory, then it simply invokes 'tree_walk', else
561
    all flushed trees are loaded to memory piece-by-piece, pieces are
562
    sorted, and action is called for each unique value.
563
    Note: so as merging resets file_ptrs state, this method can change
564
    internal Unique state to undefined: if you want to reuse Unique after
565
    walk() you must call reset() first!
566
  SYNOPSIS
567
    Unique:walk()
568
  All params are 'IN':
569
    action  function-visitor, typed in include/my_tree.h
570
            function is called for each unique element
571
    arg     argument for visitor, which is passed to it on each call
572
  RETURN VALUE
573
    0    OK
574
    <> 0 error
575
 */
576
577
bool Unique::walk(tree_walk_action action, void *walk_action_arg)
578
{
579
  int res;
481 by Brian Aker
Remove all of uchar.
580
  unsigned char *merge_buffer;
1 by brian
clean slate
581
582
  if (elements == 0)                       /* the whole tree is in memory */
583
    return tree_walk(&tree, action, walk_action_arg, left_root_right);
584
585
  /* flush current tree to the file to have some memory for merge buffer */
586
  if (flush())
587
    return 1;
588
  if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0))
589
    return 1;
641.3.8 by Monty Taylor
Removed my_malloc from drizzled.
590
  if (!(merge_buffer= (unsigned char *) malloc(max_in_memory_size)))
1 by brian
clean slate
591
    return 1;
592
  res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size,
593
                  (BUFFPEK *) file_ptrs.buffer,
594
                  (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
595
                  action, walk_action_arg,
596
                  tree.compare, tree.custom_arg, &file);
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
597
  free((char*) merge_buffer);
1 by brian
clean slate
598
  return res;
599
}
600
601
/*
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
602
  Modify the Table element so that when one calls init_records()
1 by brian
clean slate
603
  the rows will be read in priority order.
604
*/
605
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
606
bool Unique::get(Table *table)
1 by brian
clean slate
607
{
608
  SORTPARAM sort_param;
609
  table->sort.found_records=elements+tree.elements_in_tree;
610
611
  if (my_b_tell(&file) == 0)
612
  {
613
    /* Whole tree is in memory;  Don't use disk if you don't need to */
481 by Brian Aker
Remove all of uchar.
614
    if ((record_pointers=table->sort.record_pointers= (unsigned char*)
641.3.8 by Monty Taylor
Removed my_malloc from drizzled.
615
	 malloc(size * tree.elements_in_tree)))
1 by brian
clean slate
616
    {
617
      (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs,
618
		       this, left_root_right);
619
      return 0;
620
    }
621
  }
622
  /* Not enough memory; Save the result to file && free memory used by tree */
623
  if (flush())
624
    return 1;
625
626
  IO_CACHE *outfile=table->sort.io_cache;
627
  BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer;
482 by Brian Aker
Remove uint.
628
  uint32_t maxbuffer= file_ptrs.elements - 1;
481 by Brian Aker
Remove all of uchar.
629
  unsigned char *sort_buffer;
1 by brian
clean slate
630
  my_off_t save_pos;
631
  bool error=1;
632
633
      /* Open cached file if it isn't open */
684 by Brian Aker
Mass cleanup for casting.
634
  outfile=table->sort.io_cache= new IO_CACHE;
641.3.9 by Monty Taylor
More removal of my_malloc.
635
  memset(outfile, 0, sizeof(IO_CACHE));
1 by brian
clean slate
636
575.4.3 by ysano
Rename mysql to drizzle.
637
  if (!outfile || (! my_b_inited(outfile) && open_cached_file(outfile,drizzle_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, MYF(MY_WME))))
1 by brian
clean slate
638
    return 1;
684 by Brian Aker
Mass cleanup for casting.
639
  reinit_io_cache(outfile, WRITE_CACHE, 0L, 0, 0);
1 by brian
clean slate
640
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
641
  memset(&sort_param, 0, sizeof(sort_param));
1 by brian
clean slate
642
  sort_param.max_rows= elements;
643
  sort_param.sort_form=table;
644
  sort_param.rec_length= sort_param.sort_length= sort_param.ref_length=
645
    size;
895 by Brian Aker
Completion (?) of uint conversion.
646
  sort_param.keys= (uint32_t) (max_in_memory_size / sort_param.sort_length);
1 by brian
clean slate
647
  sort_param.not_killable=1;
648
641.3.8 by Monty Taylor
Removed my_malloc from drizzled.
649
  if (!(sort_buffer=(unsigned char*) malloc((sort_param.keys+1) *
650
				            sort_param.sort_length)))
1 by brian
clean slate
651
    return 1;
652
  sort_param.unique_buff= sort_buffer+(sort_param.keys*
653
				       sort_param.sort_length);
654
655
  sort_param.compare= (qsort2_cmp) buffpek_compare;
656
  sort_param.cmp_context.key_compare= tree.compare;
657
  sort_param.cmp_context.key_compare_arg= tree.custom_arg;
658
659
  /* Merge the buffers to one file, removing duplicates */
660
  if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file))
661
    goto err;
662
  if (flush_io_cache(&file) ||
663
      reinit_io_cache(&file,READ_CACHE,0L,0,0))
664
    goto err;
665
  if (merge_buffers(&sort_param, &file, outfile, sort_buffer, file_ptr,
666
		    file_ptr, file_ptr+maxbuffer,0))
667
    goto err;
668
  error=0;
669
err:
460 by Monty Taylor
Removed x_free calls.
670
  if (sort_buffer)
671
    free(sort_buffer);
1 by brian
clean slate
672
  if (flush_io_cache(outfile))
673
    error=1;
674
675
  /* Setup io_cache for reading */
676
  save_pos=outfile->pos_in_file;
677
  if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
678
    error=1;
679
  outfile->end_of_file=save_pos;
680
  return error;
681
}