btr0sea.c 43.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/************************************************************************
The index tree adaptive search

(c) 1996 Innobase Oy

Created 2/17/1996 Heikki Tuuri
*************************************************************************/

#include "btr0sea.h"
#ifdef UNIV_NONINL
#include "btr0sea.ic"
#endif

#include "buf0buf.h"
#include "page0page.h"
#include "page0cur.h"
#include "btr0cur.h"
unknown's avatar
unknown committed
18
#include "btr0pcur.h"
19
#include "btr0btr.h"
unknown's avatar
unknown committed
20
#include "ha0ha.h"
21

unknown's avatar
unknown committed
22 23 24
ulint	btr_search_this_is_zero = 0;	/* A dummy variable to fool the
					compiler */

25
#ifdef UNIV_SEARCH_PERF_STAT
26 27
ulint	btr_search_n_succ	= 0;
ulint	btr_search_n_hash_fail	= 0;
unknown's avatar
unknown committed
28
#endif /* UNIV_SEARCH_PERF_STAT */
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

byte	btr_sea_pad1[64];	/* padding to prevent other memory update
				hotspots from residing on the same memory
				cache line as btr_search_latch */

/* The latch protecting the adaptive search system: this latch protects the
(1) positions of records on those pages where a hash index has been built.
NOTE: It does not protect values of non-ordering fields within a record from
being updated in-place! We can use fact (1) to perform unique searches to
indexes. */

rw_lock_t*	btr_search_latch_temp; /* We will allocate the latch from
					dynamic memory to get it to the
					same DRAM page as other hotspot
					semaphores */

byte	btr_sea_pad2[64];	/* padding to prevent other memory update
				hotspots from residing on the same memory
				cache line */

btr_search_sys_t*	btr_search_sys;

/* If the number of records on the page divided by this parameter
would have been successfully accessed using a hash index, the index
is then built on the page, assuming the global limit has been reached */

#define BTR_SEARCH_PAGE_BUILD_LIMIT	16

/* The global limit for consecutive potentially successful hash searches,
before hash index building is started */

#define BTR_SEARCH_BUILD_LIMIT		100

/************************************************************************
Builds a hash index on a page with the given parameters. If the page already
unknown's avatar
unknown committed
64 65 66
has a hash index with different parameters, the old hash index is removed.
If index is non-NULL, this function checks if n_fields and n_bytes are
sensible values, and does not build a hash index if not. */
67 68 69 70
static
void
btr_search_build_page_hash_index(
/*=============================*/
unknown's avatar
unknown committed
71 72 73 74 75
	dict_index_t*	index,	/* in: index for which to build, or NULL if
				not known */
	page_t*		page,	/* in: index page, s- or x-latched */
	ulint		n_fields,/* in: hash this many full fields */
	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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

/*********************************************************************
This function should be called before reserving any btr search mutex, if
the intended operation might add nodes to the search system hash table.
Because of the latching order, once we have reserved the btr search system
latch, we cannot allocate a free frame from the buffer pool. Checks that
there is a free buffer frame allocated for hash table heap in the btr search
system. If not, allocates a free frames for the heap. This check makes it
probable that, when have reserved the btr search system latch and we need to
allocate a new node to the hash table, it will succeed. However, the check
will not guarantee success. */
static
void
btr_search_check_free_space_in_heap(void)
/*=====================================*/
{
	buf_frame_t*	frame;
	hash_table_t*	table;
	mem_heap_t*	heap;

98 99 100 101
#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
102 103 104 105

	table = btr_search_sys->hash_index;

	heap = table->heap;
106

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	/* Note that we peek the value of heap->free_block without reserving
	the latch: this is ok, because we will not guarantee that there will
	be enough free space in the hash table. */

	if (heap->free_block == NULL) {
		frame = buf_frame_alloc();

		rw_lock_x_lock(&btr_search_latch);

		if (heap->free_block == NULL) {
			heap->free_block = frame;
		} else {
			buf_frame_free(frame);
		}

		rw_lock_x_unlock(&btr_search_latch);
	}
}

/*********************************************************************
Creates and initializes the adaptive search system at a database start. */

void
btr_search_sys_create(
/*==================*/
	ulint	hash_size)	/* in: hash index hash table size */
{
	/* We allocate the search latch from dynamic memory:
	see above at the global variable definition */
136

137
	btr_search_latch_temp = mem_alloc(sizeof(rw_lock_t));
138

unknown's avatar
unknown committed
139
	rw_lock_create(&btr_search_latch, SYNC_SEARCH_SYS);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

	btr_search_sys = mem_alloc(sizeof(btr_search_sys_t));

	btr_search_sys->hash_index = ha_create(TRUE, hash_size, 0, 0);

}

/*********************************************************************
Creates and initializes a search info struct. */

btr_search_t*
btr_search_info_create(
/*===================*/
				/* out, own: search info struct */
	mem_heap_t*	heap)	/* in: heap where created */
{
	btr_search_t*	info;

	info = mem_heap_alloc(heap, sizeof(btr_search_t));

160
#ifdef UNIV_DEBUG
unknown's avatar
unknown committed
161
	info->magic_n = BTR_SEARCH_MAGIC_N;
162
#endif /* UNIV_DEBUG */
unknown's avatar
unknown committed
163

164
	info->ref_count = 0;
165 166 167 168 169 170 171
	info->root_guess = NULL;

	info->hash_analysis = 0;
	info->n_hash_potential = 0;

	info->last_hash_succ = FALSE;

unknown's avatar
unknown committed
172
#ifdef UNIV_SEARCH_PERF_STAT
173 174 175 176
	info->n_hash_succ = 0;
	info->n_hash_fail = 0;
	info->n_patt_succ = 0;
	info->n_searches = 0;
unknown's avatar
unknown committed
177
#endif /* UNIV_SEARCH_PERF_STAT */
178

unknown's avatar
unknown committed
179 180 181 182
	/* Set some sensible values */
	info->n_fields = 1;
	info->n_bytes = 0;

183
	info->left_side = TRUE;
unknown's avatar
unknown committed
184

185 186 187
	return(info);
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
/*********************************************************************
Returns the value of ref_count. The value is protected by
btr_search_latch. */
ulint
btr_search_info_get_ref_count(
/*==========================*/
				/* out: ref_count value. */
	btr_search_t*   info)	/* in: search info. */
{
	ulint ret;

	ut_ad(info);

#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */

	rw_lock_s_lock(&btr_search_latch);
	ret = info->ref_count;
	rw_lock_s_unlock(&btr_search_latch);

	return(ret);
}

213
/*************************************************************************
unknown's avatar
unknown committed
214 215 216
Updates the search info of an index about hash successes. NOTE that info
is NOT protected by any semaphore, to save CPU time! Do not assume its fields
are consistent. */
217 218 219 220
static
void
btr_search_info_update_hash(
/*========================*/
221
	btr_search_t*	info,	/* in/out: search info */
222 223 224 225 226 227
	btr_cur_t*	cursor)	/* in: cursor which was just positioned */
{
	dict_index_t*	index;
	ulint		n_unique;
	int		cmp;

228 229 230 231
#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
232 233 234 235 236 237 238 239 240 241

	index = cursor->index;

	if (index->type & DICT_IBUF) {
		/* So many deletes are performed on an insert buffer tree
		that we do not consider a hash index useful on it: */

		return;
	}

242 243
	n_unique = dict_index_get_n_unique_in_tree(index);

244 245 246 247 248 249 250 251
	if (info->n_hash_potential == 0) {

		goto set_new_recomm;
	}

	/* Test if the search would have succeeded using the recommended
	hash prefix */

unknown's avatar
unknown committed
252
	if (info->n_fields >= n_unique && cursor->up_match >= n_unique) {
253
increment_potential:
254 255 256 257 258 259
		info->n_hash_potential++;

		return;
	}

	cmp = ut_pair_cmp(info->n_fields, info->n_bytes,
unknown's avatar
unknown committed
260
			  cursor->low_match, cursor->low_bytes);
261

262
	if (info->left_side ? cmp <= 0 : cmp > 0) {
263 264 265 266 267

		goto set_new_recomm;
	}

	cmp = ut_pair_cmp(info->n_fields, info->n_bytes,
unknown's avatar
unknown committed
268
			  cursor->up_match, cursor->up_bytes);
269

270
	if (info->left_side ? cmp <= 0 : cmp > 0) {
271

272
		goto increment_potential;
273 274 275 276 277 278
	}

set_new_recomm:
	/* We have to set a new recommendation; skip the hash analysis
	for a while to avoid unnecessary CPU time usage when there is no
	chance for success */
279

280
	info->hash_analysis = 0;
281

282
	cmp = ut_pair_cmp(cursor->up_match, cursor->up_bytes,
unknown's avatar
unknown committed
283
			  cursor->low_match, cursor->low_bytes);
284 285 286
	if (cmp == 0) {
		info->n_hash_potential = 0;

unknown's avatar
unknown committed
287 288 289 290 291
		/* For extra safety, we set some sensible values here */

		info->n_fields = 1;
		info->n_bytes = 0;

292
		info->left_side = TRUE;
unknown's avatar
unknown committed
293

294 295 296 297 298 299 300 301 302 303 304 305
	} else if (cmp > 0) {
		info->n_hash_potential = 1;

		if (cursor->up_match >= n_unique) {

			info->n_fields = n_unique;
			info->n_bytes = 0;

		} else if (cursor->low_match < cursor->up_match) {

			info->n_fields = cursor->low_match + 1;
			info->n_bytes = 0;
306
		} else {
307 308 309 310
			info->n_fields = cursor->low_match;
			info->n_bytes = cursor->low_bytes + 1;
		}

311
		info->left_side = TRUE;
312 313 314 315 316 317 318 319 320 321 322 323
	} else {
		info->n_hash_potential = 1;

		if (cursor->low_match >= n_unique) {

			info->n_fields = n_unique;
			info->n_bytes = 0;

		} else if (cursor->low_match > cursor->up_match) {

			info->n_fields = cursor->up_match + 1;
			info->n_bytes = 0;
324
		} else {
325 326 327 328
			info->n_fields = cursor->up_match;
			info->n_bytes = cursor->up_bytes + 1;
		}

329
		info->left_side = FALSE;
330 331
	}
}
332

333
/*************************************************************************
unknown's avatar
unknown committed
334 335 336
Updates the block search info on hash successes. NOTE that info and
block->n_hash_helps, n_fields, n_bytes, side are NOT protected by any
semaphore, to save CPU time! Do not assume the fields are consistent. */
337 338 339 340 341 342 343 344
static
ibool
btr_search_update_block_hash_info(
/*==============================*/
				/* out: TRUE if building a (new) hash index on
				the block is recommended */
	btr_search_t*	info,	/* in: search info */
	buf_block_t*	block,	/* in: buffer block */
345 346
	btr_cur_t*	cursor __attribute__((unused)))
				/* in: cursor */
347
{
348 349 350 351
#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
	ut_ad(rw_lock_own(&((buf_block_t*) block)->lock, RW_LOCK_SHARED)
unknown's avatar
unknown committed
352
	      || rw_lock_own(&((buf_block_t*) block)->lock, RW_LOCK_EX));
353
#endif /* UNIV_SYNC_DEBUG */
354 355 356 357
	ut_ad(cursor);

	info->last_hash_succ = FALSE;

unknown's avatar
unknown committed
358
	ut_a(block->magic_n == BUF_BLOCK_MAGIC_N);
359
	ut_ad(info->magic_n == BTR_SEARCH_MAGIC_N);
unknown's avatar
unknown committed
360

361
	if ((block->n_hash_helps > 0)
unknown's avatar
unknown committed
362 363 364
	    && (info->n_hash_potential > 0)
	    && (block->n_fields == info->n_fields)
	    && (block->n_bytes == info->n_bytes)
365
	    && (block->left_side == info->left_side)) {
366

367
		if ((block->is_hashed)
unknown's avatar
unknown committed
368 369
		    && (block->curr_n_fields == info->n_fields)
		    && (block->curr_n_bytes == info->n_bytes)
370
		    && (block->curr_left_side == info->left_side)) {
371 372 373

			/* The search would presumably have succeeded using
			the hash index */
374

375 376 377 378 379 380 381 382
			info->last_hash_succ = TRUE;
		}

		block->n_hash_helps++;
	} else {
		block->n_hash_helps = 1;
		block->n_fields = info->n_fields;
		block->n_bytes = info->n_bytes;
383
		block->left_side = info->left_side;
384 385
	}

386
#ifdef UNIV_DEBUG
387 388 389
	if (cursor->index->table->does_not_fit_in_memory) {
		block->n_hash_helps = 0;
	}
390
#endif /* UNIV_DEBUG */
391 392

	if ((block->n_hash_helps > page_get_n_recs(block->frame)
unknown's avatar
unknown committed
393 394
	     / BTR_SEARCH_PAGE_BUILD_LIMIT)
	    && (info->n_hash_potential >= BTR_SEARCH_BUILD_LIMIT)) {
395

396
		if ((!block->is_hashed)
unknown's avatar
unknown committed
397 398 399 400
		    || (block->n_hash_helps
			> 2 * page_get_n_recs(block->frame))
		    || (block->n_fields != block->curr_n_fields)
		    || (block->n_bytes != block->curr_n_bytes)
401
		    || (block->left_side != block->curr_left_side)) {
402

403
			/* Build a new hash index on the page */
404

405
			return(TRUE);
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
		}
	}

	return(FALSE);
}

/*************************************************************************
Updates a hash node reference when it has been unsuccessfully used in a
search which could have succeeded with the used hash parameters. This can
happen because when building a hash index for a page, we do not check
what happens at page boundaries, and therefore there can be misleading
hash nodes. Also, collisions in the fold value can lead to misleading
references. This function lazily fixes these imperfections in the hash
index. */
static
void
btr_search_update_hash_ref(
/*=======================*/
	btr_search_t*	info,	/* in: search info */
	buf_block_t*	block,	/* in: buffer block where cursor positioned */
	btr_cur_t*	cursor)	/* in: cursor */
{
	ulint	fold;
	rec_t*	rec;
430
	dulint	index_id;
431 432

	ut_ad(cursor->flag == BTR_CUR_HASH_FAIL);
433
#ifdef UNIV_SYNC_DEBUG
434 435
	ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX));
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED)
unknown's avatar
unknown committed
436
	      || rw_lock_own(&(block->lock), RW_LOCK_EX));
437
#endif /* UNIV_SYNC_DEBUG */
unknown's avatar
unknown committed
438 439 440
	ut_ad(buf_block_align(btr_cur_get_rec(cursor)) == block);
	ut_a(!block->is_hashed || block->index == cursor->index);

441
	if (block->is_hashed
unknown's avatar
unknown committed
442 443 444
	    && (info->n_hash_potential > 0)
	    && (block->curr_n_fields == info->n_fields)
	    && (block->curr_n_bytes == info->n_bytes)
445
	    && (block->curr_left_side == info->left_side)) {
446
		mem_heap_t*	heap		= NULL;
447
		ulint		offsets_[REC_OFFS_NORMAL_SIZE];
448
		*offsets_ = (sizeof offsets_) / sizeof *offsets_;
449

450 451 452
		rec = btr_cur_get_rec(cursor);

		if (!page_rec_is_user_rec(rec)) {
453

454 455
			return;
		}
456

457
		index_id = cursor->index->id;
unknown's avatar
unknown committed
458 459 460
		fold = rec_fold(rec,
				rec_get_offsets(rec, cursor->index, offsets_,
						ULINT_UNDEFINED, &heap),
461
				block->curr_n_fields,
462
				block->curr_n_bytes, index_id);
463
		if (UNIV_LIKELY_NULL(heap)) {
464 465
			mem_heap_free(heap);
		}
466
#ifdef UNIV_SYNC_DEBUG
467
		ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX));
468
#endif /* UNIV_SYNC_DEBUG */
469 470 471

		ha_insert_for_fold(btr_search_sys->hash_index, fold, rec);
	}
472 473
}

474 475 476 477 478 479
/*************************************************************************
Updates the search info. */

void
btr_search_info_update_slow(
/*========================*/
480
	btr_search_t*	info,	/* in/out: search info */
481 482 483 484
	btr_cur_t*	cursor)	/* in: cursor which was just positioned */
{
	buf_block_t*	block;
	ibool		build_index;
unknown's avatar
unknown committed
485 486
	ulint*		params;
	ulint*		params2;
487

488 489 490 491
#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
492 493 494

	block = buf_block_align(btr_cur_get_rec(cursor));

unknown's avatar
unknown committed
495 496 497 498 499
	/* NOTE that the following two function calls do NOT protect
	info or block->n_fields etc. with any semaphore, to save CPU time!
	We cannot assume the fields are consistent when we return from
	those functions! */

500 501 502 503 504 505 506 507
	btr_search_info_update_hash(info, cursor);

	build_index = btr_search_update_block_hash_info(info, block, cursor);

	if (build_index || (cursor->flag == BTR_CUR_HASH_FAIL)) {

		btr_search_check_free_space_in_heap();
	}
508

509 510 511
	if (cursor->flag == BTR_CUR_HASH_FAIL) {
		/* Update the hash node reference, if appropriate */

unknown's avatar
unknown committed
512
#ifdef UNIV_SEARCH_PERF_STAT
513
		btr_search_n_hash_fail++;
unknown's avatar
unknown committed
514
#endif /* UNIV_SEARCH_PERF_STAT */
515 516 517 518 519 520 521 522

		rw_lock_x_lock(&btr_search_latch);

		btr_search_update_hash_ref(info, block, cursor);

		rw_lock_x_unlock(&btr_search_latch);
	}

unknown's avatar
unknown committed
523
	if (build_index) {
unknown's avatar
unknown committed
524 525 526 527 528 529 530 531 532 533 534
		/* Note that since we did not protect block->n_fields etc.
		with any semaphore, the values can be inconsistent. We have
		to check inside the function call that they make sense. We
		also malloc an array and store the values there to make sure
		the compiler does not let the function call parameters change
		inside the called function. It might be that the compiler
		would optimize the call just to pass pointers to block. */

		params = mem_alloc(3 * sizeof(ulint));
		params[0] = block->n_fields;
		params[1] = block->n_bytes;
535
		params[2] = block->left_side;
unknown's avatar
unknown committed
536 537 538 539 540

		/* Make sure the compiler cannot deduce the values and do
		optimizations */

		params2 = params + btr_search_this_is_zero;
541

unknown's avatar
unknown committed
542
		btr_search_build_page_hash_index(cursor->index,
unknown's avatar
unknown committed
543 544 545 546
						 block->frame,
						 params2[0],
						 params2[1],
						 params2[2]);
unknown's avatar
unknown committed
547
		mem_free(params);
548 549 550 551 552 553 554
	}
}

/**********************************************************************
Checks if a guessed position for a tree cursor is right. Note that if
mode is PAGE_CUR_LE, which is used in inserts, and the function returns
TRUE, then cursor->up_match and cursor->low_match both have sensible values. */
unknown's avatar
unknown committed
555
static
556 557 558 559 560
ibool
btr_search_check_guess(
/*===================*/
				/* out: TRUE if success */
	btr_cur_t*	cursor,	/* in: guessed cursor position */
561 562
	ibool		can_only_compare_to_cursor_rec,
				/* in: if we do not have a latch on the page
unknown's avatar
unknown committed
563
				of cursor, but only a latch on
564
				btr_search_latch, then ONLY the columns
unknown's avatar
unknown committed
565 566 567 568
				of the record UNDER the cursor are
				protected, not the next or previous record
				in the chain: we cannot look at the next or
				previous record to check our guess! */
569
	dtuple_t*	tuple,	/* in: data tuple */
570 571 572 573
	ulint		mode,	/* in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G,
				or PAGE_CUR_GE */
	mtr_t*		mtr)	/* in: mtr */
{
unknown's avatar
unknown committed
574 575 576 577 578
	rec_t*		rec;
	ulint		n_unique;
	ulint		match;
	ulint		bytes;
	int		cmp;
579
	mem_heap_t*	heap		= NULL;
580
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
581 582
	ulint*		offsets		= offsets_;
	ibool		success		= FALSE;
583
	*offsets_ = (sizeof offsets_) / sizeof *offsets_;
unknown's avatar
unknown committed
584

585
	n_unique = dict_index_get_n_unique_in_tree(cursor->index);
586

587 588 589 590 591 592 593
	rec = btr_cur_get_rec(cursor);

	ut_ad(page_rec_is_user_rec(rec));

	match = 0;
	bytes = 0;

594
	offsets = rec_get_offsets(rec, cursor->index, offsets,
unknown's avatar
unknown committed
595
				  n_unique, &heap);
unknown's avatar
unknown committed
596
	cmp = page_cmp_dtuple_rec_with_match(tuple, rec,
unknown's avatar
unknown committed
597
					     offsets, &match, &bytes);
598 599 600

	if (mode == PAGE_CUR_GE) {
		if (cmp == 1) {
601
			goto exit_func;
602 603 604 605 606
		}

		cursor->up_match = match;

		if (match >= n_unique) {
607 608
			success = TRUE;
			goto exit_func;
609
		}
610 611
	} else if (mode == PAGE_CUR_LE) {
		if (cmp == -1) {
612
			goto exit_func;
613 614 615 616 617 618
		}

		cursor->low_match = match;

	} else if (mode == PAGE_CUR_G) {
		if (cmp != -1) {
619
			goto exit_func;
620 621 622
		}
	} else if (mode == PAGE_CUR_L) {
		if (cmp != 1) {
623
			goto exit_func;
624 625 626
		}
	}

unknown's avatar
unknown committed
627
	if (can_only_compare_to_cursor_rec) {
628 629
		/* Since we could not determine if our guess is right just by
		looking at the record under the cursor, return FALSE */
630
		goto exit_func;
unknown's avatar
unknown committed
631 632
	}

633 634 635 636
	match = 0;
	bytes = 0;

	if ((mode == PAGE_CUR_G) || (mode == PAGE_CUR_GE)) {
637
		rec_t*	prev_rec;
638

639
		ut_ad(!page_rec_is_infimum(rec));
640

641 642
		prev_rec = page_rec_get_prev(rec);

643
		if (page_rec_is_infimum(prev_rec)) {
644 645
			success = btr_page_get_prev(
				buf_frame_align(prev_rec), mtr) == FIL_NULL;
646

647
			goto exit_func;
648 649
		}

650
		offsets = rec_get_offsets(prev_rec, cursor->index, offsets,
unknown's avatar
unknown committed
651
					  n_unique, &heap);
652
		cmp = page_cmp_dtuple_rec_with_match(tuple, prev_rec,
unknown's avatar
unknown committed
653
						     offsets, &match, &bytes);
654
		if (mode == PAGE_CUR_GE) {
655
			success = cmp == 1;
656
		} else {
657
			success = cmp != -1;
658 659
		}

660
		goto exit_func;
661 662 663 664
	} else {
		rec_t*	next_rec;

		ut_ad(!page_rec_is_supremum(rec));
665

666
		next_rec = page_rec_get_next(rec);
667

668
		if (page_rec_is_supremum(next_rec)) {
669 670 671
			if (btr_page_get_next(
				    buf_frame_align(next_rec), mtr)
			    == FIL_NULL) {
672

673 674 675
				cursor->up_match = 0;
				success = TRUE;
			}
676

677 678
			goto exit_func;
		}
679

680
		offsets = rec_get_offsets(next_rec, cursor->index, offsets,
unknown's avatar
unknown committed
681
					  n_unique, &heap);
682
		cmp = page_cmp_dtuple_rec_with_match(tuple, next_rec,
unknown's avatar
unknown committed
683
						     offsets, &match, &bytes);
684 685 686 687 688 689
		if (mode == PAGE_CUR_LE) {
			success = cmp == -1;
			cursor->up_match = match;
		} else {
			success = cmp != 1;
		}
690
	}
691
exit_func:
692
	if (UNIV_LIKELY_NULL(heap)) {
693 694 695
		mem_heap_free(heap);
	}
	return(success);
696 697 698 699 700 701 702 703 704 705 706
}

/**********************************************************************
Tries to guess the right search position based on the hash search info
of the index. Note that if mode is PAGE_CUR_LE, which is used in inserts,
and the function returns TRUE, then cursor->up_match and cursor->low_match
both have sensible values. */

ibool
btr_search_guess_on_hash(
/*=====================*/
707
					/* out: TRUE if succeeded */
708 709 710 711
	dict_index_t*	index,		/* in: index */
	btr_search_t*	info,		/* in: index search info */
	dtuple_t*	tuple,		/* in: logical record */
	ulint		mode,		/* in: PAGE_CUR_L, ... */
712
	ulint		latch_mode,	/* in: BTR_SEARCH_LEAF, ...;
unknown's avatar
unknown committed
713 714 715 716 717
					NOTE that only if has_search_latch
					is 0, we will have a latch set on
					the cursor page, otherwise we assume
					the caller uses his search latch
					to protect the record! */
718
	btr_cur_t*	cursor,		/* out: tree cursor */
719 720 721 722 723 724 725 726 727 728
	ulint		has_search_latch,/* in: latch mode the caller
					currently has on btr_search_latch:
					RW_S_LATCH, RW_X_LATCH, or 0 */
	mtr_t*		mtr)		/* in: mtr */
{
	buf_block_t*	block;
	rec_t*		rec;
	page_t*		page;
	ulint		fold;
	ulint		tuple_n_fields;
729
	dulint		index_id;
730
	ibool		can_only_compare_to_cursor_rec = TRUE;
731 732
#ifdef notdefined
	btr_cur_t	cursor2;
unknown's avatar
unknown committed
733
	btr_pcur_t	pcur;
734 735 736
#endif
	ut_ad(index && info && tuple && cursor && mtr);
	ut_ad((latch_mode == BTR_SEARCH_LEAF)
unknown's avatar
unknown committed
737
	      || (latch_mode == BTR_MODIFY_LEAF));
738 739 740 741

	/* Note that, for efficiency, the struct info may not be protected by
	any latch here! */

742
	if (UNIV_UNLIKELY(info->n_hash_potential == 0)) {
743 744 745 746 747 748 749 750 751

		return(FALSE);
	}

	cursor->n_fields = info->n_fields;
	cursor->n_bytes = info->n_bytes;

	tuple_n_fields = dtuple_get_n_fields(tuple);

752
	if (UNIV_UNLIKELY(tuple_n_fields < cursor->n_fields)) {
753 754 755 756

		return(FALSE);
	}

757
	if (UNIV_UNLIKELY(tuple_n_fields == cursor->n_fields)
unknown's avatar
unknown committed
758
	    && (cursor->n_bytes > 0)) {
759

760
		return(FALSE);
761 762
	}

763
	index_id = index->id;
764 765 766 767

#ifdef UNIV_SEARCH_PERF_STAT
	info->n_hash_succ++;
#endif
768
	fold = dtuple_fold(tuple, cursor->n_fields, cursor->n_bytes, index_id);
769 770 771

	cursor->fold = fold;
	cursor->flag = BTR_CUR_HASH;
772

773
	if (UNIV_LIKELY(!has_search_latch)) {
774 775 776
		rw_lock_s_lock(&btr_search_latch);
	}

777 778
	ut_ad(btr_search_latch.writer != RW_LOCK_EX);
	ut_ad(btr_search_latch.reader_count > 0);
unknown's avatar
unknown committed
779

780 781
	rec = ha_search_and_get_data(btr_search_sys->hash_index, fold);

782 783
	if (UNIV_UNLIKELY(!rec)) {
		goto failure_unlock;
784 785 786 787
	}

	page = buf_frame_align(rec);

788
	if (UNIV_LIKELY(!has_search_latch)) {
789

790 791 792 793 794
		if (UNIV_UNLIKELY(
			    !buf_page_get_known_nowait(latch_mode, page,
						       BUF_MAKE_YOUNG,
						       __FILE__, __LINE__,
						       mtr))) {
795
			goto failure_unlock;
796 797
		}

798
		rw_lock_s_unlock(&btr_search_latch);
unknown's avatar
unknown committed
799 800
		can_only_compare_to_cursor_rec = FALSE;

801
#ifdef UNIV_SYNC_DEBUG
802
		buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH);
803
#endif /* UNIV_SYNC_DEBUG */
804 805 806 807
	}

	block = buf_block_align(page);

808 809
	if (UNIV_UNLIKELY(block->state == BUF_BLOCK_REMOVE_HASH)) {
		if (UNIV_LIKELY(!has_search_latch)) {
810

811 812 813 814 815 816
			btr_leaf_page_release(page, latch_mode, mtr);
		}

		goto failure;
	}

817 818
	ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
	ut_ad(page_rec_is_user_rec(rec));
819 820 821 822 823

	btr_cur_position(index, rec, cursor);

	/* Check the validity of the guess within the page */

824 825 826 827 828
	/* If we only have the latch on btr_search_latch, not on the
	page, it only protects the columns of the record the cursor
	is positioned on. We cannot look at the next of the previous
	record to determine if our guess for the cursor position is
	right. */
829 830
	if (UNIV_EXPECT(
		    ut_dulint_cmp(index_id, btr_page_get_index_id(page)), 0)
unknown's avatar
unknown committed
831 832 833
	    || !btr_search_check_guess(cursor,
				       can_only_compare_to_cursor_rec,
				       tuple, mode, mtr)) {
834
		if (UNIV_LIKELY(!has_search_latch)) {
unknown's avatar
unknown committed
835
			btr_leaf_page_release(page, latch_mode, mtr);
unknown's avatar
unknown committed
836
		}
837 838 839 840

		goto failure;
	}

841
	if (UNIV_LIKELY(info->n_hash_potential < BTR_SEARCH_BUILD_LIMIT + 5)) {
842

843 844 845 846 847
		info->n_hash_potential++;
	}

#ifdef notdefined
	/* These lines of code can be used in a debug version to check
unknown's avatar
unknown committed
848
	the correctness of the searched cursor position: */
849

850 851 852
	info->last_hash_succ = FALSE;

	/* Currently, does not work if the following fails: */
853
	ut_ad(!has_search_latch);
854

855 856 857
	btr_leaf_page_release(page, latch_mode, mtr);

	btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode,
unknown's avatar
unknown committed
858
				    &cursor2, 0, mtr);
unknown's avatar
unknown committed
859
	if (mode == PAGE_CUR_GE
unknown's avatar
unknown committed
860
	    && page_rec_is_supremum(btr_cur_get_rec(&cursor2))) {
unknown's avatar
unknown committed
861 862 863 864

		/* If mode is PAGE_CUR_GE, then the binary search
		in the index tree may actually take us to the supremum
		of the previous page */
865

unknown's avatar
unknown committed
866 867 868
		info->last_hash_succ = FALSE;

		btr_pcur_open_on_user_rec(index, tuple, mode, latch_mode,
unknown's avatar
unknown committed
869
					  &pcur, mtr);
870
		ut_ad(btr_pcur_get_rec(&pcur) == btr_cur_get_rec(cursor));
unknown's avatar
unknown committed
871
	} else {
872
		ut_ad(btr_cur_get_rec(&cursor2) == btr_cur_get_rec(cursor));
unknown's avatar
unknown committed
873 874 875 876 877
	}

	/* NOTE that it is theoretically possible that the above assertions
	fail if the page of the cursor gets removed from the buffer pool
	meanwhile! Thus it might not be a bug. */
878
#endif
879
	info->last_hash_succ = TRUE;
880 881 882 883

#ifdef UNIV_SEARCH_PERF_STAT
	btr_search_n_succ++;
#endif
884
	if (UNIV_LIKELY(!has_search_latch)
unknown's avatar
unknown committed
885
	    && buf_block_peek_if_too_old(block)) {
886 887

		buf_page_make_young(page);
888
	}
889

890 891 892 893 894
	/* Increment the page get statistics though we did not really
	fix the page: for user info only */

	buf_pool->n_page_gets++;

895
	return(TRUE);
896 897

	/*-------------------------------------------*/
898 899 900 901
failure_unlock:
	if (UNIV_LIKELY(!has_search_latch)) {
		rw_lock_s_unlock(&btr_search_latch);
	}
902 903 904 905
failure:
	cursor->flag = BTR_CUR_HASH_FAIL;

#ifdef UNIV_SEARCH_PERF_STAT
unknown's avatar
unknown committed
906 907
	info->n_hash_fail++;

908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
	if (info->n_hash_succ > 0) {
		info->n_hash_succ--;
	}
#endif
	info->last_hash_succ = FALSE;

	return(FALSE);
}

/************************************************************************
Drops a page hash index. */

void
btr_search_drop_page_hash_index(
/*============================*/
923 924
	page_t*	page)	/* in: index page, s- or x-latched, or an index page
			for which we know that block->buf_fix_count == 0 */
925 926 927
{
	hash_table_t*	table;
	buf_block_t*	block;
928 929 930 931 932
	ulint		n_fields;
	ulint		n_bytes;
	rec_t*		rec;
	ulint		fold;
	ulint		prev_fold;
933
	dulint		index_id;
934 935 936 937
	ulint		n_cached;
	ulint		n_recs;
	ulint*		folds;
	ulint		i;
938
	mem_heap_t*	heap;
939
	dict_index_t*	index;
940
	ulint*		offsets;
941 942 943 944 945

#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
946
retry:
947 948 949 950
	rw_lock_s_lock(&btr_search_latch);

	block = buf_block_align(page);

951
	if (UNIV_LIKELY(!block->is_hashed)) {
952 953 954 955 956 957 958 959

		rw_lock_s_unlock(&btr_search_latch);

		return;
	}

	table = btr_search_sys->hash_index;

960
#ifdef UNIV_SYNC_DEBUG
961
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED)
unknown's avatar
unknown committed
962 963
	      || rw_lock_own(&(block->lock), RW_LOCK_EX)
	      || (block->buf_fix_count == 0));
964
#endif /* UNIV_SYNC_DEBUG */
965

966 967
	n_fields = block->curr_n_fields;
	n_bytes = block->curr_n_bytes;
968
	index = block->index;
969

970 971 972
	/* NOTE: The fields of block must not be accessed after
	releasing btr_search_latch, as the index page might only
	be s-latched! */
unknown's avatar
unknown committed
973

974
	rw_lock_s_unlock(&btr_search_latch);
975 976 977

	ut_a(n_fields + n_bytes > 0);

978 979 980 981 982 983 984 985 986 987 988 989
	n_recs = page_get_n_recs(page);

	/* Calculate and cache fold values into an array for fast deletion
	from the hash index */

	folds = mem_alloc(n_recs * sizeof(ulint));

	n_cached = 0;

	rec = page_get_infimum_rec(page);
	rec = page_rec_get_next(rec);

990
	index_id = btr_page_get_index_id(page);
991

992
	ut_a(0 == ut_dulint_cmp(index_id, index->id));
993 994 995

	prev_fold = 0;

996
	heap = NULL;
997 998
	offsets = NULL;

999
	while (!page_rec_is_supremum(rec)) {
1000
		offsets = rec_get_offsets(rec, index, offsets,
unknown's avatar
unknown committed
1001
					  n_fields + (n_bytes > 0), &heap);
1002
		ut_a(rec_offs_n_fields(offsets) == n_fields + (n_bytes > 0));
1003
		fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019

		if (fold == prev_fold && prev_fold != 0) {

			goto next_rec;
		}

		/* Remove all hash nodes pointing to this page from the
		hash chain */

		folds[n_cached] = fold;
		n_cached++;
next_rec:
		rec = page_rec_get_next(rec);
		prev_fold = fold;
	}

1020
	if (UNIV_LIKELY_NULL(heap)) {
1021 1022
		mem_heap_free(heap);
	}
1023

1024 1025
	rw_lock_x_lock(&btr_search_latch);

1026 1027 1028 1029 1030 1031 1032 1033 1034
	if (UNIV_UNLIKELY(!block->is_hashed)) {
		/* Someone else has meanwhile dropped the hash index */

		goto cleanup;
	}

	ut_a(block->index == index);

	if (UNIV_UNLIKELY(block->curr_n_fields != n_fields)
unknown's avatar
unknown committed
1035
	    || UNIV_UNLIKELY(block->curr_n_bytes != n_bytes)) {
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

		/* Someone else has meanwhile built a new hash index on the
		page, with different parameters */

		rw_lock_x_unlock(&btr_search_latch);

		mem_free(folds);
		goto retry;
	}

1046 1047 1048 1049
	for (i = 0; i < n_cached; i++) {

		ha_remove_all_nodes_to_page(table, folds[i], page);
	}
1050

1051 1052 1053
	ut_a(index->search_info->ref_count > 0);
	index->search_info->ref_count--;

1054
	block->is_hashed = FALSE;
unknown's avatar
unknown committed
1055
	block->index = NULL;
1056
	
1057 1058 1059 1060 1061
cleanup:
	if (UNIV_UNLIKELY(block->n_pointers)) {
		/* Corruption */
		ut_print_timestamp(stderr);
		fprintf(stderr,
unknown's avatar
unknown committed
1062 1063 1064 1065
			"  InnoDB: Corruption of adaptive hash index."
			" After dropping\n"
			"InnoDB: the hash index to a page of %s,"
			" still %lu hash nodes remain.\n",
1066 1067
			index->name, (ulong) block->n_pointers);
		rw_lock_x_unlock(&btr_search_latch);
1068

1069 1070 1071 1072
		btr_search_validate();
	} else {
		rw_lock_x_unlock(&btr_search_latch);
	}
1073 1074

	mem_free(folds);
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
}

/************************************************************************
Drops a page hash index when a page is freed from a fseg to the file system.
Drops possible hash index if the page happens to be in the buffer pool. */

void
btr_search_drop_page_hash_when_freed(
/*=================================*/
	ulint	space,		/* in: space id */
	ulint	page_no)	/* in: page number */
{
	ibool	is_hashed;
	page_t*	page;
	mtr_t	mtr;

	is_hashed = buf_page_peek_if_search_hashed(space, page_no);

	if (!is_hashed) {

		return;
	}
1097

1098 1099
	mtr_start(&mtr);

unknown's avatar
unknown committed
1100 1101 1102 1103
	/* We assume that if the caller has a latch on the page, then the
	caller has already dropped the hash index for the page, and we never
	get here. Therefore we can acquire the s-latch to the page without
	having to fear a deadlock. */
1104

unknown's avatar
unknown committed
1105
	page = buf_page_get_gen(space, page_no, RW_S_LATCH, NULL,
1106
				BUF_GET_IF_IN_POOL, __FILE__, __LINE__,
unknown's avatar
unknown committed
1107
				&mtr);
1108 1109 1110 1111 1112 1113 1114
	/* Because the buffer pool mutex was released by
	buf_page_peek_if_search_hashed(), it is possible that the
	block was removed from the buffer pool by another thread
	before buf_page_get_gen() got a chance to acquire the buffer
	pool mutex again.  Thus, we must check for a NULL return. */

	if (UNIV_LIKELY(page != NULL)) {
1115

1116
#ifdef UNIV_SYNC_DEBUG
1117
		buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH);
1118 1119
#endif /* UNIV_SYNC_DEBUG */

1120 1121
		btr_search_drop_page_hash_index(page);
	}
1122 1123 1124 1125 1126 1127

	mtr_commit(&mtr);
}

/************************************************************************
Builds a hash index on a page with the given parameters. If the page already
unknown's avatar
unknown committed
1128 1129 1130
has a hash index with different parameters, the old hash index is removed.
If index is non-NULL, this function checks if n_fields and n_bytes are
sensible values, and does not build a hash index if not. */
1131 1132 1133 1134
static
void
btr_search_build_page_hash_index(
/*=============================*/
unknown's avatar
unknown committed
1135
	dict_index_t*	index,	/* in: index for which to build */
unknown's avatar
unknown committed
1136 1137 1138
	page_t*		page,	/* in: index page, s- or x-latched */
	ulint		n_fields,/* in: hash this many full fields */
	ulint		n_bytes,/* in: hash this many bytes from the next
1139
				field */
1140
	ibool		left_side)/* in: hash for searches from left side? */
1141 1142 1143 1144 1145 1146 1147
{
	hash_table_t*	table;
	buf_block_t*	block;
	rec_t*		rec;
	rec_t*		next_rec;
	ulint		fold;
	ulint		next_fold;
1148
	dulint		index_id;
1149 1150 1151 1152 1153
	ulint		n_cached;
	ulint		n_recs;
	ulint*		folds;
	rec_t**		recs;
	ulint		i;
1154
	mem_heap_t*	heap		= NULL;
1155
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
1156
	ulint*		offsets		= offsets_;
1157
	*offsets_ = (sizeof offsets_) / sizeof *offsets_;
unknown's avatar
unknown committed
1158 1159 1160

	ut_ad(index);

1161 1162 1163
	block = buf_block_align(page);
	table = btr_search_sys->hash_index;

1164
#ifdef UNIV_SYNC_DEBUG
1165 1166
	ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX));
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED)
unknown's avatar
unknown committed
1167
	      || rw_lock_own(&(block->lock), RW_LOCK_EX));
1168
#endif /* UNIV_SYNC_DEBUG */
1169 1170

	rw_lock_s_lock(&btr_search_latch);
1171

1172
	if (block->is_hashed && ((block->curr_n_fields != n_fields)
unknown's avatar
unknown committed
1173
				 || (block->curr_n_bytes != n_bytes)
1174
				 || (block->curr_left_side != left_side))) {
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189

		rw_lock_s_unlock(&btr_search_latch);

		btr_search_drop_page_hash_index(page);
	} else {
		rw_lock_s_unlock(&btr_search_latch);
	}

	n_recs = page_get_n_recs(page);

	if (n_recs == 0) {

		return;
	}

unknown's avatar
unknown committed
1190
	/* Check that the values for hash index build are sensible */
1191

unknown's avatar
unknown committed
1192 1193
	if (n_fields + n_bytes == 0) {

unknown's avatar
unknown committed
1194 1195 1196
		return;
	}

unknown's avatar
unknown committed
1197
	if (dict_index_get_n_unique_in_tree(index) < n_fields
unknown's avatar
unknown committed
1198 1199
	    || (dict_index_get_n_unique_in_tree(index) == n_fields
		&& n_bytes > 0)) {
unknown's avatar
unknown committed
1200
		return;
unknown's avatar
unknown committed
1201
	}
unknown's avatar
unknown committed
1202

1203 1204 1205 1206 1207 1208 1209 1210
	/* Calculate and cache fold values and corresponding records into
	an array for fast insertion to the hash index */

	folds = mem_alloc(n_recs * sizeof(ulint));
	recs = mem_alloc(n_recs * sizeof(rec_t*));

	n_cached = 0;

1211
	index_id = btr_page_get_index_id(page);
1212 1213 1214 1215

	rec = page_get_infimum_rec(page);
	rec = page_rec_get_next(rec);

1216
	offsets = rec_get_offsets(rec, index, offsets,
unknown's avatar
unknown committed
1217
				  n_fields + (n_bytes > 0), &heap);
unknown's avatar
unknown committed
1218

1219
	if (!page_rec_is_supremum(rec)) {
unknown's avatar
unknown committed
1220
		ut_a(n_fields <= rec_offs_n_fields(offsets));
unknown's avatar
unknown committed
1221 1222

		if (n_bytes > 0) {
unknown's avatar
unknown committed
1223
			ut_a(n_fields < rec_offs_n_fields(offsets));
unknown's avatar
unknown committed
1224 1225 1226
		}
	}

1227
	fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
1228

1229
	if (left_side) {
1230 1231 1232 1233 1234

		folds[n_cached] = fold;
		recs[n_cached] = rec;
		n_cached++;
	}
1235

1236 1237 1238
	for (;;) {
		next_rec = page_rec_get_next(rec);

1239
		if (page_rec_is_supremum(next_rec)) {
1240

1241
			if (!left_side) {
1242

1243 1244 1245 1246 1247
				folds[n_cached] = fold;
				recs[n_cached] = rec;
				n_cached++;
			}

1248
			break;
1249 1250
		}

1251
		offsets = rec_get_offsets(next_rec, index, offsets,
unknown's avatar
unknown committed
1252
					  n_fields + (n_bytes > 0), &heap);
unknown's avatar
unknown committed
1253
		next_fold = rec_fold(next_rec, offsets, n_fields,
1254
				     n_bytes, index_id);
1255 1256 1257 1258

		if (fold != next_fold) {
			/* Insert an entry into the hash index */

1259
			if (left_side) {
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279

				folds[n_cached] = next_fold;
				recs[n_cached] = next_rec;
				n_cached++;
			} else {
				folds[n_cached] = fold;
				recs[n_cached] = rec;
				n_cached++;
			}
		}

		rec = next_rec;
		fold = next_fold;
	}

	btr_search_check_free_space_in_heap();

	rw_lock_x_lock(&btr_search_latch);

	if (block->is_hashed && ((block->curr_n_fields != n_fields)
unknown's avatar
unknown committed
1280
				 || (block->curr_n_bytes != n_bytes)
1281
				 || (block->curr_left_side != left_side))) {
unknown's avatar
unknown committed
1282
		goto exit_func;
1283
	}
1284

1285 1286 1287 1288 1289 1290 1291 1292 1293
	/* This counter is decremented every time we drop page
	hash index entries and is incremented here. Since we can
	rebuild hash index for a page that is already hashed, we
	have to take care not to increment the counter in that
	case. */
	if (!block->is_hashed) {
		index->search_info->ref_count++;
	}

1294 1295
	block->is_hashed = TRUE;
	block->n_hash_helps = 0;
1296

1297 1298
	block->curr_n_fields = n_fields;
	block->curr_n_bytes = n_bytes;
1299
	block->curr_left_side = left_side;
unknown's avatar
unknown committed
1300
	block->index = index;
1301 1302

	for (i = 0; i < n_cached; i++) {
1303

1304 1305 1306
		ha_insert_for_fold(table, folds[i], recs[i]);
	}

unknown's avatar
unknown committed
1307
exit_func:
1308 1309 1310 1311
	rw_lock_x_unlock(&btr_search_latch);

	mem_free(folds);
	mem_free(recs);
1312
	if (UNIV_LIKELY_NULL(heap)) {
1313 1314
		mem_heap_free(heap);
	}
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
}

/************************************************************************
Moves or deletes hash entries for moved records. If new_page is already hashed,
then the hash index for page, if any, is dropped. If new_page is not hashed,
and page is hashed, then a new hash index is built to new_page with the same
parameters as page (this often happens when a page is split). */

void
btr_search_move_or_delete_hash_entries(
/*===================================*/
unknown's avatar
unknown committed
1326 1327 1328 1329 1330 1331 1332
	page_t*		new_page,	/* in: records are copied
					to this page */
	page_t*		page,		/* in: index page from which
					records were copied, and the
					copied records will be deleted
					from this page */
	dict_index_t*	index)		/* in: record descriptor */
1333 1334 1335 1336 1337
{
	buf_block_t*	block;
	buf_block_t*	new_block;
	ulint		n_fields;
	ulint		n_bytes;
1338
	ibool		left_side;
1339 1340 1341

	block = buf_block_align(page);
	new_block = buf_block_align(new_page);
unknown's avatar
unknown committed
1342
	ut_a(page_is_comp(page) == page_is_comp(new_page));
1343

1344 1345 1346 1347
#ifdef UNIV_SYNC_DEBUG
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
	ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
unknown's avatar
unknown committed
1348 1349
	ut_a(!new_block->is_hashed || new_block->index == index);
	ut_a(!block->is_hashed || block->index == index);
1350 1351

	rw_lock_s_lock(&btr_search_latch);
1352

1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
	if (new_block->is_hashed) {

		rw_lock_s_unlock(&btr_search_latch);

		btr_search_drop_page_hash_index(page);

		return;
	}

	if (block->is_hashed) {

		n_fields = block->curr_n_fields;
		n_bytes = block->curr_n_bytes;
1366
		left_side = block->curr_left_side;
1367 1368 1369

		new_block->n_fields = block->curr_n_fields;
		new_block->n_bytes = block->curr_n_bytes;
1370
		new_block->left_side = left_side;
1371 1372 1373

		rw_lock_s_unlock(&btr_search_latch);

unknown's avatar
unknown committed
1374
		ut_a(n_fields + n_bytes > 0);
unknown's avatar
unknown committed
1375 1376

		btr_search_build_page_hash_index(index, new_page, n_fields,
1377 1378
						 n_bytes, left_side);
#if 1 /* TODO: safe to remove? */
1379 1380
		ut_a(n_fields == block->curr_n_fields);
		ut_a(n_bytes == block->curr_n_bytes);
1381 1382
		ut_a(left_side == block->curr_left_side);
#endif
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402
		return;
	}

	rw_lock_s_unlock(&btr_search_latch);
}

/************************************************************************
Updates the page hash index when a single record is deleted from a page. */

void
btr_search_update_hash_on_delete(
/*=============================*/
	btr_cur_t*	cursor)	/* in: cursor which was positioned on the
				record to delete using btr_cur_search_...,
				the record is not yet deleted */
{
	hash_table_t*	table;
	buf_block_t*	block;
	rec_t*		rec;
	ulint		fold;
1403
	dulint		index_id;
1404
	ibool		found;
1405
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
1406
	mem_heap_t*	heap		= NULL;
1407
	*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1408 1409 1410 1411 1412

	rec = btr_cur_get_rec(cursor);

	block = buf_block_align(rec);

1413
#ifdef UNIV_SYNC_DEBUG
1414
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
1415
#endif /* UNIV_SYNC_DEBUG */
1416 1417 1418 1419 1420 1421

	if (!block->is_hashed) {

		return;
	}

unknown's avatar
unknown committed
1422
	ut_a(block->index == cursor->index);
unknown's avatar
unknown committed
1423 1424
	ut_a(block->curr_n_fields + block->curr_n_bytes > 0);

1425 1426
	table = btr_search_sys->hash_index;

1427
	index_id = cursor->index->id;
1428
	fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, offsets_,
unknown's avatar
unknown committed
1429
					     ULINT_UNDEFINED, &heap),
1430
			block->curr_n_fields, block->curr_n_bytes, index_id);
1431
	if (UNIV_LIKELY_NULL(heap)) {
1432 1433
		mem_heap_free(heap);
	}
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
	rw_lock_x_lock(&btr_search_latch);

	found = ha_search_and_delete_if_found(table, fold, rec);

	rw_lock_x_unlock(&btr_search_latch);
}

/************************************************************************
Updates the page hash index when a single record is inserted on a page. */

void
btr_search_update_hash_node_on_insert(
/*==================================*/
	btr_cur_t*	cursor)	/* in: cursor which was positioned to the
				place to insert using btr_cur_search_...,
				and the new record has been inserted next
				to the cursor */
{
	hash_table_t*	table;
	buf_block_t*	block;
	rec_t*		rec;

	rec = btr_cur_get_rec(cursor);

	block = buf_block_align(rec);

1460
#ifdef UNIV_SYNC_DEBUG
1461
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
1462
#endif /* UNIV_SYNC_DEBUG */
1463 1464 1465 1466 1467 1468

	if (!block->is_hashed) {

		return;
	}

unknown's avatar
unknown committed
1469 1470
	ut_a(block->index == cursor->index);

1471 1472 1473
	rw_lock_x_lock(&btr_search_latch);

	if ((cursor->flag == BTR_CUR_HASH)
unknown's avatar
unknown committed
1474 1475
	    && (cursor->n_fields == block->curr_n_fields)
	    && (cursor->n_bytes == block->curr_n_bytes)
1476
	    && !block->curr_left_side) {
1477 1478

		table = btr_search_sys->hash_index;
1479

1480
		ha_search_and_update_if_found(table, cursor->fold, rec,
unknown's avatar
unknown committed
1481
					      page_rec_get_next(rec));
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501

		rw_lock_x_unlock(&btr_search_latch);
	} else {
		rw_lock_x_unlock(&btr_search_latch);

		btr_search_update_hash_on_insert(cursor);
	}
}

/************************************************************************
Updates the page hash index when a single record is inserted on a page. */

void
btr_search_update_hash_on_insert(
/*=============================*/
	btr_cur_t*	cursor)	/* in: cursor which was positioned to the
				place to insert using btr_cur_search_...,
				and the new record has been inserted next
				to the cursor */
{
1502
	hash_table_t*	table;
1503 1504 1505 1506
	buf_block_t*	block;
	rec_t*		rec;
	rec_t*		ins_rec;
	rec_t*		next_rec;
1507
	dulint		index_id;
1508 1509
	ulint		fold;
	ulint		ins_fold;
unknown's avatar
unknown committed
1510
	ulint		next_fold = 0; /* remove warning (??? bug ???) */
1511 1512
	ulint		n_fields;
	ulint		n_bytes;
1513
	ibool		left_side;
1514 1515
	ibool		locked		= FALSE;
	mem_heap_t*	heap		= NULL;
1516
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
1517
	ulint*		offsets		= offsets_;
1518
	*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1519 1520 1521 1522 1523 1524 1525 1526 1527

	table = btr_search_sys->hash_index;

	btr_search_check_free_space_in_heap();

	rec = btr_cur_get_rec(cursor);

	block = buf_block_align(rec);

1528
#ifdef UNIV_SYNC_DEBUG
1529
	ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX));
1530
#endif /* UNIV_SYNC_DEBUG */
1531

1532 1533 1534 1535 1536
	if (!block->is_hashed) {

		return;
	}

unknown's avatar
unknown committed
1537 1538
	ut_a(block->index == cursor->index);

1539
	index_id = cursor->index->id;
1540 1541 1542

	n_fields = block->curr_n_fields;
	n_bytes = block->curr_n_bytes;
1543
	left_side = block->curr_left_side;
1544 1545 1546 1547

	ins_rec = page_rec_get_next(rec);
	next_rec = page_rec_get_next(ins_rec);

1548
	offsets = rec_get_offsets(ins_rec, cursor->index, offsets,
unknown's avatar
unknown committed
1549
				  ULINT_UNDEFINED, &heap);
1550
	ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, index_id);
1551

1552
	if (!page_rec_is_supremum(next_rec)) {
1553
		offsets = rec_get_offsets(next_rec, cursor->index, offsets,
unknown's avatar
unknown committed
1554
					  n_fields + (n_bytes > 0), &heap);
unknown's avatar
unknown committed
1555
		next_fold = rec_fold(next_rec, offsets, n_fields,
1556
				     n_bytes, index_id);
1557 1558
	}

1559
	if (!page_rec_is_infimum(rec)) {
1560
		offsets = rec_get_offsets(rec, cursor->index, offsets,
unknown's avatar
unknown committed
1561
					  n_fields + (n_bytes > 0), &heap);
1562
		fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id);
1563
	} else {
1564
		if (left_side) {
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575

			rw_lock_x_lock(&btr_search_latch);

			locked = TRUE;

			ha_insert_for_fold(table, ins_fold, ins_rec);
		}

		goto check_next_rec;
	}

1576 1577 1578
	if (fold != ins_fold) {

		if (!locked) {
1579 1580 1581 1582 1583 1584

			rw_lock_x_lock(&btr_search_latch);

			locked = TRUE;
		}

1585
		if (!left_side) {
1586 1587 1588 1589 1590 1591 1592
			ha_insert_for_fold(table, fold, rec);
		} else {
			ha_insert_for_fold(table, ins_fold, ins_rec);
		}
	}

check_next_rec:
1593
	if (page_rec_is_supremum(next_rec)) {
1594

1595
		if (!left_side) {
1596

1597
			if (!locked) {
1598 1599 1600 1601
				rw_lock_x_lock(&btr_search_latch);

				locked = TRUE;
			}
1602

1603 1604 1605 1606 1607
			ha_insert_for_fold(table, ins_fold, ins_rec);
		}

		goto function_exit;
	}
1608

1609 1610
	if (ins_fold != next_fold) {

1611 1612
		if (!locked) {

1613 1614 1615 1616 1617
			rw_lock_x_lock(&btr_search_latch);

			locked = TRUE;
		}

1618
		if (!left_side) {
1619 1620

			ha_insert_for_fold(table, ins_fold, ins_rec);
unknown's avatar
unknown committed
1621
			/*
1622 1623 1624
			fputs("Hash insert for ", stderr);
			dict_index_name_print(stderr, cursor->index);
			fprintf(stderr, " fold %lu\n", ins_fold);
unknown's avatar
unknown committed
1625
			*/
1626 1627 1628
		} else {
			ha_insert_for_fold(table, next_fold, next_rec);
		}
1629 1630
	}

1631
function_exit:
1632
	if (UNIV_LIKELY_NULL(heap)) {
1633 1634
		mem_heap_free(heap);
	}
1635 1636 1637 1638 1639 1640 1641 1642 1643
	if (locked) {
		rw_lock_x_unlock(&btr_search_latch);
	}
}

/************************************************************************
Validates the search system. */

ibool
unknown's avatar
unknown committed
1644 1645
btr_search_validate(void)
/*=====================*/
1646 1647
				/* out: TRUE if ok */
{
unknown's avatar
unknown committed
1648 1649 1650 1651 1652 1653
	buf_block_t*	block;
	page_t*		page;
	ha_node_t*	node;
	ulint		n_page_dumps	= 0;
	ibool		ok		= TRUE;
	ulint		i;
1654
	ulint		cell_count;
1655
	mem_heap_t*	heap		= NULL;
1656
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
1657
	ulint*		offsets		= offsets_;
unknown's avatar
unknown committed
1658 1659 1660 1661 1662

	/* How many cells to check before temporarily releasing
	btr_search_latch. */
	ulint		chunk_size = 10000;

unknown's avatar
unknown committed
1663 1664
	*offsets_ = (sizeof offsets_) / sizeof *offsets_;

1665
	rw_lock_x_lock(&btr_search_latch);
unknown's avatar
unknown committed
1666

1667
	cell_count = hash_get_n_cells(btr_search_sys->hash_index);
unknown's avatar
unknown committed
1668

1669 1670 1671
	for (i = 0; i < cell_count; i++) {
		/* We release btr_search_latch every once in a while to
		give other queries a chance to run. */
unknown's avatar
unknown committed
1672
		if ((i != 0) && ((i % chunk_size) == 0)) {
1673 1674 1675 1676
			rw_lock_x_unlock(&btr_search_latch);
			os_thread_yield();
			rw_lock_x_lock(&btr_search_latch);
		}
unknown's avatar
unknown committed
1677

unknown's avatar
unknown committed
1678 1679 1680 1681 1682
		node = hash_get_nth_cell(btr_search_sys->hash_index, i)->node;

		while (node != NULL) {
			block = buf_block_align(node->data);
			page = buf_frame_align(node->data);
1683
			offsets = rec_get_offsets((rec_t*) node->data,
unknown's avatar
unknown committed
1684 1685 1686 1687
						  block->index, offsets,
						  block->curr_n_fields
						  + (block->curr_n_bytes > 0),
						  &heap);
unknown's avatar
unknown committed
1688

unknown's avatar
unknown committed
1689 1690
			if (!block->is_hashed || node->fold
			    != rec_fold((rec_t*)(node->data),
1691 1692 1693 1694
					offsets,
					block->curr_n_fields,
					block->curr_n_bytes,
					btr_page_get_index_id(page))) {
unknown's avatar
unknown committed
1695 1696 1697 1698
				ok = FALSE;
				ut_print_timestamp(stderr);

				fprintf(stderr,
unknown's avatar
unknown committed
1699 1700 1701 1702 1703
					"  InnoDB: Error in an adaptive hash"
					" index pointer to page %lu\n"
					"InnoDB: ptr mem address %p"
					" index id %lu %lu,"
					" node fold %lu, rec fold %lu\n",
unknown's avatar
unknown committed
1704
					(ulong) buf_frame_get_page_no(page),
1705
					node->data,
1706 1707 1708 1709
					(ulong) ut_dulint_get_high(
						btr_page_get_index_id(page)),
					(ulong) ut_dulint_get_low(
						btr_page_get_index_id(page)),
unknown's avatar
unknown committed
1710 1711
					(ulong) node->fold,
					(ulong) rec_fold((rec_t*)(node->data),
unknown's avatar
unknown committed
1712 1713 1714
							 offsets,
							 block->curr_n_fields,
							 block->curr_n_bytes,
1715 1716
							 btr_page_get_index_id(
								 page)));
unknown's avatar
unknown committed
1717

1718
				fputs("InnoDB: Record ", stderr);
1719
				rec_print_new(stderr, (rec_t*)node->data,
unknown's avatar
unknown committed
1720
					      offsets);
1721
				fprintf(stderr, "\nInnoDB: on that page."
unknown's avatar
unknown committed
1722 1723 1724 1725 1726 1727
					" Page mem address %p, is hashed %lu,"
					" n fields %lu, n bytes %lu\n"
					"InnoDB: side %lu\n",
					(void*) page, (ulong) block->is_hashed,
					(ulong) block->curr_n_fields,
					(ulong) block->curr_n_bytes,
1728
					(ulong) block->curr_left_side);
unknown's avatar
unknown committed
1729

1730
				if (n_page_dumps < 20) {
unknown's avatar
unknown committed
1731 1732 1733 1734 1735 1736 1737 1738 1739
					buf_page_print(page);
					n_page_dumps++;
				}
			}

			node = node->next;
		}
	}

unknown's avatar
unknown committed
1740 1741 1742
	for (i = 0; i < cell_count; i += chunk_size) {
		ulint end_index = ut_min(i + chunk_size - 1, cell_count - 1);

1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753
		/* We release btr_search_latch every once in a while to
		give other queries a chance to run. */
		if (i != 0) {
			rw_lock_x_unlock(&btr_search_latch);
			os_thread_yield();
			rw_lock_x_lock(&btr_search_latch);
		}

		if (!ha_validate(btr_search_sys->hash_index, i, end_index)) {
			ok = FALSE;
		}
unknown's avatar
unknown committed
1754
	}
1755 1756

	rw_lock_x_unlock(&btr_search_latch);
1757
	if (UNIV_LIKELY_NULL(heap)) {
1758 1759
		mem_heap_free(heap);
	}
1760

unknown's avatar
unknown committed
1761
	return(ok);
1762
}