1
/*****************************************************************************
3
Copyright (c) 1996, 2010, Innobase Oy. 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., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 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 = mem_alloc(sizeof(btr_pcur_t));
48
pcur->btr_cur.index = NULL;
54
/**************************************************************//**
55
Frees the memory for a persistent cursor object. */
58
btr_pcur_free_for_mysql(
59
/*====================*/
60
btr_pcur_t* cursor) /*!< in, own: persistent cursor */
62
if (cursor->old_rec_buf != NULL) {
64
mem_free(cursor->old_rec_buf);
66
cursor->old_rec_buf = NULL;
69
cursor->btr_cur.page_cur.rec = NULL;
70
cursor->old_rec = NULL;
71
cursor->old_n_fields = 0;
72
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
74
cursor->latch_mode = BTR_NO_LATCHES;
75
cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
80
/**************************************************************//**
81
The position of the cursor is stored by taking an initial segment of the
82
record the cursor is positioned on, before, or after, and copying it to the
83
cursor data structure, or just setting a flag if the cursor id before the
84
first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
85
page where the cursor is positioned must not be empty if the index tree is
89
btr_pcur_store_position(
90
/*====================*/
91
btr_pcur_t* cursor, /*!< in: persistent cursor */
92
mtr_t* mtr) /*!< in: mtr */
94
page_cur_t* page_cursor;
101
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
102
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
104
block = btr_pcur_get_block(cursor);
105
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
107
page_cursor = btr_pcur_get_page_cur(cursor);
109
rec = page_cur_get_rec(page_cursor);
110
page = page_align(rec);
111
offs = page_offset(rec);
113
ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
114
|| mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
115
ut_a(cursor->latch_mode != BTR_NO_LATCHES);
117
if (UNIV_UNLIKELY(page_get_n_recs(page) == 0)) {
118
/* It must be an empty index tree; NOTE that in this case
119
we do not store the modify_clock, but always do a search
120
if we restore the cursor position */
122
ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
123
ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
125
cursor->old_stored = BTR_PCUR_OLD_STORED;
127
if (page_rec_is_supremum_low(offs)) {
129
cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
131
cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
137
if (page_rec_is_supremum_low(offs)) {
139
rec = page_rec_get_prev(rec);
141
cursor->rel_pos = BTR_PCUR_AFTER;
143
} else if (page_rec_is_infimum_low(offs)) {
145
rec = page_rec_get_next(rec);
147
cursor->rel_pos = BTR_PCUR_BEFORE;
149
cursor->rel_pos = BTR_PCUR_ON;
152
cursor->old_stored = BTR_PCUR_OLD_STORED;
153
cursor->old_rec = dict_index_copy_rec_order_prefix(
154
index, rec, &cursor->old_n_fields,
155
&cursor->old_rec_buf, &cursor->buf_size);
157
cursor->block_when_stored = block;
158
cursor->modify_clock = buf_block_get_modify_clock(block);
161
/**************************************************************//**
162
Copies the stored position of a pcur to another pcur. */
165
btr_pcur_copy_stored_position(
166
/*==========================*/
167
btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the
169
btr_pcur_t* pcur_donate) /*!< in: pcur from which the info is
172
if (pcur_receive->old_rec_buf) {
173
mem_free(pcur_receive->old_rec_buf);
176
ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
178
if (pcur_donate->old_rec_buf) {
180
pcur_receive->old_rec_buf = mem_alloc(pcur_donate->buf_size);
182
ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
183
pcur_donate->buf_size);
184
pcur_receive->old_rec = pcur_receive->old_rec_buf
185
+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
188
pcur_receive->old_n_fields = pcur_donate->old_n_fields;
191
/**************************************************************//**
192
Restores the stored position of a persistent cursor bufferfixing the page and
193
obtaining the specified latches. If the cursor position was saved when the
194
(1) cursor was positioned on a user record: this function restores the position
195
to the last record LESS OR EQUAL to the stored record;
196
(2) cursor was positioned on a page infimum record: restores the position to
197
the last record LESS than the user record which was the successor of the page
199
(3) cursor was positioned on the page supremum: restores to the first record
200
GREATER than the user record which was the predecessor of the supremum.
201
(4) cursor was positioned before the first or after the last in an empty tree:
202
restores to before first or after the last in the tree.
203
@return TRUE if the cursor position was stored when it was on a user
204
record and it can be restored on a user record whose ordering fields
205
are identical to the ones of the original user record */
208
btr_pcur_restore_position_func(
209
/*===========================*/
210
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */
211
btr_pcur_t* cursor, /*!< in: detached persistent cursor */
212
const char* file, /*!< in: file name */
213
ulint line, /*!< in: line where called */
214
mtr_t* mtr) /*!< in: mtr */
223
ut_ad(mtr->state == MTR_ACTIVE);
225
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
227
if (UNIV_UNLIKELY(cursor->old_stored != BTR_PCUR_OLD_STORED)
228
|| UNIV_UNLIKELY(cursor->pos_state != BTR_PCUR_WAS_POSITIONED
229
&& cursor->pos_state != BTR_PCUR_IS_POSITIONED)) {
230
ut_print_buf(stderr, cursor, sizeof(btr_pcur_t));
232
if (cursor->trx_if_known) {
233
trx_print(stderr, cursor->trx_if_known, 0);
240
(cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
241
|| cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
243
/* In these cases we do not try an optimistic restoration,
244
but always do a search */
246
btr_cur_open_at_index_side(
247
cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
248
index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr);
250
cursor->block_when_stored = btr_pcur_get_block(cursor);
255
ut_a(cursor->old_rec);
256
ut_a(cursor->old_n_fields);
258
if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF)
259
|| UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) {
260
/* Try optimistic restoration */
262
if (UNIV_LIKELY(buf_page_optimistic_get(
264
cursor->block_when_stored,
265
cursor->modify_clock,
267
cursor->pos_state = BTR_PCUR_IS_POSITIONED;
269
buf_block_dbg_add_level(btr_pcur_get_block(cursor),
272
if (cursor->rel_pos == BTR_PCUR_ON) {
275
const ulint* offsets1;
276
const ulint* offsets2;
277
#endif /* UNIV_DEBUG */
278
cursor->latch_mode = latch_mode;
280
rec = btr_pcur_get_rec(cursor);
282
heap = mem_heap_create(256);
283
offsets1 = rec_get_offsets(
284
cursor->old_rec, index, NULL,
285
cursor->old_n_fields, &heap);
286
offsets2 = rec_get_offsets(
288
cursor->old_n_fields, &heap);
290
ut_ad(!cmp_rec_rec(cursor->old_rec,
291
rec, offsets1, offsets2,
294
#endif /* UNIV_DEBUG */
302
/* If optimistic restoration did not succeed, open the cursor anew */
304
heap = mem_heap_create(256);
306
tuple = dict_index_build_data_tuple(index, cursor->old_rec,
307
cursor->old_n_fields, heap);
309
/* Save the old search mode of the cursor */
310
old_mode = cursor->search_mode;
312
if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) {
314
} else if (cursor->rel_pos == BTR_PCUR_AFTER) {
317
ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE);
321
btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode,
322
cursor, 0, file, line, mtr);
324
/* Restore the old search mode */
325
cursor->search_mode = old_mode;
327
if (cursor->rel_pos == BTR_PCUR_ON
328
&& btr_pcur_is_on_user_rec(cursor)
329
&& 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor),
331
btr_pcur_get_rec(cursor), index,
332
NULL, ULINT_UNDEFINED, &heap))) {
334
/* We have to store the NEW value for the modify clock, since
335
the cursor can now be on a different page! But we can retain
336
the value of old_rec */
338
cursor->block_when_stored = btr_pcur_get_block(cursor);
339
cursor->modify_clock = buf_block_get_modify_clock(
340
cursor->block_when_stored);
341
cursor->old_stored = BTR_PCUR_OLD_STORED;
350
/* We have to store new position information, modify_clock etc.,
351
to the cursor because it can now be on a different page, the record
352
under it may have been removed, etc. */
354
btr_pcur_store_position(cursor, mtr);
359
/**************************************************************//**
360
If the latch mode of the cursor is BTR_LEAF_SEARCH or BTR_LEAF_MODIFY,
361
releases the page latch and bufferfix reserved by the cursor.
362
NOTE! In the case of BTR_LEAF_MODIFY, there should not exist changes
363
made by the current mini-transaction to the data protected by the
364
cursor latch, as then the latch must not be released until mtr_commit. */
367
btr_pcur_release_leaf(
368
/*==================*/
369
btr_pcur_t* cursor, /*!< in: persistent cursor */
370
mtr_t* mtr) /*!< in: mtr */
374
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
375
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
377
block = btr_pcur_get_block(cursor);
379
btr_leaf_page_release(block, cursor->latch_mode, mtr);
381
cursor->latch_mode = BTR_NO_LATCHES;
383
cursor->pos_state = BTR_PCUR_WAS_POSITIONED;
386
/*********************************************************//**
387
Moves the persistent cursor to the first record on the next page. Releases the
388
latch on the current page, and bufferunfixes it. Note that there must not be
389
modifications on the current page, as then the x-latch can be released only in
393
btr_pcur_move_to_next_page(
394
/*=======================*/
395
btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the
396
last record of the current page */
397
mtr_t* mtr) /*!< in: mtr */
403
buf_block_t* next_block;
406
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
407
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
408
ut_ad(btr_pcur_is_after_last_on_page(cursor));
410
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
412
page = btr_pcur_get_page(cursor);
413
next_page_no = btr_page_get_next(page, mtr);
414
space = buf_block_get_space(btr_pcur_get_block(cursor));
415
zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor));
417
ut_ad(next_page_no != FIL_NULL);
419
next_block = btr_block_get(space, zip_size, next_page_no,
420
cursor->latch_mode, mtr);
421
next_page = buf_block_get_frame(next_block);
422
#ifdef UNIV_BTR_DEBUG
423
ut_a(page_is_comp(next_page) == page_is_comp(page));
424
ut_a(btr_page_get_prev(next_page, mtr)
425
== buf_block_get_page_no(btr_pcur_get_block(cursor)));
426
#endif /* UNIV_BTR_DEBUG */
427
next_block->check_index_page_at_flush = TRUE;
429
btr_leaf_page_release(btr_pcur_get_block(cursor),
430
cursor->latch_mode, mtr);
432
page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
434
page_check_dir(next_page);
437
/*********************************************************//**
438
Moves the persistent cursor backward if it is on the first record of the page.
439
Commits mtr. Note that to prevent a possible deadlock, the operation
440
first stores the position of the cursor, commits mtr, acquires the necessary
441
latches and restores the cursor position again before returning. The
442
alphabetical position of the cursor is guaranteed to be sensible on
443
return, but it may happen that the cursor is not positioned on the last
444
record of any page, because the structure of the tree may have changed
445
during the time when the cursor had no latches. */
448
btr_pcur_move_backward_from_page(
449
/*=============================*/
450
btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first
451
record of the current page */
452
mtr_t* mtr) /*!< in: mtr */
457
buf_block_t* prev_block;
461
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
462
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
463
ut_ad(btr_pcur_is_before_first_on_page(cursor));
464
ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
466
latch_mode = cursor->latch_mode;
468
if (latch_mode == BTR_SEARCH_LEAF) {
470
latch_mode2 = BTR_SEARCH_PREV;
472
} else if (latch_mode == BTR_MODIFY_LEAF) {
474
latch_mode2 = BTR_MODIFY_PREV;
476
latch_mode2 = 0; /* To eliminate compiler warning */
480
btr_pcur_store_position(cursor, mtr);
486
btr_pcur_restore_position(latch_mode2, cursor, mtr);
488
page = btr_pcur_get_page(cursor);
490
prev_page_no = btr_page_get_prev(page, mtr);
491
space = buf_block_get_space(btr_pcur_get_block(cursor));
493
if (prev_page_no == FIL_NULL) {
494
} else if (btr_pcur_is_before_first_on_page(cursor)) {
496
prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
498
btr_leaf_page_release(btr_pcur_get_block(cursor),
501
page_cur_set_after_last(prev_block,
502
btr_pcur_get_page_cur(cursor));
505
/* The repositioned cursor did not end on an infimum record on
506
a page. Cursor repositioning acquired a latch also on the
507
previous page, but we do not need the latch: release it. */
509
prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
511
btr_leaf_page_release(prev_block, latch_mode, mtr);
514
cursor->latch_mode = latch_mode;
516
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
519
/*********************************************************//**
520
Moves the persistent cursor to the previous record in the tree. If no records
521
are left, the cursor stays 'before first in tree'.
522
@return TRUE if the cursor was not before first in tree */
525
btr_pcur_move_to_prev(
526
/*==================*/
527
btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the
528
function may release the page latch */
529
mtr_t* mtr) /*!< in: mtr */
531
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
532
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
534
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
536
if (btr_pcur_is_before_first_on_page(cursor)) {
538
if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
543
btr_pcur_move_backward_from_page(cursor, mtr);
548
btr_pcur_move_to_prev_on_page(cursor);
553
/**************************************************************//**
554
If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
555
user record satisfying the search condition, in the case PAGE_CUR_L or
556
PAGE_CUR_LE, on the last user record. If no such user record exists, then
557
in the first case sets the cursor after last in tree, and in the latter case
558
before first in tree. The latching mode must be BTR_SEARCH_LEAF or
562
btr_pcur_open_on_user_rec_func(
563
/*===========================*/
564
dict_index_t* index, /*!< in: index */
565
const dtuple_t* tuple, /*!< in: tuple on which search done */
566
ulint mode, /*!< in: PAGE_CUR_L, ... */
567
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or
569
btr_pcur_t* cursor, /*!< in: memory buffer for persistent
571
const char* file, /*!< in: file name */
572
ulint line, /*!< in: line where called */
573
mtr_t* mtr) /*!< in: mtr */
575
btr_pcur_open_func(index, tuple, mode, latch_mode, cursor,
578
if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
580
if (btr_pcur_is_after_last_on_page(cursor)) {
582
btr_pcur_move_to_next_user_rec(cursor, mtr);
585
ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
587
/* Not implemented yet */