~drizzle-trunk/drizzle/development

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