1 /*****************************************************************************
2
3 Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2008, Google Inc.
5 Copyright (c) 2017, 2021, MariaDB Corporation.
6
7 Portions of this file contain modifications contributed and copyrighted by
8 Google, Inc. Those modifications are gratefully acknowledged and are described
9 briefly in the InnoDB documentation. The contributions by Google are
10 incorporated with their permission, and subject to the conditions contained in
11 the file COPYING.Google.
12
13 This program is free software; you can redistribute it and/or modify it under
14 the terms of the GNU General Public License as published by the Free Software
15 Foundation; version 2 of the License.
16
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
24
25 *****************************************************************************/
26
27 /********************************************************************//**
28 @file btr/btr0sea.cc
29 The index tree adaptive search
30
31 Created 2/17/1996 Heikki Tuuri
32 *************************************************************************/
33
34 #include "btr0sea.h"
35 #ifdef BTR_CUR_HASH_ADAPT
36 #include "buf0buf.h"
37 #include "page0page.h"
38 #include "page0cur.h"
39 #include "btr0cur.h"
40 #include "btr0pcur.h"
41 #include "btr0btr.h"
42 #include "ha0ha.h"
43 #include "srv0mon.h"
44 #include "sync0sync.h"
45
46 /** Is search system enabled.
47 Search system is protected by array of latches. */
48 char btr_search_enabled;
49
50 /** Number of adaptive hash index partition. */
51 ulong btr_ahi_parts;
52
53 #ifdef UNIV_SEARCH_PERF_STAT
54 /** Number of successful adaptive hash index lookups */
55 ulint btr_search_n_succ = 0;
56 /** Number of failed adaptive hash index lookups */
57 ulint btr_search_n_hash_fail = 0;
58 #endif /* UNIV_SEARCH_PERF_STAT */
59
60 /** padding to prevent other memory update
61 hotspots from residing on the same memory
62 cache line as btr_search_latches */
63 UNIV_INTERN byte btr_sea_pad1[CACHE_LINE_SIZE];
64
65 /** The latches protecting the adaptive search system: this latches protects the
66 (1) positions of records on those pages where a hash index has been built.
67 NOTE: It does not protect values of non-ordering fields within a record from
68 being updated in-place! We can use fact (1) to perform unique searches to
69 indexes. We will allocate the latches from dynamic memory to get it to the
70 same DRAM page as other hotspot semaphores */
71 rw_lock_t** btr_search_latches;
72
73 /** padding to prevent other memory update hotspots from residing on
74 the same memory cache line */
75 UNIV_INTERN byte btr_sea_pad2[CACHE_LINE_SIZE];
76
77 /** The adaptive hash index */
78 btr_search_sys_t* btr_search_sys;
79
80 /** If the number of records on the page divided by this parameter
81 would have been successfully accessed using a hash index, the index
82 is then built on the page, assuming the global limit has been reached */
83 #define BTR_SEARCH_PAGE_BUILD_LIMIT 16U
84
85 /** The global limit for consecutive potentially successful hash searches,
86 before hash index building is started */
87 #define BTR_SEARCH_BUILD_LIMIT 100U
88
89 /** Compute a hash value of a record in a page.
90 @param[in] rec index record
91 @param[in] offsets return value of rec_get_offsets()
92 @param[in] n_fields number of complete fields to fold
93 @param[in] n_bytes number of bytes to fold in the last field
94 @param[in] index_id index tree ID
95 @return the hash value */
96 static inline
97 ulint
rec_fold(const rec_t * rec,const rec_offs * offsets,ulint n_fields,ulint n_bytes,index_id_t tree_id)98 rec_fold(
99 const rec_t* rec,
100 const rec_offs* offsets,
101 ulint n_fields,
102 ulint n_bytes,
103 index_id_t tree_id)
104 {
105 ulint i;
106 const byte* data;
107 ulint len;
108 ulint fold;
109 ulint n_fields_rec;
110
111 ut_ad(rec_offs_validate(rec, NULL, offsets));
112 ut_ad(rec_validate(rec, offsets));
113 ut_ad(page_rec_is_leaf(rec));
114 ut_ad(!page_rec_is_metadata(rec));
115 ut_ad(n_fields > 0 || n_bytes > 0);
116
117 n_fields_rec = rec_offs_n_fields(offsets);
118 ut_ad(n_fields <= n_fields_rec);
119 ut_ad(n_fields < n_fields_rec || n_bytes == 0);
120
121 if (n_fields > n_fields_rec) {
122 n_fields = n_fields_rec;
123 }
124
125 if (n_fields == n_fields_rec) {
126 n_bytes = 0;
127 }
128
129 fold = ut_fold_ull(tree_id);
130
131 for (i = 0; i < n_fields; i++) {
132 data = rec_get_nth_field(rec, offsets, i, &len);
133
134 if (len != UNIV_SQL_NULL) {
135 fold = ut_fold_ulint_pair(fold,
136 ut_fold_binary(data, len));
137 }
138 }
139
140 if (n_bytes > 0) {
141 data = rec_get_nth_field(rec, offsets, i, &len);
142
143 if (len != UNIV_SQL_NULL) {
144 if (len > n_bytes) {
145 len = n_bytes;
146 }
147
148 fold = ut_fold_ulint_pair(fold,
149 ut_fold_binary(data, len));
150 }
151 }
152
153 return(fold);
154 }
155
156 /** Determine the number of accessed key fields.
157 @param[in] n_fields number of complete fields
158 @param[in] n_bytes number of bytes in an incomplete last field
159 @return number of complete or incomplete fields */
160 inline MY_ATTRIBUTE((warn_unused_result))
161 ulint
btr_search_get_n_fields(ulint n_fields,ulint n_bytes)162 btr_search_get_n_fields(
163 ulint n_fields,
164 ulint n_bytes)
165 {
166 return(n_fields + (n_bytes > 0 ? 1 : 0));
167 }
168
169 /** Determine the number of accessed key fields.
170 @param[in] cursor b-tree cursor
171 @return number of complete or incomplete fields */
172 inline MY_ATTRIBUTE((warn_unused_result))
173 ulint
btr_search_get_n_fields(const btr_cur_t * cursor)174 btr_search_get_n_fields(
175 const btr_cur_t* cursor)
176 {
177 return(btr_search_get_n_fields(cursor->n_fields, cursor->n_bytes));
178 }
179
180 /** This function should be called before reserving any btr search mutex, if
181 the intended operation might add nodes to the search system hash table.
182 Because of the latching order, once we have reserved the btr search system
183 latch, we cannot allocate a free frame from the buffer pool. Checks that
184 there is a free buffer frame allocated for hash table heap in the btr search
185 system. If not, allocates a free frames for the heap. This check makes it
186 probable that, when have reserved the btr search system latch and we need to
187 allocate a new node to the hash table, it will succeed. However, the check
188 will not guarantee success.
189 @param[in] index index handler */
190 static
191 void
btr_search_check_free_space_in_heap(const dict_index_t * index)192 btr_search_check_free_space_in_heap(const dict_index_t* index)
193 {
194 /* Note that we peek the value of heap->free_block without reserving
195 the latch: this is ok, because we will not guarantee that there will
196 be enough free space in the hash table. */
197
198 buf_block_t* block = buf_block_alloc(NULL);
199 rw_lock_t* latch = btr_get_search_latch(index);
200 hash_table_t* table;
201 mem_heap_t* heap;
202
203 rw_lock_x_lock(latch);
204
205 if (!btr_search_enabled) {
206 goto func_exit;
207 }
208
209 table = btr_get_search_table(index);
210 heap = table->heap;
211
212 if (heap->free_block == NULL) {
213 heap->free_block = block;
214 } else {
215 func_exit:
216 buf_block_free(block);
217 }
218
219 rw_lock_x_unlock(latch);
220 }
221
222 /** Creates and initializes the adaptive search system at a database start.
223 @param[in] hash_size hash table size. */
btr_search_sys_create(ulint hash_size)224 void btr_search_sys_create(ulint hash_size)
225 {
226 /* Search System is divided into n parts.
227 Each part controls access to distinct set of hash buckets from
228 hash table through its own latch. */
229
230 /* Step-1: Allocate latches (1 per part). */
231 btr_search_latches = reinterpret_cast<rw_lock_t**>(
232 ut_malloc(sizeof(rw_lock_t*) * btr_ahi_parts, mem_key_ahi));
233
234 for (ulint i = 0; i < btr_ahi_parts; ++i) {
235
236 btr_search_latches[i] = reinterpret_cast<rw_lock_t*>(
237 ut_malloc(sizeof(rw_lock_t), mem_key_ahi));
238
239 rw_lock_create(btr_search_latch_key,
240 btr_search_latches[i], SYNC_SEARCH_SYS);
241 }
242
243 /* Step-2: Allocate hash tablees. */
244 btr_search_sys = reinterpret_cast<btr_search_sys_t*>(
245 ut_malloc(sizeof(btr_search_sys_t), mem_key_ahi));
246
247 btr_search_sys->hash_tables = NULL;
248
249 if (btr_search_enabled) {
250 btr_search_enable();
251 }
252 }
253
254 /** Frees the adaptive search system at a database shutdown. */
btr_search_sys_free()255 void btr_search_sys_free()
256 {
257 if (!btr_search_sys)
258 {
259 ut_ad(!btr_search_latches);
260 return;
261 }
262
263 ut_ad(btr_search_sys);
264 ut_ad(btr_search_latches);
265
266 if (btr_search_sys->hash_tables)
267 {
268 for (ulint i= 0; i < btr_ahi_parts; ++i)
269 {
270 mem_heap_free(btr_search_sys->hash_tables[i]->heap);
271 hash_table_free(btr_search_sys->hash_tables[i]);
272 }
273 ut_free(btr_search_sys->hash_tables);
274 }
275
276 ut_free(btr_search_sys);
277 btr_search_sys= NULL;
278
279 /* Free all latches. */
280 for (ulint i= 0; i < btr_ahi_parts; ++i)
281 {
282 rw_lock_free(btr_search_latches[i]);
283 ut_free(btr_search_latches[i]);
284 }
285
286 ut_free(btr_search_latches);
287 btr_search_latches= NULL;
288 }
289
290 /** Set index->ref_count = 0 on all indexes of a table.
291 @param[in,out] table table handler */
292 static
293 void
btr_search_disable_ref_count(dict_table_t * table)294 btr_search_disable_ref_count(
295 dict_table_t* table)
296 {
297 dict_index_t* index;
298
299 for (index = dict_table_get_first_index(table);
300 index != NULL;
301 index = dict_table_get_next_index(index)) {
302 index->search_info->ref_count = 0;
303 }
304 }
305
306 /** Lazily free detached metadata when removing the last reference. */
btr_search_lazy_free(dict_index_t * index)307 ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index)
308 {
309 ut_ad(index->freed());
310 dict_table_t *table= index->table;
311 mysql_mutex_lock(&table->autoinc_mutex);
312
313 /* Perform the skipped steps of dict_index_remove_from_cache_low(). */
314 UT_LIST_REMOVE(table->freed_indexes, index);
315 rw_lock_free(&index->lock);
316 dict_mem_index_free(index);
317
318 if (!UT_LIST_GET_LEN(table->freed_indexes) &&
319 !UT_LIST_GET_LEN(table->indexes))
320 {
321 ut_ad(!table->id);
322 mysql_mutex_unlock(&table->autoinc_mutex);
323 mysql_mutex_destroy(&table->autoinc_mutex);
324 dict_mem_table_free(table);
325 return;
326 }
327
328 mysql_mutex_unlock(&table->autoinc_mutex);
329 }
330
331 /** Clear the adaptive hash index on all pages in the buffer pool. */
buf_pool_clear_hash_index()332 static void buf_pool_clear_hash_index()
333 {
334 ut_ad(btr_search_own_all(RW_LOCK_X));
335 ut_ad(!btr_search_enabled);
336
337 std::set<dict_index_t*> garbage;
338
339 for (ulong p = 0; p < srv_buf_pool_instances; p++)
340 {
341 buf_pool_t *buf_pool= buf_pool_from_array(p);
342 buf_chunk_t *chunks= buf_pool->chunks;
343 buf_chunk_t *chunk= chunks + buf_pool->n_chunks;
344
345 while (--chunk >= chunks)
346 {
347 buf_block_t *block= chunk->blocks;
348 for (ulint i= chunk->size; i--; block++)
349 {
350 dict_index_t *index= block->index;
351 assert_block_ahi_valid(block);
352
353 /* We can clear block->index and block->n_pointers when
354 btr_search_own_all(RW_LOCK_X); see the comments in buf0buf.h */
355
356 if (!index)
357 {
358 # if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
359 ut_a(!block->n_pointers);
360 # endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
361 continue;
362 }
363
364 ut_d(buf_page_state state= buf_block_get_state(block));
365 /* Another thread may have set the state to
366 BUF_BLOCK_REMOVE_HASH in buf_LRU_block_remove_hashed().
367
368 The state change in buf_page_realloc() is not observable here,
369 because in that case we would have !block->index.
370
371 In the end, the entire adaptive hash index will be removed. */
372 ut_ad(state == BUF_BLOCK_FILE_PAGE || state == BUF_BLOCK_REMOVE_HASH);
373 # if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
374 block->n_pointers= 0;
375 # endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
376 if (index->freed())
377 garbage.insert(index);
378 block->index= NULL;
379 }
380 }
381 }
382
383 for (std::set<dict_index_t*>::iterator i= garbage.begin();
384 i != garbage.end(); i++)
385 btr_search_lazy_free(*i);
386 }
387
388 /** Disable the adaptive hash search system and empty the index. */
btr_search_disable()389 void btr_search_disable()
390 {
391 dict_table_t* table;
392
393 mutex_enter(&dict_sys->mutex);
394
395 btr_search_x_lock_all();
396
397 if (!btr_search_enabled) {
398 mutex_exit(&dict_sys->mutex);
399 btr_search_x_unlock_all();
400 return;
401 }
402
403 btr_search_enabled = false;
404
405 /* Clear the index->search_info->ref_count of every index in
406 the data dictionary cache. */
407 for (table = UT_LIST_GET_FIRST(dict_sys->table_LRU); table;
408 table = UT_LIST_GET_NEXT(table_LRU, table)) {
409
410 btr_search_disable_ref_count(table);
411 }
412
413 for (table = UT_LIST_GET_FIRST(dict_sys->table_non_LRU); table;
414 table = UT_LIST_GET_NEXT(table_LRU, table)) {
415
416 btr_search_disable_ref_count(table);
417 }
418
419 mutex_exit(&dict_sys->mutex);
420
421 /* Set all block->index = NULL. */
422 buf_pool_clear_hash_index();
423
424 /* Clear the adaptive hash index. */
425 for (ulint i = 0; i < btr_ahi_parts; ++i) {
426 mem_heap_free(btr_search_sys->hash_tables[i]->heap);
427 hash_table_free(btr_search_sys->hash_tables[i]);
428 }
429 ut_free(btr_search_sys->hash_tables);
430 btr_search_sys->hash_tables = NULL;
431
432 btr_search_x_unlock_all();
433 }
434
435 /** Enable the adaptive hash search system.
436 @param resize whether buf_pool_resize() is the caller */
btr_search_enable(bool resize)437 void btr_search_enable(bool resize)
438 {
439 if (!resize) {
440 buf_pool_mutex_enter_all();
441 if (srv_buf_pool_old_size != srv_buf_pool_size) {
442 buf_pool_mutex_exit_all();
443 return;
444 }
445 buf_pool_mutex_exit_all();
446 }
447
448 ulint hash_size = buf_pool_get_curr_size() / sizeof(void *) / 64;
449 btr_search_x_lock_all();
450
451 if (btr_search_sys->hash_tables) {
452 ut_ad(btr_search_enabled);
453 btr_search_x_unlock_all();
454 return;
455 }
456
457 btr_search_sys->hash_tables = reinterpret_cast<hash_table_t**>(
458 ut_malloc(sizeof(hash_table_t*) * btr_ahi_parts, mem_key_ahi));
459 for (ulint i = 0; i < btr_ahi_parts; ++i) {
460 btr_search_sys->hash_tables[i] =
461 ib_create((hash_size / btr_ahi_parts),
462 LATCH_ID_HASH_TABLE_MUTEX,
463 0, MEM_HEAP_FOR_BTR_SEARCH);
464 #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
465 btr_search_sys->hash_tables[i]->adaptive = TRUE;
466 #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
467 }
468
469 btr_search_enabled = true;
470 btr_search_x_unlock_all();
471 }
472
473 /** Updates the search info of an index about hash successes. NOTE that info
474 is NOT protected by any semaphore, to save CPU time! Do not assume its fields
475 are consistent.
476 @param[in,out] info search info
477 @param[in] cursor cursor which was just positioned */
478 static
479 void
btr_search_info_update_hash(btr_search_t * info,btr_cur_t * cursor)480 btr_search_info_update_hash(
481 btr_search_t* info,
482 btr_cur_t* cursor)
483 {
484 dict_index_t* index = cursor->index;
485 ulint n_unique;
486 int cmp;
487
488 ut_ad(!btr_search_own_any(RW_LOCK_S));
489 ut_ad(!btr_search_own_any(RW_LOCK_X));
490
491 if (dict_index_is_ibuf(index)) {
492 /* So many deletes are performed on an insert buffer tree
493 that we do not consider a hash index useful on it: */
494
495 return;
496 }
497
498 n_unique = dict_index_get_n_unique_in_tree(index);
499
500 if (info->n_hash_potential == 0) {
501
502 goto set_new_recomm;
503 }
504
505 /* Test if the search would have succeeded using the recommended
506 hash prefix */
507
508 if (info->n_fields >= n_unique && cursor->up_match >= n_unique) {
509 increment_potential:
510 info->n_hash_potential++;
511
512 return;
513 }
514
515 cmp = ut_pair_cmp(info->n_fields, info->n_bytes,
516 cursor->low_match, cursor->low_bytes);
517
518 if (info->left_side ? cmp <= 0 : cmp > 0) {
519
520 goto set_new_recomm;
521 }
522
523 cmp = ut_pair_cmp(info->n_fields, info->n_bytes,
524 cursor->up_match, cursor->up_bytes);
525
526 if (info->left_side ? cmp <= 0 : cmp > 0) {
527
528 goto increment_potential;
529 }
530
531 set_new_recomm:
532 /* We have to set a new recommendation; skip the hash analysis
533 for a while to avoid unnecessary CPU time usage when there is no
534 chance for success */
535
536 info->hash_analysis = 0;
537
538 cmp = ut_pair_cmp(cursor->up_match, cursor->up_bytes,
539 cursor->low_match, cursor->low_bytes);
540 if (cmp == 0) {
541 info->n_hash_potential = 0;
542
543 /* For extra safety, we set some sensible values here */
544
545 info->n_fields = 1;
546 info->n_bytes = 0;
547
548 info->left_side = TRUE;
549
550 } else if (cmp > 0) {
551 info->n_hash_potential = 1;
552
553 if (cursor->up_match >= n_unique) {
554
555 info->n_fields = n_unique;
556 info->n_bytes = 0;
557
558 } else if (cursor->low_match < cursor->up_match) {
559
560 info->n_fields = cursor->low_match + 1;
561 info->n_bytes = 0;
562 } else {
563 info->n_fields = cursor->low_match;
564 info->n_bytes = cursor->low_bytes + 1;
565 }
566
567 info->left_side = TRUE;
568 } else {
569 info->n_hash_potential = 1;
570
571 if (cursor->low_match >= n_unique) {
572
573 info->n_fields = n_unique;
574 info->n_bytes = 0;
575 } else if (cursor->low_match > cursor->up_match) {
576
577 info->n_fields = cursor->up_match + 1;
578 info->n_bytes = 0;
579 } else {
580 info->n_fields = cursor->up_match;
581 info->n_bytes = cursor->up_bytes + 1;
582 }
583
584 info->left_side = FALSE;
585 }
586 }
587
588 /** Update the block search info on hash successes. NOTE that info and
589 block->n_hash_helps, n_fields, n_bytes, left_side are NOT protected by any
590 semaphore, to save CPU time! Do not assume the fields are consistent.
591 @return TRUE if building a (new) hash index on the block is recommended
592 @param[in,out] info search info
593 @param[in,out] block buffer block */
594 static
595 bool
btr_search_update_block_hash_info(btr_search_t * info,buf_block_t * block)596 btr_search_update_block_hash_info(btr_search_t* info, buf_block_t* block)
597 {
598 ut_ad(!btr_search_own_any());
599 ut_ad(rw_lock_own_flagged(&block->lock,
600 RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
601
602 info->last_hash_succ = FALSE;
603
604 ut_a(buf_block_state_valid(block));
605 ut_ad(info->magic_n == BTR_SEARCH_MAGIC_N);
606
607 if ((block->n_hash_helps > 0)
608 && (info->n_hash_potential > 0)
609 && (block->n_fields == info->n_fields)
610 && (block->n_bytes == info->n_bytes)
611 && (block->left_side == info->left_side)) {
612
613 if ((block->index)
614 && (block->curr_n_fields == info->n_fields)
615 && (block->curr_n_bytes == info->n_bytes)
616 && (block->curr_left_side == info->left_side)) {
617
618 /* The search would presumably have succeeded using
619 the hash index */
620
621 info->last_hash_succ = TRUE;
622 }
623
624 block->n_hash_helps++;
625 } else {
626 block->n_hash_helps = 1;
627 block->n_fields = info->n_fields;
628 block->n_bytes = info->n_bytes;
629 block->left_side = info->left_side;
630 }
631
632 if ((block->n_hash_helps > page_get_n_recs(block->frame)
633 / BTR_SEARCH_PAGE_BUILD_LIMIT)
634 && (info->n_hash_potential >= BTR_SEARCH_BUILD_LIMIT)) {
635
636 if ((!block->index)
637 || (block->n_hash_helps
638 > 2U * page_get_n_recs(block->frame))
639 || (block->n_fields != block->curr_n_fields)
640 || (block->n_bytes != block->curr_n_bytes)
641 || (block->left_side != block->curr_left_side)) {
642
643 /* Build a new hash index on the page */
644
645 return(true);
646 }
647 }
648
649 return(false);
650 }
651
652 /** Updates a hash node reference when it has been unsuccessfully used in a
653 search which could have succeeded with the used hash parameters. This can
654 happen because when building a hash index for a page, we do not check
655 what happens at page boundaries, and therefore there can be misleading
656 hash nodes. Also, collisions in the fold value can lead to misleading
657 references. This function lazily fixes these imperfections in the hash
658 index.
659 @param[in] info search info
660 @param[in] block buffer block where cursor positioned
661 @param[in] cursor cursor */
662 static
663 void
btr_search_update_hash_ref(const btr_search_t * info,buf_block_t * block,const btr_cur_t * cursor)664 btr_search_update_hash_ref(
665 const btr_search_t* info,
666 buf_block_t* block,
667 const btr_cur_t* cursor)
668 {
669 ut_ad(cursor->flag == BTR_CUR_HASH_FAIL);
670
671 ut_ad(rw_lock_own_flagged(&block->lock,
672 RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
673 ut_ad(page_align(btr_cur_get_rec(cursor)) == block->frame);
674 ut_ad(page_is_leaf(block->frame));
675 assert_block_ahi_valid(block);
676
677 dict_index_t* index = block->index;
678
679 if (!index || !info->n_hash_potential) {
680 return;
681 }
682
683 if (cursor->index != index) {
684 ut_ad(cursor->index->id == index->id);
685 btr_search_drop_page_hash_index(block);
686 return;
687 }
688
689 ut_ad(block->page.id.space() == index->table->space_id);
690 ut_ad(index == cursor->index);
691 ut_ad(!dict_index_is_ibuf(index));
692 rw_lock_t* const latch = btr_get_search_latch(index);
693 rw_lock_x_lock(latch);
694 ut_ad(!block->index || block->index == index);
695
696 if (block->index
697 && (block->curr_n_fields == info->n_fields)
698 && (block->curr_n_bytes == info->n_bytes)
699 && (block->curr_left_side == info->left_side)
700 && btr_search_enabled) {
701 mem_heap_t* heap = NULL;
702 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
703 rec_offs_init(offsets_);
704
705 const rec_t* rec = btr_cur_get_rec(cursor);
706
707 if (!page_rec_is_user_rec(rec)) {
708 goto func_exit;
709 }
710
711 ulint fold = rec_fold(
712 rec,
713 rec_get_offsets(rec, index, offsets_,
714 index->n_core_fields,
715 ULINT_UNDEFINED, &heap),
716 block->curr_n_fields,
717 block->curr_n_bytes, index->id);
718 if (UNIV_LIKELY_NULL(heap)) {
719 mem_heap_free(heap);
720 }
721
722 ha_insert_for_fold(btr_get_search_table(index), fold,
723 block, rec);
724
725 MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_ADDED);
726 }
727
728 func_exit:
729 rw_lock_x_unlock(latch);
730 }
731
732 /** Checks if a guessed position for a tree cursor is right. Note that if
733 mode is PAGE_CUR_LE, which is used in inserts, and the function returns
734 TRUE, then cursor->up_match and cursor->low_match both have sensible values.
735 @param[in,out] cursor guess cursor position
736 @param[in] can_only_compare_to_cursor_rec
737 if we do not have a latch on the page of cursor,
738 but a latch corresponding search system, then
739 ONLY the columns of the record UNDER the cursor
740 are protected, not the next or previous record
741 in the chain: we cannot look at the next or
742 previous record to check our guess!
743 @param[in] tuple data tuple
744 @param[in] mode PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, PAGE_CUR_GE
745 @return whether a match was found */
746 static
747 bool
btr_search_check_guess(btr_cur_t * cursor,bool can_only_compare_to_cursor_rec,const dtuple_t * tuple,ulint mode)748 btr_search_check_guess(
749 btr_cur_t* cursor,
750 bool can_only_compare_to_cursor_rec,
751 const dtuple_t* tuple,
752 ulint mode)
753 {
754 rec_t* rec;
755 ulint n_unique;
756 ulint match;
757 int cmp;
758 mem_heap_t* heap = NULL;
759 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
760 rec_offs* offsets = offsets_;
761 ibool success = FALSE;
762 rec_offs_init(offsets_);
763
764 n_unique = dict_index_get_n_unique_in_tree(cursor->index);
765
766 rec = btr_cur_get_rec(cursor);
767
768 ut_ad(page_rec_is_user_rec(rec));
769 ut_ad(page_rec_is_leaf(rec));
770
771 match = 0;
772
773 offsets = rec_get_offsets(rec, cursor->index, offsets,
774 cursor->index->n_core_fields,
775 n_unique, &heap);
776 cmp = cmp_dtuple_rec_with_match(tuple, rec, offsets, &match);
777
778 if (mode == PAGE_CUR_GE) {
779 if (cmp > 0) {
780 goto exit_func;
781 }
782
783 cursor->up_match = match;
784
785 if (match >= n_unique) {
786 success = TRUE;
787 goto exit_func;
788 }
789 } else if (mode == PAGE_CUR_LE) {
790 if (cmp < 0) {
791 goto exit_func;
792 }
793
794 cursor->low_match = match;
795
796 } else if (mode == PAGE_CUR_G) {
797 if (cmp >= 0) {
798 goto exit_func;
799 }
800 } else if (mode == PAGE_CUR_L) {
801 if (cmp <= 0) {
802 goto exit_func;
803 }
804 }
805
806 if (can_only_compare_to_cursor_rec) {
807 /* Since we could not determine if our guess is right just by
808 looking at the record under the cursor, return FALSE */
809 goto exit_func;
810 }
811
812 match = 0;
813
814 if ((mode == PAGE_CUR_G) || (mode == PAGE_CUR_GE)) {
815 ut_ad(!page_rec_is_infimum(rec));
816
817 const rec_t* prev_rec = page_rec_get_prev(rec);
818
819 if (page_rec_is_infimum(prev_rec)) {
820 success = !page_has_prev(page_align(prev_rec));
821 goto exit_func;
822 }
823
824 offsets = rec_get_offsets(prev_rec, cursor->index, offsets,
825 cursor->index->n_core_fields,
826 n_unique, &heap);
827 cmp = cmp_dtuple_rec_with_match(
828 tuple, prev_rec, offsets, &match);
829 if (mode == PAGE_CUR_GE) {
830 success = cmp > 0;
831 } else {
832 success = cmp >= 0;
833 }
834 } else {
835 ut_ad(!page_rec_is_supremum(rec));
836
837 const rec_t* next_rec = page_rec_get_next(rec);
838
839 if (page_rec_is_supremum(next_rec)) {
840 if (!page_has_next(page_align(next_rec))) {
841 cursor->up_match = 0;
842 success = TRUE;
843 }
844
845 goto exit_func;
846 }
847
848 offsets = rec_get_offsets(next_rec, cursor->index, offsets,
849 cursor->index->n_core_fields,
850 n_unique, &heap);
851 cmp = cmp_dtuple_rec_with_match(
852 tuple, next_rec, offsets, &match);
853 if (mode == PAGE_CUR_LE) {
854 success = cmp < 0;
855 cursor->up_match = match;
856 } else {
857 success = cmp <= 0;
858 }
859 }
860 exit_func:
861 if (UNIV_LIKELY_NULL(heap)) {
862 mem_heap_free(heap);
863 }
864 return(success);
865 }
866
867 static
868 void
btr_search_failure(btr_search_t * info,btr_cur_t * cursor)869 btr_search_failure(btr_search_t* info, btr_cur_t* cursor)
870 {
871 cursor->flag = BTR_CUR_HASH_FAIL;
872
873 #ifdef UNIV_SEARCH_PERF_STAT
874 ++info->n_hash_fail;
875
876 if (info->n_hash_succ > 0) {
877 --info->n_hash_succ;
878 }
879 #endif /* UNIV_SEARCH_PERF_STAT */
880
881 info->last_hash_succ = FALSE;
882 }
883
884 /** Tries to guess the right search position based on the hash search info
885 of the index. Note that if mode is PAGE_CUR_LE, which is used in inserts,
886 and the function returns TRUE, then cursor->up_match and cursor->low_match
887 both have sensible values.
888 @param[in,out] index index
889 @param[in,out] info index search info
890 @param[in] tuple logical record
891 @param[in] mode PAGE_CUR_L, ....
892 @param[in] latch_mode BTR_SEARCH_LEAF, ...;
893 NOTE that only if has_search_latch is 0, we will
894 have a latch set on the cursor page, otherwise
895 we assume the caller uses his search latch
896 to protect the record!
897 @param[out] cursor tree cursor
898 @param[in] ahi_latch the adaptive hash index latch being held,
899 or NULL
900 @param[in] mtr mini transaction
901 @return whether the search succeeded */
902 bool
btr_search_guess_on_hash(dict_index_t * index,btr_search_t * info,const dtuple_t * tuple,ulint mode,ulint latch_mode,btr_cur_t * cursor,rw_lock_t * ahi_latch,mtr_t * mtr)903 btr_search_guess_on_hash(
904 dict_index_t* index,
905 btr_search_t* info,
906 const dtuple_t* tuple,
907 ulint mode,
908 ulint latch_mode,
909 btr_cur_t* cursor,
910 rw_lock_t* ahi_latch,
911 mtr_t* mtr)
912 {
913 ulint fold;
914 index_id_t index_id;
915 #ifdef notdefined
916 btr_cur_t cursor2;
917 btr_pcur_t pcur;
918 #endif
919 ut_ad(!ahi_latch || rw_lock_own_flagged(
920 ahi_latch, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
921
922 if (!btr_search_enabled) {
923 return false;
924 }
925
926 ut_ad(index && info && tuple && cursor && mtr);
927 ut_ad(!dict_index_is_ibuf(index));
928 ut_ad(!ahi_latch || ahi_latch == btr_get_search_latch(index));
929 ut_ad((latch_mode == BTR_SEARCH_LEAF)
930 || (latch_mode == BTR_MODIFY_LEAF));
931
932 /* Not supported for spatial index */
933 ut_ad(!dict_index_is_spatial(index));
934
935 /* Note that, for efficiency, the struct info may not be protected by
936 any latch here! */
937
938 if (info->n_hash_potential == 0) {
939 return false;
940 }
941
942 cursor->n_fields = info->n_fields;
943 cursor->n_bytes = info->n_bytes;
944
945 if (dtuple_get_n_fields(tuple) < btr_search_get_n_fields(cursor)) {
946 return false;
947 }
948
949 index_id = index->id;
950
951 #ifdef UNIV_SEARCH_PERF_STAT
952 info->n_hash_succ++;
953 #endif
954 fold = dtuple_fold(tuple, cursor->n_fields, cursor->n_bytes, index_id);
955
956 cursor->fold = fold;
957 cursor->flag = BTR_CUR_HASH;
958
959 rw_lock_t* use_latch = ahi_latch ? NULL : btr_get_search_latch(index);
960 const rec_t* rec;
961
962 if (use_latch) {
963 rw_lock_s_lock(use_latch);
964
965 if (!btr_search_enabled) {
966 goto fail;
967 }
968 } else {
969 ut_ad(btr_search_enabled);
970 ut_ad(rw_lock_own(ahi_latch, RW_LOCK_S));
971 }
972
973 rec = static_cast<const rec_t*>(
974 ha_search_and_get_data(btr_get_search_table(index), fold));
975
976 if (!rec) {
977 if (use_latch) {
978 fail:
979 rw_lock_s_unlock(use_latch);
980 }
981
982 btr_search_failure(info, cursor);
983 return false;
984 }
985
986 buf_block_t* block = buf_block_from_ahi(rec);
987
988 if (use_latch) {
989 if (!buf_page_get_known_nowait(
990 latch_mode, block, BUF_MAKE_YOUNG,
991 __FILE__, __LINE__, mtr)) {
992 goto fail;
993 }
994
995 const bool fail = index != block->index
996 && index_id == block->index->id;
997 ut_a(!fail || block->index->freed());
998 rw_lock_s_unlock(use_latch);
999
1000 buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH);
1001 if (UNIV_UNLIKELY(fail)) {
1002 goto fail_and_release_page;
1003 }
1004 } else if (UNIV_UNLIKELY(index != block->index
1005 && index_id == block->index->id)) {
1006 ut_a(block->index->freed());
1007 goto fail_and_release_page;
1008 }
1009
1010 if (buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) {
1011
1012 ut_ad(buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH);
1013
1014 fail_and_release_page:
1015 if (!ahi_latch) {
1016 btr_leaf_page_release(block, latch_mode, mtr);
1017 }
1018
1019 btr_search_failure(info, cursor);
1020 return false;
1021 }
1022
1023 ut_ad(page_rec_is_user_rec(rec));
1024
1025 btr_cur_position(index, (rec_t*) rec, block, cursor);
1026
1027 /* Check the validity of the guess within the page */
1028
1029 /* If we only have the latch on search system, not on the
1030 page, it only protects the columns of the record the cursor
1031 is positioned on. We cannot look at the next of the previous
1032 record to determine if our guess for the cursor position is
1033 right. */
1034 if (index_id != btr_page_get_index_id(block->frame)
1035 || !btr_search_check_guess(cursor, !!ahi_latch, tuple, mode)) {
1036 goto fail_and_release_page;
1037 }
1038
1039 if (info->n_hash_potential < BTR_SEARCH_BUILD_LIMIT + 5) {
1040
1041 info->n_hash_potential++;
1042 }
1043
1044 #ifdef notdefined
1045 /* These lines of code can be used in a debug version to check
1046 the correctness of the searched cursor position: */
1047
1048 info->last_hash_succ = FALSE;
1049
1050 /* Currently, does not work if the following fails: */
1051 ut_ad(!ahi_latch);
1052
1053 btr_leaf_page_release(block, latch_mode, mtr);
1054
1055 btr_cur_search_to_nth_level(
1056 index, 0, tuple, mode, latch_mode, &cursor2, 0, mtr);
1057
1058 if (mode == PAGE_CUR_GE
1059 && page_rec_is_supremum(btr_cur_get_rec(&cursor2))) {
1060
1061 /* If mode is PAGE_CUR_GE, then the binary search
1062 in the index tree may actually take us to the supremum
1063 of the previous page */
1064
1065 info->last_hash_succ = FALSE;
1066
1067 btr_pcur_open_on_user_rec(
1068 index, tuple, mode, latch_mode, &pcur, mtr);
1069
1070 ut_ad(btr_pcur_get_rec(&pcur) == btr_cur_get_rec(cursor));
1071 } else {
1072 ut_ad(btr_cur_get_rec(&cursor2) == btr_cur_get_rec(cursor));
1073 }
1074
1075 /* NOTE that it is theoretically possible that the above assertions
1076 fail if the page of the cursor gets removed from the buffer pool
1077 meanwhile! Thus it might not be a bug. */
1078 #endif
1079 info->last_hash_succ = TRUE;
1080
1081 #ifdef UNIV_SEARCH_PERF_STAT
1082 btr_search_n_succ++;
1083 #endif
1084 if (!ahi_latch && buf_page_peek_if_too_old(&block->page)) {
1085
1086 buf_page_make_young(&block->page);
1087 }
1088
1089 /* Increment the page get statistics though we did not really
1090 fix the page: for user info only */
1091 {
1092 buf_pool_t* buf_pool = buf_pool_from_bpage(&block->page);
1093
1094 ++buf_pool->stat.n_page_gets;
1095 }
1096
1097 return true;
1098 }
1099
1100 /** Drop any adaptive hash index entries that point to an index page.
1101 @param[in,out] block block containing index page, s- or x-latched, or an
1102 index page for which we know that
1103 block->buf_fix_count == 0 or it is an index page which
1104 has already been removed from the buf_pool->page_hash
1105 i.e.: it is in state BUF_BLOCK_REMOVE_HASH */
btr_search_drop_page_hash_index(buf_block_t * block)1106 void btr_search_drop_page_hash_index(buf_block_t* block)
1107 {
1108 ulint n_fields;
1109 ulint n_bytes;
1110 const page_t* page;
1111 const rec_t* rec;
1112 ulint fold;
1113 ulint prev_fold;
1114 ulint n_cached;
1115 ulint n_recs;
1116 ulint* folds;
1117 ulint i;
1118 mem_heap_t* heap;
1119 rec_offs* offsets;
1120 rw_lock_t* latch;
1121
1122 retry:
1123 /* This debug check uses a dirty read that could theoretically cause
1124 false positives while buf_pool_clear_hash_index() is executing. */
1125 assert_block_ahi_valid(block);
1126 ut_ad(!btr_search_own_any(RW_LOCK_S));
1127 ut_ad(!btr_search_own_any(RW_LOCK_X));
1128
1129 if (!block->index) {
1130 return;
1131 }
1132
1133 ut_ad(block->page.buf_fix_count == 0
1134 || buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH
1135 || rw_lock_own_flagged(&block->lock,
1136 RW_LOCK_FLAG_X | RW_LOCK_FLAG_S
1137 | RW_LOCK_FLAG_SX));
1138 ut_ad(page_is_leaf(block->frame));
1139
1140 /* We must not dereference block->index here, because it could be freed
1141 if (index->table->n_ref_count == 0 && !mutex_own(&dict_sys->mutex)).
1142 Determine the ahi_slot based on the block contents. */
1143
1144 const index_id_t index_id
1145 = btr_page_get_index_id(block->frame);
1146 const ulint ahi_slot
1147 = ut_fold_ulint_pair(static_cast<ulint>(index_id),
1148 static_cast<ulint>(block->page.id.space()))
1149 % btr_ahi_parts;
1150 latch = btr_search_latches[ahi_slot];
1151
1152 dict_index_t* index = block->index;
1153
1154 bool is_freed = index && index->freed();
1155 if (is_freed) {
1156 rw_lock_x_lock(latch);
1157 } else {
1158 rw_lock_s_lock(latch);
1159 }
1160
1161 assert_block_ahi_valid(block);
1162
1163 if (!index || !btr_search_enabled) {
1164 if (is_freed) {
1165 rw_lock_x_unlock(latch);
1166 } else {
1167 rw_lock_s_unlock(latch);
1168 }
1169 return;
1170 }
1171
1172 #ifdef MYSQL_INDEX_DISABLE_AHI
1173 ut_ad(!index->disable_ahi);
1174 #endif
1175 ut_ad(btr_search_enabled);
1176
1177 ut_ad(block->page.id.space() == index->table->space_id);
1178 ut_a(index_id == index->id);
1179 ut_ad(!dict_index_is_ibuf(index));
1180
1181 n_fields = block->curr_n_fields;
1182 n_bytes = block->curr_n_bytes;
1183
1184 /* NOTE: The AHI fields of block must not be accessed after
1185 releasing search latch, as the index page might only be s-latched! */
1186
1187 if (!is_freed) {
1188 rw_lock_s_unlock(latch);
1189 }
1190
1191 ut_a(n_fields > 0 || n_bytes > 0);
1192
1193 page = block->frame;
1194 n_recs = page_get_n_recs(page);
1195
1196 /* Calculate and cache fold values into an array for fast deletion
1197 from the hash index */
1198
1199 folds = (ulint*) ut_malloc_nokey(n_recs * sizeof(ulint));
1200
1201 n_cached = 0;
1202
1203 rec = page_get_infimum_rec(page);
1204 rec = page_rec_get_next_low(rec, page_is_comp(page));
1205 if (rec_is_metadata(rec, index)) {
1206 rec = page_rec_get_next_low(rec, page_is_comp(page));
1207 }
1208
1209 prev_fold = 0;
1210
1211 heap = NULL;
1212 offsets = NULL;
1213
1214 while (!page_rec_is_supremum(rec)) {
1215 offsets = rec_get_offsets(
1216 rec, index, offsets, index->n_core_fields,
1217 btr_search_get_n_fields(n_fields, n_bytes),
1218 &heap);
1219 fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
1220
1221 if (fold == prev_fold && prev_fold != 0) {
1222
1223 goto next_rec;
1224 }
1225
1226 /* Remove all hash nodes pointing to this page from the
1227 hash chain */
1228
1229 folds[n_cached] = fold;
1230 n_cached++;
1231 next_rec:
1232 rec = page_rec_get_next_low(rec, page_rec_is_comp(rec));
1233 prev_fold = fold;
1234 }
1235
1236 if (UNIV_LIKELY_NULL(heap)) {
1237 mem_heap_free(heap);
1238 }
1239
1240 if (!is_freed) {
1241 rw_lock_x_lock(latch);
1242
1243 if (UNIV_UNLIKELY(!block->index)) {
1244 /* Someone else has meanwhile dropped the
1245 hash index */
1246 goto cleanup;
1247 }
1248
1249 ut_a(block->index == index);
1250 }
1251
1252 if (block->curr_n_fields != n_fields
1253 || block->curr_n_bytes != n_bytes) {
1254
1255 /* Someone else has meanwhile built a new hash index on the
1256 page, with different parameters */
1257
1258 rw_lock_x_unlock(latch);
1259
1260 ut_free(folds);
1261 goto retry;
1262 }
1263
1264 for (i = 0; i < n_cached; i++) {
1265
1266 ha_remove_all_nodes_to_page(
1267 btr_search_sys->hash_tables[ahi_slot],
1268 folds[i], page);
1269 }
1270
1271 switch (index->search_info->ref_count--) {
1272 case 0:
1273 ut_error;
1274 case 1:
1275 if (index->freed()) {
1276 btr_search_lazy_free(index);
1277 }
1278 }
1279
1280 block->index = NULL;
1281
1282 MONITOR_INC(MONITOR_ADAPTIVE_HASH_PAGE_REMOVED);
1283 MONITOR_INC_VALUE(MONITOR_ADAPTIVE_HASH_ROW_REMOVED, n_cached);
1284
1285 cleanup:
1286 assert_block_ahi_valid(block);
1287 rw_lock_x_unlock(latch);
1288
1289 ut_free(folds);
1290 }
1291
1292 /** Drop possible adaptive hash index entries when a page is evicted
1293 from the buffer pool or freed in a file, or the index is being dropped.
1294 @param[in] page_id page id */
btr_search_drop_page_hash_when_freed(const page_id_t page_id)1295 void btr_search_drop_page_hash_when_freed(const page_id_t page_id)
1296 {
1297 buf_block_t* block;
1298 mtr_t mtr;
1299 dberr_t err = DB_SUCCESS;
1300
1301 mtr_start(&mtr);
1302
1303 /* If the caller has a latch on the page, then the caller must
1304 have a x-latch on the page and it must have already dropped
1305 the hash index for the page. Because of the x-latch that we
1306 are possibly holding, we cannot s-latch the page, but must
1307 (recursively) x-latch it, even though we are only reading. */
1308
1309 block = buf_page_get_gen(page_id, univ_page_size, RW_X_LATCH, NULL,
1310 BUF_PEEK_IF_IN_POOL, __FILE__, __LINE__,
1311 &mtr, &err);
1312
1313 if (block) {
1314
1315 /* If AHI is still valid, page can't be in free state.
1316 AHI is dropped when page is freed. */
1317 ut_ad(!block->page.file_page_was_freed);
1318
1319 buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH);
1320
1321 dict_index_t* index = block->index;
1322 if (index != NULL) {
1323 /* In all our callers, the table handle should
1324 be open, or we should be in the process of
1325 dropping the table (preventing eviction). */
1326 ut_ad(index->table->get_ref_count() > 0
1327 || mutex_own(&dict_sys->mutex));
1328 btr_search_drop_page_hash_index(block);
1329 }
1330 }
1331
1332 mtr_commit(&mtr);
1333 }
1334
1335 /** Build a hash index on a page with the given parameters. If the page already
1336 has a hash index with different parameters, the old hash index is removed.
1337 If index is non-NULL, this function checks if n_fields and n_bytes are
1338 sensible, and does not build a hash index if not.
1339 @param[in,out] index index for which to build.
1340 @param[in,out] block index page, s-/x- latched.
1341 @param[in,out] ahi_latch the adaptive search latch
1342 @param[in] n_fields hash this many full fields
1343 @param[in] n_bytes hash this many bytes of the next field
1344 @param[in] left_side hash for searches from left side */
1345 static
1346 void
btr_search_build_page_hash_index(dict_index_t * index,buf_block_t * block,rw_lock_t * ahi_latch,ulint n_fields,ulint n_bytes,ibool left_side)1347 btr_search_build_page_hash_index(
1348 dict_index_t* index,
1349 buf_block_t* block,
1350 rw_lock_t* ahi_latch,
1351 ulint n_fields,
1352 ulint n_bytes,
1353 ibool left_side)
1354 {
1355 const rec_t* rec;
1356 const rec_t* next_rec;
1357 ulint fold;
1358 ulint next_fold;
1359 ulint n_cached;
1360 ulint n_recs;
1361 ulint* folds;
1362 const rec_t** recs;
1363 mem_heap_t* heap = NULL;
1364 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
1365 rec_offs* offsets = offsets_;
1366
1367 #ifdef MYSQL_INDEX_DISABLE_AHI
1368 if (index->disable_ahi) return;
1369 #endif
1370 if (!btr_search_enabled) {
1371 return;
1372 }
1373
1374 rec_offs_init(offsets_);
1375 ut_ad(ahi_latch == btr_get_search_latch(index));
1376 ut_ad(index);
1377 ut_ad(block->page.id.space() == index->table->space_id);
1378 ut_ad(!dict_index_is_ibuf(index));
1379 ut_ad(page_is_leaf(block->frame));
1380
1381 ut_ad(rw_lock_own_flagged(&block->lock,
1382 RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
1383 ut_ad(block->page.id.page_no() >= 3);
1384
1385 rw_lock_s_lock(ahi_latch);
1386
1387 const bool enabled = btr_search_enabled;
1388 const bool rebuild = enabled && block->index
1389 && (block->curr_n_fields != n_fields
1390 || block->curr_n_bytes != n_bytes
1391 || block->curr_left_side != left_side);
1392
1393 rw_lock_s_unlock(ahi_latch);
1394
1395 if (!enabled) {
1396 return;
1397 }
1398
1399 if (rebuild) {
1400 btr_search_drop_page_hash_index(block);
1401 }
1402
1403 /* Check that the values for hash index build are sensible */
1404
1405 if (n_fields == 0 && n_bytes == 0) {
1406
1407 return;
1408 }
1409
1410 if (dict_index_get_n_unique_in_tree(index)
1411 < btr_search_get_n_fields(n_fields, n_bytes)) {
1412 return;
1413 }
1414
1415 page_t* page = buf_block_get_frame(block);
1416 n_recs = page_get_n_recs(page);
1417
1418 if (n_recs == 0) {
1419
1420 return;
1421 }
1422
1423 rec = page_rec_get_next_const(page_get_infimum_rec(page));
1424
1425 if (rec_is_metadata(rec, index)) {
1426 rec = page_rec_get_next_const(rec);
1427 if (!--n_recs) return;
1428 }
1429
1430 /* Calculate and cache fold values and corresponding records into
1431 an array for fast insertion to the hash index */
1432
1433 folds = static_cast<ulint*>(ut_malloc_nokey(n_recs * sizeof *folds));
1434 recs = static_cast<const rec_t**>(
1435 ut_malloc_nokey(n_recs * sizeof *recs));
1436
1437 n_cached = 0;
1438
1439 ut_a(index->id == btr_page_get_index_id(page));
1440
1441 offsets = rec_get_offsets(
1442 rec, index, offsets, index->n_core_fields,
1443 btr_search_get_n_fields(n_fields, n_bytes),
1444 &heap);
1445 ut_ad(page_rec_is_supremum(rec)
1446 || n_fields + (n_bytes > 0) == rec_offs_n_fields(offsets));
1447
1448 fold = rec_fold(rec, offsets, n_fields, n_bytes, index->id);
1449
1450 if (left_side) {
1451
1452 folds[n_cached] = fold;
1453 recs[n_cached] = rec;
1454 n_cached++;
1455 }
1456
1457 for (;;) {
1458 next_rec = page_rec_get_next_const(rec);
1459
1460 if (page_rec_is_supremum(next_rec)) {
1461
1462 if (!left_side) {
1463
1464 folds[n_cached] = fold;
1465 recs[n_cached] = rec;
1466 n_cached++;
1467 }
1468
1469 break;
1470 }
1471
1472 offsets = rec_get_offsets(
1473 next_rec, index, offsets, index->n_core_fields,
1474 btr_search_get_n_fields(n_fields, n_bytes), &heap);
1475 next_fold = rec_fold(next_rec, offsets, n_fields,
1476 n_bytes, index->id);
1477
1478 if (fold != next_fold) {
1479 /* Insert an entry into the hash index */
1480
1481 if (left_side) {
1482
1483 folds[n_cached] = next_fold;
1484 recs[n_cached] = next_rec;
1485 n_cached++;
1486 } else {
1487 folds[n_cached] = fold;
1488 recs[n_cached] = rec;
1489 n_cached++;
1490 }
1491 }
1492
1493 rec = next_rec;
1494 fold = next_fold;
1495 }
1496
1497 btr_search_check_free_space_in_heap(index);
1498
1499 rw_lock_x_lock(ahi_latch);
1500
1501 if (!btr_search_enabled) {
1502 goto exit_func;
1503 }
1504
1505 /* This counter is decremented every time we drop page
1506 hash index entries and is incremented here. Since we can
1507 rebuild hash index for a page that is already hashed, we
1508 have to take care not to increment the counter in that
1509 case. */
1510 if (!block->index) {
1511 assert_block_ahi_empty(block);
1512 index->search_info->ref_count++;
1513 } else if (block->curr_n_fields != n_fields
1514 || block->curr_n_bytes != n_bytes
1515 || block->curr_left_side != left_side) {
1516 goto exit_func;
1517 }
1518
1519 block->n_hash_helps = 0;
1520
1521 block->curr_n_fields = unsigned(n_fields);
1522 block->curr_n_bytes = unsigned(n_bytes);
1523 block->curr_left_side = unsigned(left_side);
1524 block->index = index;
1525
1526 {
1527 hash_table_t* table = btr_get_search_table(index);
1528 for (ulint i = 0; i < n_cached; i++) {
1529 ha_insert_for_fold(table, folds[i], block, recs[i]);
1530 }
1531 }
1532
1533 MONITOR_INC(MONITOR_ADAPTIVE_HASH_PAGE_ADDED);
1534 MONITOR_INC_VALUE(MONITOR_ADAPTIVE_HASH_ROW_ADDED, n_cached);
1535 exit_func:
1536 assert_block_ahi_valid(block);
1537 rw_lock_x_unlock(ahi_latch);
1538
1539 ut_free(folds);
1540 ut_free(recs);
1541 if (UNIV_LIKELY_NULL(heap)) {
1542 mem_heap_free(heap);
1543 }
1544 }
1545
1546 /** Updates the search info.
1547 @param[in,out] info search info
1548 @param[in,out] cursor cursor which was just positioned */
1549 void
btr_search_info_update_slow(btr_search_t * info,btr_cur_t * cursor)1550 btr_search_info_update_slow(btr_search_t* info, btr_cur_t* cursor)
1551 {
1552 rw_lock_t* ahi_latch = btr_get_search_latch(cursor->index);
1553
1554 ut_ad(!rw_lock_own_flagged(ahi_latch,
1555 RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
1556
1557 buf_block_t* block = btr_cur_get_block(cursor);
1558
1559 /* NOTE that the following two function calls do NOT protect
1560 info or block->n_fields etc. with any semaphore, to save CPU time!
1561 We cannot assume the fields are consistent when we return from
1562 those functions! */
1563
1564 btr_search_info_update_hash(info, cursor);
1565
1566 bool build_index = btr_search_update_block_hash_info(info, block);
1567
1568 if (build_index || (cursor->flag == BTR_CUR_HASH_FAIL)) {
1569
1570 btr_search_check_free_space_in_heap(cursor->index);
1571 }
1572
1573 if (cursor->flag == BTR_CUR_HASH_FAIL) {
1574 /* Update the hash node reference, if appropriate */
1575
1576 #ifdef UNIV_SEARCH_PERF_STAT
1577 btr_search_n_hash_fail++;
1578 #endif /* UNIV_SEARCH_PERF_STAT */
1579
1580 btr_search_update_hash_ref(info, block, cursor);
1581 }
1582
1583 if (build_index) {
1584 /* Note that since we did not protect block->n_fields etc.
1585 with any semaphore, the values can be inconsistent. We have
1586 to check inside the function call that they make sense. */
1587 btr_search_build_page_hash_index(cursor->index, block,
1588 ahi_latch,
1589 block->n_fields,
1590 block->n_bytes,
1591 block->left_side);
1592 }
1593 }
1594
1595 /** Move or delete hash entries for moved records, usually in a page split.
1596 If new_block is already hashed, then any hash index for block is dropped.
1597 If new_block is not hashed, and block is hashed, then a new hash index is
1598 built to new_block with the same parameters as block.
1599 @param[in,out] new_block destination page
1600 @param[in,out] block source page (subject to deletion later) */
1601 void
btr_search_move_or_delete_hash_entries(buf_block_t * new_block,buf_block_t * block)1602 btr_search_move_or_delete_hash_entries(
1603 buf_block_t* new_block,
1604 buf_block_t* block)
1605 {
1606 ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X));
1607 ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_X));
1608
1609 if (!btr_search_enabled) {
1610 return;
1611 }
1612
1613 dict_index_t* index = block->index;
1614 if (!index) {
1615 index = new_block->index;
1616 } else {
1617 ut_ad(!new_block->index || index == new_block->index);
1618 }
1619 assert_block_ahi_valid(block);
1620 assert_block_ahi_valid(new_block);
1621
1622 rw_lock_t* ahi_latch = index ? btr_get_search_latch(index) : NULL;
1623
1624 if (new_block->index) {
1625 drop_exit:
1626 btr_search_drop_page_hash_index(block);
1627 return;
1628 }
1629
1630 if (!index) {
1631 return;
1632 }
1633
1634 rw_lock_s_lock(ahi_latch);
1635
1636 if (block->index) {
1637
1638 if (block->index != index) {
1639 rw_lock_s_unlock(ahi_latch);
1640 goto drop_exit;
1641 }
1642
1643 ulint n_fields = block->curr_n_fields;
1644 ulint n_bytes = block->curr_n_bytes;
1645 ibool left_side = block->curr_left_side;
1646
1647 new_block->n_fields = block->curr_n_fields;
1648 new_block->n_bytes = block->curr_n_bytes;
1649 new_block->left_side = left_side;
1650
1651 rw_lock_s_unlock(ahi_latch);
1652
1653 ut_a(n_fields > 0 || n_bytes > 0);
1654
1655 btr_search_build_page_hash_index(
1656 index, new_block, ahi_latch,
1657 n_fields, n_bytes, left_side);
1658 ut_ad(n_fields == block->curr_n_fields);
1659 ut_ad(n_bytes == block->curr_n_bytes);
1660 ut_ad(left_side == block->curr_left_side);
1661 return;
1662 }
1663 rw_lock_s_unlock(ahi_latch);
1664 }
1665
1666 /** Updates the page hash index when a single record is deleted from a page.
1667 @param[in] cursor cursor which was positioned on the record to delete
1668 using btr_cur_search_, the record is not yet deleted.*/
btr_search_update_hash_on_delete(btr_cur_t * cursor)1669 void btr_search_update_hash_on_delete(btr_cur_t* cursor)
1670 {
1671 buf_block_t* block;
1672 const rec_t* rec;
1673 ulint fold;
1674 dict_index_t* index;
1675 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
1676 mem_heap_t* heap = NULL;
1677 rec_offs_init(offsets_);
1678
1679 ut_ad(page_is_leaf(btr_cur_get_page(cursor)));
1680 #ifdef MYSQL_INDEX_DISABLE_AHI
1681 if (cursor->index->disable_ahi) return;
1682 #endif
1683
1684 if (!btr_search_enabled) {
1685 return;
1686 }
1687
1688 block = btr_cur_get_block(cursor);
1689
1690 ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X));
1691
1692 assert_block_ahi_valid(block);
1693 index = block->index;
1694
1695 if (!index) {
1696
1697 return;
1698 }
1699
1700 if (index != cursor->index) {
1701 ut_ad(index->id == cursor->index->id);
1702 btr_search_drop_page_hash_index(block);
1703 return;
1704 }
1705
1706 ut_ad(block->page.id.space() == index->table->space_id);
1707 ut_a(index == cursor->index);
1708 ut_a(block->curr_n_fields > 0 || block->curr_n_bytes > 0);
1709 ut_ad(!dict_index_is_ibuf(index));
1710
1711 rec = btr_cur_get_rec(cursor);
1712
1713 fold = rec_fold(rec, rec_get_offsets(rec, index, offsets_,
1714 index->n_core_fields,
1715 ULINT_UNDEFINED, &heap),
1716 block->curr_n_fields, block->curr_n_bytes, index->id);
1717 if (UNIV_LIKELY_NULL(heap)) {
1718 mem_heap_free(heap);
1719 }
1720
1721 rw_lock_t* ahi_latch = btr_get_search_latch(index);
1722
1723 rw_lock_x_lock(ahi_latch);
1724 assert_block_ahi_valid(block);
1725
1726 if (btr_search_enabled) {
1727 hash_table_t* table = btr_get_search_table(index);
1728 if (block->index) {
1729 ut_a(block->index == index);
1730
1731 if (ha_search_and_delete_if_found(table, fold, rec)) {
1732 MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_REMOVED);
1733 } else {
1734 MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_REMOVE_NOT_FOUND);
1735 }
1736
1737 assert_block_ahi_valid(block);
1738 }
1739 }
1740
1741 rw_lock_x_unlock(ahi_latch);
1742 }
1743
1744 /** Updates the page hash index when a single record is inserted on a page.
1745 @param[in] cursor cursor which was positioned to the place to insert
1746 using btr_cur_search_, and the new record has been
1747 inserted next to the cursor.
1748 @param[in] ahi_latch the adaptive hash index latch */
1749 void
btr_search_update_hash_node_on_insert(btr_cur_t * cursor,rw_lock_t * ahi_latch)1750 btr_search_update_hash_node_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch)
1751 {
1752 hash_table_t* table;
1753 buf_block_t* block;
1754 dict_index_t* index;
1755 rec_t* rec;
1756
1757 ut_ad(ahi_latch == btr_get_search_latch(cursor->index));
1758 ut_ad(!btr_search_own_any(RW_LOCK_S));
1759 ut_ad(!btr_search_own_any(RW_LOCK_X));
1760 #ifdef MYSQL_INDEX_DISABLE_AHI
1761 if (cursor->index->disable_ahi) return;
1762 #endif
1763 if (!btr_search_enabled) {
1764 return;
1765 }
1766
1767 rec = btr_cur_get_rec(cursor);
1768
1769 block = btr_cur_get_block(cursor);
1770
1771 ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X));
1772
1773 index = block->index;
1774
1775 if (!index) {
1776
1777 return;
1778 }
1779
1780 if (cursor->index != index) {
1781 ut_ad(cursor->index->id == index->id);
1782 btr_search_drop_page_hash_index(block);
1783 return;
1784 }
1785
1786 ut_a(cursor->index == index);
1787 ut_ad(!dict_index_is_ibuf(index));
1788 rw_lock_x_lock(ahi_latch);
1789
1790 if (!block->index || !btr_search_enabled) {
1791
1792 goto func_exit;
1793 }
1794
1795 ut_a(block->index == index);
1796
1797 if ((cursor->flag == BTR_CUR_HASH)
1798 && (cursor->n_fields == block->curr_n_fields)
1799 && (cursor->n_bytes == block->curr_n_bytes)
1800 && !block->curr_left_side) {
1801
1802 table = btr_get_search_table(index);
1803
1804 if (ha_search_and_update_if_found(
1805 table, cursor->fold, rec, block,
1806 page_rec_get_next(rec))) {
1807 MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_UPDATED);
1808 }
1809
1810 func_exit:
1811 assert_block_ahi_valid(block);
1812 rw_lock_x_unlock(ahi_latch);
1813 } else {
1814 rw_lock_x_unlock(ahi_latch);
1815
1816 btr_search_update_hash_on_insert(cursor, ahi_latch);
1817 }
1818 }
1819
1820 /** Updates the page hash index when a single record is inserted on a page.
1821 @param[in,out] cursor cursor which was positioned to the
1822 place to insert using btr_cur_search_...,
1823 and the new record has been inserted next
1824 to the cursor
1825 @param[in] ahi_latch the adaptive hash index latch */
1826 void
btr_search_update_hash_on_insert(btr_cur_t * cursor,rw_lock_t * ahi_latch)1827 btr_search_update_hash_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch)
1828 {
1829 buf_block_t* block;
1830 dict_index_t* index;
1831 const rec_t* rec;
1832 const rec_t* ins_rec;
1833 const rec_t* next_rec;
1834 ulint fold;
1835 ulint ins_fold;
1836 ulint next_fold = 0; /* remove warning (??? bug ???) */
1837 ulint n_fields;
1838 ulint n_bytes;
1839 mem_heap_t* heap = NULL;
1840 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
1841 rec_offs* offsets = offsets_;
1842 rec_offs_init(offsets_);
1843
1844 ut_ad(ahi_latch == btr_get_search_latch(cursor->index));
1845 ut_ad(page_is_leaf(btr_cur_get_page(cursor)));
1846 ut_ad(!btr_search_own_any(RW_LOCK_S));
1847 ut_ad(!btr_search_own_any(RW_LOCK_X));
1848 #ifdef MYSQL_INDEX_DISABLE_AHI
1849 if (cursor->index->disable_ahi) return;
1850 #endif
1851 if (!btr_search_enabled) {
1852 return;
1853 }
1854
1855 block = btr_cur_get_block(cursor);
1856
1857 ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X));
1858 assert_block_ahi_valid(block);
1859
1860 index = block->index;
1861
1862 if (!index) {
1863
1864 return;
1865 }
1866
1867 ut_ad(block->page.id.space() == index->table->space_id);
1868 btr_search_check_free_space_in_heap(index);
1869
1870 rec = btr_cur_get_rec(cursor);
1871
1872 #ifdef MYSQL_INDEX_DISABLE_AHI
1873 ut_a(!index->disable_ahi);
1874 #endif
1875 if (index != cursor->index) {
1876 ut_ad(index->id == cursor->index->id);
1877 btr_search_drop_page_hash_index(block);
1878 return;
1879 }
1880
1881 ut_a(index == cursor->index);
1882 ut_ad(!dict_index_is_ibuf(index));
1883
1884 n_fields = block->curr_n_fields;
1885 n_bytes = block->curr_n_bytes;
1886 const bool left_side = block->curr_left_side;
1887
1888 ins_rec = page_rec_get_next_const(rec);
1889 next_rec = page_rec_get_next_const(ins_rec);
1890
1891 offsets = rec_get_offsets(ins_rec, index, offsets,
1892 index->n_core_fields,
1893 ULINT_UNDEFINED, &heap);
1894 ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, index->id);
1895
1896 if (!page_rec_is_supremum(next_rec)) {
1897 offsets = rec_get_offsets(
1898 next_rec, index, offsets, index->n_core_fields,
1899 btr_search_get_n_fields(n_fields, n_bytes), &heap);
1900 next_fold = rec_fold(next_rec, offsets, n_fields,
1901 n_bytes, index->id);
1902 }
1903
1904 /* We must not look up "table" before acquiring ahi_latch. */
1905 hash_table_t* table = NULL;
1906 bool locked = false;
1907
1908 if (!page_rec_is_infimum(rec) && !rec_is_metadata(rec, index)) {
1909 offsets = rec_get_offsets(
1910 rec, index, offsets, index->n_core_fields,
1911 btr_search_get_n_fields(n_fields, n_bytes), &heap);
1912 fold = rec_fold(rec, offsets, n_fields, n_bytes, index->id);
1913 } else {
1914 if (left_side) {
1915 locked = true;
1916 rw_lock_x_lock(ahi_latch);
1917
1918 if (!btr_search_enabled || !block->index) {
1919 goto function_exit;
1920 }
1921
1922 table = btr_get_search_table(index);
1923 ha_insert_for_fold(table, ins_fold, block, ins_rec);
1924 }
1925
1926 goto check_next_rec;
1927 }
1928
1929 if (fold != ins_fold) {
1930
1931 if (!locked) {
1932 locked = true;
1933 rw_lock_x_lock(ahi_latch);
1934
1935 if (!btr_search_enabled || !block->index) {
1936 goto function_exit;
1937 }
1938
1939 table = btr_get_search_table(index);
1940 }
1941
1942 if (!left_side) {
1943 ha_insert_for_fold(table, fold, block, rec);
1944 } else {
1945 ha_insert_for_fold(table, ins_fold, block, ins_rec);
1946 }
1947 }
1948
1949 check_next_rec:
1950 if (page_rec_is_supremum(next_rec)) {
1951
1952 if (!left_side) {
1953 if (!locked) {
1954 locked = true;
1955 rw_lock_x_lock(ahi_latch);
1956
1957 if (!btr_search_enabled || !block->index) {
1958 goto function_exit;
1959 }
1960
1961 table = btr_get_search_table(index);
1962 }
1963
1964 ha_insert_for_fold(table, ins_fold, block, ins_rec);
1965 }
1966
1967 goto function_exit;
1968 }
1969
1970 if (ins_fold != next_fold) {
1971 if (!locked) {
1972 locked = true;
1973 rw_lock_x_lock(ahi_latch);
1974
1975 if (!btr_search_enabled || !block->index) {
1976 goto function_exit;
1977 }
1978
1979 table = btr_get_search_table(index);
1980 }
1981
1982 if (!left_side) {
1983 ha_insert_for_fold(table, ins_fold, block, ins_rec);
1984 } else {
1985 ha_insert_for_fold(table, next_fold, block, next_rec);
1986 }
1987 }
1988
1989 function_exit:
1990 if (UNIV_LIKELY_NULL(heap)) {
1991 mem_heap_free(heap);
1992 }
1993 if (locked) {
1994 rw_lock_x_unlock(ahi_latch);
1995 }
1996 ut_ad(!rw_lock_own(ahi_latch, RW_LOCK_X));
1997 }
1998
1999 #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
2000
2001 /** Validates the search system for given hash table.
2002 @param[in] hash_table_id hash table to validate
2003 @return TRUE if ok */
2004 static
2005 ibool
btr_search_hash_table_validate(ulint hash_table_id)2006 btr_search_hash_table_validate(ulint hash_table_id)
2007 {
2008 ha_node_t* node;
2009 ibool ok = TRUE;
2010 ulint i;
2011 ulint cell_count;
2012 mem_heap_t* heap = NULL;
2013 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
2014 rec_offs* offsets = offsets_;
2015
2016 btr_search_x_lock_all();
2017 if (!btr_search_enabled) {
2018 btr_search_x_unlock_all();
2019 return(TRUE);
2020 }
2021
2022 /* How many cells to check before temporarily releasing
2023 search latches. */
2024 ulint chunk_size = 10000;
2025
2026 rec_offs_init(offsets_);
2027
2028 buf_pool_mutex_enter_all();
2029
2030 cell_count = hash_get_n_cells(
2031 btr_search_sys->hash_tables[hash_table_id]);
2032
2033 for (i = 0; i < cell_count; i++) {
2034 /* We release search latches every once in a while to
2035 give other queries a chance to run. */
2036 if ((i != 0) && ((i % chunk_size) == 0)) {
2037
2038 buf_pool_mutex_exit_all();
2039 btr_search_x_unlock_all();
2040
2041 os_thread_yield();
2042
2043 btr_search_x_lock_all();
2044
2045 if (!btr_search_enabled) {
2046 ok = true;
2047 goto func_exit;
2048 }
2049
2050 buf_pool_mutex_enter_all();
2051
2052 ulint curr_cell_count = hash_get_n_cells(
2053 btr_search_sys->hash_tables[hash_table_id]);
2054
2055 if (cell_count != curr_cell_count) {
2056
2057 cell_count = curr_cell_count;
2058
2059 if (i >= cell_count) {
2060 break;
2061 }
2062 }
2063 }
2064
2065 node = (ha_node_t*) hash_get_nth_cell(
2066 btr_search_sys->hash_tables[hash_table_id], i)->node;
2067
2068 for (; node != NULL; node = node->next) {
2069 const buf_block_t* block
2070 = buf_block_from_ahi((byte*) node->data);
2071 const buf_block_t* hash_block;
2072 buf_pool_t* buf_pool;
2073 index_id_t page_index_id;
2074
2075 buf_pool = buf_pool_from_bpage((buf_page_t*) block);
2076
2077 if (UNIV_LIKELY(buf_block_get_state(block)
2078 == BUF_BLOCK_FILE_PAGE)) {
2079
2080 /* The space and offset are only valid
2081 for file blocks. It is possible that
2082 the block is being freed
2083 (BUF_BLOCK_REMOVE_HASH, see the
2084 assertion and the comment below) */
2085 hash_block = buf_block_hash_get(
2086 buf_pool,
2087 block->page.id);
2088 } else {
2089 hash_block = NULL;
2090 }
2091
2092 if (hash_block) {
2093 ut_a(hash_block == block);
2094 } else {
2095 /* When a block is being freed,
2096 buf_LRU_search_and_free_block() first
2097 removes the block from
2098 buf_pool->page_hash by calling
2099 buf_LRU_block_remove_hashed_page().
2100 After that, it invokes
2101 btr_search_drop_page_hash_index() to
2102 remove the block from
2103 btr_search_sys->hash_tables[i]. */
2104
2105 ut_a(buf_block_get_state(block)
2106 == BUF_BLOCK_REMOVE_HASH);
2107 }
2108
2109 ut_ad(!dict_index_is_ibuf(block->index));
2110 ut_ad(block->page.id.space()
2111 == block->index->table->space_id);
2112
2113 page_index_id = btr_page_get_index_id(block->frame);
2114
2115 offsets = rec_get_offsets(
2116 node->data, block->index, offsets,
2117 block->index->n_core_fields,
2118 btr_search_get_n_fields(block->curr_n_fields,
2119 block->curr_n_bytes),
2120 &heap);
2121
2122 const ulint fold = rec_fold(
2123 node->data, offsets,
2124 block->curr_n_fields,
2125 block->curr_n_bytes,
2126 page_index_id);
2127
2128 if (node->fold != fold) {
2129 const page_t* page = block->frame;
2130
2131 ok = FALSE;
2132
2133 ib::error() << "Error in an adaptive hash"
2134 << " index pointer to page "
2135 << page_id_t(page_get_space_id(page),
2136 page_get_page_no(page))
2137 << ", ptr mem address "
2138 << reinterpret_cast<const void*>(
2139 node->data)
2140 << ", index id " << page_index_id
2141 << ", node fold " << node->fold
2142 << ", rec fold " << fold;
2143
2144 fputs("InnoDB: Record ", stderr);
2145 rec_print_new(stderr, node->data, offsets);
2146 fprintf(stderr, "\nInnoDB: on that page."
2147 " Page mem address %p, is hashed %p,"
2148 " n fields %lu\n"
2149 "InnoDB: side %lu\n",
2150 (void*) page, (void*) block->index,
2151 (ulong) block->curr_n_fields,
2152 (ulong) block->curr_left_side);
2153 ut_ad(0);
2154 }
2155 }
2156 }
2157
2158 for (i = 0; i < cell_count; i += chunk_size) {
2159 /* We release search latches every once in a while to
2160 give other queries a chance to run. */
2161 if (i != 0) {
2162
2163 buf_pool_mutex_exit_all();
2164 btr_search_x_unlock_all();
2165
2166 os_thread_yield();
2167
2168 btr_search_x_lock_all();
2169
2170 if (!btr_search_enabled) {
2171 ok = true;
2172 goto func_exit;
2173 }
2174
2175 buf_pool_mutex_enter_all();
2176
2177 ulint curr_cell_count = hash_get_n_cells(
2178 btr_search_sys->hash_tables[hash_table_id]);
2179
2180 if (cell_count != curr_cell_count) {
2181
2182 cell_count = curr_cell_count;
2183
2184 if (i >= cell_count) {
2185 break;
2186 }
2187 }
2188 }
2189
2190 ulint end_index = ut_min(i + chunk_size - 1, cell_count - 1);
2191
2192 if (!ha_validate(btr_search_sys->hash_tables[hash_table_id],
2193 i, end_index)) {
2194 ok = FALSE;
2195 }
2196 }
2197
2198 buf_pool_mutex_exit_all();
2199 func_exit:
2200 btr_search_x_unlock_all();
2201
2202 if (UNIV_LIKELY_NULL(heap)) {
2203 mem_heap_free(heap);
2204 }
2205
2206 return(ok);
2207 }
2208
2209 /** Validate the search system.
2210 @return true if ok. */
2211 bool
btr_search_validate()2212 btr_search_validate()
2213 {
2214 for (ulint i = 0; i < btr_ahi_parts; ++i) {
2215 if (!btr_search_hash_table_validate(i)) {
2216 return(false);
2217 }
2218 }
2219
2220 return(true);
2221 }
2222
2223 #endif /* defined UNIV_AHI_DEBUG || defined UNIV_DEBUG */
2224 #endif /* BTR_CUR_HASH_ADAPT */
2225