1
/******************************************************
2
The index tree persistent cursor
6
Created 2/23/1996 Heikki Tuuri
7
*******************************************************/
12
#include "btr0pcur.ic"
19
/******************************************************************
20
Allocates memory for a persistent cursor object and initializes the cursor. */
23
btr_pcur_create_for_mysql(void)
24
/*============================*/
25
/* out, own: persistent cursor */
29
pcur = mem_alloc(sizeof(btr_pcur_t));
31
pcur->btr_cur.index = NULL;
37
/******************************************************************
38
Frees the memory for a persistent cursor object. */
41
btr_pcur_free_for_mysql(
42
/*====================*/
43
btr_pcur_t* cursor) /* in, own: persistent cursor */
45
if (cursor->old_rec_buf != NULL) {
47
mem_free(cursor->old_rec_buf);
49
cursor->old_rec_buf = NULL;
52
cursor->btr_cur.page_cur.rec = NULL;
53
cursor->old_rec = NULL;
54
cursor->old_n_fields = 0;
55
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
57
cursor->latch_mode = BTR_NO_LATCHES;
58
cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
63
/******************************************************************
64
The position of the cursor is stored by taking an initial segment of the
65
record the cursor is positioned on, before, or after, and copying it to the
66
cursor data structure, or just setting a flag if the cursor id before the
67
first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
68
page where the cursor is positioned must not be empty if the index tree is
72
btr_pcur_store_position(
73
/*====================*/
74
btr_pcur_t* cursor, /* in: persistent cursor */
75
mtr_t* mtr) /* in: mtr */
77
page_cur_t* page_cursor;
83
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
84
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
86
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
88
page_cursor = btr_pcur_get_page_cur(cursor);
90
rec = page_cur_get_rec(page_cursor);
91
page = page_align(rec);
92
offs = page_offset(rec);
94
ut_ad(mtr_memo_contains(mtr, buf_block_align(page),
96
|| mtr_memo_contains(mtr, buf_block_align(page),
97
MTR_MEMO_PAGE_X_FIX));
98
ut_a(cursor->latch_mode != BTR_NO_LATCHES);
100
if (UNIV_UNLIKELY(page_get_n_recs(page) == 0)) {
101
/* It must be an empty index tree; NOTE that in this case
102
we do not store the modify_clock, but always do a search
103
if we restore the cursor position */
105
ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
106
ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
108
cursor->old_stored = BTR_PCUR_OLD_STORED;
110
if (page_rec_is_supremum_low(offs)) {
112
cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
114
cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
120
if (page_rec_is_supremum_low(offs)) {
122
rec = page_rec_get_prev(rec);
124
cursor->rel_pos = BTR_PCUR_AFTER;
126
} else if (page_rec_is_infimum_low(offs)) {
128
rec = page_rec_get_next(rec);
130
cursor->rel_pos = BTR_PCUR_BEFORE;
132
cursor->rel_pos = BTR_PCUR_ON;
135
cursor->old_stored = BTR_PCUR_OLD_STORED;
136
cursor->old_rec = dict_index_copy_rec_order_prefix(
137
index, rec, &cursor->old_n_fields,
138
&cursor->old_rec_buf, &cursor->buf_size);
140
cursor->block_when_stored = buf_block_align(page);
141
cursor->modify_clock = buf_block_get_modify_clock(
142
cursor->block_when_stored);
145
/******************************************************************
146
Copies the stored position of a pcur to another pcur. */
149
btr_pcur_copy_stored_position(
150
/*==========================*/
151
btr_pcur_t* pcur_receive, /* in: pcur which will receive the
153
btr_pcur_t* pcur_donate) /* in: pcur from which the info is
156
if (pcur_receive->old_rec_buf) {
157
mem_free(pcur_receive->old_rec_buf);
160
ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
162
if (pcur_donate->old_rec_buf) {
164
pcur_receive->old_rec_buf = mem_alloc(pcur_donate->buf_size);
166
ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
167
pcur_donate->buf_size);
168
pcur_receive->old_rec = pcur_receive->old_rec_buf
169
+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
172
pcur_receive->old_n_fields = pcur_donate->old_n_fields;
175
/******************************************************************
176
Restores the stored position of a persistent cursor bufferfixing the page and
177
obtaining the specified latches. If the cursor position was saved when the
178
(1) cursor was positioned on a user record: this function restores the position
179
to the last record LESS OR EQUAL to the stored record;
180
(2) cursor was positioned on a page infimum record: restores the position to
181
the last record LESS than the user record which was the successor of the page
183
(3) cursor was positioned on the page supremum: restores to the first record
184
GREATER than the user record which was the predecessor of the supremum.
185
(4) cursor was positioned before the first or after the last in an empty tree:
186
restores to before first or after the last in the tree. */
189
btr_pcur_restore_position(
190
/*======================*/
191
/* out: TRUE if the cursor position
192
was stored when it was on a user record
193
and it can be restored on a user record
194
whose ordering fields are identical to
195
the ones of the original user record */
196
ulint latch_mode, /* in: BTR_SEARCH_LEAF, ... */
197
btr_pcur_t* cursor, /* in: detached persistent cursor */
198
mtr_t* mtr) /* in: mtr */
207
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
209
if (UNIV_UNLIKELY(cursor->old_stored != BTR_PCUR_OLD_STORED)
210
|| UNIV_UNLIKELY(cursor->pos_state != BTR_PCUR_WAS_POSITIONED
211
&& cursor->pos_state != BTR_PCUR_IS_POSITIONED)) {
212
ut_print_buf(stderr, cursor, sizeof(btr_pcur_t));
213
if (cursor->trx_if_known) {
214
trx_print(stderr, cursor->trx_if_known, 0);
221
cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
222
|| cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
224
/* In these cases we do not try an optimistic restoration,
225
but always do a search */
227
btr_cur_open_at_index_side(
228
cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
229
index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr);
231
cursor->block_when_stored
232
= buf_block_align(btr_pcur_get_page(cursor));
237
ut_a(cursor->old_rec);
238
ut_a(cursor->old_n_fields);
240
page = btr_cur_get_page(btr_pcur_get_btr_cur(cursor));
242
if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF)
243
|| UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) {
244
/* Try optimistic restoration */
246
if (UNIV_LIKELY(buf_page_optimistic_get(
248
cursor->block_when_stored, page,
249
cursor->modify_clock, mtr))) {
250
cursor->pos_state = BTR_PCUR_IS_POSITIONED;
251
#ifdef UNIV_SYNC_DEBUG
252
buf_page_dbg_add_level(page, SYNC_TREE_NODE);
253
#endif /* UNIV_SYNC_DEBUG */
254
if (cursor->rel_pos == BTR_PCUR_ON) {
259
#endif /* UNIV_DEBUG */
260
cursor->latch_mode = latch_mode;
262
rec = btr_pcur_get_rec(cursor);
264
heap = mem_heap_create(256);
265
offsets1 = rec_get_offsets(
266
cursor->old_rec, index, NULL,
267
cursor->old_n_fields, &heap);
268
offsets2 = rec_get_offsets(
270
cursor->old_n_fields, &heap);
272
ut_ad(!cmp_rec_rec(cursor->old_rec,
273
rec, offsets1, offsets2,
276
#endif /* UNIV_DEBUG */
284
/* If optimistic restoration did not succeed, open the cursor anew */
286
heap = mem_heap_create(256);
288
tuple = dict_index_build_data_tuple(index, cursor->old_rec,
289
cursor->old_n_fields, heap);
291
/* Save the old search mode of the cursor */
292
old_mode = cursor->search_mode;
294
if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) {
296
} else if (cursor->rel_pos == BTR_PCUR_AFTER) {
299
ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE);
303
btr_pcur_open_with_no_init(index, tuple, mode, latch_mode,
306
/* Restore the old search mode */
307
cursor->search_mode = old_mode;
309
if (cursor->rel_pos == BTR_PCUR_ON
310
&& btr_pcur_is_on_user_rec(cursor, mtr)
311
&& 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor),
313
btr_pcur_get_rec(cursor), index,
314
NULL, ULINT_UNDEFINED, &heap))) {
316
/* We have to store the NEW value for the modify clock, since
317
the cursor can now be on a different page! But we can retain
318
the value of old_rec */
320
cursor->block_when_stored = buf_block_align(
321
btr_pcur_get_page(cursor));
322
cursor->modify_clock = buf_block_get_modify_clock(
323
cursor->block_when_stored);
324
cursor->old_stored = BTR_PCUR_OLD_STORED;
333
/* We have to store new position information, modify_clock etc.,
334
to the cursor because it can now be on a different page, the record
335
under it may have been removed, etc. */
337
btr_pcur_store_position(cursor, mtr);
342
/******************************************************************
343
If the latch mode of the cursor is BTR_LEAF_SEARCH or BTR_LEAF_MODIFY,
344
releases the page latch and bufferfix reserved by the cursor.
345
NOTE! In the case of BTR_LEAF_MODIFY, there should not exist changes
346
made by the current mini-transaction to the data protected by the
347
cursor latch, as then the latch must not be released until mtr_commit. */
350
btr_pcur_release_leaf(
351
/*==================*/
352
btr_pcur_t* cursor, /* in: persistent cursor */
353
mtr_t* mtr) /* in: mtr */
357
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
358
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
360
page = btr_cur_get_page(btr_pcur_get_btr_cur(cursor));
362
btr_leaf_page_release(page, cursor->latch_mode, mtr);
364
cursor->latch_mode = BTR_NO_LATCHES;
366
cursor->pos_state = BTR_PCUR_WAS_POSITIONED;
369
/*************************************************************
370
Moves the persistent cursor to the first record on the next page. Releases the
371
latch on the current page, and bufferunfixes it. Note that there must not be
372
modifications on the current page, as then the x-latch can be released only in
376
btr_pcur_move_to_next_page(
377
/*=======================*/
378
btr_pcur_t* cursor, /* in: persistent cursor; must be on the
379
last record of the current page */
380
mtr_t* mtr) /* in: mtr */
387
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
388
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
389
ut_ad(btr_pcur_is_after_last_on_page(cursor, mtr));
391
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
393
page = btr_pcur_get_page(cursor);
395
next_page_no = btr_page_get_next(page, mtr);
396
space = buf_frame_get_space_id(page);
398
ut_ad(next_page_no != FIL_NULL);
400
next_page = btr_page_get(space, next_page_no, cursor->latch_mode, mtr);
401
#ifdef UNIV_BTR_DEBUG
402
ut_a(btr_page_get_prev(next_page, mtr) == buf_frame_get_page_no(page));
403
#endif /* UNIV_BTR_DEBUG */
404
ut_a(page_is_comp(next_page) == page_is_comp(page));
405
buf_block_align(next_page)->check_index_page_at_flush = TRUE;
407
btr_leaf_page_release(page, cursor->latch_mode, mtr);
409
page_cur_set_before_first(next_page, btr_pcur_get_page_cur(cursor));
411
page_check_dir(next_page);
414
/*************************************************************
415
Moves the persistent cursor backward if it is on the first record of the page.
416
Commits mtr. Note that to prevent a possible deadlock, the operation
417
first stores the position of the cursor, commits mtr, acquires the necessary
418
latches and restores the cursor position again before returning. The
419
alphabetical position of the cursor is guaranteed to be sensible on
420
return, but it may happen that the cursor is not positioned on the last
421
record of any page, because the structure of the tree may have changed
422
during the time when the cursor had no latches. */
425
btr_pcur_move_backward_from_page(
426
/*=============================*/
427
btr_pcur_t* cursor, /* in: persistent cursor, must be on the first
428
record of the current page */
429
mtr_t* mtr) /* in: mtr */
438
ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
439
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
440
ut_ad(btr_pcur_is_before_first_on_page(cursor, mtr));
441
ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
443
latch_mode = cursor->latch_mode;
445
if (latch_mode == BTR_SEARCH_LEAF) {
447
latch_mode2 = BTR_SEARCH_PREV;
449
} else if (latch_mode == BTR_MODIFY_LEAF) {
451
latch_mode2 = BTR_MODIFY_PREV;
453
latch_mode2 = 0; /* To eliminate compiler warning */
457
btr_pcur_store_position(cursor, mtr);
463
btr_pcur_restore_position(latch_mode2, cursor, mtr);
465
page = btr_pcur_get_page(cursor);
467
prev_page_no = btr_page_get_prev(page, mtr);
468
space = buf_frame_get_space_id(page);
470
if (btr_pcur_is_before_first_on_page(cursor, mtr)
471
&& (prev_page_no != FIL_NULL)) {
473
prev_page = btr_pcur_get_btr_cur(cursor)->left_page;
475
btr_leaf_page_release(page, latch_mode, mtr);
477
page_cur_set_after_last(prev_page,
478
btr_pcur_get_page_cur(cursor));
479
} else if (prev_page_no != FIL_NULL) {
481
/* The repositioned cursor did not end on an infimum record on
482
a page. Cursor repositioning acquired a latch also on the
483
previous page, but we do not need the latch: release it. */
485
prev_page = btr_pcur_get_btr_cur(cursor)->left_page;
487
btr_leaf_page_release(prev_page, latch_mode, mtr);
490
cursor->latch_mode = latch_mode;
492
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
495
/*************************************************************
496
Moves the persistent cursor to the previous record in the tree. If no records
497
are left, the cursor stays 'before first in tree'. */
500
btr_pcur_move_to_prev(
501
/*==================*/
502
/* out: TRUE if the cursor was not before first
504
btr_pcur_t* cursor, /* in: persistent cursor; NOTE that the
505
function may release the page latch */
506
mtr_t* mtr) /* in: mtr */
508
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
509
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
511
cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
513
if (btr_pcur_is_before_first_on_page(cursor, mtr)) {
515
if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
520
btr_pcur_move_backward_from_page(cursor, mtr);
525
btr_pcur_move_to_prev_on_page(cursor, mtr);
530
/******************************************************************
531
If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
532
user record satisfying the search condition, in the case PAGE_CUR_L or
533
PAGE_CUR_LE, on the last user record. If no such user record exists, then
534
in the first case sets the cursor after last in tree, and in the latter case
535
before first in tree. The latching mode must be BTR_SEARCH_LEAF or
539
btr_pcur_open_on_user_rec(
540
/*======================*/
541
dict_index_t* index, /* in: index */
542
dtuple_t* tuple, /* in: tuple on which search done */
543
ulint mode, /* in: PAGE_CUR_L, ... */
544
ulint latch_mode, /* in: BTR_SEARCH_LEAF or
546
btr_pcur_t* cursor, /* in: memory buffer for persistent
548
mtr_t* mtr) /* in: mtr */
550
btr_pcur_open(index, tuple, mode, latch_mode, cursor, mtr);
552
if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
554
if (btr_pcur_is_after_last_on_page(cursor, mtr)) {
556
btr_pcur_move_to_next_user_rec(cursor, mtr);
559
ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
561
/* Not implemented yet */