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