1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8 
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation.  The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License, version 2.0, for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24 
25 *****************************************************************************/
26 
27 /**************************************************//**
28 @file btr/btr0pcur.cc
29 The index tree persistent cursor
30 
31 Created 2/23/1996 Heikki Tuuri
32 *******************************************************/
33 
34 #include "btr0pcur.h"
35 
36 #ifdef UNIV_NONINL
37 #include "btr0pcur.ic"
38 #endif
39 
40 #include "ut0byte.h"
41 #include "rem0cmp.h"
42 #include "trx0trx.h"
43 #include "srv0srv.h"
44 
45 /** Updates fragmentation statistics for a single page transition.
46 @param[in]	page			the current page being processed
47 @param[in]	page_no			page number to move to (next_page_no
48 					if forward_direction is true,
49 					prev_page_no otherwise.
50 @param[in]	forward_direction	move direction: true means moving
51 					forward, false - backward. */
52 static
53 void
btr_update_scan_stats(const page_t * page,ulint page_no,bool forward_direction)54 btr_update_scan_stats(const page_t* page, ulint page_no, bool forward_direction)
55 {
56 	fragmentation_stats_t	stats;
57 	memset(&stats, 0, sizeof(stats));
58 	ulint	extracted_page_no = page_get_page_no(page);
59 	ulint	delta = forward_direction ?
60 		page_no - extracted_page_no :
61 		extracted_page_no - page_no;
62 
63 	if (delta == 1) {
64 		++stats.scan_pages_contiguous;
65 	} else {
66 		++stats.scan_pages_disjointed;
67 	}
68 	stats.scan_pages_total_seek_distance +=
69 		extracted_page_no > page_no ?
70 		extracted_page_no - page_no :
71 		page_no - extracted_page_no;
72 
73 	stats.scan_data_size += page_get_data_size(page);
74 	stats.scan_deleted_recs_size +=
75 		page_header_get_field(page, PAGE_GARBAGE);
76 	thd_add_fragmentation_stats(current_thd, &stats);
77 }
78 /**************************************************************//**
79 Allocates memory for a persistent cursor object and initializes the cursor.
80 @return	own: persistent cursor */
81 UNIV_INTERN
82 btr_pcur_t*
btr_pcur_create_for_mysql(void)83 btr_pcur_create_for_mysql(void)
84 /*============================*/
85 {
86 	btr_pcur_t*	pcur;
87 
88 	pcur = (btr_pcur_t*) mem_alloc(sizeof(btr_pcur_t));
89 
90 	pcur->btr_cur.index = NULL;
91 	btr_pcur_init(pcur);
92 	pcur->btr_cur.tree_height = ULINT_UNDEFINED;
93 
94 	return(pcur);
95 }
96 
97 /**************************************************************//**
98 Resets a persistent cursor object, freeing ::old_rec_buf if it is
99 allocated and resetting the other members to their initial values. */
100 UNIV_INTERN
101 void
btr_pcur_reset(btr_pcur_t * cursor)102 btr_pcur_reset(
103 /*===========*/
104 	btr_pcur_t*	cursor)	/*!< in, out: persistent cursor */
105 {
106 	if (cursor->old_rec_buf != NULL) {
107 
108 		mem_free(cursor->old_rec_buf);
109 
110 		cursor->old_rec_buf = NULL;
111 	}
112 
113 	cursor->btr_cur.index = NULL;
114 	cursor->btr_cur.page_cur.rec = NULL;
115 	cursor->old_rec = NULL;
116 	cursor->old_n_fields = 0;
117 	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
118 
119 	cursor->latch_mode = BTR_NO_LATCHES;
120 	cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
121 }
122 
123 /**************************************************************//**
124 Frees the memory for a persistent cursor object. */
125 UNIV_INTERN
126 void
btr_pcur_free_for_mysql(btr_pcur_t * cursor)127 btr_pcur_free_for_mysql(
128 /*====================*/
129 	btr_pcur_t*	cursor)	/*!< in, own: persistent cursor */
130 {
131 	btr_pcur_reset(cursor);
132 	mem_free(cursor);
133 }
134 
135 /**************************************************************//**
136 The position of the cursor is stored by taking an initial segment of the
137 record the cursor is positioned on, before, or after, and copying it to the
138 cursor data structure, or just setting a flag if the cursor id before the
139 first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
140 page where the cursor is positioned must not be empty if the index tree is
141 not totally empty! */
142 UNIV_INTERN
143 void
btr_pcur_store_position(btr_pcur_t * cursor,mtr_t * mtr)144 btr_pcur_store_position(
145 /*====================*/
146 	btr_pcur_t*	cursor, /*!< in: persistent cursor */
147 	mtr_t*		mtr)	/*!< in: mtr */
148 {
149 	page_cur_t*	page_cursor;
150 	buf_block_t*	block;
151 	rec_t*		rec;
152 	dict_index_t*	index;
153 	page_t*		page;
154 	ulint		offs;
155 
156 	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
157 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
158 
159 	block = btr_pcur_get_block(cursor);
160 
161 	SRV_CORRUPT_TABLE_CHECK(block, return;);
162 
163 	index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
164 
165 	page_cursor = btr_pcur_get_page_cur(cursor);
166 
167 	rec = page_cur_get_rec(page_cursor);
168 	page = page_align(rec);
169 	offs = page_offset(rec);
170 
171 	ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
172 	      || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
173 
174 	if (page_is_empty(page)) {
175 		/* It must be an empty index tree; NOTE that in this case
176 		we do not store the modify_clock, but always do a search
177 		if we restore the cursor position */
178 
179 		ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
180 		ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
181 		ut_ad(page_is_leaf(page));
182 		ut_ad(page_get_page_no(page) == index->page);
183 
184 		cursor->old_stored = BTR_PCUR_OLD_STORED;
185 
186 		if (page_rec_is_supremum_low(offs)) {
187 
188 			cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
189 		} else {
190 			cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
191 		}
192 
193 		return;
194 	}
195 
196 	if (page_rec_is_supremum_low(offs)) {
197 
198 		rec = page_rec_get_prev(rec);
199 
200 		cursor->rel_pos = BTR_PCUR_AFTER;
201 
202 	} else if (page_rec_is_infimum_low(offs)) {
203 
204 		rec = page_rec_get_next(rec);
205 
206 		cursor->rel_pos = BTR_PCUR_BEFORE;
207 	} else {
208 		cursor->rel_pos = BTR_PCUR_ON;
209 	}
210 
211 	cursor->old_stored = BTR_PCUR_OLD_STORED;
212 	cursor->old_rec = dict_index_copy_rec_order_prefix(
213 		index, rec, &cursor->old_n_fields,
214 		&cursor->old_rec_buf, &cursor->buf_size);
215 
216 	cursor->block_when_stored = block;
217 	cursor->modify_clock = buf_block_get_modify_clock(block);
218 }
219 
220 /**************************************************************//**
221 Copies the stored position of a pcur to another pcur. */
222 UNIV_INTERN
223 void
btr_pcur_copy_stored_position(btr_pcur_t * pcur_receive,btr_pcur_t * pcur_donate)224 btr_pcur_copy_stored_position(
225 /*==========================*/
226 	btr_pcur_t*	pcur_receive,	/*!< in: pcur which will receive the
227 					position info */
228 	btr_pcur_t*	pcur_donate)	/*!< in: pcur from which the info is
229 					copied */
230 {
231 	if (pcur_receive->old_rec_buf) {
232 		mem_free(pcur_receive->old_rec_buf);
233 	}
234 
235 	ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
236 
237 	if (pcur_donate->old_rec_buf) {
238 
239 		pcur_receive->old_rec_buf = (byte*)
240 			mem_alloc(pcur_donate->buf_size);
241 
242 		ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
243 			  pcur_donate->buf_size);
244 		pcur_receive->old_rec = pcur_receive->old_rec_buf
245 			+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
246 	}
247 
248 	pcur_receive->old_n_fields = pcur_donate->old_n_fields;
249 }
250 
251 /**************************************************************//**
252 Restores the stored position of a persistent cursor bufferfixing the page and
253 obtaining the specified latches. If the cursor position was saved when the
254 (1) cursor was positioned on a user record: this function restores the position
255 to the last record LESS OR EQUAL to the stored record;
256 (2) cursor was positioned on a page infimum record: restores the position to
257 the last record LESS than the user record which was the successor of the page
258 infimum;
259 (3) cursor was positioned on the page supremum: restores to the first record
260 GREATER than the user record which was the predecessor of the supremum.
261 (4) cursor was positioned before the first or after the last in an empty tree:
262 restores to before first or after the last in the tree.
263 @return TRUE if the cursor position was stored when it was on a user
264 record and it can be restored on a user record whose ordering fields
265 are identical to the ones of the original user record */
266 UNIV_INTERN
267 ibool
btr_pcur_restore_position_func(ulint latch_mode,btr_pcur_t * cursor,const char * file,ulint line,mtr_t * mtr)268 btr_pcur_restore_position_func(
269 /*===========================*/
270 	ulint		latch_mode,	/*!< in: BTR_SEARCH_LEAF, ... */
271 	btr_pcur_t*	cursor,		/*!< in: detached persistent cursor */
272 	const char*	file,		/*!< in: file name */
273 	ulint		line,		/*!< in: line where called */
274 	mtr_t*		mtr)		/*!< in: mtr */
275 {
276 	dict_index_t*	index;
277 	dtuple_t*	tuple;
278 	ulint		mode;
279 	ulint		old_mode;
280 	mem_heap_t*	heap;
281 
282 	ut_ad(mtr);
283 	ut_ad(mtr->state == MTR_ACTIVE);
284 	ut_ad(cursor->old_stored == BTR_PCUR_OLD_STORED);
285 	ut_ad(cursor->pos_state == BTR_PCUR_WAS_POSITIONED
286 	      || cursor->pos_state == BTR_PCUR_IS_POSITIONED);
287 
288 	index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
289 
290 	if (UNIV_UNLIKELY
291 	    (cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
292 	     || cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
293 
294 		/* In these cases we do not try an optimistic restoration,
295 		but always do a search */
296 
297 		btr_cur_open_at_index_side(
298 			cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
299 			index, latch_mode,
300 			btr_pcur_get_btr_cur(cursor), 0, mtr);
301 
302 		cursor->latch_mode = latch_mode;
303 		cursor->pos_state = BTR_PCUR_IS_POSITIONED;
304 		cursor->block_when_stored = btr_pcur_get_block(cursor);
305 
306 		return(FALSE);
307 	}
308 
309 	ut_a(cursor->old_rec);
310 	ut_a(cursor->old_n_fields);
311 
312 	if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF)
313 	    || UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) {
314 		/* Try optimistic restoration. */
315 
316 		if (buf_page_optimistic_get(latch_mode,
317 					    cursor->block_when_stored,
318 					    cursor->modify_clock,
319 					    file, line, mtr)) {
320 			cursor->pos_state = BTR_PCUR_IS_POSITIONED;
321 			cursor->latch_mode = latch_mode;
322 
323 			buf_block_dbg_add_level(
324 				btr_pcur_get_block(cursor),
325 				dict_index_is_ibuf(index)
326 				? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE);
327 
328 			if (cursor->rel_pos == BTR_PCUR_ON) {
329 #ifdef UNIV_DEBUG
330 				const rec_t*	rec;
331 				const ulint*	offsets1;
332 				const ulint*	offsets2;
333 				rec = btr_pcur_get_rec(cursor);
334 
335 				heap = mem_heap_create(256);
336 				offsets1 = rec_get_offsets(
337 					cursor->old_rec, index, NULL,
338 					cursor->old_n_fields, &heap);
339 				offsets2 = rec_get_offsets(
340 					rec, index, NULL,
341 					cursor->old_n_fields, &heap);
342 
343 				ut_ad(!cmp_rec_rec(cursor->old_rec,
344 						   rec, offsets1, offsets2,
345 						   index));
346 				mem_heap_free(heap);
347 #endif /* UNIV_DEBUG */
348 				return(TRUE);
349 			}
350 			/* This is the same record as stored,
351 			may need to be adjusted for BTR_PCUR_BEFORE/AFTER,
352 			depending on search mode and direction. */
353 			if (btr_pcur_is_on_user_rec(cursor)) {
354 				cursor->pos_state
355 					= BTR_PCUR_IS_POSITIONED_OPTIMISTIC;
356 			}
357 			return(FALSE);
358 		}
359 	}
360 
361 	/* If optimistic restoration did not succeed, open the cursor anew */
362 
363 	heap = mem_heap_create(256);
364 
365 	tuple = dict_index_build_data_tuple(index, cursor->old_rec,
366 					    cursor->old_n_fields, heap);
367 
368 	/* Save the old search mode of the cursor */
369 	old_mode = cursor->search_mode;
370 
371 	switch (cursor->rel_pos) {
372 	case BTR_PCUR_ON:
373 		mode = PAGE_CUR_LE;
374 		break;
375 	case BTR_PCUR_AFTER:
376 		mode = PAGE_CUR_G;
377 		break;
378 	case BTR_PCUR_BEFORE:
379 		mode = PAGE_CUR_L;
380 		break;
381 	default:
382 		ut_error;
383 		mode = 0;
384 	}
385 
386 	btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode,
387 					cursor, 0, file, line, mtr);
388 
389 	/* Restore the old search mode */
390 	cursor->search_mode = old_mode;
391 
392 	switch (cursor->rel_pos) {
393 	case BTR_PCUR_ON:
394 		if (btr_pcur_is_on_user_rec(cursor)
395 		    && !cmp_dtuple_rec(
396 			    tuple, btr_pcur_get_rec(cursor),
397 			    rec_get_offsets(btr_pcur_get_rec(cursor),
398 					    index, NULL,
399 					    ULINT_UNDEFINED, &heap))) {
400 
401 			/* We have to store the NEW value for
402 			the modify clock, since the cursor can
403 			now be on a different page! But we can
404 			retain the value of old_rec */
405 
406 			cursor->block_when_stored =
407 				btr_pcur_get_block(cursor);
408 			cursor->modify_clock =
409 				buf_block_get_modify_clock(
410 					cursor->block_when_stored);
411 			cursor->old_stored = BTR_PCUR_OLD_STORED;
412 
413 			mem_heap_free(heap);
414 
415 			return(TRUE);
416 		}
417 #ifdef UNIV_DEBUG
418 		/* fall through */
419 	case BTR_PCUR_BEFORE:
420 	case BTR_PCUR_AFTER:
421 		break;
422 	default:
423 		ut_error;
424 #endif /* UNIV_DEBUG */
425 	}
426 
427 	mem_heap_free(heap);
428 
429 	/* We have to store new position information, modify_clock etc.,
430 	to the cursor because it can now be on a different page, the record
431 	under it may have been removed, etc. */
432 
433 	btr_pcur_store_position(cursor, mtr);
434 
435 	return(FALSE);
436 }
437 
438 /*********************************************************//**
439 Moves the persistent cursor to the first record on the next page. Releases the
440 latch on the current page, and bufferunfixes it. Note that there must not be
441 modifications on the current page, as then the x-latch can be released only in
442 mtr_commit. */
443 UNIV_INTERN
444 void
btr_pcur_move_to_next_page(btr_pcur_t * cursor,mtr_t * mtr)445 btr_pcur_move_to_next_page(
446 /*=======================*/
447 	btr_pcur_t*	cursor,	/*!< in: persistent cursor; must be on the
448 				last record of the current page */
449 	mtr_t*		mtr)	/*!< in: mtr */
450 {
451 	ulint		next_page_no;
452 	ulint		space;
453 	ulint		zip_size;
454 	page_t*		page;
455 	buf_block_t*	next_block;
456 	page_t*		next_page;
457 
458 	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
459 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
460 	ut_ad(btr_pcur_is_after_last_on_page(cursor));
461 
462 	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
463 
464 	page = btr_pcur_get_page(cursor);
465 	next_page_no = btr_page_get_next(page, mtr);
466 	space = buf_block_get_space(btr_pcur_get_block(cursor));
467 	zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor));
468 
469 	ut_ad(next_page_no != FIL_NULL);
470 
471 	btr_update_scan_stats(page, next_page_no, true /* forward */);
472 
473 	next_block = btr_block_get(space, zip_size, next_page_no,
474 				   cursor->latch_mode,
475 				   btr_pcur_get_btr_cur(cursor)->index, mtr);
476 	next_page = buf_block_get_frame(next_block);
477 
478 	SRV_CORRUPT_TABLE_CHECK(next_page,
479 	{
480 		btr_leaf_page_release(btr_pcur_get_block(cursor),
481 				      cursor->latch_mode, mtr);
482 		btr_pcur_get_page_cur(cursor)->block = 0;
483 		btr_pcur_get_page_cur(cursor)->rec = 0;
484 
485 		return;
486 	});
487 
488 #ifdef UNIV_BTR_DEBUG
489 	ut_a(page_is_comp(next_page) == page_is_comp(page));
490 	ut_a(btr_page_get_prev(next_page, mtr)
491 	     == buf_block_get_page_no(btr_pcur_get_block(cursor)));
492 #endif /* UNIV_BTR_DEBUG */
493 	next_block->check_index_page_at_flush = TRUE;
494 
495 	btr_leaf_page_release(btr_pcur_get_block(cursor),
496 			      cursor->latch_mode, mtr);
497 
498 	page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
499 
500 	page_check_dir(next_page);
501 }
502 
503 /*********************************************************//**
504 Moves the persistent cursor backward if it is on the first record of the page.
505 Commits mtr. Note that to prevent a possible deadlock, the operation
506 first stores the position of the cursor, commits mtr, acquires the necessary
507 latches and restores the cursor position again before returning. The
508 alphabetical position of the cursor is guaranteed to be sensible on
509 return, but it may happen that the cursor is not positioned on the last
510 record of any page, because the structure of the tree may have changed
511 during the time when the cursor had no latches. */
512 UNIV_INTERN
513 void
btr_pcur_move_backward_from_page(btr_pcur_t * cursor,mtr_t * mtr)514 btr_pcur_move_backward_from_page(
515 /*=============================*/
516 	btr_pcur_t*	cursor,	/*!< in: persistent cursor, must be on the first
517 				record of the current page */
518 	mtr_t*		mtr)	/*!< in: mtr */
519 {
520 	ulint		prev_page_no;
521 	page_t*		page;
522 	buf_block_t*	prev_block;
523 	ulint		latch_mode;
524 	ulint		latch_mode2;
525 
526 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
527 	ut_ad(btr_pcur_is_before_first_on_page(cursor));
528 	ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
529 
530 	latch_mode = cursor->latch_mode;
531 
532 	if (latch_mode == BTR_SEARCH_LEAF) {
533 
534 		latch_mode2 = BTR_SEARCH_PREV;
535 
536 	} else if (latch_mode == BTR_MODIFY_LEAF) {
537 
538 		latch_mode2 = BTR_MODIFY_PREV;
539 	} else {
540 		latch_mode2 = 0; /* To eliminate compiler warning */
541 		ut_error;
542 	}
543 
544 	btr_pcur_store_position(cursor, mtr);
545 
546 	mtr_commit(mtr);
547 
548 	mtr_start(mtr);
549 
550 	btr_pcur_restore_position(latch_mode2, cursor, mtr);
551 
552 	page = btr_pcur_get_page(cursor);
553 
554 	prev_page_no = btr_page_get_prev(page, mtr);
555 
556 	if (prev_page_no != FIL_NULL) {
557 		btr_update_scan_stats(page, prev_page_no, false /* backward */);
558 	}
559 
560 	if (prev_page_no == FIL_NULL) {
561 	} else if (btr_pcur_is_before_first_on_page(cursor)) {
562 
563 		prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
564 
565 		btr_leaf_page_release(btr_pcur_get_block(cursor),
566 				      latch_mode, mtr);
567 
568 		page_cur_set_after_last(prev_block,
569 					btr_pcur_get_page_cur(cursor));
570 	} else {
571 
572 		/* The repositioned cursor did not end on an infimum record on
573 		a page. Cursor repositioning acquired a latch also on the
574 		previous page, but we do not need the latch: release it. */
575 
576 		prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
577 
578 		btr_leaf_page_release(prev_block, latch_mode, mtr);
579 	}
580 
581 	cursor->latch_mode = latch_mode;
582 
583 	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
584 }
585 
586 /*********************************************************//**
587 Moves the persistent cursor to the previous record in the tree. If no records
588 are left, the cursor stays 'before first in tree'.
589 @return	TRUE if the cursor was not before first in tree */
590 UNIV_INTERN
591 ibool
btr_pcur_move_to_prev(btr_pcur_t * cursor,mtr_t * mtr)592 btr_pcur_move_to_prev(
593 /*==================*/
594 	btr_pcur_t*	cursor,	/*!< in: persistent cursor; NOTE that the
595 				function may release the page latch */
596 	mtr_t*		mtr)	/*!< in: mtr */
597 {
598 	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
599 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
600 
601 	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
602 
603 	if (btr_pcur_is_before_first_on_page(cursor)) {
604 
605 		if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
606 
607 			return(FALSE);
608 		}
609 
610 		btr_pcur_move_backward_from_page(cursor, mtr);
611 
612 		return(TRUE);
613 	}
614 
615 	btr_pcur_move_to_prev_on_page(cursor);
616 
617 	return(TRUE);
618 }
619 
620 /**************************************************************//**
621 If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
622 user record satisfying the search condition, in the case PAGE_CUR_L or
623 PAGE_CUR_LE, on the last user record. If no such user record exists, then
624 in the first case sets the cursor after last in tree, and in the latter case
625 before first in tree. The latching mode must be BTR_SEARCH_LEAF or
626 BTR_MODIFY_LEAF. */
627 UNIV_INTERN
628 void
btr_pcur_open_on_user_rec_func(dict_index_t * index,const dtuple_t * tuple,ulint mode,ulint latch_mode,btr_pcur_t * cursor,const char * file,ulint line,mtr_t * mtr)629 btr_pcur_open_on_user_rec_func(
630 /*===========================*/
631 	dict_index_t*	index,		/*!< in: index */
632 	const dtuple_t*	tuple,		/*!< in: tuple on which search done */
633 	ulint		mode,		/*!< in: PAGE_CUR_L, ... */
634 	ulint		latch_mode,	/*!< in: BTR_SEARCH_LEAF or
635 					BTR_MODIFY_LEAF */
636 	btr_pcur_t*	cursor,		/*!< in: memory buffer for persistent
637 					cursor */
638 	const char*	file,		/*!< in: file name */
639 	ulint		line,		/*!< in: line where called */
640 	mtr_t*		mtr)		/*!< in: mtr */
641 {
642 	btr_pcur_open_low(index, 0, tuple, mode, latch_mode, cursor,
643 			  file, line, mtr);
644 
645 	if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
646 
647 		if (btr_pcur_is_after_last_on_page(cursor)) {
648 
649 			btr_pcur_move_to_next_user_rec(cursor, mtr);
650 		}
651 	} else {
652 		ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
653 
654 		/* Not implemented yet */
655 
656 		ut_error;
657 	}
658 }
659