| 1 | /***************************************************************************** |
| 2 | |
| 3 | Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. |
| 4 | Copyright (c) 2016, 2017, 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, Suite 500, 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_fields = 0; |
| 65 | cursor->old_stored = false; |
| 66 | |
| 67 | cursor->latch_mode = BTR_NO_LATCHES; |
| 68 | cursor->pos_state = BTR_PCUR_NOT_POSITIONED; |
| 69 | } |
| 70 | |
| 71 | /**************************************************************//** |
| 72 | Frees the memory for a persistent cursor object. */ |
| 73 | void |
| 74 | btr_pcur_free_for_mysql( |
| 75 | /*====================*/ |
| 76 | btr_pcur_t* cursor) /*!< in, own: persistent cursor */ |
| 77 | { |
| 78 | DBUG_ENTER("btr_pcur_free_for_mysql" ); |
| 79 | DBUG_PRINT("btr_pcur_free_for_mysql" , ("pcur: %p" , cursor)); |
| 80 | |
| 81 | btr_pcur_free(cursor); |
| 82 | ut_free(cursor); |
| 83 | DBUG_VOID_RETURN; |
| 84 | } |
| 85 | |
| 86 | /**************************************************************//** |
| 87 | The position of the cursor is stored by taking an initial segment of the |
| 88 | record the cursor is positioned on, before, or after, and copying it to the |
| 89 | cursor data structure, or just setting a flag if the cursor id before the |
| 90 | first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the |
| 91 | page where the cursor is positioned must not be empty if the index tree is |
| 92 | not totally empty! */ |
| 93 | void |
| 94 | btr_pcur_store_position( |
| 95 | /*====================*/ |
| 96 | btr_pcur_t* cursor, /*!< in: persistent cursor */ |
| 97 | mtr_t* mtr) /*!< in: mtr */ |
| 98 | { |
| 99 | page_cur_t* page_cursor; |
| 100 | buf_block_t* block; |
| 101 | rec_t* rec; |
| 102 | dict_index_t* index; |
| 103 | page_t* page; |
| 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 | page = page_align(rec); |
| 116 | offs = page_offset(rec); |
| 117 | |
| 118 | ut_ad(block->page.buf_fix_count); |
| 119 | /* For spatial index, when we do positioning on parent |
| 120 | buffer if necessary, it might not hold latches, but the |
| 121 | tree must be locked to prevent change on the page */ |
| 122 | ut_ad(mtr_memo_contains_flagged(mtr, block, |
| 123 | MTR_MEMO_PAGE_S_FIX |
| 124 | | MTR_MEMO_PAGE_X_FIX) |
| 125 | || (dict_index_is_spatial(index) |
| 126 | && mtr_memo_contains_flagged( |
| 127 | mtr, dict_index_get_lock(index), |
| 128 | MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK))); |
| 129 | |
| 130 | cursor->old_stored = true; |
| 131 | |
| 132 | if (page_is_empty(page)) { |
| 133 | /* It must be an empty index tree; NOTE that in this case |
| 134 | we do not store the modify_clock, but always do a search |
| 135 | if we restore the cursor position */ |
| 136 | |
| 137 | ut_a(!page_has_siblings(page)); |
| 138 | ut_ad(page_is_leaf(page)); |
| 139 | ut_ad(page_get_page_no(page) == index->page); |
| 140 | |
| 141 | if (page_rec_is_supremum_low(offs)) { |
| 142 | cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE; |
| 143 | } else { |
| 144 | cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE; |
| 145 | } |
| 146 | |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | if (page_rec_is_supremum_low(offs)) { |
| 151 | rec = page_rec_get_prev(rec); |
| 152 | |
| 153 | ut_ad(!page_rec_is_infimum(rec)); |
| 154 | ut_ad(!rec_is_default_row(rec, index)); |
| 155 | |
| 156 | cursor->rel_pos = BTR_PCUR_AFTER; |
| 157 | } else if (page_rec_is_infimum_low(offs)) { |
| 158 | rec = page_rec_get_next(rec); |
| 159 | |
| 160 | if (rec_is_default_row(rec, index)) { |
| 161 | rec = page_rec_get_next(rec); |
| 162 | ut_ad(!page_rec_is_supremum(rec)); |
| 163 | } |
| 164 | |
| 165 | cursor->rel_pos = BTR_PCUR_BEFORE; |
| 166 | } else { |
| 167 | cursor->rel_pos = BTR_PCUR_ON; |
| 168 | } |
| 169 | |
| 170 | cursor->old_rec = dict_index_copy_rec_order_prefix( |
| 171 | index, rec, &cursor->old_n_fields, |
| 172 | &cursor->old_rec_buf, &cursor->buf_size); |
| 173 | |
| 174 | cursor->block_when_stored = block; |
| 175 | |
| 176 | /* Function try to check if block is S/X latch. */ |
| 177 | cursor->modify_clock = buf_block_get_modify_clock(block); |
| 178 | cursor->withdraw_clock = buf_withdraw_clock; |
| 179 | } |
| 180 | |
| 181 | /**************************************************************//** |
| 182 | Copies the stored position of a pcur to another pcur. */ |
| 183 | void |
| 184 | btr_pcur_copy_stored_position( |
| 185 | /*==========================*/ |
| 186 | btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the |
| 187 | position info */ |
| 188 | btr_pcur_t* pcur_donate) /*!< in: pcur from which the info is |
| 189 | copied */ |
| 190 | { |
| 191 | ut_free(pcur_receive->old_rec_buf); |
| 192 | ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t)); |
| 193 | |
| 194 | if (pcur_donate->old_rec_buf) { |
| 195 | |
| 196 | pcur_receive->old_rec_buf = (byte*) |
| 197 | ut_malloc_nokey(pcur_donate->buf_size); |
| 198 | |
| 199 | ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf, |
| 200 | pcur_donate->buf_size); |
| 201 | pcur_receive->old_rec = pcur_receive->old_rec_buf |
| 202 | + (pcur_donate->old_rec - pcur_donate->old_rec_buf); |
| 203 | } |
| 204 | |
| 205 | pcur_receive->old_n_fields = pcur_donate->old_n_fields; |
| 206 | } |
| 207 | |
| 208 | /**************************************************************//** |
| 209 | Restores the stored position of a persistent cursor bufferfixing the page and |
| 210 | obtaining the specified latches. If the cursor position was saved when the |
| 211 | (1) cursor was positioned on a user record: this function restores the position |
| 212 | to the last record LESS OR EQUAL to the stored record; |
| 213 | (2) cursor was positioned on a page infimum record: restores the position to |
| 214 | the last record LESS than the user record which was the successor of the page |
| 215 | infimum; |
| 216 | (3) cursor was positioned on the page supremum: restores to the first record |
| 217 | GREATER than the user record which was the predecessor of the supremum. |
| 218 | (4) cursor was positioned before the first or after the last in an empty tree: |
| 219 | restores to before first or after the last in the tree. |
| 220 | @return TRUE if the cursor position was stored when it was on a user |
| 221 | record and it can be restored on a user record whose ordering fields |
| 222 | are identical to the ones of the original user record */ |
| 223 | ibool |
| 224 | btr_pcur_restore_position_func( |
| 225 | /*===========================*/ |
| 226 | ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ |
| 227 | btr_pcur_t* cursor, /*!< in: detached persistent cursor */ |
| 228 | const char* file, /*!< in: file name */ |
| 229 | unsigned line, /*!< in: line where called */ |
| 230 | mtr_t* mtr) /*!< in: mtr */ |
| 231 | { |
| 232 | dict_index_t* index; |
| 233 | dtuple_t* tuple; |
| 234 | page_cur_mode_t mode; |
| 235 | page_cur_mode_t old_mode; |
| 236 | mem_heap_t* heap; |
| 237 | |
| 238 | ut_ad(mtr->is_active()); |
| 239 | //ut_ad(cursor->old_stored); |
| 240 | ut_ad(cursor->pos_state == BTR_PCUR_WAS_POSITIONED |
| 241 | || cursor->pos_state == BTR_PCUR_IS_POSITIONED); |
| 242 | |
| 243 | index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor)); |
| 244 | |
| 245 | if (UNIV_UNLIKELY |
| 246 | (cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE |
| 247 | || cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) { |
| 248 | dberr_t err = DB_SUCCESS; |
| 249 | |
| 250 | /* In these cases we do not try an optimistic restoration, |
| 251 | but always do a search */ |
| 252 | |
| 253 | err = btr_cur_open_at_index_side( |
| 254 | cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE, |
| 255 | index, latch_mode, |
| 256 | btr_pcur_get_btr_cur(cursor), 0, mtr); |
| 257 | |
| 258 | if (err != DB_SUCCESS) { |
| 259 | ib::warn() << " Error code: " << err |
| 260 | << " btr_pcur_restore_position_func " |
| 261 | << " called from file: " |
| 262 | << file << " line: " << line |
| 263 | << " table: " << index->table->name |
| 264 | << " index: " << index->name; |
| 265 | } |
| 266 | |
| 267 | cursor->latch_mode = |
| 268 | BTR_LATCH_MODE_WITHOUT_INTENTION(latch_mode); |
| 269 | cursor->pos_state = BTR_PCUR_IS_POSITIONED; |
| 270 | cursor->block_when_stored = btr_pcur_get_block(cursor); |
| 271 | |
| 272 | return(FALSE); |
| 273 | } |
| 274 | |
| 275 | ut_a(cursor->old_rec); |
| 276 | ut_a(cursor->old_n_fields); |
| 277 | |
| 278 | switch (latch_mode) { |
| 279 | case BTR_SEARCH_LEAF: |
| 280 | case BTR_MODIFY_LEAF: |
| 281 | case BTR_SEARCH_PREV: |
| 282 | case BTR_MODIFY_PREV: |
| 283 | /* Try optimistic restoration. */ |
| 284 | |
| 285 | if (!buf_pool_is_obsolete(cursor->withdraw_clock) |
| 286 | && btr_cur_optimistic_latch_leaves( |
| 287 | cursor->block_when_stored, cursor->modify_clock, |
| 288 | &latch_mode, btr_pcur_get_btr_cur(cursor), |
| 289 | file, line, mtr)) { |
| 290 | |
| 291 | cursor->pos_state = BTR_PCUR_IS_POSITIONED; |
| 292 | cursor->latch_mode = latch_mode; |
| 293 | |
| 294 | buf_block_dbg_add_level( |
| 295 | btr_pcur_get_block(cursor), |
| 296 | dict_index_is_ibuf(index) |
| 297 | ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); |
| 298 | |
| 299 | if (cursor->rel_pos == BTR_PCUR_ON) { |
| 300 | #ifdef UNIV_DEBUG |
| 301 | const rec_t* rec; |
| 302 | const ulint* offsets1; |
| 303 | const ulint* offsets2; |
| 304 | rec = btr_pcur_get_rec(cursor); |
| 305 | |
| 306 | heap = mem_heap_create(256); |
| 307 | offsets1 = rec_get_offsets( |
| 308 | cursor->old_rec, index, NULL, true, |
| 309 | cursor->old_n_fields, &heap); |
| 310 | offsets2 = rec_get_offsets( |
| 311 | rec, index, NULL, true, |
| 312 | cursor->old_n_fields, &heap); |
| 313 | |
| 314 | ut_ad(!cmp_rec_rec(cursor->old_rec, |
| 315 | rec, offsets1, offsets2, |
| 316 | index)); |
| 317 | mem_heap_free(heap); |
| 318 | #endif /* UNIV_DEBUG */ |
| 319 | return(TRUE); |
| 320 | } |
| 321 | /* This is the same record as stored, |
| 322 | may need to be adjusted for BTR_PCUR_BEFORE/AFTER, |
| 323 | depending on search mode and direction. */ |
| 324 | if (btr_pcur_is_on_user_rec(cursor)) { |
| 325 | cursor->pos_state |
| 326 | = BTR_PCUR_IS_POSITIONED_OPTIMISTIC; |
| 327 | } |
| 328 | return(FALSE); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | /* If optimistic restoration did not succeed, open the cursor anew */ |
| 333 | |
| 334 | heap = mem_heap_create(256); |
| 335 | |
| 336 | tuple = dict_index_build_data_tuple(cursor->old_rec, index, true, |
| 337 | cursor->old_n_fields, heap); |
| 338 | |
| 339 | /* Save the old search mode of the cursor */ |
| 340 | old_mode = cursor->search_mode; |
| 341 | |
| 342 | switch (cursor->rel_pos) { |
| 343 | case BTR_PCUR_ON: |
| 344 | mode = PAGE_CUR_LE; |
| 345 | break; |
| 346 | case BTR_PCUR_AFTER: |
| 347 | mode = PAGE_CUR_G; |
| 348 | break; |
| 349 | case BTR_PCUR_BEFORE: |
| 350 | mode = PAGE_CUR_L; |
| 351 | break; |
| 352 | default: |
| 353 | ut_error; |
| 354 | mode = PAGE_CUR_UNSUPP; |
| 355 | } |
| 356 | |
| 357 | btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, |
| 358 | cursor, |
| 359 | #ifdef BTR_CUR_HASH_ADAPT |
| 360 | NULL, |
| 361 | #endif /* BTR_CUR_HASH_ADAPT */ |
| 362 | file, line, mtr); |
| 363 | |
| 364 | /* Restore the old search mode */ |
| 365 | cursor->search_mode = old_mode; |
| 366 | |
| 367 | ut_ad(cursor->rel_pos == BTR_PCUR_ON |
| 368 | || cursor->rel_pos == BTR_PCUR_BEFORE |
| 369 | || cursor->rel_pos == BTR_PCUR_AFTER); |
| 370 | if (cursor->rel_pos == BTR_PCUR_ON |
| 371 | && btr_pcur_is_on_user_rec(cursor) |
| 372 | && !cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor), |
| 373 | rec_get_offsets(btr_pcur_get_rec(cursor), |
| 374 | index, NULL, true, |
| 375 | ULINT_UNDEFINED, &heap))) { |
| 376 | |
| 377 | /* We have to store the NEW value for the modify clock, |
| 378 | since the cursor can now be on a different page! |
| 379 | But we can retain the value of old_rec */ |
| 380 | |
| 381 | cursor->block_when_stored = btr_pcur_get_block(cursor); |
| 382 | cursor->modify_clock = buf_block_get_modify_clock( |
| 383 | cursor->block_when_stored); |
| 384 | cursor->old_stored = true; |
| 385 | cursor->withdraw_clock = buf_withdraw_clock; |
| 386 | |
| 387 | mem_heap_free(heap); |
| 388 | |
| 389 | return(TRUE); |
| 390 | } |
| 391 | |
| 392 | mem_heap_free(heap); |
| 393 | |
| 394 | /* We have to store new position information, modify_clock etc., |
| 395 | to the cursor because it can now be on a different page, the record |
| 396 | under it may have been removed, etc. */ |
| 397 | |
| 398 | btr_pcur_store_position(cursor, mtr); |
| 399 | |
| 400 | return(FALSE); |
| 401 | } |
| 402 | |
| 403 | /*********************************************************//** |
| 404 | Moves the persistent cursor to the first record on the next page. Releases the |
| 405 | latch on the current page, and bufferunfixes it. Note that there must not be |
| 406 | modifications on the current page, as then the x-latch can be released only in |
| 407 | mtr_commit. */ |
| 408 | void |
| 409 | btr_pcur_move_to_next_page( |
| 410 | /*=======================*/ |
| 411 | btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the |
| 412 | last record of the current page */ |
| 413 | mtr_t* mtr) /*!< in: mtr */ |
| 414 | { |
| 415 | ulint next_page_no; |
| 416 | page_t* page; |
| 417 | buf_block_t* next_block; |
| 418 | page_t* next_page; |
| 419 | ulint mode; |
| 420 | |
| 421 | ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); |
| 422 | ut_ad(cursor->latch_mode != BTR_NO_LATCHES); |
| 423 | ut_ad(btr_pcur_is_after_last_on_page(cursor)); |
| 424 | |
| 425 | cursor->old_stored = false; |
| 426 | |
| 427 | page = btr_pcur_get_page(cursor); |
| 428 | |
| 429 | if (UNIV_UNLIKELY(!page)) { |
| 430 | return; |
| 431 | } |
| 432 | |
| 433 | next_page_no = btr_page_get_next(page, mtr); |
| 434 | |
| 435 | ut_ad(next_page_no != FIL_NULL); |
| 436 | |
| 437 | mode = cursor->latch_mode; |
| 438 | switch (mode) { |
| 439 | case BTR_SEARCH_TREE: |
| 440 | mode = BTR_SEARCH_LEAF; |
| 441 | break; |
| 442 | case BTR_MODIFY_TREE: |
| 443 | mode = BTR_MODIFY_LEAF; |
| 444 | } |
| 445 | |
| 446 | buf_block_t* block = btr_pcur_get_block(cursor); |
| 447 | |
| 448 | next_block = btr_block_get( |
| 449 | page_id_t(block->page.id.space(), next_page_no), |
| 450 | block->page.size, mode, |
| 451 | btr_pcur_get_btr_cur(cursor)->index, mtr); |
| 452 | |
| 453 | if (UNIV_UNLIKELY(!next_block)) { |
| 454 | return; |
| 455 | } |
| 456 | |
| 457 | next_page = buf_block_get_frame(next_block); |
| 458 | #ifdef UNIV_BTR_DEBUG |
| 459 | ut_a(page_is_comp(next_page) == page_is_comp(page)); |
| 460 | ut_a(btr_page_get_prev(next_page, mtr) |
| 461 | == btr_pcur_get_block(cursor)->page.id.page_no()); |
| 462 | #endif /* UNIV_BTR_DEBUG */ |
| 463 | |
| 464 | btr_leaf_page_release(btr_pcur_get_block(cursor), mode, mtr); |
| 465 | |
| 466 | page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor)); |
| 467 | |
| 468 | ut_d(page_check_dir(next_page)); |
| 469 | } |
| 470 | |
| 471 | /*********************************************************//** |
| 472 | Moves the persistent cursor backward if it is on the first record of the page. |
| 473 | Commits mtr. Note that to prevent a possible deadlock, the operation |
| 474 | first stores the position of the cursor, commits mtr, acquires the necessary |
| 475 | latches and restores the cursor position again before returning. The |
| 476 | alphabetical position of the cursor is guaranteed to be sensible on |
| 477 | return, but it may happen that the cursor is not positioned on the last |
| 478 | record of any page, because the structure of the tree may have changed |
| 479 | during the time when the cursor had no latches. */ |
| 480 | static |
| 481 | void |
| 482 | btr_pcur_move_backward_from_page( |
| 483 | /*=============================*/ |
| 484 | btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first |
| 485 | record of the current page */ |
| 486 | mtr_t* mtr) /*!< in: mtr */ |
| 487 | { |
| 488 | ulint prev_page_no; |
| 489 | page_t* page; |
| 490 | buf_block_t* prev_block; |
| 491 | ulint latch_mode; |
| 492 | ulint latch_mode2; |
| 493 | |
| 494 | ut_ad(cursor->latch_mode != BTR_NO_LATCHES); |
| 495 | ut_ad(btr_pcur_is_before_first_on_page(cursor)); |
| 496 | ut_ad(!btr_pcur_is_before_first_in_tree(cursor)); |
| 497 | |
| 498 | latch_mode = cursor->latch_mode; |
| 499 | |
| 500 | if (latch_mode == BTR_SEARCH_LEAF) { |
| 501 | |
| 502 | latch_mode2 = BTR_SEARCH_PREV; |
| 503 | |
| 504 | } else if (latch_mode == BTR_MODIFY_LEAF) { |
| 505 | |
| 506 | latch_mode2 = BTR_MODIFY_PREV; |
| 507 | } else { |
| 508 | latch_mode2 = 0; /* To eliminate compiler warning */ |
| 509 | ut_error; |
| 510 | } |
| 511 | |
| 512 | btr_pcur_store_position(cursor, mtr); |
| 513 | |
| 514 | mtr_commit(mtr); |
| 515 | |
| 516 | mtr_start(mtr); |
| 517 | |
| 518 | btr_pcur_restore_position(latch_mode2, cursor, mtr); |
| 519 | |
| 520 | page = btr_pcur_get_page(cursor); |
| 521 | |
| 522 | prev_page_no = btr_page_get_prev(page, mtr); |
| 523 | |
| 524 | if (prev_page_no == FIL_NULL) { |
| 525 | } else if (btr_pcur_is_before_first_on_page(cursor)) { |
| 526 | |
| 527 | prev_block = btr_pcur_get_btr_cur(cursor)->left_block; |
| 528 | |
| 529 | btr_leaf_page_release(btr_pcur_get_block(cursor), |
| 530 | latch_mode, mtr); |
| 531 | |
| 532 | page_cur_set_after_last(prev_block, |
| 533 | btr_pcur_get_page_cur(cursor)); |
| 534 | } else { |
| 535 | |
| 536 | /* The repositioned cursor did not end on an infimum |
| 537 | record on a page. Cursor repositioning acquired a latch |
| 538 | also on the previous page, but we do not need the latch: |
| 539 | release it. */ |
| 540 | |
| 541 | prev_block = btr_pcur_get_btr_cur(cursor)->left_block; |
| 542 | |
| 543 | btr_leaf_page_release(prev_block, latch_mode, mtr); |
| 544 | } |
| 545 | |
| 546 | cursor->latch_mode = latch_mode; |
| 547 | cursor->old_stored = false; |
| 548 | } |
| 549 | |
| 550 | /*********************************************************//** |
| 551 | Moves the persistent cursor to the previous record in the tree. If no records |
| 552 | are left, the cursor stays 'before first in tree'. |
| 553 | @return TRUE if the cursor was not before first in tree */ |
| 554 | ibool |
| 555 | btr_pcur_move_to_prev( |
| 556 | /*==================*/ |
| 557 | btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the |
| 558 | function may release the page latch */ |
| 559 | mtr_t* mtr) /*!< in: mtr */ |
| 560 | { |
| 561 | ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); |
| 562 | ut_ad(cursor->latch_mode != BTR_NO_LATCHES); |
| 563 | |
| 564 | cursor->old_stored = false; |
| 565 | |
| 566 | if (btr_pcur_is_before_first_on_page(cursor)) { |
| 567 | |
| 568 | if (btr_pcur_is_before_first_in_tree(cursor)) { |
| 569 | |
| 570 | return(FALSE); |
| 571 | } |
| 572 | |
| 573 | btr_pcur_move_backward_from_page(cursor, mtr); |
| 574 | |
| 575 | return(TRUE); |
| 576 | } |
| 577 | |
| 578 | btr_pcur_move_to_prev_on_page(cursor); |
| 579 | |
| 580 | return(TRUE); |
| 581 | } |
| 582 | |
| 583 | /**************************************************************//** |
| 584 | If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first |
| 585 | user record satisfying the search condition, in the case PAGE_CUR_L or |
| 586 | PAGE_CUR_LE, on the last user record. If no such user record exists, then |
| 587 | in the first case sets the cursor after last in tree, and in the latter case |
| 588 | before first in tree. The latching mode must be BTR_SEARCH_LEAF or |
| 589 | BTR_MODIFY_LEAF. */ |
| 590 | void |
| 591 | btr_pcur_open_on_user_rec_func( |
| 592 | /*===========================*/ |
| 593 | dict_index_t* index, /*!< in: index */ |
| 594 | const dtuple_t* tuple, /*!< in: tuple on which search done */ |
| 595 | page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ... */ |
| 596 | ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or |
| 597 | BTR_MODIFY_LEAF */ |
| 598 | btr_pcur_t* cursor, /*!< in: memory buffer for persistent |
| 599 | cursor */ |
| 600 | const char* file, /*!< in: file name */ |
| 601 | unsigned line, /*!< in: line where called */ |
| 602 | mtr_t* mtr) /*!< in: mtr */ |
| 603 | { |
| 604 | btr_pcur_open_low(index, 0, tuple, mode, latch_mode, cursor, |
| 605 | file, line, 0, mtr); |
| 606 | |
| 607 | if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) { |
| 608 | |
| 609 | if (btr_pcur_is_after_last_on_page(cursor)) { |
| 610 | |
| 611 | btr_pcur_move_to_next_user_rec(cursor, mtr); |
| 612 | } |
| 613 | } else { |
| 614 | ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L)); |
| 615 | |
| 616 | /* Not implemented yet */ |
| 617 | |
| 618 | ut_error; |
| 619 | } |
| 620 | } |
| 621 | |