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