~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/btr/btr0sea.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/************************************************************************
 
2
The index tree adaptive search
 
3
 
 
4
(c) 1996 Innobase Oy
 
5
 
 
6
Created 2/17/1996 Heikki Tuuri
 
7
*************************************************************************/
 
8
 
 
9
#include "btr0sea.h"
 
10
#ifdef UNIV_NONINL
 
11
#include "btr0sea.ic"
 
12
#endif
 
13
 
 
14
#include "buf0buf.h"
 
15
#include "page0page.h"
 
16
#include "page0cur.h"
 
17
#include "btr0cur.h"
 
18
#include "btr0pcur.h"
 
19
#include "btr0btr.h"
 
20
#include "ha0ha.h"
 
21
 
 
22
ulint   btr_search_this_is_zero = 0;    /* A dummy variable to fool the
 
23
                                        compiler */
 
24
 
 
25
#ifdef UNIV_SEARCH_PERF_STAT
 
26
ulint   btr_search_n_succ       = 0;
 
27
ulint   btr_search_n_hash_fail  = 0;
 
28
#endif /* UNIV_SEARCH_PERF_STAT */
 
29
 
 
30
byte    btr_sea_pad1[64];       /* padding to prevent other memory update
 
31
                                hotspots from residing on the same memory
 
32
                                cache line as btr_search_latch */
 
33
 
 
34
/* The latch protecting the adaptive search system: this latch protects the
 
35
(1) positions of records on those pages where a hash index has been built.
 
36
NOTE: It does not protect values of non-ordering fields within a record from
 
37
being updated in-place! We can use fact (1) to perform unique searches to
 
38
indexes. */
 
39
 
 
40
rw_lock_t*      btr_search_latch_temp; /* We will allocate the latch from
 
41
                                        dynamic memory to get it to the
 
42
                                        same DRAM page as other hotspot
 
43
                                        semaphores */
 
44
 
 
45
byte    btr_sea_pad2[64];       /* padding to prevent other memory update
 
46
                                hotspots from residing on the same memory
 
47
                                cache line */
 
48
 
 
49
btr_search_sys_t*       btr_search_sys;
 
50
 
 
51
/* If the number of records on the page divided by this parameter
 
52
would have been successfully accessed using a hash index, the index
 
53
is then built on the page, assuming the global limit has been reached */
 
54
 
 
55
#define BTR_SEARCH_PAGE_BUILD_LIMIT     16
 
56
 
 
57
/* The global limit for consecutive potentially successful hash searches,
 
58
before hash index building is started */
 
59
 
 
60
#define BTR_SEARCH_BUILD_LIMIT          100
 
61
 
 
62
/************************************************************************
 
63
Builds a hash index on a page with the given parameters. If the page already
 
64
has a hash index with different parameters, the old hash index is removed.
 
65
If index is non-NULL, this function checks if n_fields and n_bytes are
 
66
sensible values, and does not build a hash index if not. */
 
67
static
 
68
void
 
69
btr_search_build_page_hash_index(
 
70
/*=============================*/
 
71
        dict_index_t*   index,  /* in: index for which to build, or NULL if
 
72
                                not known */
 
73
        page_t*         page,   /* in: index page, s- or x-latched */
 
74
        ulint           n_fields,/* in: hash this many full fields */
 
75
        ulint           n_bytes,/* in: hash this many bytes from the next
 
76
                                field */
 
77
        ibool           left_side);/* in: hash for searches from left side? */
 
78
 
 
79
/*********************************************************************
 
80
This function should be called before reserving any btr search mutex, if
 
81
the intended operation might add nodes to the search system hash table.
 
82
Because of the latching order, once we have reserved the btr search system
 
83
latch, we cannot allocate a free frame from the buffer pool. Checks that
 
84
there is a free buffer frame allocated for hash table heap in the btr search
 
85
system. If not, allocates a free frames for the heap. This check makes it
 
86
probable that, when have reserved the btr search system latch and we need to
 
87
allocate a new node to the hash table, it will succeed. However, the check
 
88
will not guarantee success. */
 
89
static
 
90
void
 
91
btr_search_check_free_space_in_heap(void)
 
92
/*=====================================*/
 
93
{
 
94
        buf_frame_t*    frame;
 
95
        hash_table_t*   table;
 
96
        mem_heap_t*     heap;
 
97
 
 
98
#ifdef UNIV_SYNC_DEBUG
 
99
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
 
100
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
101
#endif /* UNIV_SYNC_DEBUG */
 
102
 
 
103
        table = btr_search_sys->hash_index;
 
104
 
 
105
        heap = table->heap;
 
106
 
 
107
        /* Note that we peek the value of heap->free_block without reserving
 
108
        the latch: this is ok, because we will not guarantee that there will
 
109
        be enough free space in the hash table. */
 
110
 
 
111
        if (heap->free_block == NULL) {
 
112
                frame = buf_frame_alloc();
 
113
 
 
114
                rw_lock_x_lock(&btr_search_latch);
 
115
 
 
116
                if (heap->free_block == NULL) {
 
117
                        heap->free_block = frame;
 
118
                } else {
 
119
                        buf_frame_free(frame);
 
120
                }
 
121
 
 
122
                rw_lock_x_unlock(&btr_search_latch);
 
123
        }
 
124
}
 
125
 
 
126
/*********************************************************************
 
127
Creates and initializes the adaptive search system at a database start. */
 
128
 
 
129
void
 
130
btr_search_sys_create(
 
131
/*==================*/
 
132
        ulint   hash_size)      /* in: hash index hash table size */
 
133
{
 
134
        /* We allocate the search latch from dynamic memory:
 
135
        see above at the global variable definition */
 
136
 
 
137
        btr_search_latch_temp = mem_alloc(sizeof(rw_lock_t));
 
138
 
 
139
        rw_lock_create(&btr_search_latch, SYNC_SEARCH_SYS);
 
140
 
 
141
        btr_search_sys = mem_alloc(sizeof(btr_search_sys_t));
 
142
 
 
143
        btr_search_sys->hash_index = ha_create(TRUE, hash_size, 0, 0);
 
144
 
 
145
}
 
146
 
 
147
/*********************************************************************
 
148
Creates and initializes a search info struct. */
 
149
 
 
150
btr_search_t*
 
151
btr_search_info_create(
 
152
/*===================*/
 
153
                                /* out, own: search info struct */
 
154
        mem_heap_t*     heap)   /* in: heap where created */
 
155
{
 
156
        btr_search_t*   info;
 
157
 
 
158
        info = mem_heap_alloc(heap, sizeof(btr_search_t));
 
159
 
 
160
#ifdef UNIV_DEBUG
 
161
        info->magic_n = BTR_SEARCH_MAGIC_N;
 
162
#endif /* UNIV_DEBUG */
 
163
 
 
164
        info->root_guess = NULL;
 
165
 
 
166
        info->hash_analysis = 0;
 
167
        info->n_hash_potential = 0;
 
168
 
 
169
        info->last_hash_succ = FALSE;
 
170
 
 
171
#ifdef UNIV_SEARCH_PERF_STAT
 
172
        info->n_hash_succ = 0;
 
173
        info->n_hash_fail = 0;
 
174
        info->n_patt_succ = 0;
 
175
        info->n_searches = 0;
 
176
#endif /* UNIV_SEARCH_PERF_STAT */
 
177
 
 
178
        /* Set some sensible values */
 
179
        info->n_fields = 1;
 
180
        info->n_bytes = 0;
 
181
 
 
182
        info->left_side = TRUE;
 
183
 
 
184
        return(info);
 
185
}
 
186
 
 
187
/*************************************************************************
 
188
Updates the search info of an index about hash successes. NOTE that info
 
189
is NOT protected by any semaphore, to save CPU time! Do not assume its fields
 
190
are consistent. */
 
191
static
 
192
void
 
193
btr_search_info_update_hash(
 
194
/*========================*/
 
195
        btr_search_t*   info,   /* in/out: search info */
 
196
        btr_cur_t*      cursor) /* in: cursor which was just positioned */
 
197
{
 
198
        dict_index_t*   index;
 
199
        ulint           n_unique;
 
200
        int             cmp;
 
201
 
 
202
#ifdef UNIV_SYNC_DEBUG
 
203
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
 
204
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
205
#endif /* UNIV_SYNC_DEBUG */
 
206
 
 
207
        index = cursor->index;
 
208
 
 
209
        if (index->type & DICT_IBUF) {
 
210
                /* So many deletes are performed on an insert buffer tree
 
211
                that we do not consider a hash index useful on it: */
 
212
 
 
213
                return;
 
214
        }
 
215
 
 
216
        n_unique = dict_index_get_n_unique_in_tree(index);
 
217
 
 
218
        if (info->n_hash_potential == 0) {
 
219
 
 
220
                goto set_new_recomm;
 
221
        }
 
222
 
 
223
        /* Test if the search would have succeeded using the recommended
 
224
        hash prefix */
 
225
 
 
226
        if (info->n_fields >= n_unique && cursor->up_match >= n_unique) {
 
227
increment_potential:
 
228
                info->n_hash_potential++;
 
229
 
 
230
                return;
 
231
        }
 
232
 
 
233
        cmp = ut_pair_cmp(info->n_fields, info->n_bytes,
 
234
                          cursor->low_match, cursor->low_bytes);
 
235
 
 
236
        if (info->left_side ? cmp <= 0 : cmp > 0) {
 
237
 
 
238
                goto set_new_recomm;
 
239
        }
 
240
 
 
241
        cmp = ut_pair_cmp(info->n_fields, info->n_bytes,
 
242
                          cursor->up_match, cursor->up_bytes);
 
243
 
 
244
        if (info->left_side ? cmp <= 0 : cmp > 0) {
 
245
 
 
246
                goto increment_potential;
 
247
        }
 
248
 
 
249
set_new_recomm:
 
250
        /* We have to set a new recommendation; skip the hash analysis
 
251
        for a while to avoid unnecessary CPU time usage when there is no
 
252
        chance for success */
 
253
 
 
254
        info->hash_analysis = 0;
 
255
 
 
256
        cmp = ut_pair_cmp(cursor->up_match, cursor->up_bytes,
 
257
                          cursor->low_match, cursor->low_bytes);
 
258
        if (cmp == 0) {
 
259
                info->n_hash_potential = 0;
 
260
 
 
261
                /* For extra safety, we set some sensible values here */
 
262
 
 
263
                info->n_fields = 1;
 
264
                info->n_bytes = 0;
 
265
 
 
266
                info->left_side = TRUE;
 
267
 
 
268
        } else if (cmp > 0) {
 
269
                info->n_hash_potential = 1;
 
270
 
 
271
                if (cursor->up_match >= n_unique) {
 
272
 
 
273
                        info->n_fields = n_unique;
 
274
                        info->n_bytes = 0;
 
275
 
 
276
                } else if (cursor->low_match < cursor->up_match) {
 
277
 
 
278
                        info->n_fields = cursor->low_match + 1;
 
279
                        info->n_bytes = 0;
 
280
                } else {
 
281
                        info->n_fields = cursor->low_match;
 
282
                        info->n_bytes = cursor->low_bytes + 1;
 
283
                }
 
284
 
 
285
                info->left_side = TRUE;
 
286
        } else {
 
287
                info->n_hash_potential = 1;
 
288
 
 
289
                if (cursor->low_match >= n_unique) {
 
290
 
 
291
                        info->n_fields = n_unique;
 
292
                        info->n_bytes = 0;
 
293
 
 
294
                } else if (cursor->low_match > cursor->up_match) {
 
295
 
 
296
                        info->n_fields = cursor->up_match + 1;
 
297
                        info->n_bytes = 0;
 
298
                } else {
 
299
                        info->n_fields = cursor->up_match;
 
300
                        info->n_bytes = cursor->up_bytes + 1;
 
301
                }
 
302
 
 
303
                info->left_side = FALSE;
 
304
        }
 
305
}
 
306
 
 
307
/*************************************************************************
 
308
Updates the block search info on hash successes. NOTE that info and
 
309
block->n_hash_helps, n_fields, n_bytes, side are NOT protected by any
 
310
semaphore, to save CPU time! Do not assume the fields are consistent. */
 
311
static
 
312
ibool
 
313
btr_search_update_block_hash_info(
 
314
/*==============================*/
 
315
                                /* out: TRUE if building a (new) hash index on
 
316
                                the block is recommended */
 
317
        btr_search_t*   info,   /* in: search info */
 
318
        buf_block_t*    block,  /* in: buffer block */
 
319
        btr_cur_t*      cursor __attribute__((unused)))
 
320
                                /* in: cursor */
 
321
{
 
322
#ifdef UNIV_SYNC_DEBUG
 
323
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
 
324
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
325
        ut_ad(rw_lock_own(&((buf_block_t*) block)->lock, RW_LOCK_SHARED)
 
326
              || rw_lock_own(&((buf_block_t*) block)->lock, RW_LOCK_EX));
 
327
#endif /* UNIV_SYNC_DEBUG */
 
328
        ut_ad(cursor);
 
329
 
 
330
        info->last_hash_succ = FALSE;
 
331
 
 
332
        ut_a(block->magic_n == BUF_BLOCK_MAGIC_N);
 
333
        ut_ad(info->magic_n == BTR_SEARCH_MAGIC_N);
 
334
 
 
335
        if ((block->n_hash_helps > 0)
 
336
            && (info->n_hash_potential > 0)
 
337
            && (block->n_fields == info->n_fields)
 
338
            && (block->n_bytes == info->n_bytes)
 
339
            && (block->left_side == info->left_side)) {
 
340
 
 
341
                if ((block->is_hashed)
 
342
                    && (block->curr_n_fields == info->n_fields)
 
343
                    && (block->curr_n_bytes == info->n_bytes)
 
344
                    && (block->curr_left_side == info->left_side)) {
 
345
 
 
346
                        /* The search would presumably have succeeded using
 
347
                        the hash index */
 
348
 
 
349
                        info->last_hash_succ = TRUE;
 
350
                }
 
351
 
 
352
                block->n_hash_helps++;
 
353
        } else {
 
354
                block->n_hash_helps = 1;
 
355
                block->n_fields = info->n_fields;
 
356
                block->n_bytes = info->n_bytes;
 
357
                block->left_side = info->left_side;
 
358
        }
 
359
 
 
360
#ifdef UNIV_DEBUG
 
361
        if (cursor->index->table->does_not_fit_in_memory) {
 
362
                block->n_hash_helps = 0;
 
363
        }
 
364
#endif /* UNIV_DEBUG */
 
365
 
 
366
        if ((block->n_hash_helps > page_get_n_recs(block->frame)
 
367
             / BTR_SEARCH_PAGE_BUILD_LIMIT)
 
368
            && (info->n_hash_potential >= BTR_SEARCH_BUILD_LIMIT)) {
 
369
 
 
370
                if ((!block->is_hashed)
 
371
                    || (block->n_hash_helps
 
372
                        > 2 * page_get_n_recs(block->frame))
 
373
                    || (block->n_fields != block->curr_n_fields)
 
374
                    || (block->n_bytes != block->curr_n_bytes)
 
375
                    || (block->left_side != block->curr_left_side)) {
 
376
 
 
377
                        /* Build a new hash index on the page */
 
378
 
 
379
                        return(TRUE);
 
380
                }
 
381
        }
 
382
 
 
383
        return(FALSE);
 
384
}
 
385
 
 
386
/*************************************************************************
 
387
Updates a hash node reference when it has been unsuccessfully used in a
 
388
search which could have succeeded with the used hash parameters. This can
 
389
happen because when building a hash index for a page, we do not check
 
390
what happens at page boundaries, and therefore there can be misleading
 
391
hash nodes. Also, collisions in the fold value can lead to misleading
 
392
references. This function lazily fixes these imperfections in the hash
 
393
index. */
 
394
static
 
395
void
 
396
btr_search_update_hash_ref(
 
397
/*=======================*/
 
398
        btr_search_t*   info,   /* in: search info */
 
399
        buf_block_t*    block,  /* in: buffer block where cursor positioned */
 
400
        btr_cur_t*      cursor) /* in: cursor */
 
401
{
 
402
        ulint   fold;
 
403
        rec_t*  rec;
 
404
        dulint  index_id;
 
405
 
 
406
        ut_ad(cursor->flag == BTR_CUR_HASH_FAIL);
 
407
#ifdef UNIV_SYNC_DEBUG
 
408
        ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
409
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED)
 
410
              || rw_lock_own(&(block->lock), RW_LOCK_EX));
 
411
#endif /* UNIV_SYNC_DEBUG */
 
412
        ut_ad(buf_block_align(btr_cur_get_rec(cursor)) == block);
 
413
        ut_a(!block->is_hashed || block->index == cursor->index);
 
414
 
 
415
        if (block->is_hashed
 
416
            && (info->n_hash_potential > 0)
 
417
            && (block->curr_n_fields == info->n_fields)
 
418
            && (block->curr_n_bytes == info->n_bytes)
 
419
            && (block->curr_left_side == info->left_side)) {
 
420
                mem_heap_t*     heap            = NULL;
 
421
                ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
422
                *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
423
 
 
424
                rec = btr_cur_get_rec(cursor);
 
425
 
 
426
                if (!page_rec_is_user_rec(rec)) {
 
427
 
 
428
                        return;
 
429
                }
 
430
 
 
431
                index_id = cursor->index->id;
 
432
                fold = rec_fold(rec,
 
433
                                rec_get_offsets(rec, cursor->index, offsets_,
 
434
                                                ULINT_UNDEFINED, &heap),
 
435
                                block->curr_n_fields,
 
436
                                block->curr_n_bytes, index_id);
 
437
                if (UNIV_LIKELY_NULL(heap)) {
 
438
                        mem_heap_free(heap);
 
439
                }
 
440
#ifdef UNIV_SYNC_DEBUG
 
441
                ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
442
#endif /* UNIV_SYNC_DEBUG */
 
443
 
 
444
                ha_insert_for_fold(btr_search_sys->hash_index, fold, rec);
 
445
        }
 
446
}
 
447
 
 
448
/*************************************************************************
 
449
Updates the search info. */
 
450
 
 
451
void
 
452
btr_search_info_update_slow(
 
453
/*========================*/
 
454
        btr_search_t*   info,   /* in/out: search info */
 
455
        btr_cur_t*      cursor) /* in: cursor which was just positioned */
 
456
{
 
457
        buf_block_t*    block;
 
458
        ibool           build_index;
 
459
        ulint*          params;
 
460
        ulint*          params2;
 
461
 
 
462
#ifdef UNIV_SYNC_DEBUG
 
463
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
 
464
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
465
#endif /* UNIV_SYNC_DEBUG */
 
466
 
 
467
        block = buf_block_align(btr_cur_get_rec(cursor));
 
468
 
 
469
        /* NOTE that the following two function calls do NOT protect
 
470
        info or block->n_fields etc. with any semaphore, to save CPU time!
 
471
        We cannot assume the fields are consistent when we return from
 
472
        those functions! */
 
473
 
 
474
        btr_search_info_update_hash(info, cursor);
 
475
 
 
476
        build_index = btr_search_update_block_hash_info(info, block, cursor);
 
477
 
 
478
        if (build_index || (cursor->flag == BTR_CUR_HASH_FAIL)) {
 
479
 
 
480
                btr_search_check_free_space_in_heap();
 
481
        }
 
482
 
 
483
        if (cursor->flag == BTR_CUR_HASH_FAIL) {
 
484
                /* Update the hash node reference, if appropriate */
 
485
 
 
486
#ifdef UNIV_SEARCH_PERF_STAT
 
487
                btr_search_n_hash_fail++;
 
488
#endif /* UNIV_SEARCH_PERF_STAT */
 
489
 
 
490
                rw_lock_x_lock(&btr_search_latch);
 
491
 
 
492
                btr_search_update_hash_ref(info, block, cursor);
 
493
 
 
494
                rw_lock_x_unlock(&btr_search_latch);
 
495
        }
 
496
 
 
497
        if (build_index) {
 
498
                /* Note that since we did not protect block->n_fields etc.
 
499
                with any semaphore, the values can be inconsistent. We have
 
500
                to check inside the function call that they make sense. We
 
501
                also malloc an array and store the values there to make sure
 
502
                the compiler does not let the function call parameters change
 
503
                inside the called function. It might be that the compiler
 
504
                would optimize the call just to pass pointers to block. */
 
505
 
 
506
                params = mem_alloc(3 * sizeof(ulint));
 
507
                params[0] = block->n_fields;
 
508
                params[1] = block->n_bytes;
 
509
                params[2] = block->left_side;
 
510
 
 
511
                /* Make sure the compiler cannot deduce the values and do
 
512
                optimizations */
 
513
 
 
514
                params2 = params + btr_search_this_is_zero;
 
515
 
 
516
                btr_search_build_page_hash_index(cursor->index,
 
517
                                                 block->frame,
 
518
                                                 params2[0],
 
519
                                                 params2[1],
 
520
                                                 params2[2]);
 
521
                mem_free(params);
 
522
        }
 
523
}
 
524
 
 
525
/**********************************************************************
 
526
Checks if a guessed position for a tree cursor is right. Note that if
 
527
mode is PAGE_CUR_LE, which is used in inserts, and the function returns
 
528
TRUE, then cursor->up_match and cursor->low_match both have sensible values. */
 
529
static
 
530
ibool
 
531
btr_search_check_guess(
 
532
/*===================*/
 
533
                                /* out: TRUE if success */
 
534
        btr_cur_t*      cursor, /* in: guessed cursor position */
 
535
        ibool           can_only_compare_to_cursor_rec,
 
536
                                /* in: if we do not have a latch on the page
 
537
                                of cursor, but only a latch on
 
538
                                btr_search_latch, then ONLY the columns
 
539
                                of the record UNDER the cursor are
 
540
                                protected, not the next or previous record
 
541
                                in the chain: we cannot look at the next or
 
542
                                previous record to check our guess! */
 
543
        dtuple_t*       tuple,  /* in: data tuple */
 
544
        ulint           mode,   /* in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G,
 
545
                                or PAGE_CUR_GE */
 
546
        mtr_t*          mtr)    /* in: mtr */
 
547
{
 
548
        rec_t*          rec;
 
549
        ulint           n_unique;
 
550
        ulint           match;
 
551
        ulint           bytes;
 
552
        int             cmp;
 
553
        mem_heap_t*     heap            = NULL;
 
554
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
555
        ulint*          offsets         = offsets_;
 
556
        ibool           success         = FALSE;
 
557
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
558
 
 
559
        n_unique = dict_index_get_n_unique_in_tree(cursor->index);
 
560
 
 
561
        rec = btr_cur_get_rec(cursor);
 
562
 
 
563
        ut_ad(page_rec_is_user_rec(rec));
 
564
 
 
565
        match = 0;
 
566
        bytes = 0;
 
567
 
 
568
        offsets = rec_get_offsets(rec, cursor->index, offsets,
 
569
                                  n_unique, &heap);
 
570
        cmp = page_cmp_dtuple_rec_with_match(tuple, rec,
 
571
                                             offsets, &match, &bytes);
 
572
 
 
573
        if (mode == PAGE_CUR_GE) {
 
574
                if (cmp == 1) {
 
575
                        goto exit_func;
 
576
                }
 
577
 
 
578
                cursor->up_match = match;
 
579
 
 
580
                if (match >= n_unique) {
 
581
                        success = TRUE;
 
582
                        goto exit_func;
 
583
                }
 
584
        } else if (mode == PAGE_CUR_LE) {
 
585
                if (cmp == -1) {
 
586
                        goto exit_func;
 
587
                }
 
588
 
 
589
                cursor->low_match = match;
 
590
 
 
591
        } else if (mode == PAGE_CUR_G) {
 
592
                if (cmp != -1) {
 
593
                        goto exit_func;
 
594
                }
 
595
        } else if (mode == PAGE_CUR_L) {
 
596
                if (cmp != 1) {
 
597
                        goto exit_func;
 
598
                }
 
599
        }
 
600
 
 
601
        if (can_only_compare_to_cursor_rec) {
 
602
                /* Since we could not determine if our guess is right just by
 
603
                looking at the record under the cursor, return FALSE */
 
604
                goto exit_func;
 
605
        }
 
606
 
 
607
        match = 0;
 
608
        bytes = 0;
 
609
 
 
610
        if ((mode == PAGE_CUR_G) || (mode == PAGE_CUR_GE)) {
 
611
                rec_t*  prev_rec;
 
612
 
 
613
                ut_ad(!page_rec_is_infimum(rec));
 
614
 
 
615
                prev_rec = page_rec_get_prev(rec);
 
616
 
 
617
                if (page_rec_is_infimum(prev_rec)) {
 
618
                        success = btr_page_get_prev(
 
619
                                buf_frame_align(prev_rec), mtr) == FIL_NULL;
 
620
 
 
621
                        goto exit_func;
 
622
                }
 
623
 
 
624
                offsets = rec_get_offsets(prev_rec, cursor->index, offsets,
 
625
                                          n_unique, &heap);
 
626
                cmp = page_cmp_dtuple_rec_with_match(tuple, prev_rec,
 
627
                                                     offsets, &match, &bytes);
 
628
                if (mode == PAGE_CUR_GE) {
 
629
                        success = cmp == 1;
 
630
                } else {
 
631
                        success = cmp != -1;
 
632
                }
 
633
 
 
634
                goto exit_func;
 
635
        } else {
 
636
                rec_t*  next_rec;
 
637
 
 
638
                ut_ad(!page_rec_is_supremum(rec));
 
639
 
 
640
                next_rec = page_rec_get_next(rec);
 
641
 
 
642
                if (page_rec_is_supremum(next_rec)) {
 
643
                        if (btr_page_get_next(
 
644
                                    buf_frame_align(next_rec), mtr)
 
645
                            == FIL_NULL) {
 
646
 
 
647
                                cursor->up_match = 0;
 
648
                                success = TRUE;
 
649
                        }
 
650
 
 
651
                        goto exit_func;
 
652
                }
 
653
 
 
654
                offsets = rec_get_offsets(next_rec, cursor->index, offsets,
 
655
                                          n_unique, &heap);
 
656
                cmp = page_cmp_dtuple_rec_with_match(tuple, next_rec,
 
657
                                                     offsets, &match, &bytes);
 
658
                if (mode == PAGE_CUR_LE) {
 
659
                        success = cmp == -1;
 
660
                        cursor->up_match = match;
 
661
                } else {
 
662
                        success = cmp != 1;
 
663
                }
 
664
        }
 
665
exit_func:
 
666
        if (UNIV_LIKELY_NULL(heap)) {
 
667
                mem_heap_free(heap);
 
668
        }
 
669
        return(success);
 
670
}
 
671
 
 
672
/**********************************************************************
 
673
Tries to guess the right search position based on the hash search info
 
674
of the index. Note that if mode is PAGE_CUR_LE, which is used in inserts,
 
675
and the function returns TRUE, then cursor->up_match and cursor->low_match
 
676
both have sensible values. */
 
677
 
 
678
ibool
 
679
btr_search_guess_on_hash(
 
680
/*=====================*/
 
681
                                        /* out: TRUE if succeeded */
 
682
        dict_index_t*   index,          /* in: index */
 
683
        btr_search_t*   info,           /* in: index search info */
 
684
        dtuple_t*       tuple,          /* in: logical record */
 
685
        ulint           mode,           /* in: PAGE_CUR_L, ... */
 
686
        ulint           latch_mode,     /* in: BTR_SEARCH_LEAF, ...;
 
687
                                        NOTE that only if has_search_latch
 
688
                                        is 0, we will have a latch set on
 
689
                                        the cursor page, otherwise we assume
 
690
                                        the caller uses his search latch
 
691
                                        to protect the record! */
 
692
        btr_cur_t*      cursor,         /* out: tree cursor */
 
693
        ulint           has_search_latch,/* in: latch mode the caller
 
694
                                        currently has on btr_search_latch:
 
695
                                        RW_S_LATCH, RW_X_LATCH, or 0 */
 
696
        mtr_t*          mtr)            /* in: mtr */
 
697
{
 
698
        buf_block_t*    block;
 
699
        rec_t*          rec;
 
700
        page_t*         page;
 
701
        ulint           fold;
 
702
        ulint           tuple_n_fields;
 
703
        dulint          index_id;
 
704
        ibool           can_only_compare_to_cursor_rec = TRUE;
 
705
#ifdef notdefined
 
706
        btr_cur_t       cursor2;
 
707
        btr_pcur_t      pcur;
 
708
#endif
 
709
        ut_ad(index && info && tuple && cursor && mtr);
 
710
        ut_ad((latch_mode == BTR_SEARCH_LEAF)
 
711
              || (latch_mode == BTR_MODIFY_LEAF));
 
712
 
 
713
        /* Note that, for efficiency, the struct info may not be protected by
 
714
        any latch here! */
 
715
 
 
716
        if (UNIV_UNLIKELY(info->n_hash_potential == 0)) {
 
717
 
 
718
                return(FALSE);
 
719
        }
 
720
 
 
721
        cursor->n_fields = info->n_fields;
 
722
        cursor->n_bytes = info->n_bytes;
 
723
 
 
724
        tuple_n_fields = dtuple_get_n_fields(tuple);
 
725
 
 
726
        if (UNIV_UNLIKELY(tuple_n_fields < cursor->n_fields)) {
 
727
 
 
728
                return(FALSE);
 
729
        }
 
730
 
 
731
        if (UNIV_UNLIKELY(tuple_n_fields == cursor->n_fields)
 
732
            && (cursor->n_bytes > 0)) {
 
733
 
 
734
                return(FALSE);
 
735
        }
 
736
 
 
737
        index_id = index->id;
 
738
 
 
739
#ifdef UNIV_SEARCH_PERF_STAT
 
740
        info->n_hash_succ++;
 
741
#endif
 
742
        fold = dtuple_fold(tuple, cursor->n_fields, cursor->n_bytes, index_id);
 
743
 
 
744
        cursor->fold = fold;
 
745
        cursor->flag = BTR_CUR_HASH;
 
746
 
 
747
        if (UNIV_LIKELY(!has_search_latch)) {
 
748
                rw_lock_s_lock(&btr_search_latch);
 
749
        }
 
750
 
 
751
        ut_ad(btr_search_latch.writer != RW_LOCK_EX);
 
752
        ut_ad(btr_search_latch.reader_count > 0);
 
753
 
 
754
        rec = ha_search_and_get_data(btr_search_sys->hash_index, fold);
 
755
 
 
756
        if (UNIV_UNLIKELY(!rec)) {
 
757
                goto failure_unlock;
 
758
        }
 
759
 
 
760
        page = buf_frame_align(rec);
 
761
 
 
762
        if (UNIV_LIKELY(!has_search_latch)) {
 
763
 
 
764
                if (UNIV_UNLIKELY(
 
765
                            !buf_page_get_known_nowait(latch_mode, page,
 
766
                                                       BUF_MAKE_YOUNG,
 
767
                                                       __FILE__, __LINE__,
 
768
                                                       mtr))) {
 
769
                        goto failure_unlock;
 
770
                }
 
771
 
 
772
                rw_lock_s_unlock(&btr_search_latch);
 
773
                can_only_compare_to_cursor_rec = FALSE;
 
774
 
 
775
#ifdef UNIV_SYNC_DEBUG
 
776
                buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH);
 
777
#endif /* UNIV_SYNC_DEBUG */
 
778
        }
 
779
 
 
780
        block = buf_block_align(page);
 
781
 
 
782
        if (UNIV_UNLIKELY(block->state == BUF_BLOCK_REMOVE_HASH)) {
 
783
                if (UNIV_LIKELY(!has_search_latch)) {
 
784
 
 
785
                        btr_leaf_page_release(page, latch_mode, mtr);
 
786
                }
 
787
 
 
788
                goto failure;
 
789
        }
 
790
 
 
791
        ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
 
792
        ut_ad(page_rec_is_user_rec(rec));
 
793
 
 
794
        btr_cur_position(index, rec, cursor);
 
795
 
 
796
        /* Check the validity of the guess within the page */
 
797
 
 
798
        /* If we only have the latch on btr_search_latch, not on the
 
799
        page, it only protects the columns of the record the cursor
 
800
        is positioned on. We cannot look at the next of the previous
 
801
        record to determine if our guess for the cursor position is
 
802
        right. */
 
803
        if (UNIV_EXPECT(
 
804
                    ut_dulint_cmp(index_id, btr_page_get_index_id(page)), 0)
 
805
            || !btr_search_check_guess(cursor,
 
806
                                       can_only_compare_to_cursor_rec,
 
807
                                       tuple, mode, mtr)) {
 
808
                if (UNIV_LIKELY(!has_search_latch)) {
 
809
                        btr_leaf_page_release(page, latch_mode, mtr);
 
810
                }
 
811
 
 
812
                goto failure;
 
813
        }
 
814
 
 
815
        if (UNIV_LIKELY(info->n_hash_potential < BTR_SEARCH_BUILD_LIMIT + 5)) {
 
816
 
 
817
                info->n_hash_potential++;
 
818
        }
 
819
 
 
820
#ifdef notdefined
 
821
        /* These lines of code can be used in a debug version to check
 
822
        the correctness of the searched cursor position: */
 
823
 
 
824
        info->last_hash_succ = FALSE;
 
825
 
 
826
        /* Currently, does not work if the following fails: */
 
827
        ut_ad(!has_search_latch);
 
828
 
 
829
        btr_leaf_page_release(page, latch_mode, mtr);
 
830
 
 
831
        btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode,
 
832
                                    &cursor2, 0, mtr);
 
833
        if (mode == PAGE_CUR_GE
 
834
            && page_rec_is_supremum(btr_cur_get_rec(&cursor2))) {
 
835
 
 
836
                /* If mode is PAGE_CUR_GE, then the binary search
 
837
                in the index tree may actually take us to the supremum
 
838
                of the previous page */
 
839
 
 
840
                info->last_hash_succ = FALSE;
 
841
 
 
842
                btr_pcur_open_on_user_rec(index, tuple, mode, latch_mode,
 
843
                                          &pcur, mtr);
 
844
                ut_ad(btr_pcur_get_rec(&pcur) == btr_cur_get_rec(cursor));
 
845
        } else {
 
846
                ut_ad(btr_cur_get_rec(&cursor2) == btr_cur_get_rec(cursor));
 
847
        }
 
848
 
 
849
        /* NOTE that it is theoretically possible that the above assertions
 
850
        fail if the page of the cursor gets removed from the buffer pool
 
851
        meanwhile! Thus it might not be a bug. */
 
852
#endif
 
853
        info->last_hash_succ = TRUE;
 
854
 
 
855
#ifdef UNIV_SEARCH_PERF_STAT
 
856
        btr_search_n_succ++;
 
857
#endif
 
858
        if (UNIV_LIKELY(!has_search_latch)
 
859
            && buf_block_peek_if_too_old(block)) {
 
860
 
 
861
                buf_page_make_young(page);
 
862
        }
 
863
 
 
864
        /* Increment the page get statistics though we did not really
 
865
        fix the page: for user info only */
 
866
 
 
867
        buf_pool->n_page_gets++;
 
868
 
 
869
        return(TRUE);
 
870
 
 
871
        /*-------------------------------------------*/
 
872
failure_unlock:
 
873
        if (UNIV_LIKELY(!has_search_latch)) {
 
874
                rw_lock_s_unlock(&btr_search_latch);
 
875
        }
 
876
failure:
 
877
        cursor->flag = BTR_CUR_HASH_FAIL;
 
878
 
 
879
#ifdef UNIV_SEARCH_PERF_STAT
 
880
        info->n_hash_fail++;
 
881
 
 
882
        if (info->n_hash_succ > 0) {
 
883
                info->n_hash_succ--;
 
884
        }
 
885
#endif
 
886
        info->last_hash_succ = FALSE;
 
887
 
 
888
        return(FALSE);
 
889
}
 
890
 
 
891
/************************************************************************
 
892
Drops a page hash index. */
 
893
 
 
894
void
 
895
btr_search_drop_page_hash_index(
 
896
/*============================*/
 
897
        page_t* page)   /* in: index page, s- or x-latched, or an index page
 
898
                        for which we know that block->buf_fix_count == 0 */
 
899
{
 
900
        hash_table_t*   table;
 
901
        buf_block_t*    block;
 
902
        ulint           n_fields;
 
903
        ulint           n_bytes;
 
904
        rec_t*          rec;
 
905
        ulint           fold;
 
906
        ulint           prev_fold;
 
907
        dulint          index_id;
 
908
        ulint           n_cached;
 
909
        ulint           n_recs;
 
910
        ulint*          folds;
 
911
        ulint           i;
 
912
        mem_heap_t*     heap;
 
913
        dict_index_t*   index;
 
914
        ulint*          offsets;
 
915
 
 
916
#ifdef UNIV_SYNC_DEBUG
 
917
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
 
918
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
919
#endif /* UNIV_SYNC_DEBUG */
 
920
retry:
 
921
        rw_lock_s_lock(&btr_search_latch);
 
922
 
 
923
        block = buf_block_align(page);
 
924
 
 
925
        if (UNIV_LIKELY(!block->is_hashed)) {
 
926
 
 
927
                rw_lock_s_unlock(&btr_search_latch);
 
928
 
 
929
                return;
 
930
        }
 
931
 
 
932
        table = btr_search_sys->hash_index;
 
933
 
 
934
#ifdef UNIV_SYNC_DEBUG
 
935
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED)
 
936
              || rw_lock_own(&(block->lock), RW_LOCK_EX)
 
937
              || (block->buf_fix_count == 0));
 
938
#endif /* UNIV_SYNC_DEBUG */
 
939
 
 
940
        n_fields = block->curr_n_fields;
 
941
        n_bytes = block->curr_n_bytes;
 
942
        index = block->index;
 
943
 
 
944
        /* NOTE: The fields of block must not be accessed after
 
945
        releasing btr_search_latch, as the index page might only
 
946
        be s-latched! */
 
947
 
 
948
        rw_lock_s_unlock(&btr_search_latch);
 
949
 
 
950
        ut_a(n_fields + n_bytes > 0);
 
951
 
 
952
        n_recs = page_get_n_recs(page);
 
953
 
 
954
        /* Calculate and cache fold values into an array for fast deletion
 
955
        from the hash index */
 
956
 
 
957
        folds = mem_alloc(n_recs * sizeof(ulint));
 
958
 
 
959
        n_cached = 0;
 
960
 
 
961
        rec = page_get_infimum_rec(page);
 
962
        rec = page_rec_get_next(rec);
 
963
 
 
964
        index_id = btr_page_get_index_id(page);
 
965
 
 
966
        ut_a(0 == ut_dulint_cmp(index_id, index->id));
 
967
 
 
968
        prev_fold = 0;
 
969
 
 
970
        heap = NULL;
 
971
        offsets = NULL;
 
972
 
 
973
        while (!page_rec_is_supremum(rec)) {
 
974
                offsets = rec_get_offsets(rec, index, offsets,
 
975
                                          n_fields + (n_bytes > 0), &heap);
 
976
                ut_a(rec_offs_n_fields(offsets) == n_fields + (n_bytes > 0));
 
977
                fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
 
978
 
 
979
                if (fold == prev_fold && prev_fold != 0) {
 
980
 
 
981
                        goto next_rec;
 
982
                }
 
983
 
 
984
                /* Remove all hash nodes pointing to this page from the
 
985
                hash chain */
 
986
 
 
987
                folds[n_cached] = fold;
 
988
                n_cached++;
 
989
next_rec:
 
990
                rec = page_rec_get_next(rec);
 
991
                prev_fold = fold;
 
992
        }
 
993
 
 
994
        if (UNIV_LIKELY_NULL(heap)) {
 
995
                mem_heap_free(heap);
 
996
        }
 
997
 
 
998
        rw_lock_x_lock(&btr_search_latch);
 
999
 
 
1000
        if (UNIV_UNLIKELY(!block->is_hashed)) {
 
1001
                /* Someone else has meanwhile dropped the hash index */
 
1002
 
 
1003
                goto cleanup;
 
1004
        }
 
1005
 
 
1006
        ut_a(block->index == index);
 
1007
 
 
1008
        if (UNIV_UNLIKELY(block->curr_n_fields != n_fields)
 
1009
            || UNIV_UNLIKELY(block->curr_n_bytes != n_bytes)) {
 
1010
 
 
1011
                /* Someone else has meanwhile built a new hash index on the
 
1012
                page, with different parameters */
 
1013
 
 
1014
                rw_lock_x_unlock(&btr_search_latch);
 
1015
 
 
1016
                mem_free(folds);
 
1017
                goto retry;
 
1018
        }
 
1019
 
 
1020
        for (i = 0; i < n_cached; i++) {
 
1021
 
 
1022
                ha_remove_all_nodes_to_page(table, folds[i], page);
 
1023
        }
 
1024
 
 
1025
        block->is_hashed = FALSE;
 
1026
        block->index = NULL;
 
1027
cleanup:
 
1028
        if (UNIV_UNLIKELY(block->n_pointers)) {
 
1029
                /* Corruption */
 
1030
                ut_print_timestamp(stderr);
 
1031
                fprintf(stderr,
 
1032
                        "  InnoDB: Corruption of adaptive hash index."
 
1033
                        " After dropping\n"
 
1034
                        "InnoDB: the hash index to a page of %s,"
 
1035
                        " still %lu hash nodes remain.\n",
 
1036
                        index->name, (ulong) block->n_pointers);
 
1037
                rw_lock_x_unlock(&btr_search_latch);
 
1038
 
 
1039
                btr_search_validate();
 
1040
        } else {
 
1041
                rw_lock_x_unlock(&btr_search_latch);
 
1042
        }
 
1043
 
 
1044
        mem_free(folds);
 
1045
}
 
1046
 
 
1047
/************************************************************************
 
1048
Drops a page hash index when a page is freed from a fseg to the file system.
 
1049
Drops possible hash index if the page happens to be in the buffer pool. */
 
1050
 
 
1051
void
 
1052
btr_search_drop_page_hash_when_freed(
 
1053
/*=================================*/
 
1054
        ulint   space,          /* in: space id */
 
1055
        ulint   page_no)        /* in: page number */
 
1056
{
 
1057
        ibool   is_hashed;
 
1058
        page_t* page;
 
1059
        mtr_t   mtr;
 
1060
 
 
1061
        is_hashed = buf_page_peek_if_search_hashed(space, page_no);
 
1062
 
 
1063
        if (!is_hashed) {
 
1064
 
 
1065
                return;
 
1066
        }
 
1067
 
 
1068
        mtr_start(&mtr);
 
1069
 
 
1070
        /* We assume that if the caller has a latch on the page, then the
 
1071
        caller has already dropped the hash index for the page, and we never
 
1072
        get here. Therefore we can acquire the s-latch to the page without
 
1073
        having to fear a deadlock. */
 
1074
 
 
1075
        page = buf_page_get_gen(space, page_no, RW_S_LATCH, NULL,
 
1076
                                BUF_GET_IF_IN_POOL, __FILE__, __LINE__,
 
1077
                                &mtr);
 
1078
 
 
1079
#ifdef UNIV_SYNC_DEBUG
 
1080
        buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH);
 
1081
#endif /* UNIV_SYNC_DEBUG */
 
1082
 
 
1083
        btr_search_drop_page_hash_index(page);
 
1084
 
 
1085
        mtr_commit(&mtr);
 
1086
}
 
1087
 
 
1088
/************************************************************************
 
1089
Builds a hash index on a page with the given parameters. If the page already
 
1090
has a hash index with different parameters, the old hash index is removed.
 
1091
If index is non-NULL, this function checks if n_fields and n_bytes are
 
1092
sensible values, and does not build a hash index if not. */
 
1093
static
 
1094
void
 
1095
btr_search_build_page_hash_index(
 
1096
/*=============================*/
 
1097
        dict_index_t*   index,  /* in: index for which to build */
 
1098
        page_t*         page,   /* in: index page, s- or x-latched */
 
1099
        ulint           n_fields,/* in: hash this many full fields */
 
1100
        ulint           n_bytes,/* in: hash this many bytes from the next
 
1101
                                field */
 
1102
        ibool           left_side)/* in: hash for searches from left side? */
 
1103
{
 
1104
        hash_table_t*   table;
 
1105
        buf_block_t*    block;
 
1106
        rec_t*          rec;
 
1107
        rec_t*          next_rec;
 
1108
        ulint           fold;
 
1109
        ulint           next_fold;
 
1110
        dulint          index_id;
 
1111
        ulint           n_cached;
 
1112
        ulint           n_recs;
 
1113
        ulint*          folds;
 
1114
        rec_t**         recs;
 
1115
        ulint           i;
 
1116
        mem_heap_t*     heap            = NULL;
 
1117
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1118
        ulint*          offsets         = offsets_;
 
1119
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
1120
 
 
1121
        ut_ad(index);
 
1122
 
 
1123
        block = buf_block_align(page);
 
1124
        table = btr_search_sys->hash_index;
 
1125
 
 
1126
#ifdef UNIV_SYNC_DEBUG
 
1127
        ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
 
1128
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED)
 
1129
              || rw_lock_own(&(block->lock), RW_LOCK_EX));
 
1130
#endif /* UNIV_SYNC_DEBUG */
 
1131
 
 
1132
        rw_lock_s_lock(&btr_search_latch);
 
1133
 
 
1134
        if (block->is_hashed && ((block->curr_n_fields != n_fields)
 
1135
                                 || (block->curr_n_bytes != n_bytes)
 
1136
                                 || (block->curr_left_side != left_side))) {
 
1137
 
 
1138
                rw_lock_s_unlock(&btr_search_latch);
 
1139
 
 
1140
                btr_search_drop_page_hash_index(page);
 
1141
        } else {
 
1142
                rw_lock_s_unlock(&btr_search_latch);
 
1143
        }
 
1144
 
 
1145
        n_recs = page_get_n_recs(page);
 
1146
 
 
1147
        if (n_recs == 0) {
 
1148
 
 
1149
                return;
 
1150
        }
 
1151
 
 
1152
        /* Check that the values for hash index build are sensible */
 
1153
 
 
1154
        if (n_fields + n_bytes == 0) {
 
1155
 
 
1156
                return;
 
1157
        }
 
1158
 
 
1159
        if (dict_index_get_n_unique_in_tree(index) < n_fields
 
1160
            || (dict_index_get_n_unique_in_tree(index) == n_fields
 
1161
                && n_bytes > 0)) {
 
1162
                return;
 
1163
        }
 
1164
 
 
1165
        /* Calculate and cache fold values and corresponding records into
 
1166
        an array for fast insertion to the hash index */
 
1167
 
 
1168
        folds = mem_alloc(n_recs * sizeof(ulint));
 
1169
        recs = mem_alloc(n_recs * sizeof(rec_t*));
 
1170
 
 
1171
        n_cached = 0;
 
1172
 
 
1173
        index_id = btr_page_get_index_id(page);
 
1174
 
 
1175
        rec = page_get_infimum_rec(page);
 
1176
        rec = page_rec_get_next(rec);
 
1177
 
 
1178
        offsets = rec_get_offsets(rec, index, offsets,
 
1179
                                  n_fields + (n_bytes > 0), &heap);
 
1180
 
 
1181
        if (!page_rec_is_supremum(rec)) {
 
1182
                ut_a(n_fields <= rec_offs_n_fields(offsets));
 
1183
 
 
1184
                if (n_bytes > 0) {
 
1185
                        ut_a(n_fields < rec_offs_n_fields(offsets));
 
1186
                }
 
1187
        }
 
1188
 
 
1189
        fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
 
1190
 
 
1191
        if (left_side) {
 
1192
 
 
1193
                folds[n_cached] = fold;
 
1194
                recs[n_cached] = rec;
 
1195
                n_cached++;
 
1196
        }
 
1197
 
 
1198
        for (;;) {
 
1199
                next_rec = page_rec_get_next(rec);
 
1200
 
 
1201
                if (page_rec_is_supremum(next_rec)) {
 
1202
 
 
1203
                        if (!left_side) {
 
1204
 
 
1205
                                folds[n_cached] = fold;
 
1206
                                recs[n_cached] = rec;
 
1207
                                n_cached++;
 
1208
                        }
 
1209
 
 
1210
                        break;
 
1211
                }
 
1212
 
 
1213
                offsets = rec_get_offsets(next_rec, index, offsets,
 
1214
                                          n_fields + (n_bytes > 0), &heap);
 
1215
                next_fold = rec_fold(next_rec, offsets, n_fields,
 
1216
                                     n_bytes, index_id);
 
1217
 
 
1218
                if (fold != next_fold) {
 
1219
                        /* Insert an entry into the hash index */
 
1220
 
 
1221
                        if (left_side) {
 
1222
 
 
1223
                                folds[n_cached] = next_fold;
 
1224
                                recs[n_cached] = next_rec;
 
1225
                                n_cached++;
 
1226
                        } else {
 
1227
                                folds[n_cached] = fold;
 
1228
                                recs[n_cached] = rec;
 
1229
                                n_cached++;
 
1230
                        }
 
1231
                }
 
1232
 
 
1233
                rec = next_rec;
 
1234
                fold = next_fold;
 
1235
        }
 
1236
 
 
1237
        btr_search_check_free_space_in_heap();
 
1238
 
 
1239
        rw_lock_x_lock(&btr_search_latch);
 
1240
 
 
1241
        if (block->is_hashed && ((block->curr_n_fields != n_fields)
 
1242
                                 || (block->curr_n_bytes != n_bytes)
 
1243
                                 || (block->curr_left_side != left_side))) {
 
1244
                goto exit_func;
 
1245
        }
 
1246
 
 
1247
        block->is_hashed = TRUE;
 
1248
        block->n_hash_helps = 0;
 
1249
 
 
1250
        block->curr_n_fields = n_fields;
 
1251
        block->curr_n_bytes = n_bytes;
 
1252
        block->curr_left_side = left_side;
 
1253
        block->index = index;
 
1254
 
 
1255
        for (i = 0; i < n_cached; i++) {
 
1256
 
 
1257
                ha_insert_for_fold(table, folds[i], recs[i]);
 
1258
        }
 
1259
 
 
1260
exit_func:
 
1261
        rw_lock_x_unlock(&btr_search_latch);
 
1262
 
 
1263
        mem_free(folds);
 
1264
        mem_free(recs);
 
1265
        if (UNIV_LIKELY_NULL(heap)) {
 
1266
                mem_heap_free(heap);
 
1267
        }
 
1268
}
 
1269
 
 
1270
/************************************************************************
 
1271
Moves or deletes hash entries for moved records. If new_page is already hashed,
 
1272
then the hash index for page, if any, is dropped. If new_page is not hashed,
 
1273
and page is hashed, then a new hash index is built to new_page with the same
 
1274
parameters as page (this often happens when a page is split). */
 
1275
 
 
1276
void
 
1277
btr_search_move_or_delete_hash_entries(
 
1278
/*===================================*/
 
1279
        page_t*         new_page,       /* in: records are copied
 
1280
                                        to this page */
 
1281
        page_t*         page,           /* in: index page from which
 
1282
                                        records were copied, and the
 
1283
                                        copied records will be deleted
 
1284
                                        from this page */
 
1285
        dict_index_t*   index)          /* in: record descriptor */
 
1286
{
 
1287
        buf_block_t*    block;
 
1288
        buf_block_t*    new_block;
 
1289
        ulint           n_fields;
 
1290
        ulint           n_bytes;
 
1291
        ibool           left_side;
 
1292
 
 
1293
        block = buf_block_align(page);
 
1294
        new_block = buf_block_align(new_page);
 
1295
        ut_a(page_is_comp(page) == page_is_comp(new_page));
 
1296
 
 
1297
#ifdef UNIV_SYNC_DEBUG
 
1298
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
 
1299
        ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_EX));
 
1300
#endif /* UNIV_SYNC_DEBUG */
 
1301
        ut_a(!new_block->is_hashed || new_block->index == index);
 
1302
        ut_a(!block->is_hashed || block->index == index);
 
1303
 
 
1304
        rw_lock_s_lock(&btr_search_latch);
 
1305
 
 
1306
        if (new_block->is_hashed) {
 
1307
 
 
1308
                rw_lock_s_unlock(&btr_search_latch);
 
1309
 
 
1310
                btr_search_drop_page_hash_index(page);
 
1311
 
 
1312
                return;
 
1313
        }
 
1314
 
 
1315
        if (block->is_hashed) {
 
1316
 
 
1317
                n_fields = block->curr_n_fields;
 
1318
                n_bytes = block->curr_n_bytes;
 
1319
                left_side = block->curr_left_side;
 
1320
 
 
1321
                new_block->n_fields = block->curr_n_fields;
 
1322
                new_block->n_bytes = block->curr_n_bytes;
 
1323
                new_block->left_side = left_side;
 
1324
 
 
1325
                rw_lock_s_unlock(&btr_search_latch);
 
1326
 
 
1327
                ut_a(n_fields + n_bytes > 0);
 
1328
 
 
1329
                btr_search_build_page_hash_index(index, new_page, n_fields,
 
1330
                                                 n_bytes, left_side);
 
1331
#if 1 /* TODO: safe to remove? */
 
1332
                ut_a(n_fields == block->curr_n_fields);
 
1333
                ut_a(n_bytes == block->curr_n_bytes);
 
1334
                ut_a(left_side == block->curr_left_side);
 
1335
#endif
 
1336
                return;
 
1337
        }
 
1338
 
 
1339
        rw_lock_s_unlock(&btr_search_latch);
 
1340
}
 
1341
 
 
1342
/************************************************************************
 
1343
Updates the page hash index when a single record is deleted from a page. */
 
1344
 
 
1345
void
 
1346
btr_search_update_hash_on_delete(
 
1347
/*=============================*/
 
1348
        btr_cur_t*      cursor) /* in: cursor which was positioned on the
 
1349
                                record to delete using btr_cur_search_...,
 
1350
                                the record is not yet deleted */
 
1351
{
 
1352
        hash_table_t*   table;
 
1353
        buf_block_t*    block;
 
1354
        rec_t*          rec;
 
1355
        ulint           fold;
 
1356
        dulint          index_id;
 
1357
        ibool           found;
 
1358
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1359
        mem_heap_t*     heap            = NULL;
 
1360
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
1361
 
 
1362
        rec = btr_cur_get_rec(cursor);
 
1363
 
 
1364
        block = buf_block_align(rec);
 
1365
 
 
1366
#ifdef UNIV_SYNC_DEBUG
 
1367
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
 
1368
#endif /* UNIV_SYNC_DEBUG */
 
1369
 
 
1370
        if (!block->is_hashed) {
 
1371
 
 
1372
                return;
 
1373
        }
 
1374
 
 
1375
        ut_a(block->index == cursor->index);
 
1376
        ut_a(block->curr_n_fields + block->curr_n_bytes > 0);
 
1377
 
 
1378
        table = btr_search_sys->hash_index;
 
1379
 
 
1380
        index_id = cursor->index->id;
 
1381
        fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, offsets_,
 
1382
                                             ULINT_UNDEFINED, &heap),
 
1383
                        block->curr_n_fields, block->curr_n_bytes, index_id);
 
1384
        if (UNIV_LIKELY_NULL(heap)) {
 
1385
                mem_heap_free(heap);
 
1386
        }
 
1387
        rw_lock_x_lock(&btr_search_latch);
 
1388
 
 
1389
        found = ha_search_and_delete_if_found(table, fold, rec);
 
1390
 
 
1391
        rw_lock_x_unlock(&btr_search_latch);
 
1392
}
 
1393
 
 
1394
/************************************************************************
 
1395
Updates the page hash index when a single record is inserted on a page. */
 
1396
 
 
1397
void
 
1398
btr_search_update_hash_node_on_insert(
 
1399
/*==================================*/
 
1400
        btr_cur_t*      cursor) /* in: cursor which was positioned to the
 
1401
                                place to insert using btr_cur_search_...,
 
1402
                                and the new record has been inserted next
 
1403
                                to the cursor */
 
1404
{
 
1405
        hash_table_t*   table;
 
1406
        buf_block_t*    block;
 
1407
        rec_t*          rec;
 
1408
 
 
1409
        rec = btr_cur_get_rec(cursor);
 
1410
 
 
1411
        block = buf_block_align(rec);
 
1412
 
 
1413
#ifdef UNIV_SYNC_DEBUG
 
1414
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
 
1415
#endif /* UNIV_SYNC_DEBUG */
 
1416
 
 
1417
        if (!block->is_hashed) {
 
1418
 
 
1419
                return;
 
1420
        }
 
1421
 
 
1422
        ut_a(block->index == cursor->index);
 
1423
 
 
1424
        rw_lock_x_lock(&btr_search_latch);
 
1425
 
 
1426
        if ((cursor->flag == BTR_CUR_HASH)
 
1427
            && (cursor->n_fields == block->curr_n_fields)
 
1428
            && (cursor->n_bytes == block->curr_n_bytes)
 
1429
            && !block->curr_left_side) {
 
1430
 
 
1431
                table = btr_search_sys->hash_index;
 
1432
 
 
1433
                ha_search_and_update_if_found(table, cursor->fold, rec,
 
1434
                                              page_rec_get_next(rec));
 
1435
 
 
1436
                rw_lock_x_unlock(&btr_search_latch);
 
1437
        } else {
 
1438
                rw_lock_x_unlock(&btr_search_latch);
 
1439
 
 
1440
                btr_search_update_hash_on_insert(cursor);
 
1441
        }
 
1442
}
 
1443
 
 
1444
/************************************************************************
 
1445
Updates the page hash index when a single record is inserted on a page. */
 
1446
 
 
1447
void
 
1448
btr_search_update_hash_on_insert(
 
1449
/*=============================*/
 
1450
        btr_cur_t*      cursor) /* in: cursor which was positioned to the
 
1451
                                place to insert using btr_cur_search_...,
 
1452
                                and the new record has been inserted next
 
1453
                                to the cursor */
 
1454
{
 
1455
        hash_table_t*   table;
 
1456
        buf_block_t*    block;
 
1457
        rec_t*          rec;
 
1458
        rec_t*          ins_rec;
 
1459
        rec_t*          next_rec;
 
1460
        dulint          index_id;
 
1461
        ulint           fold;
 
1462
        ulint           ins_fold;
 
1463
        ulint           next_fold = 0; /* remove warning (??? bug ???) */
 
1464
        ulint           n_fields;
 
1465
        ulint           n_bytes;
 
1466
        ibool           left_side;
 
1467
        ibool           locked          = FALSE;
 
1468
        mem_heap_t*     heap            = NULL;
 
1469
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1470
        ulint*          offsets         = offsets_;
 
1471
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
1472
 
 
1473
        table = btr_search_sys->hash_index;
 
1474
 
 
1475
        btr_search_check_free_space_in_heap();
 
1476
 
 
1477
        rec = btr_cur_get_rec(cursor);
 
1478
 
 
1479
        block = buf_block_align(rec);
 
1480
 
 
1481
#ifdef UNIV_SYNC_DEBUG
 
1482
        ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
 
1483
#endif /* UNIV_SYNC_DEBUG */
 
1484
 
 
1485
        if (!block->is_hashed) {
 
1486
 
 
1487
                return;
 
1488
        }
 
1489
 
 
1490
        ut_a(block->index == cursor->index);
 
1491
 
 
1492
        index_id = cursor->index->id;
 
1493
 
 
1494
        n_fields = block->curr_n_fields;
 
1495
        n_bytes = block->curr_n_bytes;
 
1496
        left_side = block->curr_left_side;
 
1497
 
 
1498
        ins_rec = page_rec_get_next(rec);
 
1499
        next_rec = page_rec_get_next(ins_rec);
 
1500
 
 
1501
        offsets = rec_get_offsets(ins_rec, cursor->index, offsets,
 
1502
                                  ULINT_UNDEFINED, &heap);
 
1503
        ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, index_id);
 
1504
 
 
1505
        if (!page_rec_is_supremum(next_rec)) {
 
1506
                offsets = rec_get_offsets(next_rec, cursor->index, offsets,
 
1507
                                          n_fields + (n_bytes > 0), &heap);
 
1508
                next_fold = rec_fold(next_rec, offsets, n_fields,
 
1509
                                     n_bytes, index_id);
 
1510
        }
 
1511
 
 
1512
        if (!page_rec_is_infimum(rec)) {
 
1513
                offsets = rec_get_offsets(rec, cursor->index, offsets,
 
1514
                                          n_fields + (n_bytes > 0), &heap);
 
1515
                fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
 
1516
        } else {
 
1517
                if (left_side) {
 
1518
 
 
1519
                        rw_lock_x_lock(&btr_search_latch);
 
1520
 
 
1521
                        locked = TRUE;
 
1522
 
 
1523
                        ha_insert_for_fold(table, ins_fold, ins_rec);
 
1524
                }
 
1525
 
 
1526
                goto check_next_rec;
 
1527
        }
 
1528
 
 
1529
        if (fold != ins_fold) {
 
1530
 
 
1531
                if (!locked) {
 
1532
 
 
1533
                        rw_lock_x_lock(&btr_search_latch);
 
1534
 
 
1535
                        locked = TRUE;
 
1536
                }
 
1537
 
 
1538
                if (!left_side) {
 
1539
                        ha_insert_for_fold(table, fold, rec);
 
1540
                } else {
 
1541
                        ha_insert_for_fold(table, ins_fold, ins_rec);
 
1542
                }
 
1543
        }
 
1544
 
 
1545
check_next_rec:
 
1546
        if (page_rec_is_supremum(next_rec)) {
 
1547
 
 
1548
                if (!left_side) {
 
1549
 
 
1550
                        if (!locked) {
 
1551
                                rw_lock_x_lock(&btr_search_latch);
 
1552
 
 
1553
                                locked = TRUE;
 
1554
                        }
 
1555
 
 
1556
                        ha_insert_for_fold(table, ins_fold, ins_rec);
 
1557
                }
 
1558
 
 
1559
                goto function_exit;
 
1560
        }
 
1561
 
 
1562
        if (ins_fold != next_fold) {
 
1563
 
 
1564
                if (!locked) {
 
1565
 
 
1566
                        rw_lock_x_lock(&btr_search_latch);
 
1567
 
 
1568
                        locked = TRUE;
 
1569
                }
 
1570
 
 
1571
                if (!left_side) {
 
1572
 
 
1573
                        ha_insert_for_fold(table, ins_fold, ins_rec);
 
1574
                        /*
 
1575
                        fputs("Hash insert for ", stderr);
 
1576
                        dict_index_name_print(stderr, cursor->index);
 
1577
                        fprintf(stderr, " fold %lu\n", ins_fold);
 
1578
                        */
 
1579
                } else {
 
1580
                        ha_insert_for_fold(table, next_fold, next_rec);
 
1581
                }
 
1582
        }
 
1583
 
 
1584
function_exit:
 
1585
        if (UNIV_LIKELY_NULL(heap)) {
 
1586
                mem_heap_free(heap);
 
1587
        }
 
1588
        if (locked) {
 
1589
                rw_lock_x_unlock(&btr_search_latch);
 
1590
        }
 
1591
}
 
1592
 
 
1593
/************************************************************************
 
1594
Validates the search system. */
 
1595
 
 
1596
ibool
 
1597
btr_search_validate(void)
 
1598
/*=====================*/
 
1599
                                /* out: TRUE if ok */
 
1600
{
 
1601
        buf_block_t*    block;
 
1602
        page_t*         page;
 
1603
        ha_node_t*      node;
 
1604
        ulint           n_page_dumps    = 0;
 
1605
        ibool           ok              = TRUE;
 
1606
        ulint           i;
 
1607
        ulint           cell_count;
 
1608
        mem_heap_t*     heap            = NULL;
 
1609
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1610
        ulint*          offsets         = offsets_;
 
1611
 
 
1612
        /* How many cells to check before temporarily releasing
 
1613
        btr_search_latch. */
 
1614
        ulint           chunk_size = 10000;
 
1615
 
 
1616
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
1617
 
 
1618
        rw_lock_x_lock(&btr_search_latch);
 
1619
 
 
1620
        cell_count = hash_get_n_cells(btr_search_sys->hash_index);
 
1621
 
 
1622
        for (i = 0; i < cell_count; i++) {
 
1623
                /* We release btr_search_latch every once in a while to
 
1624
                give other queries a chance to run. */
 
1625
                if ((i != 0) && ((i % chunk_size) == 0)) {
 
1626
                        rw_lock_x_unlock(&btr_search_latch);
 
1627
                        os_thread_yield();
 
1628
                        rw_lock_x_lock(&btr_search_latch);
 
1629
                }
 
1630
 
 
1631
                node = hash_get_nth_cell(btr_search_sys->hash_index, i)->node;
 
1632
 
 
1633
                while (node != NULL) {
 
1634
                        block = buf_block_align(node->data);
 
1635
                        page = buf_frame_align(node->data);
 
1636
                        offsets = rec_get_offsets((rec_t*) node->data,
 
1637
                                                  block->index, offsets,
 
1638
                                                  block->curr_n_fields
 
1639
                                                  + (block->curr_n_bytes > 0),
 
1640
                                                  &heap);
 
1641
 
 
1642
                        if (!block->is_hashed || node->fold
 
1643
                            != rec_fold((rec_t*)(node->data),
 
1644
                                        offsets,
 
1645
                                        block->curr_n_fields,
 
1646
                                        block->curr_n_bytes,
 
1647
                                        btr_page_get_index_id(page))) {
 
1648
                                ok = FALSE;
 
1649
                                ut_print_timestamp(stderr);
 
1650
 
 
1651
                                fprintf(stderr,
 
1652
                                        "  InnoDB: Error in an adaptive hash"
 
1653
                                        " index pointer to page %lu\n"
 
1654
                                        "InnoDB: ptr mem address %p"
 
1655
                                        " index id %lu %lu,"
 
1656
                                        " node fold %lu, rec fold %lu\n",
 
1657
                                        (ulong) buf_frame_get_page_no(page),
 
1658
                                        node->data,
 
1659
                                        (ulong) ut_dulint_get_high(
 
1660
                                                btr_page_get_index_id(page)),
 
1661
                                        (ulong) ut_dulint_get_low(
 
1662
                                                btr_page_get_index_id(page)),
 
1663
                                        (ulong) node->fold,
 
1664
                                        (ulong) rec_fold((rec_t*)(node->data),
 
1665
                                                         offsets,
 
1666
                                                         block->curr_n_fields,
 
1667
                                                         block->curr_n_bytes,
 
1668
                                                         btr_page_get_index_id(
 
1669
                                                                 page)));
 
1670
 
 
1671
                                fputs("InnoDB: Record ", stderr);
 
1672
                                rec_print_new(stderr, (rec_t*)node->data,
 
1673
                                              offsets);
 
1674
                                fprintf(stderr, "\nInnoDB: on that page."
 
1675
                                        " Page mem address %p, is hashed %lu,"
 
1676
                                        " n fields %lu, n bytes %lu\n"
 
1677
                                        "InnoDB: side %lu\n",
 
1678
                                        (void*) page, (ulong) block->is_hashed,
 
1679
                                        (ulong) block->curr_n_fields,
 
1680
                                        (ulong) block->curr_n_bytes,
 
1681
                                        (ulong) block->curr_left_side);
 
1682
 
 
1683
                                if (n_page_dumps < 20) {
 
1684
                                        buf_page_print(page);
 
1685
                                        n_page_dumps++;
 
1686
                                }
 
1687
                        }
 
1688
 
 
1689
                        node = node->next;
 
1690
                }
 
1691
        }
 
1692
 
 
1693
        for (i = 0; i < cell_count; i += chunk_size) {
 
1694
                ulint end_index = ut_min(i + chunk_size - 1, cell_count - 1);
 
1695
 
 
1696
                /* We release btr_search_latch every once in a while to
 
1697
                give other queries a chance to run. */
 
1698
                if (i != 0) {
 
1699
                        rw_lock_x_unlock(&btr_search_latch);
 
1700
                        os_thread_yield();
 
1701
                        rw_lock_x_lock(&btr_search_latch);
 
1702
                }
 
1703
 
 
1704
                if (!ha_validate(btr_search_sys->hash_index, i, end_index)) {
 
1705
                        ok = FALSE;
 
1706
                }
 
1707
        }
 
1708
 
 
1709
        rw_lock_x_unlock(&btr_search_latch);
 
1710
        if (UNIV_LIKELY_NULL(heap)) {
 
1711
                mem_heap_free(heap);
 
1712
        }
 
1713
 
 
1714
        return(ok);
 
1715
}