1
/******************************************************
4
(c) 1994-1996 Innobase Oy
6
Created 2/2/1994 Heikki Tuuri
7
*******************************************************/
10
#include "page0page.h"
12
#include "page0page.ic"
17
#include "lock0lock.h"
27
The index page consists of a page header which contains the page's
28
id and other information. On top of it are the the index records
29
in a heap linked into a one way linear list according to alphabetic order.
31
Just below page end is an array of pointers which we call page directory,
32
to about every sixth record in the list. The pointers are placed in
33
the directory in the alphabetical order of the records pointed to,
34
enabling us to make binary search using the array. Each slot n:o I
35
in the directory points to a record, where a 4-bit field contains a count
36
of those records which are in the linear list between pointer I and
37
the pointer I - 1 in the directory, including the record
38
pointed to by pointer I and not including the record pointed to by I - 1.
39
We say that the record pointed to by slot I, or that slot I, owns
40
these records. The count is always kept in the range 4 to 8, with
41
the exception that it is 1 for the first slot, and 1--8 for the second slot.
43
An essentially binary search can be performed in the list of index
44
records, like we could do if we had pointer to every record in the
45
page directory. The data structure is, however, more efficient when
46
we are doing inserts, because most inserts are just pushed on a heap.
47
Only every 8th insert requires block move in the directory pointer
48
table, which itself is quite small. A record is deleted from the page
49
by just taking it off the linear list and updating the number of owned
50
records-field of the record which owns it, and updating the page directory,
51
if necessary. A special case is the one when the record owns itself.
52
Because the overhead of inserts is so small, we may also increase the
53
page size from the projected default of 8 kB to 64 kB without too
54
much loss of efficiency in inserts. Bigger page becomes actual
55
when the disk transfer rate compared to seek and latency time rises.
56
On the present system, the page size is set so that the page transfer
57
time (3 ms) is 20 % of the disk random access time (15 ms).
59
When the page is split, merged, or becomes full but contains deleted
60
records, we have to reorganize the page.
62
Assuming a page size of 8 kB, a typical index page of a secondary
63
index contains 300 index entries, and the size of the page directory
64
is 50 x 4 bytes = 200 bytes. */
66
/*******************************************************************
67
Looks for the directory slot which owns the given record. */
70
page_dir_find_owner_slot(
71
/*=====================*/
72
/* out: the directory slot number */
73
rec_t* rec) /* in: the physical record */
76
register uint16 rec_offs_bytes;
77
register page_dir_slot_t* slot;
78
register const page_dir_slot_t* first_slot;
79
register rec_t* r = rec;
81
ut_ad(page_rec_check(rec));
83
page = buf_frame_align(rec);
84
first_slot = page_dir_get_nth_slot(page, 0);
85
slot = page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1);
87
if (page_is_comp(page)) {
88
while (rec_get_n_owned(r, TRUE) == 0) {
89
r = page + rec_get_next_offs(r, TRUE);
90
ut_ad(r >= page + PAGE_NEW_SUPREMUM);
91
ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
94
while (rec_get_n_owned(r, FALSE) == 0) {
95
r = page + rec_get_next_offs(r, FALSE);
96
ut_ad(r >= page + PAGE_OLD_SUPREMUM);
97
ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
101
rec_offs_bytes = mach_encode_2(r - page);
103
while (UNIV_LIKELY(*(uint16*) slot != rec_offs_bytes)) {
105
if (UNIV_UNLIKELY(slot == first_slot)) {
107
"InnoDB: Probable data corruption on"
109
"InnoDB: Original record ",
110
(ulong) buf_frame_get_page_no(page));
112
if (page_is_comp(page)) {
113
fputs("(compact record)", stderr);
115
rec_print_old(stderr, rec);
119
"InnoDB: on that page.\n"
120
"InnoDB: Cannot find the dir slot for record ",
122
if (page_is_comp(page)) {
123
fputs("(compact record)", stderr);
125
rec_print_old(stderr, page
126
+ mach_decode_2(rec_offs_bytes));
129
"InnoDB: on that page!\n", stderr);
131
buf_page_print(page);
136
slot += PAGE_DIR_SLOT_SIZE;
139
return(((ulint) (first_slot - slot)) / PAGE_DIR_SLOT_SIZE);
142
/******************************************************************
143
Used to check the consistency of a directory slot. */
148
/* out: TRUE if succeed */
149
page_dir_slot_t* slot) /* in: slot */
157
page = buf_frame_align(slot);
159
n_slots = page_dir_get_n_slots(page);
161
ut_a(slot <= page_dir_get_nth_slot(page, 0));
162
ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1));
164
ut_a(page_rec_check(page_dir_slot_get_rec(slot)));
166
n_owned = rec_get_n_owned(page_dir_slot_get_rec(slot),
169
if (slot == page_dir_get_nth_slot(page, 0)) {
171
} else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) {
173
ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
175
ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED);
176
ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
182
/*****************************************************************
183
Sets the max trx id field value. */
188
page_t* page, /* in: page */
189
dulint trx_id) /* in: transaction id */
195
block = buf_block_align(page);
197
if (block->is_hashed) {
198
rw_lock_x_lock(&btr_search_latch);
201
/* It is not necessary to write this change to the redo log, as
202
during a database recovery we assume that the max trx id of every
203
page is the maximum trx id assigned before the crash. */
205
mach_write_to_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID, trx_id);
207
if (block->is_hashed) {
208
rw_lock_x_unlock(&btr_search_latch);
212
/*****************************************************************
213
Calculates free space if a page is emptied. */
216
page_get_free_space_of_empty_noninline(
217
/*===================================*/
218
/* out: free space */
219
ulint comp) /* in: nonzero=compact page format */
221
return(page_get_free_space_of_empty(comp));
224
/****************************************************************
225
Allocates a block of memory from an index page. */
230
/* out: pointer to start of allocated
231
buffer, or NULL if allocation fails */
232
page_t* page, /* in: index page */
233
ulint need, /* in: number of bytes needed */
234
dict_index_t* index, /* in: record descriptor */
235
ulint* heap_no)/* out: this contains the heap number
236
of the allocated record
237
if allocation succeeds */
244
ut_ad(page && heap_no);
246
/* If there are records in the free list, look if the first is
249
rec = page_header_get_ptr(page, PAGE_FREE);
252
mem_heap_t* heap = NULL;
253
ulint offsets_[REC_OFFS_NORMAL_SIZE];
254
ulint* offsets = offsets_;
255
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
257
offsets = rec_get_offsets(rec, index, offsets,
258
ULINT_UNDEFINED, &heap);
260
if (rec_offs_size(offsets) >= need) {
261
page_header_set_ptr(page, PAGE_FREE,
262
page_rec_get_next(rec));
264
garbage = page_header_get_field(page, PAGE_GARBAGE);
265
ut_ad(garbage >= need);
267
page_header_set_field(page, PAGE_GARBAGE,
270
*heap_no = rec_get_heap_no(rec, page_is_comp(page));
272
block = rec_get_start(rec, offsets);
273
if (UNIV_LIKELY_NULL(heap)) {
279
if (UNIV_LIKELY_NULL(heap)) {
284
/* Could not find space from the free list, try top of heap */
286
avl_space = page_get_max_insert_size(page, 1);
288
if (avl_space >= need) {
289
block = page_header_get_ptr(page, PAGE_HEAP_TOP);
291
page_header_set_ptr(page, PAGE_HEAP_TOP, block + need);
292
*heap_no = page_dir_get_n_heap(page);
294
page_dir_set_n_heap(page, 1 + *heap_no);
302
/**************************************************************
303
Writes a log record of page creation. */
306
page_create_write_log(
307
/*==================*/
308
buf_frame_t* frame, /* in: a buffer frame where the page is
310
mtr_t* mtr, /* in: mini-transaction handle */
311
ulint comp) /* in: nonzero=compact page format */
313
mlog_write_initial_log_record(frame, comp
314
? MLOG_COMP_PAGE_CREATE
315
: MLOG_PAGE_CREATE, mtr);
318
/***************************************************************
319
Parses a redo log record of creating a page. */
324
/* out: end of log record or NULL */
325
byte* ptr, /* in: buffer */
326
byte* end_ptr __attribute__((unused)), /* in: buffer end */
327
ulint comp, /* in: nonzero=compact page format */
328
page_t* page, /* in: page or NULL */
329
mtr_t* mtr) /* in: mtr or NULL */
331
ut_ad(ptr && end_ptr);
333
/* The record is empty, except for the record initial part */
336
page_create(page, mtr, comp);
342
/**************************************************************
343
The index page creation function. */
348
/* out: pointer to the page */
349
buf_frame_t* frame, /* in: a buffer frame where the page is
351
mtr_t* mtr, /* in: mini-transaction handle */
352
ulint comp) /* in: nonzero=compact page format */
354
page_dir_slot_t* slot;
365
index = comp ? srv_sys->dummy_ind2 : srv_sys->dummy_ind1;
368
#if PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA
369
# error "PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA"
371
#if PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA
372
# error "PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA"
375
/* 1. INCREMENT MODIFY CLOCK */
376
buf_frame_modify_clock_inc(frame);
378
/* 2. WRITE LOG INFORMATION */
379
page_create_write_log(frame, mtr, comp);
383
fil_page_set_type(page, FIL_PAGE_INDEX);
385
heap = mem_heap_create(200);
387
/* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
389
/* Create first a data tuple for infimum record */
390
tuple = dtuple_create(heap, 1);
391
dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM);
392
field = dtuple_get_nth_field(tuple, 0);
394
dfield_set_data(field, "infimum", 8);
395
dtype_set(dfield_get_type(field),
396
DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8);
397
/* Set the corresponding physical record to its place in the page
400
heap_top = page + PAGE_DATA;
402
infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple);
404
ut_a(infimum_rec == page
405
+ (comp ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM));
407
rec_set_n_owned(infimum_rec, comp, 1);
408
rec_set_heap_no(infimum_rec, comp, 0);
409
offsets = rec_get_offsets(infimum_rec, index, NULL,
410
ULINT_UNDEFINED, &heap);
412
heap_top = rec_get_end(infimum_rec, offsets);
414
/* Create then a tuple for supremum */
416
tuple = dtuple_create(heap, 1);
417
dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM);
418
field = dtuple_get_nth_field(tuple, 0);
420
dfield_set_data(field, "supremum", comp ? 8 : 9);
421
dtype_set(dfield_get_type(field),
422
DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9);
424
supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple);
426
ut_a(supremum_rec == page
427
+ (comp ? PAGE_NEW_SUPREMUM : PAGE_OLD_SUPREMUM));
429
rec_set_n_owned(supremum_rec, comp, 1);
430
rec_set_heap_no(supremum_rec, comp, 1);
432
offsets = rec_get_offsets(supremum_rec, index, offsets,
433
ULINT_UNDEFINED, &heap);
434
heap_top = rec_get_end(supremum_rec, offsets);
436
ut_ad(heap_top == page
437
+ (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END));
441
/* 4. INITIALIZE THE PAGE */
443
page_header_set_field(page, PAGE_N_DIR_SLOTS, 2);
444
page_header_set_ptr(page, PAGE_HEAP_TOP, heap_top);
445
page_header_set_field(page, PAGE_N_HEAP, comp ? 0x8002 : 2);
446
page_header_set_ptr(page, PAGE_FREE, NULL);
447
page_header_set_field(page, PAGE_GARBAGE, 0);
448
page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
449
page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
450
page_header_set_field(page, PAGE_N_DIRECTION, 0);
451
page_header_set_field(page, PAGE_N_RECS, 0);
452
page_set_max_trx_id(page, ut_dulint_zero);
453
memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START
454
- (heap_top - page));
456
/* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
458
/* Set the slots to point to infimum and supremum. */
460
slot = page_dir_get_nth_slot(page, 0);
461
page_dir_slot_set_rec(slot, infimum_rec);
463
slot = page_dir_get_nth_slot(page, 1);
464
page_dir_slot_set_rec(slot, supremum_rec);
466
/* Set the next pointers in infimum and supremum */
468
rec_set_next_offs(infimum_rec, comp, (ulint)(supremum_rec - page));
469
rec_set_next_offs(supremum_rec, comp, 0);
474
/*****************************************************************
475
Differs from page_copy_rec_list_end, because this function does not
476
touch the lock table and max trx id on page. */
479
page_copy_rec_list_end_no_locks(
480
/*============================*/
481
page_t* new_page, /* in: index page to copy to */
482
page_t* page, /* in: index page */
483
rec_t* rec, /* in: record on page */
484
dict_index_t* index, /* in: record descriptor */
485
mtr_t* mtr) /* in: mtr */
490
mem_heap_t* heap = NULL;
491
ulint offsets_[REC_OFFS_NORMAL_SIZE];
492
ulint* offsets = offsets_;
493
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
495
page_cur_position(rec, &cur1);
497
if (page_cur_is_before_first(&cur1)) {
499
page_cur_move_to_next(&cur1);
502
ut_a((ibool)!!page_is_comp(new_page)
503
== dict_table_is_comp(index->table));
504
ut_a(page_is_comp(new_page) == page_is_comp(page));
505
ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == (ulint)
506
(page_is_comp(new_page)
507
? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM));
509
page_cur_set_before_first(new_page, &cur2);
511
/* Copy records from the original page to the new page */
513
sup = page_get_supremum_rec(page);
516
rec_t* cur1_rec = page_cur_get_rec(&cur1);
517
if (cur1_rec == sup) {
520
offsets = rec_get_offsets(cur1_rec, index, offsets,
521
ULINT_UNDEFINED, &heap);
522
if (UNIV_UNLIKELY(!page_cur_rec_insert(&cur2, cur1_rec, index,
524
/* Track an assertion failure reported on the mailing
525
list on June 18th, 2003 */
527
buf_page_print(new_page);
528
buf_page_print(page);
529
ut_print_timestamp(stderr);
532
"InnoDB: rec offset %lu, cur1 offset %lu,"
533
" cur2 offset %lu\n",
535
(ulong)(page_cur_get_rec(&cur1) - page),
536
(ulong)(page_cur_get_rec(&cur2) - new_page));
541
page_cur_move_to_next(&cur1);
542
page_cur_move_to_next(&cur2);
545
if (UNIV_LIKELY_NULL(heap)) {
550
/*****************************************************************
551
Copies records from page to new_page, from a given record onward,
552
including that record. Infimum and supremum records are not copied.
553
The records are copied to the start of the record list on new_page. */
556
page_copy_rec_list_end(
557
/*===================*/
558
page_t* new_page, /* in: index page to copy to */
559
page_t* page, /* in: index page */
560
rec_t* rec, /* in: record on page */
561
dict_index_t* index, /* in: record descriptor */
562
mtr_t* mtr) /* in: mtr */
564
if (page_dir_get_n_heap(new_page) == 2) {
565
page_copy_rec_list_end_to_created_page(new_page, page, rec,
568
page_copy_rec_list_end_no_locks(new_page, page, rec,
572
/* Update the lock table, MAX_TRX_ID, and possible hash index */
574
lock_move_rec_list_end(new_page, page, rec);
576
page_update_max_trx_id(new_page, page_get_max_trx_id(page));
578
btr_search_move_or_delete_hash_entries(new_page, page, index);
581
/*****************************************************************
582
Copies records from page to new_page, up to the given record,
583
NOT including that record. Infimum and supremum records are not copied.
584
The records are copied to the end of the record list on new_page. */
587
page_copy_rec_list_start(
588
/*=====================*/
589
page_t* new_page, /* in: index page to copy to */
590
page_t* page, /* in: index page */
591
rec_t* rec, /* in: record on page */
592
dict_index_t* index, /* in: record descriptor */
593
mtr_t* mtr) /* in: mtr */
598
mem_heap_t* heap = NULL;
599
ulint offsets_[REC_OFFS_NORMAL_SIZE];
600
ulint* offsets = offsets_;
601
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
603
page_cur_set_before_first(page, &cur1);
605
if (rec == page_cur_get_rec(&cur1)) {
610
page_cur_move_to_next(&cur1);
612
page_cur_set_after_last(new_page, &cur2);
613
page_cur_move_to_prev(&cur2);
614
old_end = page_cur_get_rec(&cur2);
616
/* Copy records from the original page to the new page */
618
while (page_cur_get_rec(&cur1) != rec) {
620
rec_t* cur1_rec = page_cur_get_rec(&cur1);
621
offsets = rec_get_offsets(cur1_rec, index, offsets,
622
ULINT_UNDEFINED, &heap);
623
ins_rec = page_cur_rec_insert(&cur2, cur1_rec, index,
627
page_cur_move_to_next(&cur1);
628
page_cur_move_to_next(&cur2);
631
/* Update the lock table, MAX_TRX_ID, and possible hash index */
633
lock_move_rec_list_start(new_page, page, rec, old_end);
635
page_update_max_trx_id(new_page, page_get_max_trx_id(page));
637
btr_search_move_or_delete_hash_entries(new_page, page, index);
639
if (UNIV_LIKELY_NULL(heap)) {
644
/**************************************************************
645
Writes a log record of a record list end or start deletion. */
648
page_delete_rec_list_write_log(
649
/*===========================*/
650
rec_t* rec, /* in: record on page */
651
dict_index_t* index, /* in: record descriptor */
652
byte type, /* in: operation type:
653
MLOG_LIST_END_DELETE, ... */
654
mtr_t* mtr) /* in: mtr */
657
ut_ad(type == MLOG_LIST_END_DELETE
658
|| type == MLOG_LIST_START_DELETE
659
|| type == MLOG_COMP_LIST_END_DELETE
660
|| type == MLOG_COMP_LIST_START_DELETE);
662
log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2);
664
/* Write the parameter as a 2-byte ulint */
665
mach_write_to_2(log_ptr, page_offset(rec));
666
mlog_close(mtr, log_ptr + 2);
670
/**************************************************************
671
Parses a log record of a record list end or start deletion. */
674
page_parse_delete_rec_list(
675
/*=======================*/
676
/* out: end of log record or NULL */
677
byte type, /* in: MLOG_LIST_END_DELETE,
678
MLOG_LIST_START_DELETE,
679
MLOG_COMP_LIST_END_DELETE or
680
MLOG_COMP_LIST_START_DELETE */
681
byte* ptr, /* in: buffer */
682
byte* end_ptr,/* in: buffer end */
683
dict_index_t* index, /* in: record descriptor */
684
page_t* page, /* in: page or NULL */
685
mtr_t* mtr) /* in: mtr or NULL */
689
ut_ad(type == MLOG_LIST_END_DELETE
690
|| type == MLOG_LIST_START_DELETE
691
|| type == MLOG_COMP_LIST_END_DELETE
692
|| type == MLOG_COMP_LIST_START_DELETE);
694
/* Read the record offset as a 2-byte ulint */
696
if (end_ptr < ptr + 2) {
701
offset = mach_read_from_2(ptr);
709
ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
711
if (type == MLOG_LIST_END_DELETE
712
|| type == MLOG_COMP_LIST_END_DELETE) {
713
page_delete_rec_list_end(page, page + offset, index,
715
ULINT_UNDEFINED, mtr);
717
page_delete_rec_list_start(page, page + offset, index, mtr);
723
/*****************************************************************
724
Deletes records from a page from a given record onward, including that record.
725
The infimum and supremum records are not deleted. */
728
page_delete_rec_list_end(
729
/*=====================*/
730
page_t* page, /* in: index page */
731
rec_t* rec, /* in: record on page */
732
dict_index_t* index, /* in: record descriptor */
733
ulint n_recs, /* in: number of records to delete,
734
or ULINT_UNDEFINED if not known */
735
ulint size, /* in: the sum of the sizes of the
736
records in the end of the chain to
737
delete, or ULINT_UNDEFINED if not known */
738
mtr_t* mtr) /* in: mtr */
740
page_dir_slot_t* slot;
751
/* Reset the last insert info in the page header and increment
752
the modify clock for the frame */
754
ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE);
755
page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
757
/* The page gets invalid for optimistic searches: increment the
758
frame modify clock */
760
buf_frame_modify_clock_inc(page);
762
sup = page_get_supremum_rec(page);
764
comp = page_is_comp(page);
765
if (page_rec_is_infimum_low(rec - page)) {
766
rec = page_rec_get_next(rec);
769
page_delete_rec_list_write_log(rec, index, comp
770
? MLOG_COMP_LIST_END_DELETE
771
: MLOG_LIST_END_DELETE, mtr);
778
prev_rec = page_rec_get_prev(rec);
780
last_rec = page_rec_get_prev(sup);
782
if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
783
mem_heap_t* heap = NULL;
784
ulint offsets_[REC_OFFS_NORMAL_SIZE];
785
ulint* offsets = offsets_;
786
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
787
/* Calculate the sum of sizes and the number of records */
792
while (rec2 != sup) {
794
offsets = rec_get_offsets(rec2, index, offsets,
795
ULINT_UNDEFINED, &heap);
796
s = rec_offs_size(offsets);
797
ut_ad(rec2 - page + s - rec_offs_extra_size(offsets)
799
ut_ad(size + s < UNIV_PAGE_SIZE);
803
rec2 = page_rec_get_next(rec2);
806
if (UNIV_LIKELY_NULL(heap)) {
811
ut_ad(size < UNIV_PAGE_SIZE);
813
/* Update the page directory; there is no need to balance the number
814
of the records owned by the supremum record, as it is allowed to be
815
less than PAGE_DIR_SLOT_MIN_N_OWNED */
820
while (rec_get_n_owned(rec2, comp) == 0) {
823
rec2 = page_rec_get_next(rec2);
826
ut_ad(rec_get_n_owned(rec2, comp) - count > 0);
828
n_owned = rec_get_n_owned(rec2, comp) - count;
830
slot_index = page_dir_find_owner_slot(rec2);
831
slot = page_dir_get_nth_slot(page, slot_index);
833
page_dir_slot_set_rec(slot, sup);
834
page_dir_slot_set_n_owned(slot, n_owned);
836
page_dir_set_n_slots(page, slot_index + 1);
838
/* Remove the record chain segment from the record chain */
839
page_rec_set_next(prev_rec, page_get_supremum_rec(page));
841
/* Catenate the deleted chain segment to the page free list */
843
free = page_header_get_ptr(page, PAGE_FREE);
845
page_rec_set_next(last_rec, free);
846
page_header_set_ptr(page, PAGE_FREE, rec);
848
page_header_set_field(page, PAGE_GARBAGE, size
849
+ page_header_get_field(page, PAGE_GARBAGE));
851
page_header_set_field(page, PAGE_N_RECS,
852
(ulint)(page_get_n_recs(page) - n_recs));
855
/*****************************************************************
856
Deletes records from page, up to the given record, NOT including
857
that record. Infimum and supremum records are not deleted. */
860
page_delete_rec_list_start(
861
/*=======================*/
862
page_t* page, /* in: index page */
863
rec_t* rec, /* in: record on page */
864
dict_index_t* index, /* in: record descriptor */
865
mtr_t* mtr) /* in: mtr */
869
ulint offsets_[REC_OFFS_NORMAL_SIZE];
870
ulint* offsets = offsets_;
871
mem_heap_t* heap = NULL;
873
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
875
ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
877
if (page_is_comp(page)) {
878
type = MLOG_COMP_LIST_START_DELETE;
880
type = MLOG_LIST_START_DELETE;
883
page_delete_rec_list_write_log(rec, index, type, mtr);
885
page_cur_set_before_first(page, &cur1);
887
if (rec == page_cur_get_rec(&cur1)) {
892
page_cur_move_to_next(&cur1);
894
/* Individual deletes are not logged */
896
log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
898
while (page_cur_get_rec(&cur1) != rec) {
899
offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
900
offsets, ULINT_UNDEFINED, &heap);
901
page_cur_delete_rec(&cur1, index, offsets, mtr);
904
if (UNIV_LIKELY_NULL(heap)) {
908
/* Restore log mode */
910
mtr_set_log_mode(mtr, log_mode);
913
/*****************************************************************
914
Moves record list end to another page. Moved records include
918
page_move_rec_list_end(
919
/*===================*/
920
page_t* new_page, /* in: index page where to move */
921
page_t* page, /* in: index page */
922
rec_t* split_rec, /* in: first record to move */
923
dict_index_t* index, /* in: record descriptor */
924
mtr_t* mtr) /* in: mtr */
931
old_data_size = page_get_data_size(new_page);
932
old_n_recs = page_get_n_recs(new_page);
934
page_copy_rec_list_end(new_page, page, split_rec, index, mtr);
936
new_data_size = page_get_data_size(new_page);
937
new_n_recs = page_get_n_recs(new_page);
939
ut_ad(new_data_size >= old_data_size);
941
page_delete_rec_list_end(page, split_rec, index,
942
new_n_recs - old_n_recs,
943
new_data_size - old_data_size, mtr);
946
/*****************************************************************
947
Moves record list start to another page. Moved records do not include
951
page_move_rec_list_start(
952
/*=====================*/
953
page_t* new_page, /* in: index page where to move */
954
page_t* page, /* in: index page */
955
rec_t* split_rec, /* in: first record not to move */
956
dict_index_t* index, /* in: record descriptor */
957
mtr_t* mtr) /* in: mtr */
959
page_copy_rec_list_start(new_page, page, split_rec, index, mtr);
961
page_delete_rec_list_start(page, split_rec, index, mtr);
964
/***************************************************************************
965
This is a low-level operation which is used in a database index creation
966
to update the page number of a created B-tree to a data dictionary record. */
969
page_rec_write_index_page_no(
970
/*=========================*/
971
rec_t* rec, /* in: record to update */
972
ulint i, /* in: index of the field to update */
973
ulint page_no,/* in: value to write */
974
mtr_t* mtr) /* in: mtr */
979
data = rec_get_nth_field_old(rec, i, &len);
983
mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr);
986
/******************************************************************
987
Used to delete n slots from the directory. This function updates
988
also n_owned fields in the records, so that the first slot after
989
the deleted ones inherits the records of the deleted slots. */
992
page_dir_delete_slots(
993
/*==================*/
994
page_t* page, /* in: the index page */
995
ulint start, /* in: first slot to be deleted */
996
ulint n) /* in: number of slots to delete (currently
997
only n == 1 allowed) */
999
page_dir_slot_t* slot;
1001
ulint sum_owned = 0;
1007
ut_ad(start + n < page_dir_get_n_slots(page));
1009
n_slots = page_dir_get_n_slots(page);
1011
/* 1. Reset the n_owned fields of the slots to be
1013
for (i = start; i < start + n; i++) {
1014
slot = page_dir_get_nth_slot(page, i);
1015
sum_owned += page_dir_slot_get_n_owned(slot);
1016
page_dir_slot_set_n_owned(slot, 0);
1019
/* 2. Update the n_owned value of the first non-deleted slot */
1021
slot = page_dir_get_nth_slot(page, start + n);
1022
page_dir_slot_set_n_owned(slot,
1023
sum_owned + page_dir_slot_get_n_owned(slot));
1025
/* 3. Destroy start and other slots by copying slots */
1026
for (i = start + n; i < n_slots; i++) {
1027
slot = page_dir_get_nth_slot(page, i);
1028
rec = page_dir_slot_get_rec(slot);
1030
slot = page_dir_get_nth_slot(page, i - n);
1031
page_dir_slot_set_rec(slot, rec);
1034
/* 4. Update the page header */
1035
page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots - n);
1038
/******************************************************************
1039
Used to add n slots to the directory. Does not set the record pointers
1040
in the added slots or update n_owned values: this is the responsibility
1046
page_t* page, /* in: the index page */
1047
ulint start, /* in: the slot above which the new slots are added */
1048
ulint n) /* in: number of slots to add (currently only n == 1
1051
page_dir_slot_t* slot;
1058
n_slots = page_dir_get_n_slots(page);
1060
ut_ad(start < n_slots - 1);
1062
/* Update the page header */
1063
page_dir_set_n_slots(page, n_slots + n);
1067
for (i = n_slots - 1; i > start; i--) {
1069
slot = page_dir_get_nth_slot(page, i);
1070
rec = page_dir_slot_get_rec(slot);
1072
slot = page_dir_get_nth_slot(page, i + n);
1073
page_dir_slot_set_rec(slot, rec);
1077
/********************************************************************
1078
Splits a directory slot which owns too many records. */
1081
page_dir_split_slot(
1082
/*================*/
1083
page_t* page, /* in: the index page in question */
1084
ulint slot_no) /* in: the directory slot */
1087
page_dir_slot_t* new_slot;
1088
page_dir_slot_t* prev_slot;
1089
page_dir_slot_t* slot;
1096
slot = page_dir_get_nth_slot(page, slot_no);
1098
n_owned = page_dir_slot_get_n_owned(slot);
1099
ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
1101
/* 1. We loop to find a record approximately in the middle of the
1102
records owned by the slot. */
1104
prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
1105
rec = page_dir_slot_get_rec(prev_slot);
1107
for (i = 0; i < n_owned / 2; i++) {
1108
rec = page_rec_get_next(rec);
1111
ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
1113
/* 2. We add one directory slot immediately below the slot to be
1116
page_dir_add_slots(page, slot_no - 1, 1);
1118
/* The added slot is now number slot_no, and the old slot is
1119
now number slot_no + 1 */
1121
new_slot = page_dir_get_nth_slot(page, slot_no);
1122
slot = page_dir_get_nth_slot(page, slot_no + 1);
1124
/* 3. We store the appropriate values to the new slot. */
1126
page_dir_slot_set_rec(new_slot, rec);
1127
page_dir_slot_set_n_owned(new_slot, n_owned / 2);
1129
/* 4. Finally, we update the number of records field of the
1132
page_dir_slot_set_n_owned(slot, n_owned - (n_owned / 2));
1135
/*****************************************************************
1136
Tries to balance the given directory slot with too few records with the upper
1137
neighbor, so that there are at least the minimum number of records owned by
1138
the slot; this may result in the merging of two slots. */
1141
page_dir_balance_slot(
1142
/*==================*/
1143
page_t* page, /* in: index page */
1144
ulint slot_no) /* in: the directory slot */
1146
page_dir_slot_t* slot;
1147
page_dir_slot_t* up_slot;
1156
slot = page_dir_get_nth_slot(page, slot_no);
1158
/* The last directory slot cannot be balanced with the upper
1159
neighbor, as there is none. */
1161
if (slot_no == page_dir_get_n_slots(page) - 1) {
1166
up_slot = page_dir_get_nth_slot(page, slot_no + 1);
1168
n_owned = page_dir_slot_get_n_owned(slot);
1169
up_n_owned = page_dir_slot_get_n_owned(up_slot);
1171
ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
1173
/* If the upper slot has the minimum value of n_owned, we will merge
1174
the two slots, therefore we assert: */
1175
ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
1177
if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
1179
/* In this case we can just transfer one record owned
1180
by the upper slot to the property of the lower slot */
1181
old_rec = page_dir_slot_get_rec(slot);
1182
new_rec = page_rec_get_next(old_rec);
1184
rec_set_n_owned(old_rec, page_is_comp(page), 0);
1185
rec_set_n_owned(new_rec, page_is_comp(page), n_owned + 1);
1187
page_dir_slot_set_rec(slot, new_rec);
1189
page_dir_slot_set_n_owned(up_slot, up_n_owned -1);
1191
/* In this case we may merge the two slots */
1192
page_dir_delete_slots(page, slot_no, 1);
1196
/****************************************************************
1197
Returns the middle record of the record list. If there are an even number
1198
of records in the list, returns the first record of the upper half-list. */
1201
page_get_middle_rec(
1202
/*================*/
1203
/* out: middle record */
1204
page_t* page) /* in: page */
1206
page_dir_slot_t* slot;
1213
/* This many records we must leave behind */
1214
middle = (page_get_n_recs(page) + 2) / 2;
1220
slot = page_dir_get_nth_slot(page, i);
1221
n_owned = page_dir_slot_get_n_owned(slot);
1223
if (count + n_owned > middle) {
1231
slot = page_dir_get_nth_slot(page, i - 1);
1232
rec = page_dir_slot_get_rec(slot);
1233
rec = page_rec_get_next(rec);
1235
/* There are now count records behind rec */
1237
for (i = 0; i < middle - count; i++) {
1238
rec = page_rec_get_next(rec);
1244
/*******************************************************************
1245
Returns the number of records before the given record in chain.
1246
The number includes infimum and supremum records. */
1249
page_rec_get_n_recs_before(
1250
/*=======================*/
1251
/* out: number of records */
1252
rec_t* rec) /* in: the physical record */
1254
page_dir_slot_t* slot;
1261
ut_ad(page_rec_check(rec));
1263
page = buf_frame_align(rec);
1264
comp = page_is_comp(page);
1266
while (rec_get_n_owned(rec, comp) == 0) {
1268
rec = page_rec_get_next(rec);
1272
for (i = 0; ; i++) {
1273
slot = page_dir_get_nth_slot(page, i);
1274
slot_rec = page_dir_slot_get_rec(slot);
1276
n += rec_get_n_owned(slot_rec, comp);
1278
if (rec == slot_rec) {
1291
/****************************************************************
1292
Prints record contents including the data relevant only in
1293
the index page context. */
1298
rec_t* rec, /* in: physical record */
1299
const ulint* offsets)/* in: record descriptor */
1301
ulint comp = page_is_comp(buf_frame_align(rec));
1303
ut_a(!comp == !rec_offs_comp(offsets));
1304
rec_print_new(stderr, rec, offsets);
1306
" n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1307
(ulong) rec_get_n_owned(rec, comp),
1308
(ulong) rec_get_heap_no(rec, comp),
1309
(ulong) rec_get_next_offs(rec, comp));
1311
page_rec_check(rec);
1312
rec_validate(rec, offsets);
1315
/*******************************************************************
1316
This is used to print the contents of the directory for
1317
debugging purposes. */
1322
page_t* page, /* in: index page */
1323
ulint pr_n) /* in: print n first and n last entries */
1327
page_dir_slot_t* slot;
1329
n = page_dir_get_n_slots(page);
1331
fprintf(stderr, "--------------------------------\n"
1334
"Directory stack top at offs: %lu; number of slots: %lu\n",
1335
page, (ulong)(page_dir_get_nth_slot(page, n - 1) - page),
1337
for (i = 0; i < n; i++) {
1338
slot = page_dir_get_nth_slot(page, i);
1339
if ((i == pr_n) && (i < n - pr_n)) {
1340
fputs(" ... \n", stderr);
1342
if ((i < pr_n) || (i >= n - pr_n)) {
1344
"Contents of slot: %lu: n_owned: %lu,"
1347
(ulong) page_dir_slot_get_n_owned(slot),
1348
(ulong)(page_dir_slot_get_rec(slot) - page));
1351
fprintf(stderr, "Total of %lu records\n"
1352
"--------------------------------\n",
1353
(ulong) (2 + page_get_n_recs(page)));
1356
/*******************************************************************
1357
This is used to print the contents of the page record list for
1358
debugging purposes. */
1363
page_t* page, /* in: index page */
1364
dict_index_t* index, /* in: dictionary index of the page */
1365
ulint pr_n) /* in: print n first and n last entries */
1370
mem_heap_t* heap = NULL;
1371
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1372
ulint* offsets = offsets_;
1373
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1375
ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table));
1378
"--------------------------------\n"
1379
"PAGE RECORD LIST\n"
1380
"Page address %p\n", page);
1382
n_recs = page_get_n_recs(page);
1384
page_cur_set_before_first(page, &cur);
1387
offsets = rec_get_offsets(cur.rec, index, offsets,
1388
ULINT_UNDEFINED, &heap);
1389
page_rec_print(cur.rec, offsets);
1391
if (count == pr_n) {
1394
if (page_cur_is_after_last(&cur)) {
1397
page_cur_move_to_next(&cur);
1401
if (n_recs > 2 * pr_n) {
1402
fputs(" ... \n", stderr);
1405
while (!page_cur_is_after_last(&cur)) {
1406
page_cur_move_to_next(&cur);
1408
if (count + pr_n >= n_recs) {
1409
offsets = rec_get_offsets(cur.rec, index, offsets,
1410
ULINT_UNDEFINED, &heap);
1411
page_rec_print(cur.rec, offsets);
1417
"Total of %lu records \n"
1418
"--------------------------------\n",
1419
(ulong) (count + 1));
1421
if (UNIV_LIKELY_NULL(heap)) {
1422
mem_heap_free(heap);
1426
/*******************************************************************
1427
Prints the info in a page header. */
1435
"--------------------------------\n"
1436
"PAGE HEADER INFO\n"
1437
"Page address %p, n records %lu (%s)\n"
1438
"n dir slots %lu, heap top %lu\n"
1439
"Page n heap %lu, free %lu, garbage %lu\n"
1440
"Page last insert %lu, direction %lu, n direction %lu\n",
1441
page, (ulong) page_header_get_field(page, PAGE_N_RECS),
1442
page_is_comp(page) ? "compact format" : "original format",
1443
(ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
1444
(ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1445
(ulong) page_dir_get_n_heap(page),
1446
(ulong) page_header_get_field(page, PAGE_FREE),
1447
(ulong) page_header_get_field(page, PAGE_GARBAGE),
1448
(ulong) page_header_get_field(page, PAGE_LAST_INSERT),
1449
(ulong) page_header_get_field(page, PAGE_DIRECTION),
1450
(ulong) page_header_get_field(page, PAGE_N_DIRECTION));
1453
/*******************************************************************
1454
This is used to print the contents of the page for
1455
debugging purposes. */
1460
page_t* page, /* in: index page */
1461
dict_index_t* index, /* in: dictionary index of the page */
1462
ulint dn, /* in: print dn first and last entries
1464
ulint rn) /* in: print rn first and last records
1467
page_header_print(page);
1468
page_dir_print(page, dn);
1469
page_print_list(page, index, rn);
1472
/*******************************************************************
1473
The following is used to validate a record on a page. This function
1474
differs from rec_validate as it can also check the n_owned field and
1475
the heap_no field. */
1480
/* out: TRUE if ok */
1481
rec_t* rec, /* in: physical record */
1482
const ulint* offsets)/* in: array returned by rec_get_offsets() */
1489
page = buf_frame_align(rec);
1490
comp = page_is_comp(page);
1491
ut_a(!comp == !rec_offs_comp(offsets));
1493
page_rec_check(rec);
1494
rec_validate(rec, offsets);
1496
n_owned = rec_get_n_owned(rec, comp);
1497
heap_no = rec_get_heap_no(rec, comp);
1499
if (!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED)) {
1501
"InnoDB: Dir slot of rec %lu, n owned too big %lu\n",
1502
(ulong)(rec - page), (ulong) n_owned);
1506
if (!(heap_no < page_dir_get_n_heap(page))) {
1508
"InnoDB: Heap no of rec %lu too big %lu %lu\n",
1509
(ulong)(rec - page), (ulong) heap_no,
1510
(ulong) page_dir_get_n_heap(page));
1517
/*******************************************************************
1518
Checks that the first directory slot points to the infimum record and
1519
the last to the supremum. This function is intended to track if the
1520
bug fixed in 4.0.14 has caused corruption to users' databases. */
1525
page_t* page) /* in: index page */
1529
n_slots = page_dir_get_n_slots(page);
1531
if (page_dir_slot_get_rec(page_dir_get_nth_slot(page, 0))
1532
!= page_get_infimum_rec(page)) {
1535
"InnoDB: Page directory corruption:"
1536
" infimum not pointed to\n");
1537
buf_page_print(page);
1540
if (page_dir_slot_get_rec(page_dir_get_nth_slot(page, n_slots - 1))
1541
!= page_get_supremum_rec(page)) {
1544
"InnoDB: Page directory corruption:"
1545
" supremum not pointed to\n");
1546
buf_page_print(page);
1550
/*******************************************************************
1551
This function checks the consistency of an index page when we do not
1552
know the index. This is also resilient so that this should never crash
1553
even if the page is total garbage. */
1556
page_simple_validate(
1557
/*=================*/
1558
/* out: TRUE if ok */
1559
page_t* page) /* in: index page */
1562
page_dir_slot_t* slot;
1570
ulint comp = page_is_comp(page);
1572
/* Check first that the record heap and the directory do not
1575
n_slots = page_dir_get_n_slots(page);
1577
if (n_slots > UNIV_PAGE_SIZE / 4) {
1579
"InnoDB: Nonsensical number %lu of page dir slots\n",
1585
rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
1587
if (rec_heap_top > page_dir_get_nth_slot(page, n_slots - 1)) {
1590
"InnoDB: Record heap and dir overlap on a page,"
1591
" heap top %lu, dir %lu\n",
1593
(page_header_get_ptr(page, PAGE_HEAP_TOP) - page),
1595
(page_dir_get_nth_slot(page, n_slots - 1) - page));
1600
/* Validate the record list in a loop checking also that it is
1601
consistent with the page record directory. */
1606
slot = page_dir_get_nth_slot(page, slot_no);
1608
page_cur_set_before_first(page, &cur);
1613
if (rec > rec_heap_top) {
1615
"InnoDB: Record %lu is above"
1616
" rec heap top %lu\n",
1617
(ulong)(rec - page),
1618
(ulong)(rec_heap_top - page));
1623
if (rec_get_n_owned(rec, comp) != 0) {
1624
/* This is a record pointed to by a dir slot */
1625
if (rec_get_n_owned(rec, comp) != own_count) {
1628
"InnoDB: Wrong owned count %lu, %lu,"
1630
(ulong) rec_get_n_owned(rec, comp),
1632
(ulong)(rec - page));
1637
if (page_dir_slot_get_rec(slot) != rec) {
1639
"InnoDB: Dir slot does not point"
1640
" to right rec %lu\n",
1641
(ulong)(rec - page));
1648
if (!page_cur_is_after_last(&cur)) {
1650
slot = page_dir_get_nth_slot(page, slot_no);
1654
if (page_cur_is_after_last(&cur)) {
1659
if (rec_get_next_offs(rec, comp) < FIL_PAGE_DATA
1660
|| rec_get_next_offs(rec, comp) >= UNIV_PAGE_SIZE) {
1662
"InnoDB: Next record offset"
1663
" nonsensical %lu for rec %lu\n",
1664
(ulong) rec_get_next_offs(rec, comp),
1665
(ulong)(rec - page));
1672
if (count > UNIV_PAGE_SIZE) {
1674
"InnoDB: Page record list appears"
1675
" to be circular %lu\n",
1680
page_cur_move_to_next(&cur);
1684
if (rec_get_n_owned(rec, comp) == 0) {
1685
fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n");
1690
if (slot_no != n_slots - 1) {
1691
fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
1692
(ulong) slot_no, (ulong) (n_slots - 1));
1696
if (page_header_get_field(page, PAGE_N_RECS) + 2 != count + 1) {
1697
fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
1698
(ulong) page_header_get_field(page, PAGE_N_RECS) + 2,
1699
(ulong) (count + 1));
1704
/* Check then the free list */
1705
rec = page_header_get_ptr(page, PAGE_FREE);
1707
while (rec != NULL) {
1708
if (rec < page + FIL_PAGE_DATA
1709
|| rec >= page + UNIV_PAGE_SIZE) {
1711
"InnoDB: Free list record has"
1712
" a nonsensical offset %lu\n",
1713
(ulong) (rec - page));
1718
if (rec > rec_heap_top) {
1720
"InnoDB: Free list record %lu"
1721
" is above rec heap top %lu\n",
1722
(ulong) (rec - page),
1723
(ulong) (rec_heap_top - page));
1730
if (count > UNIV_PAGE_SIZE) {
1732
"InnoDB: Page free list appears"
1733
" to be circular %lu\n",
1738
rec = page_rec_get_next(rec);
1741
if (page_dir_get_n_heap(page) != count + 1) {
1743
fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
1744
(ulong) page_dir_get_n_heap(page),
1745
(ulong) (count + 1));
1756
/*******************************************************************
1757
This function checks the consistency of an index page. */
1762
/* out: TRUE if ok */
1763
page_t* page, /* in: index page */
1764
dict_index_t* index) /* in: data dictionary index containing
1765
the page record type definition */
1767
page_dir_slot_t* slot;
1776
rec_t* old_rec = NULL;
1781
ulint comp = page_is_comp(page);
1782
ulint* offsets = NULL;
1783
ulint* old_offsets = NULL;
1785
if ((ibool)!!comp != dict_table_is_comp(index->table)) {
1786
fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
1789
if (!page_simple_validate(page)) {
1793
heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
1795
/* The following buffer is used to check that the
1796
records in the page record heap do not overlap */
1798
buf = mem_heap_alloc(heap, UNIV_PAGE_SIZE);
1799
memset(buf, 0, UNIV_PAGE_SIZE);
1801
/* Check first that the record heap and the directory do not
1804
n_slots = page_dir_get_n_slots(page);
1806
if (!(page_header_get_ptr(page, PAGE_HEAP_TOP)
1807
<= page_dir_get_nth_slot(page, n_slots - 1))) {
1809
fputs("InnoDB: Record heap and dir overlap on a page ",
1811
dict_index_name_print(stderr, NULL, index);
1812
fprintf(stderr, ", %p, %p\n",
1813
page_header_get_ptr(page, PAGE_HEAP_TOP),
1814
page_dir_get_nth_slot(page, n_slots - 1));
1819
/* Validate the record list in a loop checking also that
1820
it is consistent with the directory. */
1825
slot = page_dir_get_nth_slot(page, slot_no);
1827
page_cur_set_before_first(page, &cur);
1831
offsets = rec_get_offsets(rec, index, offsets,
1832
ULINT_UNDEFINED, &heap);
1834
if (comp && page_rec_is_user_rec(rec)
1835
&& rec_get_node_ptr_flag(rec)
1837
(btr_page_get_level_low(page) != 0)) {
1838
fputs("InnoDB: node_ptr flag mismatch\n", stderr);
1842
if (!page_rec_validate(rec, offsets)) {
1846
/* Check that the records are in the ascending order */
1847
if ((count >= 2) && (!page_cur_is_after_last(&cur))) {
1848
if (!(1 == cmp_rec_rec(rec, old_rec,
1849
offsets, old_offsets, index))) {
1851
"InnoDB: Records in wrong order"
1853
(ulong) buf_frame_get_page_no(page));
1854
dict_index_name_print(stderr, NULL, index);
1855
fputs("\nInnoDB: previous record ", stderr);
1856
rec_print_new(stderr, old_rec, old_offsets);
1857
fputs("\nInnoDB: record ", stderr);
1858
rec_print_new(stderr, rec, offsets);
1865
if (page_rec_is_user_rec(rec)) {
1867
data_size += rec_offs_size(offsets);
1870
offs = rec_get_start(rec, offsets) - page;
1872
for (i = 0; i < rec_offs_size(offsets); i++) {
1873
if (!buf[offs + i] == 0) {
1874
/* No other record may overlap this */
1876
fputs("InnoDB: Record overlaps another\n",
1884
if (rec_get_n_owned(rec, comp) != 0) {
1885
/* This is a record pointed to by a dir slot */
1886
if (rec_get_n_owned(rec, comp) != own_count) {
1888
"InnoDB: Wrong owned count %lu, %lu\n",
1889
(ulong) rec_get_n_owned(rec, comp),
1894
if (page_dir_slot_get_rec(slot) != rec) {
1895
fputs("InnoDB: Dir slot does not"
1896
" point to right rec\n",
1901
page_dir_slot_check(slot);
1904
if (!page_cur_is_after_last(&cur)) {
1906
slot = page_dir_get_nth_slot(page, slot_no);
1910
if (page_cur_is_after_last(&cur)) {
1914
if (rec_get_next_offs(rec, comp) < FIL_PAGE_DATA
1915
|| rec_get_next_offs(rec, comp) >= UNIV_PAGE_SIZE) {
1917
"InnoDB: Next record offset wrong %lu\n",
1918
(ulong) rec_get_next_offs(rec, comp));
1923
page_cur_move_to_next(&cur);
1926
/* set old_offsets to offsets; recycle offsets */
1928
ulint* offs = old_offsets;
1929
old_offsets = offsets;
1934
if (rec_get_n_owned(rec, comp) == 0) {
1935
fputs("InnoDB: n owned is zero\n", stderr);
1939
if (slot_no != n_slots - 1) {
1940
fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
1941
(ulong) slot_no, (ulong) (n_slots - 1));
1945
if (page_header_get_field(page, PAGE_N_RECS) + 2 != count + 1) {
1946
fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
1947
(ulong) page_header_get_field(page, PAGE_N_RECS) + 2,
1948
(ulong) (count + 1));
1952
if (data_size != page_get_data_size(page)) {
1954
"InnoDB: Summed data size %lu, returned by func %lu\n",
1955
(ulong) data_size, (ulong) page_get_data_size(page));
1959
/* Check then the free list */
1960
rec = page_header_get_ptr(page, PAGE_FREE);
1962
while (rec != NULL) {
1963
offsets = rec_get_offsets(rec, index, offsets,
1964
ULINT_UNDEFINED, &heap);
1965
if (!page_rec_validate(rec, offsets)) {
1971
offs = rec_get_start(rec, offsets) - page;
1973
for (i = 0; i < rec_offs_size(offsets); i++) {
1975
if (buf[offs + i] != 0) {
1976
fputs("InnoDB: Record overlaps another"
1977
" in free list\n", stderr);
1984
rec = page_rec_get_next(rec);
1987
if (page_dir_get_n_heap(page) != count + 1) {
1988
fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
1989
(ulong) page_dir_get_n_heap(page),
1997
mem_heap_free(heap);
2001
fprintf(stderr, "InnoDB: Apparent corruption in page %lu in ",
2002
(ulong) buf_frame_get_page_no(page));
2003
dict_index_name_print(stderr, NULL, index);
2005
buf_page_print(page);
2011
/*******************************************************************
2012
Looks in the page record list for a record with the given heap number. */
2015
page_find_rec_with_heap_no(
2016
/*=======================*/
2017
/* out: record, NULL if not found */
2018
page_t* page, /* in: index page */
2019
ulint heap_no)/* in: heap number */
2023
page_cur_set_before_first(page, &cur);
2026
if (rec_get_heap_no(cur.rec, page_is_comp(page)) == heap_no) {
2031
if (page_cur_is_after_last(&cur)) {
2036
page_cur_move_to_next(&cur);