Coverage Report

Created: 2024-06-18 07:03

/src/server/mysys/hash.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
2
   Copyright (c) 2011, 2020, MariaDB Corporation.
3
4
   This program is free software; you can redistribute it and/or modify
5
   it under the terms of the GNU General Public License as published by
6
   the Free Software Foundation; version 2 of the License.
7
8
   This program is distributed in the hope that it will be useful,
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
   GNU General Public License for more details.
12
13
   You should have received a copy of the GNU General Public License
14
   along with this program; if not, write to the Free Software
15
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16
17
/* The hash functions used for saveing keys */
18
/* One of key_length or key_length_offset must be given */
19
/* Key length of 0 isn't allowed */
20
21
#include "mysys_priv.h"
22
#include <m_string.h>
23
#include <m_ctype.h>
24
#include "hash.h"
25
26
0
#define NO_RECORD ~((my_hash_value_type) 0)
27
0
#define LOWFIND 1
28
0
#define LOWUSED 2
29
0
#define HIGHFIND 4
30
0
#define HIGHUSED 8
31
32
typedef struct st_hash_info {
33
  uint32 next;          /* index to next key */
34
  my_hash_value_type hash_nr;
35
  uchar *data;          /* data for current entry */
36
} HASH_LINK;
37
38
static uint my_hash_mask(my_hash_value_type hashnr,
39
                         size_t buffmax, size_t maxlength);
40
static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
41
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
42
                   size_t length);
43
44
my_hash_value_type my_hash_sort(CHARSET_INFO *cs, const uchar *key,
45
                                size_t length)
46
0
{
47
0
  ulong nr1= 1, nr2= 4;
48
0
  my_ci_hash_sort(cs, (uchar*) key, length, &nr1, &nr2);
49
0
  return (my_hash_value_type) nr1;
50
0
}
51
52
/**
53
  @brief Initialize the hash
54
  
55
  @details
56
57
  Initialize the hash, by defining and giving valid values for
58
  its elements. The failure to allocate memory for the
59
  hash->array element will not result in a fatal failure. The
60
  dynamic array that is part of the hash will allocate memory
61
  as required during insertion.
62
63
  @param[in]     psi_key      The key to register instrumented memory
64
  @param[in,out] hash         The hash that is initialized
65
  @param[in]     growth_size  size incrememnt for the underlying dynarray
66
  @param[in]     charset      The character set information
67
  @param[in]     size         The hash size
68
  @param[in]     key_offest   The key offset for the hash
69
  @param[in]     key_length   The length of the key used in
70
                              the hash
71
  @param[in]     get_key      get the key for the hash
72
  @param[in]     free_element pointer to the function that
73
                              does cleanup
74
  @param[in]     flags        flags set in the hash
75
  @return        indicates success or failure of initialization
76
    @retval 0 success
77
    @retval 1 failure
78
*/
79
my_bool
80
my_hash_init2(PSI_memory_key psi_key, HASH *hash, size_t growth_size,
81
              CHARSET_INFO *charset, size_t size, size_t key_offset,
82
              size_t key_length, my_hash_get_key get_key,
83
              my_hash_function hash_function,
84
              void (*free_element)(void*), uint flags)
85
0
{
86
0
  my_bool res;
87
0
  DBUG_ENTER("my_hash_init2");
88
0
  DBUG_PRINT("enter",("hash:%p  size: %u", hash, (uint) size));
89
90
0
  hash->records=0;
91
0
  hash->key_offset=key_offset;
92
0
  hash->key_length=key_length;
93
0
  hash->blength=1;
94
0
  hash->get_key=get_key;
95
0
  hash->hash_function= hash_function ? hash_function : my_hash_sort;
96
0
  hash->free=free_element;
97
0
  hash->flags=flags;
98
0
  hash->charset=charset;
99
0
  res= init_dynamic_array2(psi_key, &hash->array, sizeof(HASH_LINK), NULL, size,
100
0
                           growth_size, MYF((flags & HASH_THREAD_SPECIFIC ?
101
0
                                             MY_THREAD_SPECIFIC : 0)));
102
0
  DBUG_RETURN(res);
103
0
}
104
105
106
/*
107
  Call hash->free on all elements in hash.
108
109
  SYNOPSIS
110
    my_hash_free_elements()
111
    hash   hash table
112
113
  NOTES:
114
    Sets records to 0
115
*/
116
117
static inline void my_hash_free_elements(HASH *hash)
118
0
{
119
0
  uint records= hash->records;
120
0
  if (records == 0)
121
0
    return;
122
123
  /*
124
    Set records to 0 early to guard against anyone looking at the structure
125
    during the free process
126
  */
127
0
  hash->records= 0;
128
129
0
  if (hash->free)
130
0
  {
131
0
    HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
132
0
    HASH_LINK *end= data + records;
133
0
    do
134
0
    {
135
0
      (*hash->free)((data++)->data);
136
0
    } while (data < end);
137
0
  }
138
0
}
139
140
141
/*
142
  Free memory used by hash.
143
144
  SYNOPSIS
145
    my_hash_free()
146
    hash   the hash to delete elements of
147
148
  NOTES: Hash can't be reused without calling my_hash_init again.
149
*/
150
151
void my_hash_free(HASH *hash)
152
0
{
153
0
  DBUG_ENTER("my_hash_free");
154
0
  DBUG_PRINT("enter",("hash:%p  elements: %ld",
155
0
                      hash, hash->records));
156
157
0
  my_hash_free_elements(hash);
158
0
  hash->free= 0;
159
0
  delete_dynamic(&hash->array);
160
0
  hash->blength= 0;
161
0
  DBUG_VOID_RETURN;
162
0
}
163
164
165
/*
166
  Delete all elements from the hash (the hash itself is to be reused).
167
168
  SYNOPSIS
169
    my_hash_reset()
170
    hash   the hash to delete elements of
171
*/
172
173
void my_hash_reset(HASH *hash)
174
0
{
175
0
  DBUG_ENTER("my_hash_reset");
176
0
  DBUG_PRINT("enter",("hash:%p", hash));
177
178
0
  my_hash_free_elements(hash);
179
0
  reset_dynamic(&hash->array);
180
  /* Set row pointers so that the hash can be reused at once */
181
0
  hash->blength= 1;
182
0
  DBUG_VOID_RETURN;
183
0
}
184
185
/* some helper functions */
186
187
/*
188
  This function is char* instead of uchar* as HPUX11 compiler can't
189
  handle inline functions that are not defined as native types
190
*/
191
192
static inline char*
193
my_hash_key(const HASH *hash, const uchar *record, size_t *length,
194
            my_bool first)
195
0
{
196
0
  if (hash->get_key)
197
0
    return (char*) (*hash->get_key)(record,length,first);
198
0
  *length=hash->key_length;
199
0
  return (char*) record+hash->key_offset;
200
0
}
201
202
  /* Calculate pos according to keys */
203
204
static uint my_hash_mask(my_hash_value_type hashnr, size_t buffmax,
205
                         size_t maxlength)
206
0
{
207
0
  if ((hashnr & (buffmax-1)) < maxlength)
208
0
    return (uint) (hashnr & (buffmax-1));
209
0
  return (uint) (hashnr & ((buffmax >> 1) -1));
210
0
}
211
212
static inline uint my_hash_rec_mask(HASH_LINK *pos,
213
                                    size_t buffmax, size_t maxlength)
214
0
{
215
0
  return my_hash_mask(pos->hash_nr, buffmax, maxlength);
216
0
}
217
218
219
220
/* for compilers which can not handle inline */
221
static
222
#if !defined(__USLC__) && !defined(__sgi)
223
inline
224
#endif
225
my_hash_value_type rec_hashnr(HASH *hash,const uchar *record)
226
0
{
227
0
  size_t length;
228
0
  uchar *key= (uchar*) my_hash_key(hash, record, &length, 0);
229
0
  return hash->hash_function(hash->charset, key, length);
230
0
}
231
232
233
uchar* my_hash_search(const HASH *hash, const uchar *key, size_t length)
234
0
{
235
0
  HASH_SEARCH_STATE state;
236
0
  return my_hash_first(hash, key, length, &state);
237
0
}
238
239
uchar* my_hash_search_using_hash_value(const HASH *hash, 
240
                                       my_hash_value_type hash_value,
241
                                       const uchar *key,
242
                                       size_t length)
243
0
{
244
0
  HASH_SEARCH_STATE state;
245
0
  return my_hash_first_from_hash_value(hash, hash_value,
246
0
                                       key, length, &state);
247
0
}
248
249
250
/*
251
  Search after a record based on a key
252
253
  NOTE
254
   Assigns the number of the found record to HASH_SEARCH_STATE state
255
*/
256
257
uchar* my_hash_first(const HASH *hash, const uchar *key, size_t length,
258
                     HASH_SEARCH_STATE *current_record)
259
0
{
260
0
  uchar *res;
261
0
  DBUG_ASSERT(my_hash_inited(hash));
262
263
0
  res= my_hash_first_from_hash_value(hash,
264
0
                                     hash->hash_function(hash->charset, key,
265
0
                                                         length ? length :
266
0
                                                         hash->key_length),
267
0
                                     key, length, current_record);
268
0
  return res;
269
0
}
270
271
272
uchar* my_hash_first_from_hash_value(const HASH *hash,
273
                                     my_hash_value_type hash_value,
274
                                     const uchar *key,
275
                                     size_t length,
276
                                     HASH_SEARCH_STATE *current_record)
277
0
{
278
0
  HASH_LINK *pos;
279
0
  DBUG_ENTER("my_hash_first_from_hash_value");
280
281
0
  if (hash->records)
282
0
  {
283
0
    uint flag= 1;
284
0
    uint idx= my_hash_mask(hash_value,
285
0
                           hash->blength, hash->records);
286
0
    if (!length)
287
0
      length= hash->key_length; // length for fixed length keys or 0
288
0
    do
289
0
    {
290
0
      pos= dynamic_element(&hash->array,idx,HASH_LINK*);
291
0
      if (!hashcmp(hash,pos,key,length))
292
0
      {
293
0
  DBUG_PRINT("exit",("found key at %d",idx));
294
0
  *current_record= idx;
295
0
  DBUG_RETURN (pos->data);
296
0
      }
297
0
      if (flag)
298
0
      {
299
0
  flag=0;         /* Reset flag */
300
0
  if (my_hash_rec_mask(pos, hash->blength, hash->records) != idx)
301
0
    break;       /* Wrong link */
302
0
      }
303
0
    }
304
0
    while ((idx=pos->next) != NO_RECORD);
305
0
  }
306
0
  *current_record= NO_RECORD;
307
0
  DBUG_RETURN(0);
308
0
}
309
310
  /* Get next record with identical key */
311
  /* Can only be called if previous calls was my_hash_search */
312
313
uchar* my_hash_next(const HASH *hash, const uchar *key, size_t length,
314
                    HASH_SEARCH_STATE *current_record)
315
0
{
316
0
  HASH_LINK *pos;
317
0
  uint idx;
318
319
0
  if (*current_record != NO_RECORD)
320
0
  {
321
0
    HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
322
0
    if (!length)
323
0
      length= hash->key_length; // length for fixed length keys or 0
324
0
    for (idx=data[*current_record].next; idx != NO_RECORD ; idx=pos->next)
325
0
    {
326
0
      pos=data+idx;
327
0
      if (!hashcmp(hash,pos,key,length))
328
0
      {
329
0
  *current_record= idx;
330
0
  return pos->data;
331
0
      }
332
0
    }
333
0
    *current_record= NO_RECORD;
334
0
  }
335
0
  return 0;
336
0
}
337
338
339
  /* Change link from pos to new_link */
340
341
static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink)
342
0
{
343
0
  HASH_LINK *old_link;
344
0
  do
345
0
  {
346
0
    old_link=array+next_link;
347
0
  }
348
0
  while ((next_link=old_link->next) != find);
349
0
  old_link->next= newlink;
350
0
  return;
351
0
}
352
353
/*
354
  Compare a key in a record to a whole key. Return 0 if identical
355
356
  SYNOPSIS
357
    hashcmp()
358
    hash   hash table
359
    pos    position of hash record to use in comparison
360
    key    key for comparison
361
    length length of key
362
363
  NOTES:
364
    length equal 0 can mean 2 things:
365
      1) it is fixed key length hash (HASH::key_length != 0) and
366
      default length should be taken in this case
367
      2) it is really 0 length key for variable key length hash
368
      (HASH::key_length == 0)
369
370
  RETURN
371
    = 0  key of record == key
372
    != 0 key of record != key
373
 */
374
375
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
376
                   size_t length)
377
0
{
378
0
  size_t rec_keylength;
379
0
  uchar *rec_key;
380
0
  rec_key= (uchar*) my_hash_key(hash, pos->data, &rec_keylength, 1);
381
0
  return my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
382
0
           (uchar*) key, length);
383
0
}
384
385
386
/**
387
   Write a hash-key to the hash-index
388
389
   @return
390
   @retval  0  ok
391
   @retval  1  Duplicate key or out of memory
392
*/
393
394
my_bool my_hash_insert(HASH *info, const uchar *record)
395
0
{
396
0
  int flag;
397
0
  size_t idx, halfbuff, first_index;
398
0
  size_t length;
399
0
  my_hash_value_type current_hash_nr, UNINIT_VAR(rec_hash_nr),
400
0
    UNINIT_VAR(rec2_hash_nr);
401
0
  uchar *UNINIT_VAR(rec_data),*UNINIT_VAR(rec2_data), *key;
402
0
  HASH_LINK *data,*empty,*UNINIT_VAR(gpos),*UNINIT_VAR(gpos2),*pos;
403
404
0
  key= (uchar*) my_hash_key(info, record, &length, 1);
405
0
  current_hash_nr= info->hash_function(info->charset, key, length);
406
407
0
  if (info->flags & HASH_UNIQUE)
408
0
  {
409
0
    if (my_hash_search_using_hash_value(info, current_hash_nr, key, length))
410
0
      return(TRUE);        /* Duplicate entry */
411
0
  }
412
413
0
  flag=0;
414
0
  if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
415
0
    return(TRUE);        /* No more memory */
416
417
0
  data=dynamic_element(&info->array,0,HASH_LINK*);
418
0
  halfbuff= info->blength >> 1;
419
420
0
  idx=first_index=info->records-halfbuff;
421
0
  if (idx != info->records)       /* If some records */
422
0
  {
423
0
    do
424
0
    {
425
0
      my_hash_value_type hash_nr;
426
0
      pos=data+idx;
427
0
      hash_nr= pos->hash_nr;
428
0
      if (flag == 0)       /* First loop; Check if ok */
429
0
  if (my_hash_mask(hash_nr, info->blength, info->records) != first_index)
430
0
    break;
431
0
      if (!(hash_nr & halfbuff))
432
0
      {           /* Key will not move */
433
0
  if (!(flag & LOWFIND))
434
0
  {
435
0
    if (flag & HIGHFIND)
436
0
    {
437
0
      flag= LOWFIND | HIGHFIND;
438
      /* key shall be moved to the current empty position */
439
0
      gpos= empty;
440
0
            rec_data=    pos->data;
441
0
            rec_hash_nr= pos->hash_nr;
442
0
      empty=pos;        /* This place is now free */
443
0
    }
444
0
    else
445
0
    {
446
0
      flag= LOWFIND | LOWUSED;   /* key isn't changed */
447
0
      gpos= pos;
448
0
            rec_data=    pos->data;
449
0
            rec_hash_nr= pos->hash_nr;
450
0
    }
451
0
  }
452
0
  else
453
0
  {
454
0
    if (!(flag & LOWUSED))
455
0
    {
456
      /* Change link of previous LOW-key */
457
0
      gpos->data=    rec_data;
458
0
      gpos->hash_nr= rec_hash_nr;
459
0
      gpos->next=    (uint) (pos-data);
460
0
      flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
461
0
    }
462
0
    gpos= pos;
463
0
    rec_data=    pos->data;
464
0
          rec_hash_nr= pos->hash_nr;
465
0
  }
466
0
      }
467
0
      else
468
0
      {           /* key will be moved */
469
0
  if (!(flag & HIGHFIND))
470
0
  {
471
0
    flag= (flag & LOWFIND) | HIGHFIND;
472
    /* key shall be moved to the last (empty) position */
473
0
    gpos2= empty;
474
0
          empty= pos;
475
0
    rec2_data=    pos->data;
476
0
          rec2_hash_nr= pos->hash_nr;
477
0
  }
478
0
  else
479
0
  {
480
0
    if (!(flag & HIGHUSED))
481
0
    {
482
      /* Change link of previous hash-key and save */
483
0
      gpos2->data=    rec2_data;
484
0
      gpos2->hash_nr= rec2_hash_nr;
485
0
      gpos2->next=    (uint) (pos-data);
486
0
      flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
487
0
    }
488
0
    gpos2= pos;
489
0
    rec2_data=    pos->data;
490
0
          rec2_hash_nr= pos->hash_nr;
491
0
  }
492
0
      }
493
0
    }
494
0
    while ((idx=pos->next) != NO_RECORD);
495
496
0
    if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
497
0
    {
498
0
      gpos->data=    rec_data;
499
0
      gpos->hash_nr= rec_hash_nr;
500
0
      gpos->next=    NO_RECORD;
501
0
    }
502
0
    if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
503
0
    {
504
0
      gpos2->data=    rec2_data;
505
0
      gpos2->hash_nr= rec2_hash_nr;
506
0
      gpos2->next=    NO_RECORD;
507
0
    }
508
0
  }
509
510
0
  idx= my_hash_mask(current_hash_nr, info->blength, info->records + 1);
511
0
  pos= data+idx;
512
  /* Check if we are at the empty position */
513
0
  if (pos == empty)
514
0
  {
515
0
    pos->next=NO_RECORD;
516
0
  }
517
0
  else
518
0
  {
519
    /* Move conflicting record to empty position (last) */
520
0
    empty[0]= pos[0];
521
    /* Check if the moved record was in same hash-nr family */
522
0
    gpos= data + my_hash_rec_mask(pos, info->blength, info->records + 1);
523
0
    if (pos == gpos)
524
0
    {
525
      /* Point to moved record */
526
0
      pos->next= (uint32) (empty - data);
527
0
    }
528
0
    else
529
0
    {
530
0
      pos->next= NO_RECORD;
531
0
      movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
532
0
    }
533
0
  }
534
0
  pos->data=    (uchar*) record;
535
0
  pos->hash_nr= current_hash_nr;
536
0
  if (++info->records == info->blength)
537
0
    info->blength+= info->blength;
538
0
  return(0);
539
0
}
540
541
542
/**
543
   Remove one record from hash-table.
544
545
   @fn    hash_delete()
546
   @param hash    Hash tree
547
   @param record  Row to be deleted
548
549
   @notes
550
   The record with the same record ptr is removed.
551
   If there is a free-function it's called if record was found.
552
553
   hash->free() is guarantee to be called only after the row has been
554
   deleted from the hash and the hash can be reused by other threads.
555
556
   @return
557
   @retval  0  ok
558
   @retval  1 Record not found
559
*/
560
561
my_bool my_hash_delete(HASH *hash, uchar *record)
562
0
{
563
0
  uint pos2,idx,empty_index;
564
0
  my_hash_value_type pos_hashnr, lastpos_hashnr;
565
0
  size_t blength;
566
0
  HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
567
0
  DBUG_ENTER("my_hash_delete");
568
0
  if (!hash->records)
569
0
    DBUG_RETURN(1);
570
571
0
  blength=hash->blength;
572
0
  data=dynamic_element(&hash->array,0,HASH_LINK*);
573
  /* Search after record with key */
574
0
  pos= data + my_hash_mask(rec_hashnr(hash, record), blength, hash->records);
575
0
  gpos = 0;
576
577
0
  while (pos->data != record)
578
0
  {
579
0
    gpos=pos;
580
0
    if (pos->next == NO_RECORD)
581
0
      DBUG_RETURN(1);      /* Key not found */
582
0
    pos=data+pos->next;
583
0
  }
584
585
0
  if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
586
0
  lastpos=data+hash->records;
587
588
  /* Remove link to record */
589
0
  empty=pos; empty_index=(uint) (empty-data);
590
0
  if (gpos)
591
0
    gpos->next=pos->next;   /* unlink current ptr */
592
0
  else if (pos->next != NO_RECORD)
593
0
  {
594
0
    empty=data+(empty_index=pos->next);
595
0
    pos[0]= empty[0];
596
0
  }
597
598
0
  if (empty == lastpos)   /* last key at wrong pos or no next link */
599
0
    goto exit;
600
601
  /* Move the last key (lastpos) */
602
0
  lastpos_hashnr= lastpos->hash_nr;
603
  /* pos is where lastpos should be */
604
0
  pos= data + my_hash_mask(lastpos_hashnr, hash->blength, hash->records);
605
0
  if (pos == empty)     /* Move to empty position. */
606
0
  {
607
0
    empty[0]=lastpos[0];
608
0
    goto exit;
609
0
  }
610
0
  pos_hashnr= pos->hash_nr;
611
  /* pos3 is where the pos should be */
612
0
  pos3= data + my_hash_mask(pos_hashnr, hash->blength, hash->records);
613
0
  if (pos != pos3)
614
0
  {         /* pos is on wrong posit */
615
0
    empty[0]=pos[0];      /* Save it here */
616
0
    pos[0]=lastpos[0];      /* This should be here */
617
0
    movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
618
0
    goto exit;
619
0
  }
620
0
  pos2= my_hash_mask(lastpos_hashnr, blength, hash->records + 1);
621
0
  if (pos2 == my_hash_mask(pos_hashnr, blength, hash->records + 1))
622
0
  {         /* Identical key-positions */
623
0
    if (pos2 != hash->records)
624
0
    {
625
0
      empty[0]=lastpos[0];
626
0
      movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
627
0
      goto exit;
628
0
    }
629
0
    idx= (uint) (pos-data);   /* Link pos->next after lastpos */
630
0
  }
631
0
  else idx= NO_RECORD;   /* Different positions merge */
632
633
0
  empty[0]=lastpos[0];
634
0
  movelink(data,idx,empty_index,pos->next);
635
0
  pos->next=empty_index;
636
637
0
exit:
638
0
  (void) pop_dynamic(&hash->array);
639
0
  if (hash->free)
640
0
    (*hash->free)((uchar*) record);
641
0
  DBUG_RETURN(0);
642
0
}
643
644
645
/**
646
   Update keys when record has changed.
647
   This is much more efficient than using a delete & insert.
648
*/
649
650
my_bool my_hash_update(HASH *hash, uchar *record, uchar *old_key,
651
                       size_t old_key_length)
652
0
{
653
0
  uint new_index, new_pos_index, org_index, records, idx;
654
0
  size_t length, empty, blength;
655
0
  my_hash_value_type hash_nr;
656
0
  HASH_LINK org_link,*data,*previous,*pos;
657
0
  uchar *new_key;
658
0
  DBUG_ENTER("my_hash_update");
659
660
0
  new_key= (uchar*) my_hash_key(hash, record, &length, 1);
661
0
  hash_nr= hash->hash_function(hash->charset, new_key, length);
662
  
663
0
  if (HASH_UNIQUE & hash->flags)
664
0
  {
665
0
    HASH_SEARCH_STATE state;
666
0
    uchar *found;
667
668
0
    if ((found= my_hash_first_from_hash_value(hash, hash_nr, new_key, length,
669
0
                                              &state)))
670
0
    {
671
0
      do 
672
0
      {
673
0
        if (found != record)
674
0
          DBUG_RETURN(1);    /* Duplicate entry */
675
0
      } 
676
0
      while ((found= my_hash_next(hash, new_key, length, &state)));
677
0
    }
678
0
  }
679
680
0
  data=dynamic_element(&hash->array,0,HASH_LINK*);
681
0
  blength=hash->blength; records=hash->records;
682
683
  /* Search after record with key */
684
685
0
  idx= my_hash_mask(hash->hash_function(hash->charset, old_key,
686
0
                                        (old_key_length ? old_key_length :
687
0
                                                          hash->key_length)),
688
0
                    blength, records);
689
0
  org_index= idx;
690
0
  new_index= my_hash_mask(hash_nr, blength, records);
691
0
  previous=0;
692
0
  for (;;)
693
0
  {
694
0
    if ((pos= data+idx)->data == record)
695
0
      break;
696
0
    previous=pos;
697
0
    if ((idx=pos->next) == NO_RECORD)
698
0
      DBUG_RETURN(1);      /* Not found in links */
699
0
  }
700
701
0
  if (org_index == new_index)
702
0
  {
703
0
    data[idx].hash_nr= hash_nr;         /* Hash number may have changed */
704
0
    DBUG_RETURN(0);      /* Record is in right position */
705
0
  }
706
707
0
  org_link= *pos;
708
0
  empty=idx;
709
710
  /* Relink record from current chain */
711
712
0
  if (!previous)
713
0
  {
714
0
    if (pos->next != NO_RECORD)
715
0
    {
716
0
      empty=pos->next;
717
0
      *pos= data[pos->next];
718
0
    }
719
0
  }
720
0
  else
721
0
    previous->next=pos->next;   /* unlink pos */
722
723
  /* Move data to correct position */
724
0
  if (new_index == empty)
725
0
  {
726
    /*
727
      At this point record is unlinked from the old chain, thus it holds
728
      random position. By the chance this position is equal to position
729
      for the first element in the new chain. That means updated record
730
      is the only record in the new chain.
731
    */
732
0
    if (empty != idx)
733
0
    {
734
      /*
735
        Record was moved while unlinking it from the old chain.
736
        Copy data to a new position.
737
      */
738
0
      data[empty]= org_link;
739
0
    }
740
0
    data[empty].next= NO_RECORD;
741
0
    data[empty].hash_nr= hash_nr;
742
0
    DBUG_RETURN(0);
743
0
  }
744
0
  pos=data+new_index;
745
0
  new_pos_index= my_hash_rec_mask(pos, blength, records);
746
0
  if (new_index != new_pos_index)
747
0
  {         /* Other record in wrong position */
748
0
    data[empty]= *pos;
749
0
    movelink(data,new_index,new_pos_index, (uint) empty);
750
0
    org_link.next=NO_RECORD;
751
0
    data[new_index]= org_link;
752
0
    data[new_index].hash_nr= hash_nr;
753
0
  }
754
0
  else
755
0
  {         /* Link in chain at right position */
756
0
    org_link.next=data[new_index].next;
757
0
    data[empty]=org_link;
758
0
    data[empty].hash_nr= hash_nr;
759
0
    data[new_index].next= (uint) empty;
760
0
  }
761
0
  DBUG_RETURN(0);
762
0
}
763
764
765
uchar *my_hash_element(HASH *hash, size_t idx)
766
0
{
767
0
  if (idx < hash->records)
768
0
    return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
769
0
  return 0;
770
0
}
771
772
773
/*
774
  Replace old row with new row.  This should only be used when key
775
  isn't changed
776
*/
777
778
void my_hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record,
779
                     uchar *new_row)
780
0
{
781
0
  if (*current_record != NO_RECORD)            /* Safety */
782
0
    dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row;
783
0
}
784
785
786
/**
787
   Iterate over all elements in hash and call function with the element
788
789
   @param hash     hash array
790
   @param action   function to call for each argument
791
   @param argument second argument for call to action
792
793
   @notes
794
   If one of functions calls returns 1 then the iteration aborts
795
796
   @retval 0  ok
797
   @retval 1  iteration aborted becasue action returned 1
798
*/
799
800
my_bool my_hash_iterate(HASH *hash, my_hash_walk_action action, void *argument)
801
0
{
802
0
  uint records, i;
803
804
0
  records= hash->records;
805
806
0
  for (i= 0 ; i < records ; i++)
807
0
  {
808
0
    if ((*action)(dynamic_element(&hash->array, i, HASH_LINK *)->data,
809
0
                  argument))
810
0
      return 1;
811
0
  }
812
0
  return 0;
813
0
}
814
815
816
#if !defined(DBUG_OFF) || defined(MAIN)
817
818
my_bool my_hash_check(HASH *hash)
819
{
820
  int error;
821
  uint i,rec_link,found,max_links,seek,links,idx;
822
  uint records;
823
  size_t blength;
824
  HASH_LINK *data,*hash_info;
825
826
  records=hash->records; blength=hash->blength;
827
  data=dynamic_element(&hash->array,0,HASH_LINK*);
828
  error=0;
829
830
  for (i=found=max_links=seek=0 ; i < records ; i++)
831
  {
832
    size_t length;
833
    uchar *key= (uchar*) my_hash_key(hash, data[i].data, &length, 0);
834
    if (data[i].hash_nr != hash->hash_function(hash->charset, key, length))
835
    {
836
      DBUG_PRINT("error", ("record at %d has wrong hash", i));
837
      error= 1;
838
    }
839
840
    if (my_hash_rec_mask(data + i, blength, records) == i)
841
    {
842
      found++; seek++; links=1;
843
      for (idx=data[i].next ;
844
     idx != NO_RECORD && found < records + 1;
845
     idx=hash_info->next)
846
      {
847
  if (idx >= records)
848
  {
849
    DBUG_PRINT("error",
850
         ("Found pointer outside array to %d from link starting at %d",
851
          idx,i));
852
    error=1;
853
  }
854
  hash_info=data+idx;
855
  seek+= ++links;
856
  if ((rec_link= my_hash_rec_mask(hash_info,
857
                                        blength, records)) != i)
858
  {
859
          DBUG_PRINT("error", ("Record in wrong link at %d: Start %d  "
860
                               "Record:%p  Record-link %d",
861
                               idx, i, hash_info->data, rec_link));
862
    error=1;
863
  }
864
  else
865
    found++;
866
      }
867
      if (links > max_links) max_links=links;
868
    }
869
  }
870
  if (found != records)
871
  {
872
    DBUG_PRINT("error",("Found %u of %u records", found, records));
873
    error=1;
874
  }
875
  if (records)
876
    DBUG_PRINT("info",
877
         ("records: %u   seeks: %d   max links: %d   hitrate: %.2f",
878
    records,seek,max_links,(float) seek / (float) records));
879
  DBUG_ASSERT(error == 0);
880
  return error;
881
}
882
#endif
883
884
#ifdef MAIN
885
886
#define RECORDS 1000
887
888
uchar *test_get_key(uchar *data, size_t *length,
889
                    my_bool not_used __attribute__((unused)))
890
{
891
  *length= 2;
892
  return data;
893
}
894
895
896
int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
897
{
898
  uchar records[RECORDS][2], copy[2];
899
  HASH hash_test;
900
  uint i;
901
  MY_INIT(argv[0]);
902
  DBUG_PUSH("d:t:O,/tmp/test_hash.trace");
903
904
  printf("my_hash_init\n");
905
  if (my_hash_init2(PSI_INSTRUMENT_ME, &hash_test, 100, &my_charset_bin, 20,
906
                    0, 0, (my_hash_get_key) test_get_key, 0, 0, HASH_UNIQUE))
907
  {
908
    fprintf(stderr, "hash init failed\n");
909
    exit(1);
910
  }
911
912
  printf("my_hash_insert\n");
913
  for (i= 0 ; i < RECORDS ; i++)
914
  {
915
    int2store(records[i],i);
916
    my_hash_insert(&hash_test, records[i]);
917
    my_hash_check(&hash_test);
918
  }
919
  printf("my_hash_update\n");
920
  for (i= 0 ; i < RECORDS ; i+=2)
921
  {
922
    memcpy(copy, records[i], 2);
923
    int2store(records[i],i + RECORDS);
924
    if (my_hash_update(&hash_test, records[i], copy, 2))
925
    {
926
      fprintf(stderr, "hash update failed\n");
927
      exit(1);
928
    }
929
    my_hash_check(&hash_test);
930
  }
931
  printf("my_hash_delete\n");
932
  for (i= 0 ; i < RECORDS ; i++)
933
  {
934
    if (my_hash_delete(&hash_test, records[i]))
935
    {
936
      fprintf(stderr, "hash delete failed\n");
937
      exit(1);
938
    }
939
    my_hash_check(&hash_test);
940
  }
941
  my_hash_free(&hash_test);
942
  printf("ok\n");
943
  my_end(MY_CHECK_ERROR);
944
  return(0);
945
}
946
#endif /* MAIN */