1
/************************************************************************
4
(c) 1994-1996 Innobase Oy
6
Created 10/4/1994 Heikki Tuuri
7
*************************************************************************/
11
#include "page0cur.ic"
19
static ulint page_rnd = 976722341;
22
# ifdef UNIV_SEARCH_PERF_STAT
23
ulint page_cur_short_succ = 0;
24
# endif /* UNIV_SEARCH_PERF_STAT */
26
/********************************************************************
27
Tries a search shortcut based on the last insert. */
30
page_cur_try_search_shortcut(
31
/*=========================*/
32
/* out: TRUE on success */
33
page_t* page, /* in: index page */
34
dict_index_t* index, /* in: record descriptor */
35
dtuple_t* tuple, /* in: data tuple */
36
ulint* iup_matched_fields,
37
/* in/out: already matched fields in upper
39
ulint* iup_matched_bytes,
40
/* in/out: already matched bytes in a field
41
not yet completely matched */
42
ulint* ilow_matched_fields,
43
/* in/out: already matched fields in lower
45
ulint* ilow_matched_bytes,
46
/* in/out: already matched bytes in a field
47
not yet completely matched */
48
page_cur_t* cursor) /* out: page cursor */
56
#ifdef UNIV_SEARCH_DEBUG
59
ibool success = FALSE;
60
mem_heap_t* heap = NULL;
61
ulint offsets_[REC_OFFS_NORMAL_SIZE];
62
ulint* offsets = offsets_;
63
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
65
ut_ad(dtuple_check_typed(tuple));
67
rec = page_header_get_ptr(page, PAGE_LAST_INSERT);
68
offsets = rec_get_offsets(rec, index, offsets,
69
dtuple_get_n_fields(tuple), &heap);
72
ut_ad(page_rec_is_user_rec(rec));
74
ut_pair_min(&low_match, &low_bytes,
75
*ilow_matched_fields, *ilow_matched_bytes,
76
*iup_matched_fields, *iup_matched_bytes);
81
if (page_cmp_dtuple_rec_with_match(tuple, rec, offsets,
82
&low_match, &low_bytes) < 0) {
86
next_rec = page_rec_get_next(rec);
87
offsets = rec_get_offsets(next_rec, index, offsets,
88
dtuple_get_n_fields(tuple), &heap);
90
if (page_cmp_dtuple_rec_with_match(tuple, next_rec, offsets,
91
&up_match, &up_bytes) >= 0) {
97
#ifdef UNIV_SEARCH_DEBUG
98
page_cur_search_with_match(page, index, tuple, PAGE_CUR_DBG,
104
ut_a(cursor2.rec == cursor->rec);
106
if (next_rec != page_get_supremum_rec(page)) {
108
ut_a(*iup_matched_fields == up_match);
109
ut_a(*iup_matched_bytes == up_bytes);
112
ut_a(*ilow_matched_fields == low_match);
113
ut_a(*ilow_matched_bytes == low_bytes);
115
if (!page_rec_is_supremum(next_rec)) {
117
*iup_matched_fields = up_match;
118
*iup_matched_bytes = up_bytes;
121
*ilow_matched_fields = low_match;
122
*ilow_matched_bytes = low_bytes;
124
#ifdef UNIV_SEARCH_PERF_STAT
125
page_cur_short_succ++;
129
if (UNIV_LIKELY_NULL(heap)) {
137
#ifdef PAGE_CUR_LE_OR_EXTENDS
138
/********************************************************************
139
Checks if the nth field in a record is a character type field which extends
140
the nth field in tuple, i.e., the field is longer or equal in length and has
141
common first characters. */
144
page_cur_rec_field_extends(
145
/*=======================*/
146
/* out: TRUE if rec field
147
extends tuple field */
148
dtuple_t* tuple, /* in: data tuple */
149
rec_t* rec, /* in: record */
150
const ulint* offsets,/* in: array returned by rec_get_offsets() */
151
ulint n) /* in: compare nth field */
158
ut_ad(rec_offs_validate(rec, NULL, offsets));
159
dfield = dtuple_get_nth_field(tuple, n);
161
type = dfield_get_type(dfield);
163
rec_f = rec_get_nth_field(rec, offsets, n, &rec_f_len);
165
if (type->mtype == DATA_VARCHAR
166
|| type->mtype == DATA_CHAR
167
|| type->mtype == DATA_FIXBINARY
168
|| type->mtype == DATA_BINARY
169
|| type->mtype == DATA_BLOB
170
|| type->mtype == DATA_VARMYSQL
171
|| type->mtype == DATA_MYSQL) {
173
if (dfield_get_len(dfield) != UNIV_SQL_NULL
174
&& rec_f_len != UNIV_SQL_NULL
175
&& rec_f_len >= dfield_get_len(dfield)
176
&& !cmp_data_data_slow(type,
177
dfield_get_data(dfield),
178
dfield_get_len(dfield),
179
rec_f, dfield_get_len(dfield))) {
187
#endif /* PAGE_CUR_LE_OR_EXTENDS */
189
/********************************************************************
190
Searches the right position for a page cursor. */
193
page_cur_search_with_match(
194
/*=======================*/
195
page_t* page, /* in: index page */
196
dict_index_t* index, /* in: record descriptor */
197
dtuple_t* tuple, /* in: data tuple */
198
ulint mode, /* in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G,
200
ulint* iup_matched_fields,
201
/* in/out: already matched fields in upper
203
ulint* iup_matched_bytes,
204
/* in/out: already matched bytes in a field
205
not yet completely matched */
206
ulint* ilow_matched_fields,
207
/* in/out: already matched fields in lower
209
ulint* ilow_matched_bytes,
210
/* in/out: already matched bytes in a field
211
not yet completely matched */
212
page_cur_t* cursor) /* out: page cursor */
217
page_dir_slot_t* slot;
221
ulint up_matched_fields;
222
ulint up_matched_bytes;
223
ulint low_matched_fields;
224
ulint low_matched_bytes;
225
ulint cur_matched_fields;
226
ulint cur_matched_bytes;
228
#ifdef UNIV_SEARCH_DEBUG
230
ulint dbg_matched_fields;
231
ulint dbg_matched_bytes;
233
mem_heap_t* heap = NULL;
234
ulint offsets_[REC_OFFS_NORMAL_SIZE];
235
ulint* offsets = offsets_;
236
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
238
ut_ad(page && tuple && iup_matched_fields && iup_matched_bytes
239
&& ilow_matched_fields && ilow_matched_bytes && cursor);
240
ut_ad(dtuple_validate(tuple));
241
ut_ad(dtuple_check_typed(tuple));
244
if (mode != PAGE_CUR_DBG)
245
# endif /* PAGE_CUR_DBG */
246
# ifdef PAGE_CUR_LE_OR_EXTENDS
247
if (mode != PAGE_CUR_LE_OR_EXTENDS)
248
# endif /* PAGE_CUR_LE_OR_EXTENDS */
249
ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE
250
|| mode == PAGE_CUR_G || mode == PAGE_CUR_GE);
251
#endif /* UNIV_DEBUG */
253
page_check_dir(page);
255
#ifdef PAGE_CUR_ADAPT
256
if ((page_header_get_field(page, PAGE_LEVEL) == 0)
257
&& (mode == PAGE_CUR_LE)
258
&& (page_header_get_field(page, PAGE_N_DIRECTION) > 3)
259
&& (page_header_get_ptr(page, PAGE_LAST_INSERT))
260
&& (page_header_get_field(page, PAGE_DIRECTION) == PAGE_RIGHT)) {
262
if (page_cur_try_search_shortcut(
264
iup_matched_fields, iup_matched_bytes,
265
ilow_matched_fields, ilow_matched_bytes,
271
if (mode == PAGE_CUR_DBG) {
277
/* The following flag does not work for non-latin1 char sets because
278
cmp_full_field does not tell how many bytes matched */
279
#ifdef PAGE_CUR_LE_OR_EXTENDS
280
ut_a(mode != PAGE_CUR_LE_OR_EXTENDS);
281
#endif /* PAGE_CUR_LE_OR_EXTENDS */
283
/* If mode PAGE_CUR_G is specified, we are trying to position the
284
cursor to answer a query of the form "tuple < X", where tuple is
285
the input parameter, and X denotes an arbitrary physical record on
286
the page. We want to position the cursor on the first X which
287
satisfies the condition. */
289
up_matched_fields = *iup_matched_fields;
290
up_matched_bytes = *iup_matched_bytes;
291
low_matched_fields = *ilow_matched_fields;
292
low_matched_bytes = *ilow_matched_bytes;
294
/* Perform binary search. First the search is done through the page
295
directory, after that as a linear search in the list of records
296
owned by the upper limit directory slot. */
299
up = page_dir_get_n_slots(page) - 1;
301
/* Perform binary search until the lower and upper limit directory
302
slots come to the distance 1 of each other */
304
while (up - low > 1) {
305
mid = (low + up) / 2;
306
slot = page_dir_get_nth_slot(page, mid);
307
mid_rec = page_dir_slot_get_rec(slot);
309
ut_pair_min(&cur_matched_fields, &cur_matched_bytes,
310
low_matched_fields, low_matched_bytes,
311
up_matched_fields, up_matched_bytes);
313
offsets = rec_get_offsets(mid_rec, index, offsets,
314
dtuple_get_n_fields_cmp(tuple),
317
cmp = cmp_dtuple_rec_with_match(tuple, mid_rec, offsets,
320
if (UNIV_LIKELY(cmp > 0)) {
323
low_matched_fields = cur_matched_fields;
324
low_matched_bytes = cur_matched_bytes;
326
} else if (UNIV_EXPECT(cmp, -1)) {
327
#ifdef PAGE_CUR_LE_OR_EXTENDS
328
if (mode == PAGE_CUR_LE_OR_EXTENDS
329
&& page_cur_rec_field_extends(
330
tuple, mid_rec, offsets,
331
cur_matched_fields)) {
335
#endif /* PAGE_CUR_LE_OR_EXTENDS */
338
up_matched_fields = cur_matched_fields;
339
up_matched_bytes = cur_matched_bytes;
341
} else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE
342
#ifdef PAGE_CUR_LE_OR_EXTENDS
343
|| mode == PAGE_CUR_LE_OR_EXTENDS
344
#endif /* PAGE_CUR_LE_OR_EXTENDS */
354
slot = page_dir_get_nth_slot(page, low);
355
low_rec = page_dir_slot_get_rec(slot);
356
slot = page_dir_get_nth_slot(page, up);
357
up_rec = page_dir_slot_get_rec(slot);
359
/* Perform linear search until the upper and lower records come to
360
distance 1 of each other. */
362
while (page_rec_get_next(low_rec) != up_rec) {
364
mid_rec = page_rec_get_next(low_rec);
366
ut_pair_min(&cur_matched_fields, &cur_matched_bytes,
367
low_matched_fields, low_matched_bytes,
368
up_matched_fields, up_matched_bytes);
370
offsets = rec_get_offsets(mid_rec, index, offsets,
371
dtuple_get_n_fields_cmp(tuple),
374
cmp = cmp_dtuple_rec_with_match(tuple, mid_rec, offsets,
377
if (UNIV_LIKELY(cmp > 0)) {
380
low_matched_fields = cur_matched_fields;
381
low_matched_bytes = cur_matched_bytes;
383
} else if (UNIV_EXPECT(cmp, -1)) {
384
#ifdef PAGE_CUR_LE_OR_EXTENDS
385
if (mode == PAGE_CUR_LE_OR_EXTENDS
386
&& page_cur_rec_field_extends(
387
tuple, mid_rec, offsets,
388
cur_matched_fields)) {
392
#endif /* PAGE_CUR_LE_OR_EXTENDS */
395
up_matched_fields = cur_matched_fields;
396
up_matched_bytes = cur_matched_bytes;
397
} else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE
398
#ifdef PAGE_CUR_LE_OR_EXTENDS
399
|| mode == PAGE_CUR_LE_OR_EXTENDS
400
#endif /* PAGE_CUR_LE_OR_EXTENDS */
410
#ifdef UNIV_SEARCH_DEBUG
412
/* Check that the lower and upper limit records have the
413
right alphabetical order compared to tuple. */
414
dbg_matched_fields = 0;
415
dbg_matched_bytes = 0;
417
offsets = rec_get_offsets(low_rec, index, offsets,
418
ULINT_UNDEFINED, &heap);
419
dbg_cmp = page_cmp_dtuple_rec_with_match(tuple, low_rec, offsets,
422
if (mode == PAGE_CUR_G) {
424
} else if (mode == PAGE_CUR_GE) {
426
} else if (mode == PAGE_CUR_L) {
428
} else if (mode == PAGE_CUR_LE) {
432
if (low_rec != page_get_infimum_rec(page)) {
434
ut_a(low_matched_fields == dbg_matched_fields);
435
ut_a(low_matched_bytes == dbg_matched_bytes);
438
dbg_matched_fields = 0;
439
dbg_matched_bytes = 0;
441
offsets = rec_get_offsets(up_rec, index, offsets,
442
ULINT_UNDEFINED, &heap);
443
dbg_cmp = page_cmp_dtuple_rec_with_match(tuple, up_rec, offsets,
446
if (mode == PAGE_CUR_G) {
448
} else if (mode == PAGE_CUR_GE) {
450
} else if (mode == PAGE_CUR_L) {
452
} else if (mode == PAGE_CUR_LE) {
456
if (up_rec != page_get_supremum_rec(page)) {
458
ut_a(up_matched_fields == dbg_matched_fields);
459
ut_a(up_matched_bytes == dbg_matched_bytes);
462
if (mode <= PAGE_CUR_GE) {
463
cursor->rec = up_rec;
465
cursor->rec = low_rec;
468
*iup_matched_fields = up_matched_fields;
469
*iup_matched_bytes = up_matched_bytes;
470
*ilow_matched_fields = low_matched_fields;
471
*ilow_matched_bytes = low_matched_bytes;
472
if (UNIV_LIKELY_NULL(heap)) {
477
/***************************************************************
478
Positions a page cursor on a randomly chosen user record on a page. If there
479
are no user records, sets the cursor on the infimum record. */
482
page_cur_open_on_rnd_user_rec(
483
/*==========================*/
484
page_t* page, /* in: page */
485
page_cur_t* cursor) /* in/out: page cursor */
490
if (page_get_n_recs(page) == 0) {
491
page_cur_position(page_get_infimum_rec(page), cursor);
496
page_rnd += 87584577;
498
rnd = page_rnd % page_get_n_recs(page);
500
rec = page_get_infimum_rec(page);
502
rec = page_rec_get_next(rec);
505
rec = page_rec_get_next(rec);
510
page_cur_position(rec, cursor);
513
/***************************************************************
514
Writes the log record of a record insert on a page. */
517
page_cur_insert_rec_write_log(
518
/*==========================*/
519
rec_t* insert_rec, /* in: inserted physical record */
520
ulint rec_size, /* in: insert_rec size */
521
rec_t* cursor_rec, /* in: record the
522
cursor is pointing to */
523
dict_index_t* index, /* in: record descriptor */
524
mtr_t* mtr) /* in: mini-transaction handle */
528
ulint cur_extra_size;
532
ulint extra_info_yes;
538
ut_a(rec_size < UNIV_PAGE_SIZE);
539
ut_ad(buf_frame_align(insert_rec) == buf_frame_align(cursor_rec));
540
ut_ad(!page_rec_is_comp(insert_rec)
541
== !dict_table_is_comp(index->table));
542
comp = page_rec_is_comp(insert_rec);
545
mem_heap_t* heap = NULL;
546
ulint cur_offs_[REC_OFFS_NORMAL_SIZE];
547
ulint ins_offs_[REC_OFFS_NORMAL_SIZE];
552
*cur_offs_ = (sizeof cur_offs_) / sizeof *cur_offs_;
553
*ins_offs_ = (sizeof ins_offs_) / sizeof *ins_offs_;
555
cur_offs = rec_get_offsets(cursor_rec, index, cur_offs_,
556
ULINT_UNDEFINED, &heap);
557
ins_offs = rec_get_offsets(insert_rec, index, ins_offs_,
558
ULINT_UNDEFINED, &heap);
560
extra_size = rec_offs_extra_size(ins_offs);
561
cur_extra_size = rec_offs_extra_size(cur_offs);
562
ut_ad(rec_size == rec_offs_size(ins_offs));
563
cur_rec_size = rec_offs_size(cur_offs);
565
if (UNIV_LIKELY_NULL(heap)) {
570
ins_ptr = insert_rec - extra_size;
574
if (cur_extra_size == extra_size) {
575
min_rec_size = ut_min(cur_rec_size, rec_size);
577
cur_ptr = cursor_rec - cur_extra_size;
579
/* Find out the first byte in insert_rec which differs from
580
cursor_rec; skip the bytes in the record info */
583
if (i >= min_rec_size) {
586
} else if (*ins_ptr == *cur_ptr) {
590
} else if ((i < extra_size)
593
? REC_N_NEW_EXTRA_BYTES
594
: REC_N_OLD_EXTRA_BYTES))) {
596
ins_ptr = insert_rec;
597
cur_ptr = cursor_rec;
604
if (mtr_get_log_mode(mtr) != MTR_LOG_SHORT_INSERTS) {
606
log_ptr = mlog_open_and_write_index(mtr, insert_rec, index,
608
? MLOG_COMP_REC_INSERT
614
/* Logging in mtr is switched off during crash
615
recovery: in that case mlog_open returns NULL */
619
log_end = &log_ptr[2 + 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN];
620
/* Write the cursor rec offset as a 2-byte ulint */
621
mach_write_to_2(log_ptr, cursor_rec
622
- buf_frame_align(cursor_rec));
625
log_ptr = mlog_open(mtr, 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN);
627
/* Logging in mtr is switched off during crash
628
recovery: in that case mlog_open returns NULL */
631
log_end = &log_ptr[5 + 1 + 5 + 5 + MLOG_BUF_MARGIN];
634
if ((rec_get_info_and_status_bits(insert_rec, comp)
635
!= rec_get_info_and_status_bits(cursor_rec, comp))
636
|| (extra_size != cur_extra_size)
637
|| (rec_size != cur_rec_size)) {
644
/* Write the record end segment length and the extra info storage
646
log_ptr += mach_write_compressed(log_ptr, 2 * (rec_size - i)
648
if (extra_info_yes) {
649
/* Write the info bits */
650
mach_write_to_1(log_ptr,
651
rec_get_info_and_status_bits(insert_rec,
655
/* Write the record origin offset */
656
log_ptr += mach_write_compressed(log_ptr, extra_size);
658
/* Write the mismatch index */
659
log_ptr += mach_write_compressed(log_ptr, i);
661
ut_a(i < UNIV_PAGE_SIZE);
662
ut_a(extra_size < UNIV_PAGE_SIZE);
665
/* Write to the log the inserted index record end segment which
666
differs from the cursor record */
670
if (log_ptr + rec_size <= log_end) {
671
memcpy(log_ptr, ins_ptr, rec_size);
672
mlog_close(mtr, log_ptr + rec_size);
674
mlog_close(mtr, log_ptr);
675
ut_a(rec_size < UNIV_PAGE_SIZE);
676
mlog_catenate_string(mtr, ins_ptr, rec_size);
680
/***************************************************************
681
Parses a log record of a record insert on a page. */
684
page_cur_parse_insert_rec(
685
/*======================*/
686
/* out: end of log record or NULL */
687
ibool is_short,/* in: TRUE if short inserts */
688
byte* ptr, /* in: buffer */
689
byte* end_ptr,/* in: buffer end */
690
dict_index_t* index, /* in: record descriptor */
691
page_t* page, /* in: page or NULL */
692
mtr_t* mtr) /* in: mtr or NULL */
694
ulint extra_info_yes;
695
ulint offset = 0; /* remove warning */
698
ulint mismatch_index;
703
ulint info_and_status_bits = 0; /* remove warning */
705
mem_heap_t* heap = NULL;
706
ulint offsets_[REC_OFFS_NORMAL_SIZE];
707
ulint* offsets = offsets_;
708
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
711
/* Read the cursor rec offset as a 2-byte ulint */
713
if (end_ptr < ptr + 2) {
718
offset = mach_read_from_2(ptr);
720
if (offset >= UNIV_PAGE_SIZE) {
722
recv_sys->found_corrupt_log = TRUE;
730
ptr = mach_parse_compressed(ptr, end_ptr, &end_seg_len);
737
extra_info_yes = end_seg_len & 0x1UL;
740
if (end_seg_len >= UNIV_PAGE_SIZE) {
741
recv_sys->found_corrupt_log = TRUE;
746
if (extra_info_yes) {
747
/* Read the info bits */
749
if (end_ptr < ptr + 1) {
754
info_and_status_bits = mach_read_from_1(ptr);
757
ptr = mach_parse_compressed(ptr, end_ptr, &origin_offset);
764
ut_a(origin_offset < UNIV_PAGE_SIZE);
766
ptr = mach_parse_compressed(ptr, end_ptr, &mismatch_index);
773
ut_a(mismatch_index < UNIV_PAGE_SIZE);
776
if (end_ptr < ptr + end_seg_len) {
783
return(ptr + end_seg_len);
786
ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
788
/* Read from the log the inserted index record end segment which
789
differs from the cursor record */
792
cursor_rec = page_rec_get_prev(page_get_supremum_rec(page));
794
cursor_rec = page + offset;
797
offsets = rec_get_offsets(cursor_rec, index, offsets,
798
ULINT_UNDEFINED, &heap);
800
if (extra_info_yes == 0) {
801
info_and_status_bits = rec_get_info_and_status_bits(
802
cursor_rec, page_is_comp(page));
803
origin_offset = rec_offs_extra_size(offsets);
804
mismatch_index = rec_offs_size(offsets) - end_seg_len;
807
if (mismatch_index + end_seg_len < sizeof buf1) {
810
buf = mem_alloc(mismatch_index + end_seg_len);
813
/* Build the inserted record to buf */
815
if (mismatch_index >= UNIV_PAGE_SIZE) {
817
"Is short %lu, info_and_status_bits %lu, offset %lu, "
819
"mismatch index %lu, end_seg_len %lu\n"
821
(ulong) is_short, (ulong) info_and_status_bits,
823
(ulong) origin_offset,
824
(ulong) mismatch_index, (ulong) end_seg_len,
825
(ulong) (ptr - ptr2));
827
fputs("Dump of 300 bytes of log:\n", stderr);
828
ut_print_buf(stderr, ptr2, 300);
830
buf_page_print(page);
835
ut_memcpy(buf, rec_get_start(cursor_rec, offsets), mismatch_index);
836
ut_memcpy(buf + mismatch_index, ptr, end_seg_len);
838
rec_set_info_and_status_bits(buf + origin_offset, page_is_comp(page),
839
info_and_status_bits);
841
page_cur_position(cursor_rec, &cursor);
843
offsets = rec_get_offsets(buf + origin_offset, index, offsets,
844
ULINT_UNDEFINED, &heap);
845
page_cur_rec_insert(&cursor, buf + origin_offset, index, offsets, mtr);
852
if (UNIV_LIKELY_NULL(heap)) {
856
return(ptr + end_seg_len);
859
/***************************************************************
860
Inserts a record next to page cursor. Returns pointer to inserted record if
861
succeed, i.e., enough space available, NULL otherwise. The record to be
862
inserted can be in a data tuple or as a physical record. The other parameter
863
must then be NULL. The cursor stays at the same position. */
866
page_cur_insert_rec_low(
867
/*====================*/
868
/* out: pointer to record if succeed, NULL
870
page_cur_t* cursor, /* in: a page cursor */
871
dtuple_t* tuple, /* in: pointer to a data tuple or NULL */
872
dict_index_t* index, /* in: record descriptor */
873
rec_t* rec, /* in: pointer to a physical record or NULL */
874
ulint* offsets,/* in: rec_get_offsets(rec, index) or NULL */
875
mtr_t* mtr) /* in: mini-transaction handle */
877
byte* insert_buf = NULL;
879
byte* page; /* the relevant page */
880
rec_t* last_insert; /* cursor position at previous
882
rec_t* insert_rec; /* inserted record */
883
ulint heap_no; /* heap number of the inserted
885
rec_t* current_rec; /* current record after which the
886
new record is inserted */
887
rec_t* next_rec; /* next record after current before
889
ulint owner_slot; /* the slot which owns the
893
mem_heap_t* heap = NULL;
896
ut_ad(cursor && mtr);
898
ut_ad(!(tuple && rec));
899
ut_ad(rec || dtuple_check_typed(tuple));
901
page = page_cur_get_page(cursor);
902
comp = page_is_comp(page);
903
ut_ad(dict_table_is_comp(index->table) == !!comp);
905
ut_ad(cursor->rec != page_get_supremum_rec(page));
907
/* 1. Get the size of the physical record in the page */
909
rec_size = rec_get_converted_size(index, tuple);
912
offsets = rec_get_offsets(rec, index, offsets,
913
ULINT_UNDEFINED, &heap);
915
ut_ad(rec_offs_validate(rec, index, offsets));
916
rec_size = rec_offs_size(offsets);
919
/* 2. Try to find suitable space from page memory management */
920
insert_buf = page_mem_alloc(page, rec_size, index, &heap_no);
922
if (insert_buf == NULL) {
923
if (UNIV_LIKELY_NULL(heap)) {
929
/* 3. Create the record */
931
insert_rec = rec_convert_dtuple_to_rec(insert_buf,
933
offsets = rec_get_offsets(insert_rec, index, offsets,
934
ULINT_UNDEFINED, &heap);
936
insert_rec = rec_copy(insert_buf, rec, offsets);
937
ut_ad(rec_offs_validate(rec, index, offsets));
938
rec_offs_make_valid(insert_rec, index, offsets);
942
ut_ad(rec_size == rec_offs_size(offsets));
944
/* 4. Insert the record in the linked list of records */
945
current_rec = cursor->rec;
947
ut_ad(!comp || rec_get_status(current_rec) <= REC_STATUS_INFIMUM);
948
ut_ad(!comp || rec_get_status(insert_rec) < REC_STATUS_INFIMUM);
950
next_rec = page_rec_get_next(current_rec);
951
ut_ad(!comp || rec_get_status(next_rec) != REC_STATUS_INFIMUM);
952
page_rec_set_next(insert_rec, next_rec);
953
page_rec_set_next(current_rec, insert_rec);
955
page_header_set_field(page, PAGE_N_RECS, 1 + page_get_n_recs(page));
957
/* 5. Set the n_owned field in the inserted record to zero,
958
and set the heap_no field */
960
rec_set_n_owned(insert_rec, comp, 0);
961
rec_set_heap_no(insert_rec, comp, heap_no);
963
/* 6. Update the last insertion info in page header */
965
last_insert = page_header_get_ptr(page, PAGE_LAST_INSERT);
966
ut_ad(!last_insert || !comp
967
|| rec_get_node_ptr_flag(last_insert)
968
== rec_get_node_ptr_flag(insert_rec));
970
if (last_insert == NULL) {
971
page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
972
page_header_set_field(page, PAGE_N_DIRECTION, 0);
974
} else if ((last_insert == current_rec)
975
&& (page_header_get_field(page, PAGE_DIRECTION)
978
page_header_set_field(page, PAGE_DIRECTION, PAGE_RIGHT);
979
page_header_set_field(page, PAGE_N_DIRECTION,
980
page_header_get_field(
981
page, PAGE_N_DIRECTION) + 1);
983
} else if ((page_rec_get_next(insert_rec) == last_insert)
984
&& (page_header_get_field(page, PAGE_DIRECTION)
987
page_header_set_field(page, PAGE_DIRECTION, PAGE_LEFT);
988
page_header_set_field(page, PAGE_N_DIRECTION,
989
page_header_get_field(
990
page, PAGE_N_DIRECTION) + 1);
992
page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
993
page_header_set_field(page, PAGE_N_DIRECTION, 0);
996
page_header_set_ptr(page, PAGE_LAST_INSERT, insert_rec);
998
/* 7. It remains to update the owner record. */
1000
owner_rec = page_rec_find_owner_rec(insert_rec);
1001
n_owned = rec_get_n_owned(owner_rec, comp);
1002
rec_set_n_owned(owner_rec, comp, n_owned + 1);
1004
/* 8. Now we have incremented the n_owned field of the owner
1005
record. If the number exceeds PAGE_DIR_SLOT_MAX_N_OWNED,
1006
we have to split the corresponding directory slot in two. */
1008
if (n_owned == PAGE_DIR_SLOT_MAX_N_OWNED) {
1009
owner_slot = page_dir_find_owner_slot(owner_rec);
1010
page_dir_split_slot(page, owner_slot);
1013
/* 9. Write log record of the insert */
1014
page_cur_insert_rec_write_log(insert_rec, rec_size, current_rec,
1017
if (UNIV_LIKELY_NULL(heap)) {
1018
mem_heap_free(heap);
1023
/**************************************************************
1024
Writes a log record of copying a record list end to a new created page. */
1027
page_copy_rec_list_to_created_page_write_log(
1028
/*=========================================*/
1029
/* out: 4-byte field where to
1030
write the log data length */
1031
page_t* page, /* in: index page */
1032
dict_index_t* index, /* in: record descriptor */
1033
mtr_t* mtr) /* in: mtr */
1037
ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
1039
log_ptr = mlog_open_and_write_index(mtr, page, index,
1041
? MLOG_COMP_LIST_END_COPY_CREATED
1042
: MLOG_LIST_END_COPY_CREATED, 4);
1044
mlog_close(mtr, log_ptr + 4);
1049
/**************************************************************
1050
Parses a log record of copying a record list end to a new created page. */
1053
page_parse_copy_rec_list_to_created_page(
1054
/*=====================================*/
1055
/* out: end of log record or NULL */
1056
byte* ptr, /* in: buffer */
1057
byte* end_ptr,/* in: buffer end */
1058
dict_index_t* index, /* in: record descriptor */
1059
page_t* page, /* in: page or NULL */
1060
mtr_t* mtr) /* in: mtr or NULL */
1065
if (ptr + 4 > end_ptr) {
1070
log_data_len = mach_read_from_4(ptr);
1073
rec_end = ptr + log_data_len;
1075
if (rec_end > end_ptr) {
1085
while (ptr < rec_end) {
1086
ptr = page_cur_parse_insert_rec(TRUE, ptr, end_ptr,
1090
ut_a(ptr == rec_end);
1092
page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
1093
page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
1094
page_header_set_field(page, PAGE_N_DIRECTION, 0);
1099
/*****************************************************************
1100
Copies records from page to a newly created page, from a given record onward,
1101
including that record. Infimum and supremum records are not copied. */
1104
page_copy_rec_list_end_to_created_page(
1105
/*===================================*/
1106
page_t* new_page, /* in: index page to copy to */
1107
page_t* page, /* in: index page */
1108
rec_t* rec, /* in: first record to copy */
1109
dict_index_t* index, /* in: record descriptor */
1110
mtr_t* mtr) /* in: mtr */
1112
page_dir_slot_t* slot = 0; /* remove warning */
1114
rec_t* insert_rec = 0; /* remove warning */
1123
ulint comp = page_is_comp(page);
1124
mem_heap_t* heap = NULL;
1125
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1126
ulint* offsets = offsets_;
1127
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1129
ut_ad(page_dir_get_n_heap(new_page) == 2);
1130
ut_ad(page != new_page);
1131
ut_ad(comp == page_is_comp(new_page));
1133
if (rec == page_get_infimum_rec(page)) {
1135
rec = page_rec_get_next(rec);
1138
if (rec == page_get_supremum_rec(page)) {
1144
/* To pass the debug tests we have to set these dummy values
1145
in the debug version */
1146
page_dir_set_n_slots(new_page, UNIV_PAGE_SIZE / 2);
1147
page_header_set_ptr(new_page, PAGE_HEAP_TOP,
1148
new_page + UNIV_PAGE_SIZE - 1);
1151
log_ptr = page_copy_rec_list_to_created_page_write_log(new_page,
1154
log_data_len = dyn_array_get_data_size(&(mtr->log));
1156
/* Individual inserts are logged in a shorter form */
1158
log_mode = mtr_set_log_mode(mtr, MTR_LOG_SHORT_INSERTS);
1160
prev_rec = page_get_infimum_rec(new_page);
1162
heap_top = new_page + PAGE_NEW_SUPREMUM_END;
1164
heap_top = new_page + PAGE_OLD_SUPREMUM_END;
1170
/* should be do ... until, comment by Jani */
1171
while (rec != page_get_supremum_rec(page)) {
1172
offsets = rec_get_offsets(rec, index, offsets,
1173
ULINT_UNDEFINED, &heap);
1174
insert_rec = rec_copy(heap_top, rec, offsets);
1176
rec_set_next_offs(prev_rec, comp, insert_rec - new_page);
1178
rec_set_n_owned(insert_rec, comp, 0);
1179
rec_set_heap_no(insert_rec, comp, 2 + n_recs);
1181
rec_size = rec_offs_size(offsets);
1183
heap_top = heap_top + rec_size;
1185
ut_ad(heap_top < new_page + UNIV_PAGE_SIZE);
1190
if (count == (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2) {
1194
slot = page_dir_get_nth_slot(new_page, slot_index);
1196
page_dir_slot_set_rec(slot, insert_rec);
1197
page_dir_slot_set_n_owned(slot, count);
1202
page_cur_insert_rec_write_log(insert_rec, rec_size, prev_rec,
1204
prev_rec = insert_rec;
1205
rec = page_rec_get_next(rec);
1208
if ((slot_index > 0) && (count + 1
1209
+ (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2
1210
<= PAGE_DIR_SLOT_MAX_N_OWNED)) {
1211
/* We can merge the two last dir slots. This operation is
1212
here to make this function imitate exactly the equivalent
1213
task made using page_cur_insert_rec, which we use in database
1214
recovery to reproduce the task performed by this function.
1215
To be able to check the correctness of recovery, it is good
1216
that it imitates exactly. */
1218
count += (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2;
1220
page_dir_slot_set_n_owned(slot, 0);
1225
if (UNIV_LIKELY_NULL(heap)) {
1226
mem_heap_free(heap);
1229
log_data_len = dyn_array_get_data_size(&(mtr->log)) - log_data_len;
1231
ut_a(log_data_len < 100 * UNIV_PAGE_SIZE);
1233
mach_write_to_4(log_ptr, log_data_len);
1235
rec_set_next_offs(insert_rec, comp,
1236
comp ? PAGE_NEW_SUPREMUM : PAGE_OLD_SUPREMUM);
1238
slot = page_dir_get_nth_slot(new_page, 1 + slot_index);
1240
page_dir_slot_set_rec(slot, page_get_supremum_rec(new_page));
1241
page_dir_slot_set_n_owned(slot, count + 1);
1243
page_dir_set_n_slots(new_page, 2 + slot_index);
1244
page_header_set_ptr(new_page, PAGE_HEAP_TOP, heap_top);
1245
page_dir_set_n_heap(new_page, 2 + n_recs);
1246
page_header_set_field(new_page, PAGE_N_RECS, n_recs);
1248
page_header_set_ptr(new_page, PAGE_LAST_INSERT, NULL);
1249
page_header_set_field(new_page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
1250
page_header_set_field(new_page, PAGE_N_DIRECTION, 0);
1252
/* Restore the log mode */
1254
mtr_set_log_mode(mtr, log_mode);
1257
/***************************************************************
1258
Writes log record of a record delete on a page. */
1261
page_cur_delete_rec_write_log(
1262
/*==========================*/
1263
rec_t* rec, /* in: record to be deleted */
1264
dict_index_t* index, /* in: record descriptor */
1265
mtr_t* mtr) /* in: mini-transaction handle */
1269
ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table));
1271
log_ptr = mlog_open_and_write_index(mtr, rec, index,
1272
page_rec_is_comp(rec)
1273
? MLOG_COMP_REC_DELETE
1274
: MLOG_REC_DELETE, 2);
1277
/* Logging in mtr is switched off during crash recovery:
1278
in that case mlog_open returns NULL */
1282
/* Write the cursor rec offset as a 2-byte ulint */
1283
mach_write_to_2(log_ptr, page_offset(rec));
1285
mlog_close(mtr, log_ptr + 2);
1288
/***************************************************************
1289
Parses log record of a record delete on a page. */
1292
page_cur_parse_delete_rec(
1293
/*======================*/
1294
/* out: pointer to record end or NULL */
1295
byte* ptr, /* in: buffer */
1296
byte* end_ptr,/* in: buffer end */
1297
dict_index_t* index, /* in: record descriptor */
1298
page_t* page, /* in: page or NULL */
1299
mtr_t* mtr) /* in: mtr or NULL */
1304
if (end_ptr < ptr + 2) {
1309
/* Read the cursor rec offset as a 2-byte ulint */
1310
offset = mach_read_from_2(ptr);
1313
ut_a(offset <= UNIV_PAGE_SIZE);
1316
mem_heap_t* heap = NULL;
1317
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1318
rec_t* rec = page + offset;
1319
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1321
page_cur_position(rec, &cursor);
1323
page_cur_delete_rec(&cursor, index,
1324
rec_get_offsets(rec, index, offsets_,
1325
ULINT_UNDEFINED, &heap),
1327
if (UNIV_LIKELY_NULL(heap)) {
1328
mem_heap_free(heap);
1335
/***************************************************************
1336
Deletes a record at the page cursor. The cursor is moved to the next
1337
record after the deleted one. */
1340
page_cur_delete_rec(
1341
/*================*/
1342
page_cur_t* cursor, /* in: a page cursor */
1343
dict_index_t* index, /* in: record descriptor */
1344
const ulint* offsets,/* in: rec_get_offsets(cursor->rec, index) */
1345
mtr_t* mtr) /* in: mini-transaction handle */
1347
page_dir_slot_t* cur_dir_slot;
1348
page_dir_slot_t* prev_slot;
1351
rec_t* prev_rec = NULL;
1357
ut_ad(cursor && mtr);
1359
page = page_cur_get_page(cursor);
1360
current_rec = cursor->rec;
1361
ut_ad(rec_offs_validate(current_rec, index, offsets));
1362
ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
1364
/* The record must not be the supremum or infimum record. */
1365
ut_ad(current_rec != page_get_supremum_rec(page));
1366
ut_ad(current_rec != page_get_infimum_rec(page));
1368
/* Save to local variables some data associated with current_rec */
1369
cur_slot_no = page_dir_find_owner_slot(current_rec);
1370
cur_dir_slot = page_dir_get_nth_slot(page, cur_slot_no);
1371
cur_n_owned = page_dir_slot_get_n_owned(cur_dir_slot);
1373
/* 0. Write the log record */
1374
page_cur_delete_rec_write_log(current_rec, index, mtr);
1376
/* 1. Reset the last insert info in the page header and increment
1377
the modify clock for the frame */
1379
page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
1381
/* The page gets invalid for optimistic searches: increment the
1382
frame modify clock */
1384
buf_frame_modify_clock_inc(page);
1386
/* 2. Find the next and the previous record. Note that the cursor is
1387
left at the next record. */
1389
ut_ad(cur_slot_no > 0);
1390
prev_slot = page_dir_get_nth_slot(page, cur_slot_no - 1);
1392
rec = page_dir_slot_get_rec(prev_slot);
1394
/* rec now points to the record of the previous directory slot. Look
1395
for the immediate predecessor of current_rec in a loop. */
1397
while(current_rec != rec) {
1399
rec = page_rec_get_next(rec);
1402
page_cur_move_to_next(cursor);
1403
next_rec = cursor->rec;
1405
/* 3. Remove the record from the linked list of records */
1407
page_rec_set_next(prev_rec, next_rec);
1408
page_header_set_field(page, PAGE_N_RECS,
1409
(ulint)(page_get_n_recs(page) - 1));
1411
/* 4. If the deleted record is pointed to by a dir slot, update the
1412
record pointer in slot. In the following if-clause we assume that
1413
prev_rec is owned by the same slot, i.e., PAGE_DIR_SLOT_MIN_N_OWNED
1416
#if PAGE_DIR_SLOT_MIN_N_OWNED < 2
1417
# error "PAGE_DIR_SLOT_MIN_N_OWNED < 2"
1419
ut_ad(cur_n_owned > 1);
1421
if (current_rec == page_dir_slot_get_rec(cur_dir_slot)) {
1422
page_dir_slot_set_rec(cur_dir_slot, prev_rec);
1425
/* 5. Update the number of owned records of the slot */
1427
page_dir_slot_set_n_owned(cur_dir_slot, cur_n_owned - 1);
1429
/* 6. Free the memory occupied by the record */
1430
page_mem_free(page, current_rec, offsets);
1432
/* 7. Now we have decremented the number of owned records of the slot.
1433
If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the
1436
if (cur_n_owned <= PAGE_DIR_SLOT_MIN_N_OWNED) {
1437
page_dir_balance_slot(page, cur_slot_no);