1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2021, Oracle and/or its affiliates.
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 
44 /** Updates fragmentation statistics for a single page transition.
45 @param[in]	page			the current page being processed
46 @param[in]	page_no			page number to move to (next_page_no
47 					if forward_direction is true,
48 					prev_page_no otherwise.
49 @param[in]	forward_direction	move direction: true means moving
50 					forward, false - backward. */
51 static
52 void
btr_update_scan_stats(const page_t * page,ulint page_no,bool forward_direction)53 btr_update_scan_stats(const page_t* page, ulint page_no, bool forward_direction)
54 {
55 	fragmentation_stats_t	stats;
56 	memset(&stats, 0, sizeof(stats));
57 	ulint	extracted_page_no = page_get_page_no(page);
58 	ulint	delta = forward_direction ?
59 		page_no - extracted_page_no :
60 		extracted_page_no - page_no;
61 
62 	if (delta == 1) {
63 		++stats.scan_pages_contiguous;
64 	} else {
65 		++stats.scan_pages_disjointed;
66 	}
67 	stats.scan_pages_total_seek_distance +=
68 		extracted_page_no > page_no ?
69 		extracted_page_no - page_no :
70 		page_no - extracted_page_no;
71 
72 	stats.scan_data_size += page_get_data_size(page);
73 	stats.scan_deleted_recs_size +=
74 		page_header_get_field(page, PAGE_GARBAGE);
75 	thd_add_fragmentation_stats(current_thd, &stats);
76 }
77 /**************************************************************//**
78 Allocates memory for a persistent cursor object and initializes the cursor.
79 @return own: persistent cursor */
80 btr_pcur_t*
btr_pcur_create_for_mysql(void)81 btr_pcur_create_for_mysql(void)
82 /*============================*/
83 {
84 	btr_pcur_t*	pcur;
85 	DBUG_ENTER("btr_pcur_create_for_mysql");
86 
87 	pcur = (btr_pcur_t*) ut_malloc_nokey(sizeof(btr_pcur_t));
88 
89 	pcur->btr_cur.index = NULL;
90 	btr_pcur_init(pcur);
91 	pcur->btr_cur.tree_height = ULINT_UNDEFINED;
92 
93 	DBUG_PRINT("btr_pcur_create_for_mysql", ("pcur: %p", pcur));
94 	DBUG_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 void
btr_pcur_reset(btr_pcur_t * cursor)101 btr_pcur_reset(
102 /*===========*/
103 	btr_pcur_t*	cursor)	/*!< in, out: persistent cursor */
104 {
105 	btr_pcur_free(cursor);
106 	cursor->old_rec_buf = NULL;
107 	cursor->btr_cur.index = NULL;
108 	cursor->btr_cur.page_cur.rec = NULL;
109 	cursor->old_rec = NULL;
110 	cursor->old_n_fields = 0;
111 	cursor->old_stored = false;
112 
113 	cursor->latch_mode = BTR_NO_LATCHES;
114 	cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
115 }
116 
117 /**************************************************************//**
118 Frees the memory for a persistent cursor object. */
119 void
btr_pcur_free_for_mysql(btr_pcur_t * cursor)120 btr_pcur_free_for_mysql(
121 /*====================*/
122 	btr_pcur_t*	cursor)	/*!< in, own: persistent cursor */
123 {
124 	DBUG_ENTER("btr_pcur_free_for_mysql");
125 	DBUG_PRINT("btr_pcur_free_for_mysql", ("pcur: %p", cursor));
126 
127 	btr_pcur_free(cursor);
128 	ut_free(cursor);
129 	DBUG_VOID_RETURN;
130 }
131 
132 /**************************************************************//**
133 The position of the cursor is stored by taking an initial segment of the
134 record the cursor is positioned on, before, or after, and copying it to the
135 cursor data structure, or just setting a flag if the cursor id before the
136 first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
137 page where the cursor is positioned must not be empty if the index tree is
138 not totally empty! */
139 void
btr_pcur_store_position(btr_pcur_t * cursor,mtr_t * mtr)140 btr_pcur_store_position(
141 /*====================*/
142 	btr_pcur_t*	cursor, /*!< in: persistent cursor */
143 	mtr_t*		mtr)	/*!< in: mtr */
144 {
145 	page_cur_t*	page_cursor;
146 	buf_block_t*	block;
147 	rec_t*		rec;
148 	dict_index_t*	index;
149 	page_t*		page;
150 	ulint		offs;
151 
152 	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
153 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
154 
155 	block = btr_pcur_get_block(cursor);
156 
157 	SRV_CORRUPT_TABLE_CHECK(block, return;);
158 
159 	index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
160 
161 	page_cursor = btr_pcur_get_page_cur(cursor);
162 
163 	rec = page_cur_get_rec(page_cursor);
164 	page = page_align(rec);
165 	offs = page_offset(rec);
166 
167 #ifdef UNIV_DEBUG
168 	if (dict_index_is_spatial(index)) {
169 		/* For spatial index, when we do positioning on parent
170 		buffer if necessary, it might not hold latches, but the
171 		tree must be locked to prevent change on the page */
172 		ut_ad((mtr_memo_contains_flagged(
173 				mtr, dict_index_get_lock(index),
174 				MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)
175 		       || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
176 		       || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX))
177 		      && (block->page.buf_fix_count > 0));
178 	} else {
179 		ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
180 		      || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)
181 		      || dict_table_is_intrinsic(index->table));
182 	}
183 #endif /* UNIV_DEBUG */
184 
185 	if (page_is_empty(page)) {
186 		/* It must be an empty index tree; NOTE that in this case
187 		we do not store the modify_clock, but always do a search
188 		if we restore the cursor position */
189 
190 		ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
191 		ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
192 		ut_ad(page_is_leaf(page));
193 		ut_ad(page_get_page_no(page) == index->page);
194 
195 		cursor->old_stored = true;
196 
197 		if (page_rec_is_supremum_low(offs)) {
198 
199 			cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
200 		} else {
201 			cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
202 		}
203 
204 		return;
205 	}
206 
207 	if (page_rec_is_supremum_low(offs)) {
208 
209 		rec = page_rec_get_prev(rec);
210 
211 		cursor->rel_pos = BTR_PCUR_AFTER;
212 
213 	} else if (page_rec_is_infimum_low(offs)) {
214 
215 		rec = page_rec_get_next(rec);
216 
217 		cursor->rel_pos = BTR_PCUR_BEFORE;
218 	} else {
219 		cursor->rel_pos = BTR_PCUR_ON;
220 	}
221 
222 	cursor->old_stored = true;
223 	cursor->old_rec = dict_index_copy_rec_order_prefix(
224 		index, rec, &cursor->old_n_fields,
225 		&cursor->old_rec_buf, &cursor->buf_size);
226 
227 	cursor->block_when_stored.store(block);
228 
229 	/* Function try to check if block is S/X latch. */
230 	cursor->modify_clock = buf_block_get_modify_clock(block);
231 }
232 
233 /**************************************************************//**
234 Copies the stored position of a pcur to another pcur. */
235 void
btr_pcur_copy_stored_position(btr_pcur_t * pcur_receive,btr_pcur_t * pcur_donate)236 btr_pcur_copy_stored_position(
237 /*==========================*/
238 	btr_pcur_t*	pcur_receive,	/*!< in: pcur which will receive the
239 					position info */
240 	btr_pcur_t*	pcur_donate)	/*!< in: pcur from which the info is
241 					copied */
242 {
243 	ut_free(pcur_receive->old_rec_buf);
244 	ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
245 
246 	if (pcur_donate->old_rec_buf) {
247 
248 		pcur_receive->old_rec_buf = (byte*)
249 			ut_malloc_nokey(pcur_donate->buf_size);
250 
251 		ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
252 			  pcur_donate->buf_size);
253 		pcur_receive->old_rec = pcur_receive->old_rec_buf
254 			+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
255 	}
256 
257 	pcur_receive->old_n_fields = pcur_donate->old_n_fields;
258 }
259 
260 /** This is a backported version of a lambda expression:
261   [&](buf_block_t *hint) {
262      return hint != nullptr && btr_cur_optimistic_latch_leaves(hint...);
263   }
264 for compilers which do not support lambda expressions, nor passing local types
265 as template arguments. */
266 struct Btr_cur_optimistic_latch_leaves_functor_t{
267 	btr_pcur_t * &cursor;
268 	ulint &latch_mode;
269 	const char * &file;
270 	ulint &line;
271 	mtr_t * &mtr;
operator ()Btr_cur_optimistic_latch_leaves_functor_t272 	bool operator() (buf_block_t *hint) const {
273 		return hint != NULL && btr_cur_optimistic_latch_leaves(
274 			hint, cursor->modify_clock, &latch_mode,
275 			btr_pcur_get_btr_cur(cursor), file, line, mtr
276 		);
277 	}
278 };
279 
280 /**************************************************************//**
281 Restores the stored position of a persistent cursor bufferfixing the page and
282 obtaining the specified latches. If the cursor position was saved when the
283 (1) cursor was positioned on a user record: this function restores the position
284 to the last record LESS OR EQUAL to the stored record;
285 (2) cursor was positioned on a page infimum record: restores the position to
286 the last record LESS than the user record which was the successor of the page
287 infimum;
288 (3) cursor was positioned on the page supremum: restores to the first record
289 GREATER than the user record which was the predecessor of the supremum.
290 (4) cursor was positioned before the first or after the last in an empty tree:
291 restores to before first or after the last in the tree.
292 @return TRUE if the cursor position was stored when it was on a user
293 record and it can be restored on a user record whose ordering fields
294 are identical to the ones of the original user record */
295 ibool
btr_pcur_restore_position_func(ulint latch_mode,btr_pcur_t * cursor,const char * file,ulint line,mtr_t * mtr)296 btr_pcur_restore_position_func(
297 /*===========================*/
298 	ulint		latch_mode,	/*!< in: BTR_SEARCH_LEAF, ... */
299 	btr_pcur_t*	cursor,		/*!< in: detached persistent cursor */
300 	const char*	file,		/*!< in: file name */
301 	ulint		line,		/*!< in: line where called */
302 	mtr_t*		mtr)		/*!< in: mtr */
303 {
304 	dict_index_t*	index;
305 	dtuple_t*	tuple;
306 	page_cur_mode_t	mode;
307 	page_cur_mode_t	old_mode;
308 	mem_heap_t*	heap;
309 
310 	ut_ad(mtr->is_active());
311 	ut_ad(cursor->old_stored);
312 	ut_ad(cursor->pos_state == BTR_PCUR_WAS_POSITIONED
313 	      || cursor->pos_state == BTR_PCUR_IS_POSITIONED);
314 
315 	index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
316 
317 	if (UNIV_UNLIKELY
318 	    (cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
319 	     || cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
320 
321 		/* In these cases we do not try an optimistic restoration,
322 		but always do a search */
323 
324 		btr_cur_open_at_index_side(
325 			cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
326 			index, latch_mode,
327 			btr_pcur_get_btr_cur(cursor), 0, mtr);
328 
329 		cursor->latch_mode =
330 			BTR_LATCH_MODE_WITHOUT_INTENTION(latch_mode);
331 		cursor->pos_state = BTR_PCUR_IS_POSITIONED;
332 		cursor->block_when_stored.clear();
333 
334 		return(FALSE);
335 	}
336 
337 	ut_a(cursor->old_rec);
338 	ut_a(cursor->old_n_fields);
339 
340 	/* Optimistic latching involves S/X latch not required for
341 	intrinsic table instead we would prefer to search fresh. */
342 	if ((latch_mode == BTR_SEARCH_LEAF
343 	     || latch_mode == BTR_MODIFY_LEAF
344 	     || latch_mode == BTR_SEARCH_PREV
345 	     || latch_mode == BTR_MODIFY_PREV)
346             && !dict_table_is_intrinsic(cursor->btr_cur.index->table)) {
347 		/* Try optimistic restoration. */
348 
349 		Btr_cur_optimistic_latch_leaves_functor_t functor={cursor,latch_mode,file,line,mtr};
350 		if (cursor->block_when_stored.run_with_hint(functor)) {
351 
352 			cursor->pos_state = BTR_PCUR_IS_POSITIONED;
353 			cursor->latch_mode = latch_mode;
354 
355 			buf_block_dbg_add_level(
356 				btr_pcur_get_block(cursor),
357 				dict_index_is_ibuf(index)
358 				? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE);
359 
360 			if (cursor->rel_pos == BTR_PCUR_ON) {
361 #ifdef UNIV_DEBUG
362 				const rec_t*	rec;
363 				const ulint*	offsets1;
364 				const ulint*	offsets2;
365 				rec = btr_pcur_get_rec(cursor);
366 
367 				heap = mem_heap_create(256);
368 				offsets1 = rec_get_offsets(
369 					cursor->old_rec, index, NULL,
370 					cursor->old_n_fields, &heap);
371 				offsets2 = rec_get_offsets(
372 					rec, index, NULL,
373 					cursor->old_n_fields, &heap);
374 
375 				ut_ad(!cmp_rec_rec(cursor->old_rec,
376 						   rec, offsets1, offsets2,
377 						   index,page_is_spatial_non_leaf(rec, index)));
378 				mem_heap_free(heap);
379 #endif /* UNIV_DEBUG */
380 				return(TRUE);
381 			}
382 			/* This is the same record as stored,
383 			may need to be adjusted for BTR_PCUR_BEFORE/AFTER,
384 			depending on search mode and direction. */
385 			if (btr_pcur_is_on_user_rec(cursor)) {
386 				cursor->pos_state
387 					= BTR_PCUR_IS_POSITIONED_OPTIMISTIC;
388 			}
389 			return(FALSE);
390 		}
391 	}
392 
393 	/* If optimistic restoration did not succeed, open the cursor anew */
394 
395 	heap = mem_heap_create(256);
396 
397 	tuple = dict_index_build_data_tuple(index, cursor->old_rec,
398 					    cursor->old_n_fields, heap);
399 
400 	/* Save the old search mode of the cursor */
401 	old_mode = cursor->search_mode;
402 
403 	switch (cursor->rel_pos) {
404 	case BTR_PCUR_ON:
405 		mode = PAGE_CUR_LE;
406 		break;
407 	case BTR_PCUR_AFTER:
408 		mode = PAGE_CUR_G;
409 		break;
410 	case BTR_PCUR_BEFORE:
411 		mode = PAGE_CUR_L;
412 		break;
413 	default:
414 		ut_error;
415 		mode = PAGE_CUR_UNSUPP;
416 	}
417 
418 	btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode,
419 					cursor, 0, file, line, mtr);
420 
421 	/* Restore the old search mode */
422 	cursor->search_mode = old_mode;
423 
424 	ut_ad(cursor->rel_pos == BTR_PCUR_ON
425 	      || cursor->rel_pos == BTR_PCUR_BEFORE
426 	      || cursor->rel_pos == BTR_PCUR_AFTER);
427 	if (cursor->rel_pos == BTR_PCUR_ON
428 	    && btr_pcur_is_on_user_rec(cursor)
429 	    && !cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor),
430 			       rec_get_offsets(btr_pcur_get_rec(cursor),
431 			       index, NULL, ULINT_UNDEFINED, &heap))) {
432 
433 		/* We have to store the NEW value for the modify clock,
434 		since the cursor can now be on a different page!
435 		But we can retain the value of old_rec */
436 
437 		buf_block_t * block = btr_pcur_get_block(cursor);
438 		cursor->block_when_stored.store(block);
439 		cursor->modify_clock = buf_block_get_modify_clock(block);
440 		cursor->old_stored = true;
441 
442 		mem_heap_free(heap);
443 
444 		return(TRUE);
445 	}
446 
447 	mem_heap_free(heap);
448 
449 	/* We have to store new position information, modify_clock etc.,
450 	to the cursor because it can now be on a different page, the record
451 	under it may have been removed, etc. */
452 
453 	btr_pcur_store_position(cursor, mtr);
454 
455 	return(FALSE);
456 }
457 
458 /*********************************************************//**
459 Moves the persistent cursor to the first record on the next page. Releases the
460 latch on the current page, and bufferunfixes it. Note that there must not be
461 modifications on the current page, as then the x-latch can be released only in
462 mtr_commit. */
463 void
btr_pcur_move_to_next_page(btr_pcur_t * cursor,mtr_t * mtr)464 btr_pcur_move_to_next_page(
465 /*=======================*/
466 	btr_pcur_t*	cursor,	/*!< in: persistent cursor; must be on the
467 				last record of the current page */
468 	mtr_t*		mtr)	/*!< in: mtr */
469 {
470 	ulint		next_page_no;
471 	page_t*		page;
472 	buf_block_t*	next_block;
473 	page_t*		next_page;
474 	ulint		mode;
475 	dict_table_t*	table = btr_pcur_get_btr_cur(cursor)->index->table;
476 
477 	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
478 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
479 	ut_ad(btr_pcur_is_after_last_on_page(cursor));
480 
481 	cursor->old_stored = false;
482 
483 	page = btr_pcur_get_page(cursor);
484 	next_page_no = btr_page_get_next(page, mtr);
485 
486 	ut_ad(next_page_no != FIL_NULL);
487 
488 	mode = cursor->latch_mode;
489 	switch (mode) {
490 	case BTR_SEARCH_TREE:
491 		mode = BTR_SEARCH_LEAF;
492 		break;
493 	case BTR_MODIFY_TREE:
494 		mode = BTR_MODIFY_LEAF;
495 	}
496 
497 	/* For intrinsic tables we avoid taking any latches as table is
498 	accessed by only one thread at any given time. */
499 	if (dict_table_is_intrinsic(table)) {
500 		mode = BTR_NO_LATCHES;
501 	}
502 
503 	buf_block_t*	block = btr_pcur_get_block(cursor);
504 
505 	btr_update_scan_stats(page, next_page_no, true /* forward */);
506 
507 	next_block = btr_block_get(
508 		page_id_t(block->page.id.space(), next_page_no),
509 		block->page.size, mode,
510 		btr_pcur_get_btr_cur(cursor)->index, mtr);
511 
512         if (!next_block && !btr_pcur_get_btr_cur(cursor)->index->table->is_readable())
513            return; /* decryption failure */
514 
515 	next_page = buf_block_get_frame(next_block);
516 
517 	SRV_CORRUPT_TABLE_CHECK(next_page,
518 	{
519 		btr_leaf_page_release(btr_pcur_get_block(cursor),
520 				      cursor->latch_mode, mtr);
521 		btr_pcur_get_page_cur(cursor)->block = 0;
522 		btr_pcur_get_page_cur(cursor)->rec = 0;
523 
524 		return;
525 	});
526 
527 #ifdef UNIV_BTR_DEBUG
528 	if (!cursor->import_ctx) {
529 		ut_a(page_is_comp(next_page) == page_is_comp(page));
530 		ut_a(btr_page_get_prev(next_page, mtr)
531 			== btr_pcur_get_block(cursor)->page.id.page_no());
532 	}
533 	else {
534 		if (page_is_comp(next_page) != page_is_comp(page)
535 			|| btr_page_get_prev(next_page, mtr) !=
536 			btr_pcur_get_block(cursor)->page.id.page_no()) {
537 			/* next page does not contain valid previous page
538 			number, next page is corrupted, can't move cursor
539 			to the next page */
540 			cursor->import_ctx->is_error = true;
541 		}
542 		DBUG_EXECUTE_IF("ib_import_page_corrupt",
543 				cursor->import_ctx->is_error = true;);
544 	}
545 #endif /* UNIV_BTR_DEBUG */
546 
547 	btr_leaf_page_release(btr_pcur_get_block(cursor), mode, mtr);
548 
549 	page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
550 
551 	ut_d(page_check_dir(next_page));
552 }
553 
554 /*********************************************************//**
555 Moves the persistent cursor backward if it is on the first record of the page.
556 Commits mtr. Note that to prevent a possible deadlock, the operation
557 first stores the position of the cursor, commits mtr, acquires the necessary
558 latches and restores the cursor position again before returning. The
559 alphabetical position of the cursor is guaranteed to be sensible on
560 return, but it may happen that the cursor is not positioned on the last
561 record of any page, because the structure of the tree may have changed
562 during the time when the cursor had no latches. */
563 void
btr_pcur_move_backward_from_page(btr_pcur_t * cursor,mtr_t * mtr)564 btr_pcur_move_backward_from_page(
565 /*=============================*/
566 	btr_pcur_t*	cursor,	/*!< in: persistent cursor, must be on the first
567 				record of the current page */
568 	mtr_t*		mtr)	/*!< in: mtr */
569 {
570 	ulint		prev_page_no;
571 	page_t*		page;
572 	buf_block_t*	prev_block;
573 	ulint		latch_mode;
574 	ulint		latch_mode2;
575 
576 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
577 	ut_ad(btr_pcur_is_before_first_on_page(cursor));
578 	ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
579 
580 	latch_mode = cursor->latch_mode;
581 
582 	if (latch_mode == BTR_SEARCH_LEAF) {
583 
584 		latch_mode2 = BTR_SEARCH_PREV;
585 
586 	} else if (latch_mode == BTR_MODIFY_LEAF) {
587 
588 		latch_mode2 = BTR_MODIFY_PREV;
589 	} else {
590 		latch_mode2 = 0; /* To eliminate compiler warning */
591 		ut_error;
592 	}
593 
594 	btr_pcur_store_position(cursor, mtr);
595 
596 	mtr_commit(mtr);
597 
598 	mtr_start(mtr);
599 
600 	btr_pcur_restore_position(latch_mode2, cursor, mtr);
601 
602 	page = btr_pcur_get_page(cursor);
603 
604 	prev_page_no = btr_page_get_prev(page, mtr);
605 
606 	/* For intrinsic table we don't do optimistic restore and so there is
607 	no left block that is pinned that needs to be released. */
608 	if (!dict_table_is_intrinsic(
609 		btr_cur_get_index(btr_pcur_get_btr_cur(cursor))->table)) {
610 		if (prev_page_no != FIL_NULL) {
611 			btr_update_scan_stats(page, prev_page_no,
612 				false /* backward */);
613 		}
614 
615 		if (prev_page_no == FIL_NULL) {
616 		} else if (btr_pcur_is_before_first_on_page(cursor)) {
617 
618 			prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
619 
620 			btr_leaf_page_release(btr_pcur_get_block(cursor),
621 					latch_mode, mtr);
622 
623 			page_cur_set_after_last(prev_block,
624 					btr_pcur_get_page_cur(cursor));
625 		} else {
626 
627 			/* The repositioned cursor did not end on an infimum
628 			record on a page. Cursor repositioning acquired a latch
629 			also on the previous page, but we do not need the latch:
630 			release it. */
631 
632 			prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
633 
634 			btr_leaf_page_release(prev_block, latch_mode, mtr);
635 		}
636 	}
637 
638 	cursor->latch_mode = latch_mode;
639 	cursor->old_stored = false;
640 }
641 
642 /*********************************************************//**
643 Moves the persistent cursor to the previous record in the tree. If no records
644 are left, the cursor stays 'before first in tree'.
645 @return TRUE if the cursor was not before first in tree */
646 ibool
btr_pcur_move_to_prev(btr_pcur_t * cursor,mtr_t * mtr)647 btr_pcur_move_to_prev(
648 /*==================*/
649 	btr_pcur_t*	cursor,	/*!< in: persistent cursor; NOTE that the
650 				function may release the page latch */
651 	mtr_t*		mtr)	/*!< in: mtr */
652 {
653 	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
654 	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
655 
656 	cursor->old_stored = false;
657 
658 	if (btr_pcur_is_before_first_on_page(cursor)) {
659 
660 		if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
661 
662 			return(FALSE);
663 		}
664 
665 		btr_pcur_move_backward_from_page(cursor, mtr);
666 
667 		return(TRUE);
668 	}
669 
670 	btr_pcur_move_to_prev_on_page(cursor);
671 
672 	return(TRUE);
673 }
674 
675 /**************************************************************//**
676 If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
677 user record satisfying the search condition, in the case PAGE_CUR_L or
678 PAGE_CUR_LE, on the last user record. If no such user record exists, then
679 in the first case sets the cursor after last in tree, and in the latter case
680 before first in tree. The latching mode must be BTR_SEARCH_LEAF or
681 BTR_MODIFY_LEAF. */
682 void
btr_pcur_open_on_user_rec_func(dict_index_t * index,const dtuple_t * tuple,page_cur_mode_t mode,ulint latch_mode,btr_pcur_t * cursor,const char * file,ulint line,mtr_t * mtr)683 btr_pcur_open_on_user_rec_func(
684 /*===========================*/
685 	dict_index_t*	index,		/*!< in: index */
686 	const dtuple_t*	tuple,		/*!< in: tuple on which search done */
687 	page_cur_mode_t	mode,		/*!< in: PAGE_CUR_L, ... */
688 	ulint		latch_mode,	/*!< in: BTR_SEARCH_LEAF or
689 					BTR_MODIFY_LEAF */
690 	btr_pcur_t*	cursor,		/*!< in: memory buffer for persistent
691 					cursor */
692 	const char*	file,		/*!< in: file name */
693 	ulint		line,		/*!< in: line where called */
694 	mtr_t*		mtr)		/*!< in: mtr */
695 {
696 	btr_pcur_open_low(index, 0, tuple, mode, latch_mode, cursor,
697 			  file, line, mtr);
698 
699 	if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
700 
701 		if (btr_pcur_is_after_last_on_page(cursor)) {
702 
703 			btr_pcur_move_to_next_user_rec(cursor, mtr);
704 		}
705 	} else {
706 		ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
707 
708 		/* Not implemented yet */
709 
710 		ut_error;
711 	}
712 }
713