1
/*****************************************************************************
3
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc.,
15
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
*****************************************************************************/
19
/**************************************************//**
21
The index tree persistent cursor
23
Created 2/23/1996 Heikki Tuuri
24
*******************************************************/
29
#include "btr0pcur.ic"
36
/**************************************************************//**
37
Allocates memory for a persistent cursor object and initializes the cursor.
38
@return own: persistent cursor */
41
btr_pcur_create_for_mysql(void)
42
/*============================*/
46
pcur = (btr_pcur_t*) mem_alloc(sizeof(btr_pcur_t));
48
pcur->btr_cur.index = NULL;
54
/**************************************************************//**
55
Resets a persistent cursor object, freeing ::old_rec_buf if it is
56
allocated and resetting the other members to their initial values. */
61
btr_pcur_t* cursor) /*!< in, out: persistent cursor */
63
if (cursor->old_rec_buf != NULL) {
65
mem_free(cursor->old_rec_buf);
67
cursor->old_rec_buf = NULL;
70
cursor->btr_cur.index = NULL;
71
cursor->btr_cur.page_cur.rec = NULL;
72
cursor->old_rec = NULL;
73
cursor->old_n_fields = 0;
74
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
76
cursor->latch_mode = BTR_NO_LATCHES;
77
cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
80
/**************************************************************//**
81
Frees the memory for a persistent cursor object. */
84
btr_pcur_free_for_mysql(
85
/*====================*/
86
btr_pcur_t* cursor) /*!< in, own: persistent cursor */
88
btr_pcur_reset(cursor);
92
/**************************************************************//**
93
The position of the cursor is stored by taking an initial segment of the
94
record the cursor is positioned on, before, or after, and copying it to the
95
cursor data structure, or just setting a flag if the cursor id before the
96
first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
97
page where the cursor is positioned must not be empty if the index tree is
101
btr_pcur_store_position(
102
/*====================*/
103
btr_pcur_t* cursor, /*!< in: persistent cursor */
104
mtr_t* mtr) /*!< in: mtr */
106
page_cur_t* page_cursor;
113
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
114
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
116
block = btr_pcur_get_block(cursor);
117
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
119
page_cursor = btr_pcur_get_page_cur(cursor);
121
rec = page_cur_get_rec(page_cursor);
122
page = page_align(rec);
123
offs = page_offset(rec);
125
ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
126
|| mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
128
if (page_is_empty(page)) {
129
/* It must be an empty index tree; NOTE that in this case
130
we do not store the modify_clock, but always do a search
131
if we restore the cursor position */
133
ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
134
ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
135
ut_ad(page_is_leaf(page));
136
ut_ad(page_get_page_no(page) == index->page);
138
cursor->old_stored = BTR_PCUR_OLD_STORED;
140
if (page_rec_is_supremum_low(offs)) {
142
cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
144
cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
150
if (page_rec_is_supremum_low(offs)) {
152
rec = page_rec_get_prev(rec);
154
cursor->rel_pos = BTR_PCUR_AFTER;
156
} else if (page_rec_is_infimum_low(offs)) {
158
rec = page_rec_get_next(rec);
160
cursor->rel_pos = BTR_PCUR_BEFORE;
162
cursor->rel_pos = BTR_PCUR_ON;
165
cursor->old_stored = BTR_PCUR_OLD_STORED;
166
cursor->old_rec = dict_index_copy_rec_order_prefix(
167
index, rec, &cursor->old_n_fields,
168
&cursor->old_rec_buf, &cursor->buf_size);
170
cursor->block_when_stored = block;
171
cursor->modify_clock = buf_block_get_modify_clock(block);
174
/**************************************************************//**
175
Copies the stored position of a pcur to another pcur. */
178
btr_pcur_copy_stored_position(
179
/*==========================*/
180
btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the
182
btr_pcur_t* pcur_donate) /*!< in: pcur from which the info is
185
if (pcur_receive->old_rec_buf) {
186
mem_free(pcur_receive->old_rec_buf);
189
ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
191
if (pcur_donate->old_rec_buf) {
193
pcur_receive->old_rec_buf = (byte*)
194
mem_alloc(pcur_donate->buf_size);
196
ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
197
pcur_donate->buf_size);
198
pcur_receive->old_rec = pcur_receive->old_rec_buf
199
+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
202
pcur_receive->old_n_fields = pcur_donate->old_n_fields;
205
/**************************************************************//**
206
Restores the stored position of a persistent cursor bufferfixing the page and
207
obtaining the specified latches. If the cursor position was saved when the
208
(1) cursor was positioned on a user record: this function restores the position
209
to the last record LESS OR EQUAL to the stored record;
210
(2) cursor was positioned on a page infimum record: restores the position to
211
the last record LESS than the user record which was the successor of the page
213
(3) cursor was positioned on the page supremum: restores to the first record
214
GREATER than the user record which was the predecessor of the supremum.
215
(4) cursor was positioned before the first or after the last in an empty tree:
216
restores to before first or after the last in the tree.
217
@return TRUE if the cursor position was stored when it was on a user
218
record and it can be restored on a user record whose ordering fields
219
are identical to the ones of the original user record */
222
btr_pcur_restore_position_func(
223
/*===========================*/
224
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */
225
btr_pcur_t* cursor, /*!< in: detached persistent cursor */
226
const char* file, /*!< in: file name */
227
ulint line, /*!< in: line where called */
228
mtr_t* mtr) /*!< in: mtr */
237
ut_ad(mtr->state == MTR_ACTIVE);
238
ut_ad(cursor->old_stored == BTR_PCUR_OLD_STORED);
239
ut_ad(cursor->pos_state == BTR_PCUR_WAS_POSITIONED
240
|| cursor->pos_state == BTR_PCUR_IS_POSITIONED);
242
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
245
(cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
246
|| cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
248
/* In these cases we do not try an optimistic restoration,
249
but always do a search */
251
btr_cur_open_at_index_side(
252
cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
254
btr_pcur_get_btr_cur(cursor), 0, mtr);
256
cursor->latch_mode = latch_mode;
257
cursor->pos_state = BTR_PCUR_IS_POSITIONED;
258
cursor->block_when_stored = btr_pcur_get_block(cursor);
263
ut_a(cursor->old_rec);
264
ut_a(cursor->old_n_fields);
266
if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF)
267
|| UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) {
268
/* Try optimistic restoration. */
270
if (buf_page_optimistic_get(latch_mode,
271
cursor->block_when_stored,
272
cursor->modify_clock,
274
cursor->pos_state = BTR_PCUR_IS_POSITIONED;
275
cursor->latch_mode = latch_mode;
277
buf_block_dbg_add_level(
278
btr_pcur_get_block(cursor),
279
dict_index_is_ibuf(index)
280
? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE);
282
if (cursor->rel_pos == BTR_PCUR_ON) {
285
const ulint* offsets1;
286
const ulint* offsets2;
287
rec = btr_pcur_get_rec(cursor);
289
heap = mem_heap_create(256);
290
offsets1 = rec_get_offsets(
291
cursor->old_rec, index, NULL,
292
cursor->old_n_fields, &heap);
293
offsets2 = rec_get_offsets(
295
cursor->old_n_fields, &heap);
297
ut_ad(!cmp_rec_rec(cursor->old_rec,
298
rec, offsets1, offsets2,
301
#endif /* UNIV_DEBUG */
304
/* This is the same record as stored,
305
may need to be adjusted for BTR_PCUR_BEFORE/AFTER,
306
depending on search mode and direction. */
307
if (btr_pcur_is_on_user_rec(cursor)) {
309
= BTR_PCUR_IS_POSITIONED_OPTIMISTIC;
315
/* If optimistic restoration did not succeed, open the cursor anew */
317
heap = mem_heap_create(256);
319
tuple = dict_index_build_data_tuple(index, cursor->old_rec,
320
cursor->old_n_fields, heap);
322
/* Save the old search mode of the cursor */
323
old_mode = cursor->search_mode;
325
switch (cursor->rel_pos) {
332
case BTR_PCUR_BEFORE:
340
btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode,
341
cursor, 0, file, line, mtr);
343
/* Restore the old search mode */
344
cursor->search_mode = old_mode;
346
switch (cursor->rel_pos) {
348
if (btr_pcur_is_on_user_rec(cursor)
350
tuple, btr_pcur_get_rec(cursor),
351
rec_get_offsets(btr_pcur_get_rec(cursor),
353
ULINT_UNDEFINED, &heap))) {
355
/* We have to store the NEW value for
356
the modify clock, since the cursor can
357
now be on a different page! But we can
358
retain the value of old_rec */
360
cursor->block_when_stored =
361
btr_pcur_get_block(cursor);
362
cursor->modify_clock =
363
buf_block_get_modify_clock(
364
cursor->block_when_stored);
365
cursor->old_stored = BTR_PCUR_OLD_STORED;
373
case BTR_PCUR_BEFORE:
378
#endif /* UNIV_DEBUG */
383
/* We have to store new position information, modify_clock etc.,
384
to the cursor because it can now be on a different page, the record
385
under it may have been removed, etc. */
387
btr_pcur_store_position(cursor, mtr);
392
/*********************************************************//**
393
Moves the persistent cursor to the first record on the next page. Releases the
394
latch on the current page, and bufferunfixes it. Note that there must not be
395
modifications on the current page, as then the x-latch can be released only in
399
btr_pcur_move_to_next_page(
400
/*=======================*/
401
btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the
402
last record of the current page */
403
mtr_t* mtr) /*!< in: mtr */
409
buf_block_t* next_block;
412
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
413
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
414
ut_ad(btr_pcur_is_after_last_on_page(cursor));
416
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
418
page = btr_pcur_get_page(cursor);
419
next_page_no = btr_page_get_next(page, mtr);
420
space = buf_block_get_space(btr_pcur_get_block(cursor));
421
zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor));
423
ut_ad(next_page_no != FIL_NULL);
425
next_block = btr_block_get(space, zip_size, next_page_no,
427
btr_pcur_get_btr_cur(cursor)->index, mtr);
428
next_page = buf_block_get_frame(next_block);
429
#ifdef UNIV_BTR_DEBUG
430
ut_a(page_is_comp(next_page) == page_is_comp(page));
431
ut_a(btr_page_get_prev(next_page, mtr)
432
== buf_block_get_page_no(btr_pcur_get_block(cursor)));
433
#endif /* UNIV_BTR_DEBUG */
434
next_block->check_index_page_at_flush = TRUE;
436
btr_leaf_page_release(btr_pcur_get_block(cursor),
437
cursor->latch_mode, mtr);
439
page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
441
page_check_dir(next_page);
444
/*********************************************************//**
445
Moves the persistent cursor backward if it is on the first record of the page.
446
Commits mtr. Note that to prevent a possible deadlock, the operation
447
first stores the position of the cursor, commits mtr, acquires the necessary
448
latches and restores the cursor position again before returning. The
449
alphabetical position of the cursor is guaranteed to be sensible on
450
return, but it may happen that the cursor is not positioned on the last
451
record of any page, because the structure of the tree may have changed
452
during the time when the cursor had no latches. */
455
btr_pcur_move_backward_from_page(
456
/*=============================*/
457
btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first
458
record of the current page */
459
mtr_t* mtr) /*!< in: mtr */
463
buf_block_t* prev_block;
467
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
468
ut_ad(btr_pcur_is_before_first_on_page(cursor));
469
ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
471
latch_mode = cursor->latch_mode;
473
if (latch_mode == BTR_SEARCH_LEAF) {
475
latch_mode2 = BTR_SEARCH_PREV;
477
} else if (latch_mode == BTR_MODIFY_LEAF) {
479
latch_mode2 = BTR_MODIFY_PREV;
481
latch_mode2 = 0; /* To eliminate compiler warning */
485
btr_pcur_store_position(cursor, mtr);
491
btr_pcur_restore_position(latch_mode2, cursor, mtr);
493
page = btr_pcur_get_page(cursor);
495
prev_page_no = btr_page_get_prev(page, mtr);
497
if (prev_page_no == FIL_NULL) {
498
} else if (btr_pcur_is_before_first_on_page(cursor)) {
500
prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
502
btr_leaf_page_release(btr_pcur_get_block(cursor),
505
page_cur_set_after_last(prev_block,
506
btr_pcur_get_page_cur(cursor));
509
/* The repositioned cursor did not end on an infimum record on
510
a page. Cursor repositioning acquired a latch also on the
511
previous page, but we do not need the latch: release it. */
513
prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
515
btr_leaf_page_release(prev_block, latch_mode, mtr);
518
cursor->latch_mode = latch_mode;
520
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
523
/*********************************************************//**
524
Moves the persistent cursor to the previous record in the tree. If no records
525
are left, the cursor stays 'before first in tree'.
526
@return TRUE if the cursor was not before first in tree */
529
btr_pcur_move_to_prev(
530
/*==================*/
531
btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the
532
function may release the page latch */
533
mtr_t* mtr) /*!< in: mtr */
535
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
536
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
538
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
540
if (btr_pcur_is_before_first_on_page(cursor)) {
542
if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
547
btr_pcur_move_backward_from_page(cursor, mtr);
552
btr_pcur_move_to_prev_on_page(cursor);
557
/**************************************************************//**
558
If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
559
user record satisfying the search condition, in the case PAGE_CUR_L or
560
PAGE_CUR_LE, on the last user record. If no such user record exists, then
561
in the first case sets the cursor after last in tree, and in the latter case
562
before first in tree. The latching mode must be BTR_SEARCH_LEAF or
566
btr_pcur_open_on_user_rec_func(
567
/*===========================*/
568
dict_index_t* index, /*!< in: index */
569
const dtuple_t* tuple, /*!< in: tuple on which search done */
570
ulint mode, /*!< in: PAGE_CUR_L, ... */
571
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or
573
btr_pcur_t* cursor, /*!< in: memory buffer for persistent
575
const char* file, /*!< in: file name */
576
ulint line, /*!< in: line where called */
577
mtr_t* mtr) /*!< in: mtr */
579
btr_pcur_open_low(index, 0, tuple, mode, latch_mode, cursor,
582
if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
584
if (btr_pcur_is_after_last_on_page(cursor)) {
586
btr_pcur_move_to_next_user_rec(cursor, mtr);
589
ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
591
/* Not implemented yet */