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