~drizzle-trunk/drizzle/development

641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
1
/******************************************************
2
The index tree persistent cursor
3
4
(c) 1996 Innobase Oy
5
6
Created 2/23/1996 Heikki Tuuri
7
*******************************************************/
8
9
#include "btr0pcur.h"
10
11
#ifdef UNIV_NONINL
12
#include "btr0pcur.ic"
13
#endif
14
15
#include "ut0byte.h"
16
#include "rem0cmp.h"
17
#include "trx0trx.h"
18
19
/******************************************************************
20
Allocates memory for a persistent cursor object and initializes the cursor. */
21
UNIV_INTERN
22
btr_pcur_t*
23
btr_pcur_create_for_mysql(void)
24
/*============================*/
25
				/* out, own: persistent cursor */
26
{
27
	btr_pcur_t*	pcur;
28
29
	pcur = mem_alloc(sizeof(btr_pcur_t));
30
31
	pcur->btr_cur.index = NULL;
32
	btr_pcur_init(pcur);
33
34
	return(pcur);
35
}
36
37
/******************************************************************
38
Frees the memory for a persistent cursor object. */
39
UNIV_INTERN
40
void
41
btr_pcur_free_for_mysql(
42
/*====================*/
43
	btr_pcur_t*	cursor)	/* in, own: persistent cursor */
44
{
45
	if (cursor->old_rec_buf != NULL) {
46
47
		mem_free(cursor->old_rec_buf);
48
49
		cursor->old_rec_buf = NULL;
50
	}
51
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;
56
57
	cursor->latch_mode = BTR_NO_LATCHES;
58
	cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
59
60
	mem_free(cursor);
61
}
62
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
69
not totally empty! */
70
UNIV_INTERN
71
void
72
btr_pcur_store_position(
73
/*====================*/
74
	btr_pcur_t*	cursor, /* in: persistent cursor */
75
	mtr_t*		mtr)	/* in: mtr */
76
{
77
	page_cur_t*	page_cursor;
78
	buf_block_t*	block;
79
	rec_t*		rec;
80
	dict_index_t*	index;
81
	page_t*		page;
82
	ulint		offs;
83
84
	ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
85
	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
86
87
	block = btr_pcur_get_block(cursor);
88
	index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
89
90
	page_cursor = btr_pcur_get_page_cur(cursor);
91
92
	rec = page_cur_get_rec(page_cursor);
93
	page = page_align(rec);
94
	offs = page_offset(rec);
95
96
	ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX)
97
	      || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
98
	ut_a(cursor->latch_mode != BTR_NO_LATCHES);
99
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 */
104
105
		ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
106
		ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
107
108
		cursor->old_stored = BTR_PCUR_OLD_STORED;
109
110
		if (page_rec_is_supremum_low(offs)) {
111
112
			cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
113
		} else {
114
			cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
115
		}
116
117
		return;
118
	}
119
120
	if (page_rec_is_supremum_low(offs)) {
121
122
		rec = page_rec_get_prev(rec);
123
124
		cursor->rel_pos = BTR_PCUR_AFTER;
125
126
	} else if (page_rec_is_infimum_low(offs)) {
127
128
		rec = page_rec_get_next(rec);
129
130
		cursor->rel_pos = BTR_PCUR_BEFORE;
131
	} else {
132
		cursor->rel_pos = BTR_PCUR_ON;
133
	}
134
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);
139
140
	cursor->block_when_stored = block;
141
	cursor->modify_clock = buf_block_get_modify_clock(block);
142
}
143
144
/******************************************************************
145
Copies the stored position of a pcur to another pcur. */
146
UNIV_INTERN
147
void
148
btr_pcur_copy_stored_position(
149
/*==========================*/
150
	btr_pcur_t*	pcur_receive,	/* in: pcur which will receive the
151
					position info */
152
	btr_pcur_t*	pcur_donate)	/* in: pcur from which the info is
153
					copied */
154
{
155
	if (pcur_receive->old_rec_buf) {
156
		mem_free(pcur_receive->old_rec_buf);
157
	}
158
159
	ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
160
161
	if (pcur_donate->old_rec_buf) {
162
163
		pcur_receive->old_rec_buf = mem_alloc(pcur_donate->buf_size);
164
165
		ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
166
			  pcur_donate->buf_size);
167
		pcur_receive->old_rec = pcur_receive->old_rec_buf
168
			+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
169
	}
170
171
	pcur_receive->old_n_fields = pcur_donate->old_n_fields;
172
}
173
174
/******************************************************************
175
Restores the stored position of a persistent cursor bufferfixing the page and
176
obtaining the specified latches. If the cursor position was saved when the
177
(1) cursor was positioned on a user record: this function restores the position
178
to the last record LESS OR EQUAL to the stored record;
179
(2) cursor was positioned on a page infimum record: restores the position to
180
the last record LESS than the user record which was the successor of the page
181
infimum;
182
(3) cursor was positioned on the page supremum: restores to the first record
183
GREATER than the user record which was the predecessor of the supremum.
184
(4) cursor was positioned before the first or after the last in an empty tree:
185
restores to before first or after the last in the tree. */
186
UNIV_INTERN
187
ibool
188
btr_pcur_restore_position(
189
/*======================*/
190
					/* out: TRUE if the cursor position
191
					was stored when it was on a user record
192
					and it can be restored on a user record
193
					whose ordering fields are identical to
194
					the ones of the original user record */
195
	ulint		latch_mode,	/* in: BTR_SEARCH_LEAF, ... */
196
	btr_pcur_t*	cursor,		/* in: detached persistent cursor */
197
	mtr_t*		mtr)		/* in: mtr */
198
{
199
	dict_index_t*	index;
200
	dtuple_t*	tuple;
201
	ulint		mode;
202
	ulint		old_mode;
203
	mem_heap_t*	heap;
204
205
	index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
206
207
	if (UNIV_UNLIKELY(cursor->old_stored != BTR_PCUR_OLD_STORED)
208
	    || UNIV_UNLIKELY(cursor->pos_state != BTR_PCUR_WAS_POSITIONED
209
			     && cursor->pos_state != BTR_PCUR_IS_POSITIONED)) {
210
		ut_print_buf(stderr, cursor, sizeof(btr_pcur_t));
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
211
		putc('\n', stderr);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
212
		if (cursor->trx_if_known) {
213
			trx_print(stderr, cursor->trx_if_known, 0);
214
		}
215
216
		ut_error;
217
	}
218
219
	if (UNIV_UNLIKELY
220
	    (cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
221
	     || cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
222
223
		/* In these cases we do not try an optimistic restoration,
224
		but always do a search */
225
226
		btr_cur_open_at_index_side(
227
			cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
228
			index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr);
229
230
		cursor->block_when_stored = btr_pcur_get_block(cursor);
231
232
		return(FALSE);
233
	}
234
235
	ut_a(cursor->old_rec);
236
	ut_a(cursor->old_n_fields);
237
238
	if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF)
239
	    || UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) {
240
		/* Try optimistic restoration */
241
242
		if (UNIV_LIKELY(buf_page_optimistic_get(
243
					latch_mode,
244
					cursor->block_when_stored,
245
					cursor->modify_clock, mtr))) {
246
			cursor->pos_state = BTR_PCUR_IS_POSITIONED;
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
247
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
248
			buf_block_dbg_add_level(btr_pcur_get_block(cursor),
249
						SYNC_TREE_NODE);
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
250
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
251
			if (cursor->rel_pos == BTR_PCUR_ON) {
252
#ifdef UNIV_DEBUG
253
				const rec_t*	rec;
254
				const ulint*	offsets1;
255
				const ulint*	offsets2;
256
#endif /* UNIV_DEBUG */
257
				cursor->latch_mode = latch_mode;
258
#ifdef UNIV_DEBUG
259
				rec = btr_pcur_get_rec(cursor);
260
261
				heap = mem_heap_create(256);
262
				offsets1 = rec_get_offsets(
263
					cursor->old_rec, index, NULL,
264
					cursor->old_n_fields, &heap);
265
				offsets2 = rec_get_offsets(
266
					rec, index, NULL,
267
					cursor->old_n_fields, &heap);
268
269
				ut_ad(!cmp_rec_rec(cursor->old_rec,
270
						   rec, offsets1, offsets2,
271
						   index));
272
				mem_heap_free(heap);
273
#endif /* UNIV_DEBUG */
274
				return(TRUE);
275
			}
276
277
			return(FALSE);
278
		}
279
	}
280
281
	/* If optimistic restoration did not succeed, open the cursor anew */
282
283
	heap = mem_heap_create(256);
284
285
	tuple = dict_index_build_data_tuple(index, cursor->old_rec,
286
					    cursor->old_n_fields, heap);
287
288
	/* Save the old search mode of the cursor */
289
	old_mode = cursor->search_mode;
290
291
	if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) {
292
		mode = PAGE_CUR_LE;
293
	} else if (cursor->rel_pos == BTR_PCUR_AFTER) {
294
		mode = PAGE_CUR_G;
295
	} else {
296
		ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE);
297
		mode = PAGE_CUR_L;
298
	}
299
300
	btr_pcur_open_with_no_init(index, tuple, mode, latch_mode,
301
				   cursor, 0, mtr);
302
303
	/* Restore the old search mode */
304
	cursor->search_mode = old_mode;
305
306
	if (cursor->rel_pos == BTR_PCUR_ON
307
	    && btr_pcur_is_on_user_rec(cursor)
308
	    && 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor),
309
				   rec_get_offsets(
310
					   btr_pcur_get_rec(cursor), index,
311
					   NULL, ULINT_UNDEFINED, &heap))) {
312
313
		/* We have to store the NEW value for the modify clock, since
314
		the cursor can now be on a different page! But we can retain
315
		the value of old_rec */
316
317
		cursor->block_when_stored = btr_pcur_get_block(cursor);
318
		cursor->modify_clock = buf_block_get_modify_clock(
319
			cursor->block_when_stored);
320
		cursor->old_stored = BTR_PCUR_OLD_STORED;
321
322
		mem_heap_free(heap);
323
324
		return(TRUE);
325
	}
326
327
	mem_heap_free(heap);
328
329
	/* We have to store new position information, modify_clock etc.,
330
	to the cursor because it can now be on a different page, the record
331
	under it may have been removed, etc. */
332
333
	btr_pcur_store_position(cursor, mtr);
334
335
	return(FALSE);
336
}
337
338
/******************************************************************
339
If the latch mode of the cursor is BTR_LEAF_SEARCH or BTR_LEAF_MODIFY,
340
releases the page latch and bufferfix reserved by the cursor.
341
NOTE! In the case of BTR_LEAF_MODIFY, there should not exist changes
342
made by the current mini-transaction to the data protected by the
343
cursor latch, as then the latch must not be released until mtr_commit. */
344
UNIV_INTERN
345
void
346
btr_pcur_release_leaf(
347
/*==================*/
348
	btr_pcur_t*	cursor, /* in: persistent cursor */
349
	mtr_t*		mtr)	/* in: mtr */
350
{
351
	buf_block_t*	block;
352
353
	ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
354
	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
355
356
	block = btr_pcur_get_block(cursor);
357
358
	btr_leaf_page_release(block, cursor->latch_mode, mtr);
359
360
	cursor->latch_mode = BTR_NO_LATCHES;
361
362
	cursor->pos_state = BTR_PCUR_WAS_POSITIONED;
363
}
364
365
/*************************************************************
366
Moves the persistent cursor to the first record on the next page. Releases the
367
latch on the current page, and bufferunfixes it. Note that there must not be
368
modifications on the current page, as then the x-latch can be released only in
369
mtr_commit. */
370
UNIV_INTERN
371
void
372
btr_pcur_move_to_next_page(
373
/*=======================*/
374
	btr_pcur_t*	cursor,	/* in: persistent cursor; must be on the
375
				last record of the current page */
376
	mtr_t*		mtr)	/* in: mtr */
377
{
378
	ulint		next_page_no;
379
	ulint		space;
380
	ulint		zip_size;
381
	page_t*		page;
382
	buf_block_t*	next_block;
383
	page_t*		next_page;
384
385
	ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
386
	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
387
	ut_ad(btr_pcur_is_after_last_on_page(cursor));
388
389
	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
390
391
	page = btr_pcur_get_page(cursor);
392
	next_page_no = btr_page_get_next(page, mtr);
393
	space = buf_block_get_space(btr_pcur_get_block(cursor));
394
	zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor));
395
396
	ut_ad(next_page_no != FIL_NULL);
397
398
	next_block = btr_block_get(space, zip_size, next_page_no,
399
				   cursor->latch_mode, mtr);
400
	next_page = buf_block_get_frame(next_block);
401
#ifdef UNIV_BTR_DEBUG
402
	ut_a(page_is_comp(next_page) == page_is_comp(page));
403
	ut_a(btr_page_get_prev(next_page, mtr)
404
	     == buf_block_get_page_no(btr_pcur_get_block(cursor)));
405
#endif /* UNIV_BTR_DEBUG */
406
	next_block->check_index_page_at_flush = TRUE;
407
408
	btr_leaf_page_release(btr_pcur_get_block(cursor),
409
			      cursor->latch_mode, mtr);
410
411
	page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
412
413
	page_check_dir(next_page);
414
}
415
416
/*************************************************************
417
Moves the persistent cursor backward if it is on the first record of the page.
418
Commits mtr. Note that to prevent a possible deadlock, the operation
419
first stores the position of the cursor, commits mtr, acquires the necessary
420
latches and restores the cursor position again before returning. The
421
alphabetical position of the cursor is guaranteed to be sensible on
422
return, but it may happen that the cursor is not positioned on the last
423
record of any page, because the structure of the tree may have changed
424
during the time when the cursor had no latches. */
425
UNIV_INTERN
426
void
427
btr_pcur_move_backward_from_page(
428
/*=============================*/
429
	btr_pcur_t*	cursor,	/* in: persistent cursor, must be on the first
430
				record of the current page */
431
	mtr_t*		mtr)	/* in: mtr */
432
{
433
	ulint		prev_page_no;
434
	ulint		space;
435
	page_t*		page;
436
	buf_block_t*	prev_block;
437
	ulint		latch_mode;
438
	ulint		latch_mode2;
439
440
	ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
441
	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
442
	ut_ad(btr_pcur_is_before_first_on_page(cursor));
443
	ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
444
445
	latch_mode = cursor->latch_mode;
446
447
	if (latch_mode == BTR_SEARCH_LEAF) {
448
449
		latch_mode2 = BTR_SEARCH_PREV;
450
451
	} else if (latch_mode == BTR_MODIFY_LEAF) {
452
453
		latch_mode2 = BTR_MODIFY_PREV;
454
	} else {
455
		latch_mode2 = 0; /* To eliminate compiler warning */
456
		ut_error;
457
	}
458
459
	btr_pcur_store_position(cursor, mtr);
460
461
	mtr_commit(mtr);
462
463
	mtr_start(mtr);
464
465
	btr_pcur_restore_position(latch_mode2, cursor, mtr);
466
467
	page = btr_pcur_get_page(cursor);
468
469
	prev_page_no = btr_page_get_prev(page, mtr);
470
	space = buf_block_get_space(btr_pcur_get_block(cursor));
471
472
	if (prev_page_no == FIL_NULL) {
473
	} else if (btr_pcur_is_before_first_on_page(cursor)) {
474
475
		prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
476
477
		btr_leaf_page_release(btr_pcur_get_block(cursor),
478
				      latch_mode, mtr);
479
480
		page_cur_set_after_last(prev_block,
481
					btr_pcur_get_page_cur(cursor));
482
	} else {
483
484
		/* The repositioned cursor did not end on an infimum record on
485
		a page. Cursor repositioning acquired a latch also on the
486
		previous page, but we do not need the latch: release it. */
487
488
		prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
489
490
		btr_leaf_page_release(prev_block, latch_mode, mtr);
491
	}
492
493
	cursor->latch_mode = latch_mode;
494
495
	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
496
}
497
498
/*************************************************************
499
Moves the persistent cursor to the previous record in the tree. If no records
500
are left, the cursor stays 'before first in tree'. */
501
UNIV_INTERN
502
ibool
503
btr_pcur_move_to_prev(
504
/*==================*/
505
				/* out: TRUE if the cursor was not before first
506
				in tree */
507
	btr_pcur_t*	cursor,	/* in: persistent cursor; NOTE that the
508
				function may release the page latch */
509
	mtr_t*		mtr)	/* in: mtr */
510
{
511
	ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
512
	ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
513
514
	cursor->old_stored = BTR_PCUR_OLD_NOT_STORED;
515
516
	if (btr_pcur_is_before_first_on_page(cursor)) {
517
518
		if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
519
520
			return(FALSE);
521
		}
522
523
		btr_pcur_move_backward_from_page(cursor, mtr);
524
525
		return(TRUE);
526
	}
527
528
	btr_pcur_move_to_prev_on_page(cursor);
529
530
	return(TRUE);
531
}
532
533
/******************************************************************
534
If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
535
user record satisfying the search condition, in the case PAGE_CUR_L or
536
PAGE_CUR_LE, on the last user record. If no such user record exists, then
537
in the first case sets the cursor after last in tree, and in the latter case
538
before first in tree. The latching mode must be BTR_SEARCH_LEAF or
539
BTR_MODIFY_LEAF. */
540
UNIV_INTERN
541
void
542
btr_pcur_open_on_user_rec(
543
/*======================*/
544
	dict_index_t*	index,		/* in: index */
545
	const dtuple_t*	tuple,		/* in: tuple on which search done */
546
	ulint		mode,		/* in: PAGE_CUR_L, ... */
547
	ulint		latch_mode,	/* in: BTR_SEARCH_LEAF or
548
					BTR_MODIFY_LEAF */
549
	btr_pcur_t*	cursor,		/* in: memory buffer for persistent
550
					cursor */
551
	mtr_t*		mtr)		/* in: mtr */
552
{
553
	btr_pcur_open(index, tuple, mode, latch_mode, cursor, mtr);
554
555
	if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
556
557
		if (btr_pcur_is_after_last_on_page(cursor)) {
558
559
			btr_pcur_move_to_next_user_rec(cursor, mtr);
560
		}
561
	} else {
562
		ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
563
564
		/* Not implemented yet */
565
566
		ut_error;
567
	}
568
}