1 /*****************************************************************************
2
3 Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
16
17 *****************************************************************************/
18
19 /**************************************************//**
20 @file page/page0page.c
21 Index page routines
22
23 Created 2/2/1994 Heikki Tuuri
24 *******************************************************/
25
26 #define THIS_MODULE
27 #include "page0page.h"
28 #ifdef UNIV_NONINL
29 #include "page0page.ic"
30 #endif
31 #undef THIS_MODULE
32
33 #include "page0cur.h"
34 #include "page0zip.h"
35 #include "buf0buf.h"
36 #include "btr0btr.h"
37 #ifndef UNIV_HOTBACKUP
38 # include "srv0srv.h"
39 # include "lock0lock.h"
40 # include "fut0lst.h"
41 # include "btr0sea.h"
42 #endif /* !UNIV_HOTBACKUP */
43
44 /* THE INDEX PAGE
45 ==============
46
47 The index page consists of a page header which contains the page's
48 id and other information. On top of it are the index records
49 in a heap linked into a one way linear list according to alphabetic order.
50
51 Just below page end is an array of pointers which we call page directory,
52 to about every sixth record in the list. The pointers are placed in
53 the directory in the alphabetical order of the records pointed to,
54 enabling us to make binary search using the array. Each slot n:o I
55 in the directory points to a record, where a 4-bit field contains a count
56 of those records which are in the linear list between pointer I and
57 the pointer I - 1 in the directory, including the record
58 pointed to by pointer I and not including the record pointed to by I - 1.
59 We say that the record pointed to by slot I, or that slot I, owns
60 these records. The count is always kept in the range 4 to 8, with
61 the exception that it is 1 for the first slot, and 1--8 for the second slot.
62
63 An essentially binary search can be performed in the list of index
64 records, like we could do if we had pointer to every record in the
65 page directory. The data structure is, however, more efficient when
66 we are doing inserts, because most inserts are just pushed on a heap.
67 Only every 8th insert requires block move in the directory pointer
68 table, which itself is quite small. A record is deleted from the page
69 by just taking it off the linear list and updating the number of owned
70 records-field of the record which owns it, and updating the page directory,
71 if necessary. A special case is the one when the record owns itself.
72 Because the overhead of inserts is so small, we may also increase the
73 page size from the projected default of 8 kB to 64 kB without too
74 much loss of efficiency in inserts. Bigger page becomes actual
75 when the disk transfer rate compared to seek and latency time rises.
76 On the present system, the page size is set so that the page transfer
77 time (3 ms) is 20 % of the disk random access time (15 ms).
78
79 When the page is split, merged, or becomes full but contains deleted
80 records, we have to reorganize the page.
81
82 Assuming a page size of 8 kB, a typical index page of a secondary
83 index contains 300 index entries, and the size of the page directory
84 is 50 x 4 bytes = 200 bytes. */
85
86 /***************************************************************//**
87 Looks for the directory slot which owns the given record.
88 @return the directory slot number */
89 UNIV_INTERN
90 ulint
page_dir_find_owner_slot(const rec_t * rec)91 page_dir_find_owner_slot(
92 /*=====================*/
93 const rec_t* rec) /*!< in: the physical record */
94 {
95 const page_t* page;
96 register uint16 rec_offs_bytes;
97 register const page_dir_slot_t* slot;
98 register const page_dir_slot_t* first_slot;
99 register const rec_t* r = rec;
100
101 ut_ad(page_rec_check(rec));
102
103 page = page_align(rec);
104 first_slot = page_dir_get_nth_slot(page, 0);
105 slot = page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1);
106
107 if (page_is_comp(page)) {
108 while (rec_get_n_owned_new(r) == 0) {
109 r = rec_get_next_ptr_const(r, TRUE);
110 ut_ad(r >= page + PAGE_NEW_SUPREMUM);
111 ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
112 }
113 } else {
114 while (rec_get_n_owned_old(r) == 0) {
115 r = rec_get_next_ptr_const(r, FALSE);
116 ut_ad(r >= page + PAGE_OLD_SUPREMUM);
117 ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
118 }
119 }
120
121 rec_offs_bytes = mach_encode_2(r - page);
122
123 while (UNIV_LIKELY(*(uint16*) slot != rec_offs_bytes)) {
124
125 if (UNIV_UNLIKELY(slot == first_slot)) {
126 fprintf(stderr,
127 "InnoDB: Probable data corruption on"
128 " page %lu\n"
129 "InnoDB: Original record ",
130 (ulong) page_get_page_no(page));
131
132 if (page_is_comp(page)) {
133 fputs("(compact record)", stderr);
134 } else {
135 rec_print_old(stderr, rec);
136 }
137
138 fputs("\n"
139 "InnoDB: on that page.\n"
140 "InnoDB: Cannot find the dir slot for record ",
141 stderr);
142 if (page_is_comp(page)) {
143 fputs("(compact record)", stderr);
144 } else {
145 rec_print_old(stderr, page
146 + mach_decode_2(rec_offs_bytes));
147 }
148 fputs("\n"
149 "InnoDB: on that page!\n", stderr);
150
151 buf_page_print(page, 0, 0);
152
153 ut_error;
154 }
155
156 slot += PAGE_DIR_SLOT_SIZE;
157 }
158
159 return(((ulint) (first_slot - slot)) / PAGE_DIR_SLOT_SIZE);
160 }
161
162 /**************************************************************//**
163 Used to check the consistency of a directory slot.
164 @return TRUE if succeed */
165 static
166 ibool
page_dir_slot_check(const page_dir_slot_t * slot)167 page_dir_slot_check(
168 /*================*/
169 const page_dir_slot_t* slot) /*!< in: slot */
170 {
171 const page_t* page;
172 ulint n_slots;
173 ulint n_owned;
174
175 ut_a(slot);
176
177 page = page_align(slot);
178
179 n_slots = page_dir_get_n_slots(page);
180
181 ut_a(slot <= page_dir_get_nth_slot(page, 0));
182 ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1));
183
184 ut_a(page_rec_check(page_dir_slot_get_rec(slot)));
185
186 if (page_is_comp(page)) {
187 n_owned = rec_get_n_owned_new(page_dir_slot_get_rec(slot));
188 } else {
189 n_owned = rec_get_n_owned_old(page_dir_slot_get_rec(slot));
190 }
191
192 if (slot == page_dir_get_nth_slot(page, 0)) {
193 ut_a(n_owned == 1);
194 } else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) {
195 ut_a(n_owned >= 1);
196 ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
197 } else {
198 ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED);
199 ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
200 }
201
202 return(TRUE);
203 }
204
205 /*************************************************************//**
206 Sets the max trx id field value. */
207 UNIV_INTERN
208 void
page_set_max_trx_id(buf_block_t * block,page_zip_des_t * page_zip,trx_id_t trx_id,mtr_t * mtr)209 page_set_max_trx_id(
210 /*================*/
211 buf_block_t* block, /*!< in/out: page */
212 page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
213 trx_id_t trx_id, /*!< in: transaction id */
214 mtr_t* mtr) /*!< in/out: mini-transaction, or NULL */
215 {
216 page_t* page = buf_block_get_frame(block);
217 #ifndef UNIV_HOTBACKUP
218 ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
219 #endif /* !UNIV_HOTBACKUP */
220
221 /* It is not necessary to write this change to the redo log, as
222 during a database recovery we assume that the max trx id of every
223 page is the maximum trx id assigned before the crash. */
224
225 if (UNIV_LIKELY_NULL(page_zip)) {
226 mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
227 page_zip_write_header(page_zip,
228 page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
229 8, mtr);
230 #ifndef UNIV_HOTBACKUP
231 } else if (mtr) {
232 mlog_write_ull(page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
233 trx_id, mtr);
234 #endif /* !UNIV_HOTBACKUP */
235 } else {
236 mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
237 }
238 }
239
240 /************************************************************//**
241 Allocates a block of memory from the heap of an index page.
242 @return pointer to start of allocated buffer, or NULL if allocation fails */
243 UNIV_INTERN
244 byte*
page_mem_alloc_heap(page_t * page,page_zip_des_t * page_zip,ulint need,ulint * heap_no)245 page_mem_alloc_heap(
246 /*================*/
247 page_t* page, /*!< in/out: index page */
248 page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
249 space available for inserting the record,
250 or NULL */
251 ulint need, /*!< in: total number of bytes needed */
252 ulint* heap_no)/*!< out: this contains the heap number
253 of the allocated record
254 if allocation succeeds */
255 {
256 byte* block;
257 ulint avl_space;
258
259 ut_ad(page && heap_no);
260
261 avl_space = page_get_max_insert_size(page, 1);
262
263 if (avl_space >= need) {
264 block = page_header_get_ptr(page, PAGE_HEAP_TOP);
265
266 page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP,
267 block + need);
268 *heap_no = page_dir_get_n_heap(page);
269
270 page_dir_set_n_heap(page, page_zip, 1 + *heap_no);
271
272 return(block);
273 }
274
275 return(NULL);
276 }
277
278 #ifndef UNIV_HOTBACKUP
279 /**********************************************************//**
280 Writes a log record of page creation. */
281 UNIV_INLINE
282 void
page_create_write_log(buf_frame_t * frame,mtr_t * mtr,ibool comp)283 page_create_write_log(
284 /*==================*/
285 buf_frame_t* frame, /*!< in: a buffer frame where the page is
286 created */
287 mtr_t* mtr, /*!< in: mini-transaction handle */
288 ibool comp) /*!< in: TRUE=compact page format */
289 {
290 mlog_write_initial_log_record(frame, comp
291 ? MLOG_COMP_PAGE_CREATE
292 : MLOG_PAGE_CREATE, mtr);
293 }
294 #else /* !UNIV_HOTBACKUP */
295 # define page_create_write_log(frame,mtr,comp) ((void) 0)
296 #endif /* !UNIV_HOTBACKUP */
297
298 /***********************************************************//**
299 Parses a redo log record of creating a page.
300 @return end of log record or NULL */
301 UNIV_INTERN
302 byte*
page_parse_create(byte * ptr,byte * end_ptr,ulint comp,buf_block_t * block,mtr_t * mtr)303 page_parse_create(
304 /*==============*/
305 byte* ptr, /*!< in: buffer */
306 byte* end_ptr __attribute__((unused)), /*!< in: buffer end */
307 ulint comp, /*!< in: nonzero=compact page format */
308 buf_block_t* block, /*!< in: block or NULL */
309 mtr_t* mtr) /*!< in: mtr or NULL */
310 {
311 ut_ad(ptr && end_ptr);
312
313 /* The record is empty, except for the record initial part */
314
315 if (block) {
316 page_create(block, mtr, comp);
317 }
318
319 return(ptr);
320 }
321
322 /**********************************************************//**
323 The index page creation function.
324 @return pointer to the page */
325 static
326 page_t*
page_create_low(buf_block_t * block,ulint comp)327 page_create_low(
328 /*============*/
329 buf_block_t* block, /*!< in: a buffer block where the
330 page is created */
331 ulint comp) /*!< in: nonzero=compact page format */
332 {
333 page_dir_slot_t* slot;
334 mem_heap_t* heap;
335 dtuple_t* tuple;
336 dfield_t* field;
337 byte* heap_top;
338 rec_t* infimum_rec;
339 rec_t* supremum_rec;
340 page_t* page;
341 dict_index_t* index;
342 ulint* offsets;
343
344 ut_ad(block);
345 #if PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA
346 # error "PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA"
347 #endif
348 #if PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA
349 # error "PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA"
350 #endif
351
352 /* The infimum and supremum records use a dummy index. */
353 if (UNIV_LIKELY(comp)) {
354 index = dict_ind_compact;
355 } else {
356 index = dict_ind_redundant;
357 }
358
359 /* 1. INCREMENT MODIFY CLOCK */
360 buf_block_modify_clock_inc(block);
361
362 page = buf_block_get_frame(block);
363
364 fil_page_set_type(page, FIL_PAGE_INDEX);
365
366 heap = mem_heap_create(200);
367
368 /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
369
370 /* Create first a data tuple for infimum record */
371 tuple = dtuple_create(heap, 1);
372 dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM);
373 field = dtuple_get_nth_field(tuple, 0);
374
375 dfield_set_data(field, "infimum", 8);
376 dtype_set(dfield_get_type(field),
377 DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8);
378 /* Set the corresponding physical record to its place in the page
379 record heap */
380
381 heap_top = page + PAGE_DATA;
382
383 infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
384
385 if (UNIV_LIKELY(comp)) {
386 ut_a(infimum_rec == page + PAGE_NEW_INFIMUM);
387
388 rec_set_n_owned_new(infimum_rec, NULL, 1);
389 rec_set_heap_no_new(infimum_rec, 0);
390 } else {
391 ut_a(infimum_rec == page + PAGE_OLD_INFIMUM);
392
393 rec_set_n_owned_old(infimum_rec, 1);
394 rec_set_heap_no_old(infimum_rec, 0);
395 }
396
397 offsets = rec_get_offsets(infimum_rec, index, NULL,
398 ULINT_UNDEFINED, &heap);
399
400 heap_top = rec_get_end(infimum_rec, offsets);
401
402 /* Create then a tuple for supremum */
403
404 tuple = dtuple_create(heap, 1);
405 dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM);
406 field = dtuple_get_nth_field(tuple, 0);
407
408 dfield_set_data(field, "supremum", comp ? 8 : 9);
409 dtype_set(dfield_get_type(field),
410 DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9);
411
412 supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
413
414 if (UNIV_LIKELY(comp)) {
415 ut_a(supremum_rec == page + PAGE_NEW_SUPREMUM);
416
417 rec_set_n_owned_new(supremum_rec, NULL, 1);
418 rec_set_heap_no_new(supremum_rec, 1);
419 } else {
420 ut_a(supremum_rec == page + PAGE_OLD_SUPREMUM);
421
422 rec_set_n_owned_old(supremum_rec, 1);
423 rec_set_heap_no_old(supremum_rec, 1);
424 }
425
426 offsets = rec_get_offsets(supremum_rec, index, offsets,
427 ULINT_UNDEFINED, &heap);
428 heap_top = rec_get_end(supremum_rec, offsets);
429
430 ut_ad(heap_top == page
431 + (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END));
432
433 mem_heap_free(heap);
434
435 /* 4. INITIALIZE THE PAGE */
436
437 page_header_set_field(page, NULL, PAGE_N_DIR_SLOTS, 2);
438 page_header_set_ptr(page, NULL, PAGE_HEAP_TOP, heap_top);
439 page_header_set_field(page, NULL, PAGE_N_HEAP, comp
440 ? 0x8000 | PAGE_HEAP_NO_USER_LOW
441 : PAGE_HEAP_NO_USER_LOW);
442 page_header_set_ptr(page, NULL, PAGE_FREE, NULL);
443 page_header_set_field(page, NULL, PAGE_GARBAGE, 0);
444 page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, NULL);
445 page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION);
446 page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0);
447 page_header_set_field(page, NULL, PAGE_N_RECS, 0);
448 page_set_max_trx_id(block, NULL, 0, NULL);
449 memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START
450 - page_offset(heap_top));
451
452 /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
453
454 /* Set the slots to point to infimum and supremum. */
455
456 slot = page_dir_get_nth_slot(page, 0);
457 page_dir_slot_set_rec(slot, infimum_rec);
458
459 slot = page_dir_get_nth_slot(page, 1);
460 page_dir_slot_set_rec(slot, supremum_rec);
461
462 /* Set the next pointers in infimum and supremum */
463
464 if (UNIV_LIKELY(comp)) {
465 rec_set_next_offs_new(infimum_rec, PAGE_NEW_SUPREMUM);
466 rec_set_next_offs_new(supremum_rec, 0);
467 } else {
468 rec_set_next_offs_old(infimum_rec, PAGE_OLD_SUPREMUM);
469 rec_set_next_offs_old(supremum_rec, 0);
470 }
471
472 return(page);
473 }
474
475 /**********************************************************//**
476 Create an uncompressed B-tree index page.
477 @return pointer to the page */
478 UNIV_INTERN
479 page_t*
page_create(buf_block_t * block,mtr_t * mtr,ulint comp)480 page_create(
481 /*========*/
482 buf_block_t* block, /*!< in: a buffer block where the
483 page is created */
484 mtr_t* mtr, /*!< in: mini-transaction handle */
485 ulint comp) /*!< in: nonzero=compact page format */
486 {
487 page_create_write_log(buf_block_get_frame(block), mtr, comp);
488 return(page_create_low(block, comp));
489 }
490
491 /**********************************************************//**
492 Create a compressed B-tree index page.
493 @return pointer to the page */
494 UNIV_INTERN
495 page_t*
page_create_zip(buf_block_t * block,dict_index_t * index,ulint level,mtr_t * mtr)496 page_create_zip(
497 /*============*/
498 buf_block_t* block, /*!< in/out: a buffer frame where the
499 page is created */
500 dict_index_t* index, /*!< in: the index of the page */
501 ulint level, /*!< in: the B-tree level of the page */
502 mtr_t* mtr) /*!< in: mini-transaction handle */
503 {
504 page_t* page;
505 page_zip_des_t* page_zip = buf_block_get_page_zip(block);
506
507 ut_ad(block);
508 ut_ad(page_zip);
509 ut_ad(index);
510 ut_ad(dict_table_is_comp(index->table));
511
512 page = page_create_low(block, TRUE);
513 mach_write_to_2(page + PAGE_HEADER + PAGE_LEVEL, level);
514
515 if (UNIV_UNLIKELY(!page_zip_compress(page_zip, page, index, mtr))) {
516 /* The compression of a newly created page
517 should always succeed. */
518 ut_error;
519 }
520
521 return(page);
522 }
523
524 /*************************************************************//**
525 Differs from page_copy_rec_list_end, because this function does not
526 touch the lock table and max trx id on page or compress the page. */
527 UNIV_INTERN
528 void
page_copy_rec_list_end_no_locks(buf_block_t * new_block,buf_block_t * block,rec_t * rec,dict_index_t * index,mtr_t * mtr)529 page_copy_rec_list_end_no_locks(
530 /*============================*/
531 buf_block_t* new_block, /*!< in: index page to copy to */
532 buf_block_t* block, /*!< in: index page of rec */
533 rec_t* rec, /*!< in: record on page */
534 dict_index_t* index, /*!< in: record descriptor */
535 mtr_t* mtr) /*!< in: mtr */
536 {
537 page_t* new_page = buf_block_get_frame(new_block);
538 page_cur_t cur1;
539 rec_t* cur2;
540 mem_heap_t* heap = NULL;
541 ulint offsets_[REC_OFFS_NORMAL_SIZE];
542 ulint* offsets = offsets_;
543 rec_offs_init(offsets_);
544
545 page_cur_position(rec, block, &cur1);
546
547 if (page_cur_is_before_first(&cur1)) {
548
549 page_cur_move_to_next(&cur1);
550 }
551
552 btr_assert_not_corrupted(new_block, index);
553 ut_a(page_is_comp(new_page) == page_rec_is_comp(rec));
554 ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == (ulint)
555 (page_is_comp(new_page) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM));
556
557 cur2 = page_get_infimum_rec(buf_block_get_frame(new_block));
558
559 /* Copy records from the original page to the new page */
560
561 while (!page_cur_is_after_last(&cur1)) {
562 rec_t* cur1_rec = page_cur_get_rec(&cur1);
563 rec_t* ins_rec;
564 offsets = rec_get_offsets(cur1_rec, index, offsets,
565 ULINT_UNDEFINED, &heap);
566 ins_rec = page_cur_insert_rec_low(cur2, index,
567 cur1_rec, offsets, mtr);
568 if (UNIV_UNLIKELY(!ins_rec)) {
569 /* Track an assertion failure reported on the mailing
570 list on June 18th, 2003 */
571
572 buf_page_print(new_page, 0,
573 BUF_PAGE_PRINT_NO_CRASH);
574 buf_page_print(page_align(rec), 0,
575 BUF_PAGE_PRINT_NO_CRASH);
576 ut_print_timestamp(stderr);
577
578 fprintf(stderr,
579 "InnoDB: rec offset %lu, cur1 offset %lu,"
580 " cur2 offset %lu\n",
581 (ulong) page_offset(rec),
582 (ulong) page_offset(page_cur_get_rec(&cur1)),
583 (ulong) page_offset(cur2));
584 ut_error;
585 }
586
587 page_cur_move_to_next(&cur1);
588 cur2 = ins_rec;
589 }
590
591 if (UNIV_LIKELY_NULL(heap)) {
592 mem_heap_free(heap);
593 }
594 }
595
596 #ifndef UNIV_HOTBACKUP
597 /*************************************************************//**
598 Copies records from page to new_page, from a given record onward,
599 including that record. Infimum and supremum records are not copied.
600 The records are copied to the start of the record list on new_page.
601 @return pointer to the original successor of the infimum record on
602 new_page, or NULL on zip overflow (new_block will be decompressed) */
603 UNIV_INTERN
604 rec_t*
page_copy_rec_list_end(buf_block_t * new_block,buf_block_t * block,rec_t * rec,dict_index_t * index,mtr_t * mtr)605 page_copy_rec_list_end(
606 /*===================*/
607 buf_block_t* new_block, /*!< in/out: index page to copy to */
608 buf_block_t* block, /*!< in: index page containing rec */
609 rec_t* rec, /*!< in: record on page */
610 dict_index_t* index, /*!< in: record descriptor */
611 mtr_t* mtr) /*!< in: mtr */
612 {
613 page_t* new_page = buf_block_get_frame(new_block);
614 page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block);
615 page_t* page = page_align(rec);
616 rec_t* ret = page_rec_get_next(
617 page_get_infimum_rec(new_page));
618 ulint log_mode = 0; /* remove warning */
619
620 #ifdef UNIV_ZIP_DEBUG
621 if (new_page_zip) {
622 page_zip_des_t* page_zip = buf_block_get_page_zip(block);
623 ut_a(page_zip);
624
625 /* Strict page_zip_validate() may fail here.
626 Furthermore, btr_compress() may set FIL_PAGE_PREV to
627 FIL_NULL on new_page while leaving it intact on
628 new_page_zip. So, we cannot validate new_page_zip. */
629 ut_a(page_zip_validate_low(page_zip, page, index, TRUE));
630 }
631 #endif /* UNIV_ZIP_DEBUG */
632 ut_ad(buf_block_get_frame(block) == page);
633 ut_ad(page_is_leaf(page) == page_is_leaf(new_page));
634 ut_ad(page_is_comp(page) == page_is_comp(new_page));
635 /* Here, "ret" may be pointing to a user record or the
636 predefined supremum record. */
637
638 if (UNIV_LIKELY_NULL(new_page_zip)) {
639 log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
640 }
641
642 if (page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW) {
643 page_copy_rec_list_end_to_created_page(new_page, rec,
644 index, mtr);
645 } else {
646 page_copy_rec_list_end_no_locks(new_block, block, rec,
647 index, mtr);
648 }
649
650 /* Update PAGE_MAX_TRX_ID on the uncompressed page.
651 Modifications will be redo logged and copied to the compressed
652 page in page_zip_compress() or page_zip_reorganize() below. */
653 if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
654 page_update_max_trx_id(new_block, NULL,
655 page_get_max_trx_id(page), mtr);
656 }
657
658 if (UNIV_LIKELY_NULL(new_page_zip)) {
659 mtr_set_log_mode(mtr, log_mode);
660
661 if (UNIV_UNLIKELY
662 (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
663 /* Before trying to reorganize the page,
664 store the number of preceding records on the page. */
665 ulint ret_pos
666 = page_rec_get_n_recs_before(ret);
667 /* Before copying, "ret" was the successor of
668 the predefined infimum record. It must still
669 have at least one predecessor (the predefined
670 infimum record, or a freshly copied record
671 that is smaller than "ret"). */
672 ut_a(ret_pos > 0);
673
674 if (UNIV_UNLIKELY
675 (!page_zip_reorganize(new_block, index, mtr))) {
676
677 btr_blob_dbg_remove(new_page, index,
678 "copy_end_reorg_fail");
679 if (UNIV_UNLIKELY
680 (!page_zip_decompress(new_page_zip,
681 new_page, FALSE))) {
682 ut_error;
683 }
684 ut_ad(page_validate(new_page, index));
685 btr_blob_dbg_add(new_page, index,
686 "copy_end_reorg_fail");
687 return(NULL);
688 } else {
689 /* The page was reorganized:
690 Seek to ret_pos. */
691 ret = new_page + PAGE_NEW_INFIMUM;
692
693 do {
694 ret = rec_get_next_ptr(ret, TRUE);
695 } while (--ret_pos);
696 }
697 }
698 }
699
700 /* Update the lock table and possible hash index */
701
702 lock_move_rec_list_end(new_block, block, rec);
703
704 btr_search_move_or_delete_hash_entries(new_block, block, index);
705
706 return(ret);
707 }
708
709 /*************************************************************//**
710 Copies records from page to new_page, up to the given record,
711 NOT including that record. Infimum and supremum records are not copied.
712 The records are copied to the end of the record list on new_page.
713 @return pointer to the original predecessor of the supremum record on
714 new_page, or NULL on zip overflow (new_block will be decompressed) */
715 UNIV_INTERN
716 rec_t*
page_copy_rec_list_start(buf_block_t * new_block,buf_block_t * block,rec_t * rec,dict_index_t * index,mtr_t * mtr)717 page_copy_rec_list_start(
718 /*=====================*/
719 buf_block_t* new_block, /*!< in/out: index page to copy to */
720 buf_block_t* block, /*!< in: index page containing rec */
721 rec_t* rec, /*!< in: record on page */
722 dict_index_t* index, /*!< in: record descriptor */
723 mtr_t* mtr) /*!< in: mtr */
724 {
725 page_t* new_page = buf_block_get_frame(new_block);
726 page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block);
727 page_cur_t cur1;
728 rec_t* cur2;
729 ulint log_mode = 0 /* remove warning */;
730 mem_heap_t* heap = NULL;
731 rec_t* ret
732 = page_rec_get_prev(page_get_supremum_rec(new_page));
733 ulint offsets_[REC_OFFS_NORMAL_SIZE];
734 ulint* offsets = offsets_;
735 rec_offs_init(offsets_);
736
737 /* Here, "ret" may be pointing to a user record or the
738 predefined infimum record. */
739
740 if (page_rec_is_infimum(rec)) {
741
742 return(ret);
743 }
744
745 if (UNIV_LIKELY_NULL(new_page_zip)) {
746 log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
747 }
748
749 page_cur_set_before_first(block, &cur1);
750 page_cur_move_to_next(&cur1);
751
752 cur2 = ret;
753
754 /* Copy records from the original page to the new page */
755
756 while (page_cur_get_rec(&cur1) != rec) {
757 rec_t* cur1_rec = page_cur_get_rec(&cur1);
758 offsets = rec_get_offsets(cur1_rec, index, offsets,
759 ULINT_UNDEFINED, &heap);
760 cur2 = page_cur_insert_rec_low(cur2, index,
761 cur1_rec, offsets, mtr);
762 ut_a(cur2);
763
764 page_cur_move_to_next(&cur1);
765 }
766
767 if (UNIV_LIKELY_NULL(heap)) {
768 mem_heap_free(heap);
769 }
770
771 /* Update PAGE_MAX_TRX_ID on the uncompressed page.
772 Modifications will be redo logged and copied to the compressed
773 page in page_zip_compress() or page_zip_reorganize() below. */
774 if (dict_index_is_sec_or_ibuf(index)
775 && page_is_leaf(page_align(rec))) {
776 page_update_max_trx_id(new_block, NULL,
777 page_get_max_trx_id(page_align(rec)),
778 mtr);
779 }
780
781 if (UNIV_LIKELY_NULL(new_page_zip)) {
782 mtr_set_log_mode(mtr, log_mode);
783
784 DBUG_EXECUTE_IF("page_copy_rec_list_start_compress_fail",
785 goto zip_reorganize;);
786
787 if (UNIV_UNLIKELY
788 (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
789 ulint ret_pos;
790 #ifndef DBUG_OFF
791 zip_reorganize:
792 #endif /* DBUG_OFF */
793 /* Before trying to reorganize the page,
794 store the number of preceding records on the page. */
795 ret_pos = page_rec_get_n_recs_before(ret);
796 /* Before copying, "ret" was the predecessor
797 of the predefined supremum record. If it was
798 the predefined infimum record, then it would
799 still be the infimum, and we would have
800 ret_pos == 0. */
801
802 if (UNIV_UNLIKELY
803 (!page_zip_reorganize(new_block, index, mtr))) {
804
805 btr_blob_dbg_remove(new_page, index,
806 "copy_start_reorg_fail");
807 if (UNIV_UNLIKELY
808 (!page_zip_decompress(new_page_zip,
809 new_page, FALSE))) {
810 ut_error;
811 }
812 ut_ad(page_validate(new_page, index));
813 btr_blob_dbg_add(new_page, index,
814 "copy_start_reorg_fail");
815 return(NULL);
816 }
817
818 /* The page was reorganized: Seek to ret_pos. */
819 ret = page_rec_get_nth(new_page, ret_pos);
820 }
821 }
822
823 /* Update the lock table and possible hash index */
824
825 lock_move_rec_list_start(new_block, block, rec, ret);
826
827 btr_search_move_or_delete_hash_entries(new_block, block, index);
828
829 return(ret);
830 }
831
832 /**********************************************************//**
833 Writes a log record of a record list end or start deletion. */
834 UNIV_INLINE
835 void
page_delete_rec_list_write_log(rec_t * rec,dict_index_t * index,byte type,mtr_t * mtr)836 page_delete_rec_list_write_log(
837 /*===========================*/
838 rec_t* rec, /*!< in: record on page */
839 dict_index_t* index, /*!< in: record descriptor */
840 byte type, /*!< in: operation type:
841 MLOG_LIST_END_DELETE, ... */
842 mtr_t* mtr) /*!< in: mtr */
843 {
844 byte* log_ptr;
845 ut_ad(type == MLOG_LIST_END_DELETE
846 || type == MLOG_LIST_START_DELETE
847 || type == MLOG_COMP_LIST_END_DELETE
848 || type == MLOG_COMP_LIST_START_DELETE);
849
850 log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2);
851 if (log_ptr) {
852 /* Write the parameter as a 2-byte ulint */
853 mach_write_to_2(log_ptr, page_offset(rec));
854 mlog_close(mtr, log_ptr + 2);
855 }
856 }
857 #else /* !UNIV_HOTBACKUP */
858 # define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0)
859 #endif /* !UNIV_HOTBACKUP */
860
861 /**********************************************************//**
862 Parses a log record of a record list end or start deletion.
863 @return end of log record or NULL */
864 UNIV_INTERN
865 byte*
page_parse_delete_rec_list(byte type,byte * ptr,byte * end_ptr,buf_block_t * block,dict_index_t * index,mtr_t * mtr)866 page_parse_delete_rec_list(
867 /*=======================*/
868 byte type, /*!< in: MLOG_LIST_END_DELETE,
869 MLOG_LIST_START_DELETE,
870 MLOG_COMP_LIST_END_DELETE or
871 MLOG_COMP_LIST_START_DELETE */
872 byte* ptr, /*!< in: buffer */
873 byte* end_ptr,/*!< in: buffer end */
874 buf_block_t* block, /*!< in/out: buffer block or NULL */
875 dict_index_t* index, /*!< in: record descriptor */
876 mtr_t* mtr) /*!< in: mtr or NULL */
877 {
878 page_t* page;
879 ulint offset;
880
881 ut_ad(type == MLOG_LIST_END_DELETE
882 || type == MLOG_LIST_START_DELETE
883 || type == MLOG_COMP_LIST_END_DELETE
884 || type == MLOG_COMP_LIST_START_DELETE);
885
886 /* Read the record offset as a 2-byte ulint */
887
888 if (end_ptr < ptr + 2) {
889
890 return(NULL);
891 }
892
893 offset = mach_read_from_2(ptr);
894 ptr += 2;
895
896 if (!block) {
897
898 return(ptr);
899 }
900
901 page = buf_block_get_frame(block);
902
903 ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
904
905 if (type == MLOG_LIST_END_DELETE
906 || type == MLOG_COMP_LIST_END_DELETE) {
907 page_delete_rec_list_end(page + offset, block, index,
908 ULINT_UNDEFINED, ULINT_UNDEFINED,
909 mtr);
910 } else {
911 page_delete_rec_list_start(page + offset, block, index, mtr);
912 }
913
914 return(ptr);
915 }
916
917 /*************************************************************//**
918 Deletes records from a page from a given record onward, including that record.
919 The infimum and supremum records are not deleted. */
920 UNIV_INTERN
921 void
page_delete_rec_list_end(rec_t * rec,buf_block_t * block,dict_index_t * index,ulint n_recs,ulint size,mtr_t * mtr)922 page_delete_rec_list_end(
923 /*=====================*/
924 rec_t* rec, /*!< in: pointer to record on page */
925 buf_block_t* block, /*!< in: buffer block of the page */
926 dict_index_t* index, /*!< in: record descriptor */
927 ulint n_recs, /*!< in: number of records to delete,
928 or ULINT_UNDEFINED if not known */
929 ulint size, /*!< in: the sum of the sizes of the
930 records in the end of the chain to
931 delete, or ULINT_UNDEFINED if not known */
932 mtr_t* mtr) /*!< in: mtr */
933 {
934 page_dir_slot_t*slot;
935 ulint slot_index;
936 rec_t* last_rec;
937 rec_t* prev_rec;
938 ulint n_owned;
939 page_zip_des_t* page_zip = buf_block_get_page_zip(block);
940 page_t* page = page_align(rec);
941 mem_heap_t* heap = NULL;
942 ulint offsets_[REC_OFFS_NORMAL_SIZE];
943 ulint* offsets = offsets_;
944 rec_offs_init(offsets_);
945
946 ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE);
947 ut_ad(!page_zip || page_rec_is_comp(rec));
948 #ifdef UNIV_ZIP_DEBUG
949 ut_a(!page_zip || page_zip_validate(page_zip, page, index));
950 #endif /* UNIV_ZIP_DEBUG */
951
952 if (page_rec_is_infimum(rec)) {
953 rec = page_rec_get_next(rec);
954 }
955
956 if (page_rec_is_supremum(rec)) {
957
958 return;
959 }
960
961 /* Reset the last insert info in the page header and increment
962 the modify clock for the frame */
963
964 page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL);
965
966 /* The page gets invalid for optimistic searches: increment the
967 frame modify clock */
968
969 buf_block_modify_clock_inc(block);
970
971 page_delete_rec_list_write_log(rec, index, page_is_comp(page)
972 ? MLOG_COMP_LIST_END_DELETE
973 : MLOG_LIST_END_DELETE, mtr);
974
975 if (UNIV_LIKELY_NULL(page_zip)) {
976 ulint log_mode;
977
978 ut_a(page_is_comp(page));
979 /* Individual deletes are not logged */
980
981 log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
982
983 do {
984 page_cur_t cur;
985 page_cur_position(rec, block, &cur);
986
987 offsets = rec_get_offsets(rec, index, offsets,
988 ULINT_UNDEFINED, &heap);
989 rec = rec_get_next_ptr(rec, TRUE);
990 #ifdef UNIV_ZIP_DEBUG
991 ut_a(page_zip_validate(page_zip, page, index));
992 #endif /* UNIV_ZIP_DEBUG */
993 page_cur_delete_rec(&cur, index, offsets, mtr);
994 } while (page_offset(rec) != PAGE_NEW_SUPREMUM);
995
996 if (UNIV_LIKELY_NULL(heap)) {
997 mem_heap_free(heap);
998 }
999
1000 /* Restore log mode */
1001
1002 mtr_set_log_mode(mtr, log_mode);
1003 return;
1004 }
1005
1006 prev_rec = page_rec_get_prev(rec);
1007
1008 last_rec = page_rec_get_prev(page_get_supremum_rec(page));
1009
1010 if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
1011 rec_t* rec2 = rec;
1012 /* Calculate the sum of sizes and the number of records */
1013 size = 0;
1014 n_recs = 0;
1015
1016 do {
1017 ulint s;
1018 offsets = rec_get_offsets(rec2, index, offsets,
1019 ULINT_UNDEFINED, &heap);
1020 s = rec_offs_size(offsets);
1021 ut_ad(rec2 - page + s - rec_offs_extra_size(offsets)
1022 < UNIV_PAGE_SIZE);
1023 ut_ad(size + s < UNIV_PAGE_SIZE);
1024 size += s;
1025 n_recs++;
1026
1027 rec2 = page_rec_get_next(rec2);
1028 } while (!page_rec_is_supremum(rec2));
1029
1030 if (UNIV_LIKELY_NULL(heap)) {
1031 mem_heap_free(heap);
1032 }
1033 }
1034
1035 ut_ad(size < UNIV_PAGE_SIZE);
1036
1037 /* Update the page directory; there is no need to balance the number
1038 of the records owned by the supremum record, as it is allowed to be
1039 less than PAGE_DIR_SLOT_MIN_N_OWNED */
1040
1041 if (page_is_comp(page)) {
1042 rec_t* rec2 = rec;
1043 ulint count = 0;
1044
1045 while (rec_get_n_owned_new(rec2) == 0) {
1046 count++;
1047
1048 rec2 = rec_get_next_ptr(rec2, TRUE);
1049 }
1050
1051 ut_ad(rec_get_n_owned_new(rec2) > count);
1052
1053 n_owned = rec_get_n_owned_new(rec2) - count;
1054 slot_index = page_dir_find_owner_slot(rec2);
1055 ut_ad(slot_index > 0);
1056 slot = page_dir_get_nth_slot(page, slot_index);
1057 } else {
1058 rec_t* rec2 = rec;
1059 ulint count = 0;
1060
1061 while (rec_get_n_owned_old(rec2) == 0) {
1062 count++;
1063
1064 rec2 = rec_get_next_ptr(rec2, FALSE);
1065 }
1066
1067 ut_ad(rec_get_n_owned_old(rec2) > count);
1068
1069 n_owned = rec_get_n_owned_old(rec2) - count;
1070 slot_index = page_dir_find_owner_slot(rec2);
1071 ut_ad(slot_index > 0);
1072 slot = page_dir_get_nth_slot(page, slot_index);
1073 }
1074
1075 page_dir_slot_set_rec(slot, page_get_supremum_rec(page));
1076 page_dir_slot_set_n_owned(slot, NULL, n_owned);
1077
1078 page_dir_set_n_slots(page, NULL, slot_index + 1);
1079
1080 /* Remove the record chain segment from the record chain */
1081 page_rec_set_next(prev_rec, page_get_supremum_rec(page));
1082
1083 btr_blob_dbg_op(page, rec, index, "delete_end",
1084 btr_blob_dbg_remove_rec);
1085
1086 /* Catenate the deleted chain segment to the page free list */
1087
1088 page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE));
1089 page_header_set_ptr(page, NULL, PAGE_FREE, rec);
1090
1091 page_header_set_field(page, NULL, PAGE_GARBAGE, size
1092 + page_header_get_field(page, PAGE_GARBAGE));
1093
1094 page_header_set_field(page, NULL, PAGE_N_RECS,
1095 (ulint)(page_get_n_recs(page) - n_recs));
1096 }
1097
1098 /*************************************************************//**
1099 Deletes records from page, up to the given record, NOT including
1100 that record. Infimum and supremum records are not deleted. */
1101 UNIV_INTERN
1102 void
page_delete_rec_list_start(rec_t * rec,buf_block_t * block,dict_index_t * index,mtr_t * mtr)1103 page_delete_rec_list_start(
1104 /*=======================*/
1105 rec_t* rec, /*!< in: record on page */
1106 buf_block_t* block, /*!< in: buffer block of the page */
1107 dict_index_t* index, /*!< in: record descriptor */
1108 mtr_t* mtr) /*!< in: mtr */
1109 {
1110 page_cur_t cur1;
1111 ulint log_mode;
1112 ulint offsets_[REC_OFFS_NORMAL_SIZE];
1113 ulint* offsets = offsets_;
1114 mem_heap_t* heap = NULL;
1115 byte type;
1116
1117 rec_offs_init(offsets_);
1118
1119 ut_ad((ibool) !!page_rec_is_comp(rec)
1120 == dict_table_is_comp(index->table));
1121 #ifdef UNIV_ZIP_DEBUG
1122 {
1123 page_zip_des_t* page_zip= buf_block_get_page_zip(block);
1124 page_t* page = buf_block_get_frame(block);
1125
1126 /* page_zip_validate() would detect a min_rec_mark mismatch
1127 in btr_page_split_and_insert()
1128 between btr_attach_half_pages() and insert_page = ...
1129 when btr_page_get_split_rec_to_left() holds
1130 (direction == FSP_DOWN). */
1131 ut_a(!page_zip
1132 || page_zip_validate_low(page_zip, page, index, TRUE));
1133 }
1134 #endif /* UNIV_ZIP_DEBUG */
1135
1136 if (page_rec_is_infimum(rec)) {
1137
1138 return;
1139 }
1140
1141 if (page_rec_is_comp(rec)) {
1142 type = MLOG_COMP_LIST_START_DELETE;
1143 } else {
1144 type = MLOG_LIST_START_DELETE;
1145 }
1146
1147 page_delete_rec_list_write_log(rec, index, type, mtr);
1148
1149 page_cur_set_before_first(block, &cur1);
1150 page_cur_move_to_next(&cur1);
1151
1152 /* Individual deletes are not logged */
1153
1154 log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
1155
1156 while (page_cur_get_rec(&cur1) != rec) {
1157 offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
1158 offsets, ULINT_UNDEFINED, &heap);
1159 page_cur_delete_rec(&cur1, index, offsets, mtr);
1160 }
1161
1162 if (UNIV_LIKELY_NULL(heap)) {
1163 mem_heap_free(heap);
1164 }
1165
1166 /* Restore log mode */
1167
1168 mtr_set_log_mode(mtr, log_mode);
1169 }
1170
1171 #ifndef UNIV_HOTBACKUP
1172 /*************************************************************//**
1173 Moves record list end to another page. Moved records include
1174 split_rec.
1175 @return TRUE on success; FALSE on compression failure (new_block will
1176 be decompressed) */
1177 UNIV_INTERN
1178 ibool
page_move_rec_list_end(buf_block_t * new_block,buf_block_t * block,rec_t * split_rec,dict_index_t * index,mtr_t * mtr)1179 page_move_rec_list_end(
1180 /*===================*/
1181 buf_block_t* new_block, /*!< in/out: index page where to move */
1182 buf_block_t* block, /*!< in: index page from where to move */
1183 rec_t* split_rec, /*!< in: first record to move */
1184 dict_index_t* index, /*!< in: record descriptor */
1185 mtr_t* mtr) /*!< in: mtr */
1186 {
1187 page_t* new_page = buf_block_get_frame(new_block);
1188 ulint old_data_size;
1189 ulint new_data_size;
1190 ulint old_n_recs;
1191 ulint new_n_recs;
1192
1193 old_data_size = page_get_data_size(new_page);
1194 old_n_recs = page_get_n_recs(new_page);
1195 #ifdef UNIV_ZIP_DEBUG
1196 {
1197 page_zip_des_t* new_page_zip
1198 = buf_block_get_page_zip(new_block);
1199 page_zip_des_t* page_zip
1200 = buf_block_get_page_zip(block);
1201 ut_a(!new_page_zip == !page_zip);
1202 ut_a(!new_page_zip
1203 || page_zip_validate(new_page_zip, new_page, index));
1204 ut_a(!page_zip
1205 || page_zip_validate(page_zip, page_align(split_rec),
1206 index));
1207 }
1208 #endif /* UNIV_ZIP_DEBUG */
1209
1210 if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block,
1211 split_rec, index, mtr))) {
1212 return(FALSE);
1213 }
1214
1215 new_data_size = page_get_data_size(new_page);
1216 new_n_recs = page_get_n_recs(new_page);
1217
1218 ut_ad(new_data_size >= old_data_size);
1219
1220 page_delete_rec_list_end(split_rec, block, index,
1221 new_n_recs - old_n_recs,
1222 new_data_size - old_data_size, mtr);
1223
1224 return(TRUE);
1225 }
1226
1227 /*************************************************************//**
1228 Moves record list start to another page. Moved records do not include
1229 split_rec.
1230 @return TRUE on success; FALSE on compression failure */
1231 UNIV_INTERN
1232 ibool
page_move_rec_list_start(buf_block_t * new_block,buf_block_t * block,rec_t * split_rec,dict_index_t * index,mtr_t * mtr)1233 page_move_rec_list_start(
1234 /*=====================*/
1235 buf_block_t* new_block, /*!< in/out: index page where to move */
1236 buf_block_t* block, /*!< in/out: page containing split_rec */
1237 rec_t* split_rec, /*!< in: first record not to move */
1238 dict_index_t* index, /*!< in: record descriptor */
1239 mtr_t* mtr) /*!< in: mtr */
1240 {
1241 if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block,
1242 split_rec, index, mtr))) {
1243 return(FALSE);
1244 }
1245
1246 page_delete_rec_list_start(split_rec, block, index, mtr);
1247
1248 return(TRUE);
1249 }
1250 #endif /* !UNIV_HOTBACKUP */
1251
1252 /**************************************************************//**
1253 Used to delete n slots from the directory. This function updates
1254 also n_owned fields in the records, so that the first slot after
1255 the deleted ones inherits the records of the deleted slots. */
1256 UNIV_INLINE
1257 void
page_dir_delete_slot(page_t * page,page_zip_des_t * page_zip,ulint slot_no)1258 page_dir_delete_slot(
1259 /*=================*/
1260 page_t* page, /*!< in/out: the index page */
1261 page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
1262 ulint slot_no)/*!< in: slot to be deleted */
1263 {
1264 page_dir_slot_t* slot;
1265 ulint n_owned;
1266 ulint i;
1267 ulint n_slots;
1268
1269 ut_ad(!page_zip || page_is_comp(page));
1270 ut_ad(slot_no > 0);
1271 ut_ad(slot_no + 1 < page_dir_get_n_slots(page));
1272
1273 n_slots = page_dir_get_n_slots(page);
1274
1275 /* 1. Reset the n_owned fields of the slots to be
1276 deleted */
1277 slot = page_dir_get_nth_slot(page, slot_no);
1278 n_owned = page_dir_slot_get_n_owned(slot);
1279 page_dir_slot_set_n_owned(slot, page_zip, 0);
1280
1281 /* 2. Update the n_owned value of the first non-deleted slot */
1282
1283 slot = page_dir_get_nth_slot(page, slot_no + 1);
1284 page_dir_slot_set_n_owned(slot, page_zip,
1285 n_owned + page_dir_slot_get_n_owned(slot));
1286
1287 /* 3. Destroy the slot by copying slots */
1288 for (i = slot_no + 1; i < n_slots; i++) {
1289 rec_t* rec = (rec_t*)
1290 page_dir_slot_get_rec(page_dir_get_nth_slot(page, i));
1291 page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec);
1292 }
1293
1294 /* 4. Zero out the last slot, which will be removed */
1295 mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0);
1296
1297 /* 5. Update the page header */
1298 page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1);
1299 }
1300
1301 /**************************************************************//**
1302 Used to add n slots to the directory. Does not set the record pointers
1303 in the added slots or update n_owned values: this is the responsibility
1304 of the caller. */
1305 UNIV_INLINE
1306 void
page_dir_add_slot(page_t * page,page_zip_des_t * page_zip,ulint start)1307 page_dir_add_slot(
1308 /*==============*/
1309 page_t* page, /*!< in/out: the index page */
1310 page_zip_des_t* page_zip,/*!< in/out: comprssed page, or NULL */
1311 ulint start) /*!< in: the slot above which the new slots
1312 are added */
1313 {
1314 page_dir_slot_t* slot;
1315 ulint n_slots;
1316
1317 n_slots = page_dir_get_n_slots(page);
1318
1319 ut_ad(start < n_slots - 1);
1320
1321 /* Update the page header */
1322 page_dir_set_n_slots(page, page_zip, n_slots + 1);
1323
1324 /* Move slots up */
1325 slot = page_dir_get_nth_slot(page, n_slots);
1326 memmove(slot, slot + PAGE_DIR_SLOT_SIZE,
1327 (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE);
1328 }
1329
1330 /****************************************************************//**
1331 Splits a directory slot which owns too many records. */
1332 UNIV_INTERN
1333 void
page_dir_split_slot(page_t * page,page_zip_des_t * page_zip,ulint slot_no)1334 page_dir_split_slot(
1335 /*================*/
1336 page_t* page, /*!< in/out: index page */
1337 page_zip_des_t* page_zip,/*!< in/out: compressed page whose
1338 uncompressed part will be written, or NULL */
1339 ulint slot_no)/*!< in: the directory slot */
1340 {
1341 rec_t* rec;
1342 page_dir_slot_t* new_slot;
1343 page_dir_slot_t* prev_slot;
1344 page_dir_slot_t* slot;
1345 ulint i;
1346 ulint n_owned;
1347
1348 ut_ad(page);
1349 ut_ad(!page_zip || page_is_comp(page));
1350 ut_ad(slot_no > 0);
1351
1352 slot = page_dir_get_nth_slot(page, slot_no);
1353
1354 n_owned = page_dir_slot_get_n_owned(slot);
1355 ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
1356
1357 /* 1. We loop to find a record approximately in the middle of the
1358 records owned by the slot. */
1359
1360 prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
1361 rec = (rec_t*) page_dir_slot_get_rec(prev_slot);
1362
1363 for (i = 0; i < n_owned / 2; i++) {
1364 rec = page_rec_get_next(rec);
1365 }
1366
1367 ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
1368
1369 /* 2. We add one directory slot immediately below the slot to be
1370 split. */
1371
1372 page_dir_add_slot(page, page_zip, slot_no - 1);
1373
1374 /* The added slot is now number slot_no, and the old slot is
1375 now number slot_no + 1 */
1376
1377 new_slot = page_dir_get_nth_slot(page, slot_no);
1378 slot = page_dir_get_nth_slot(page, slot_no + 1);
1379
1380 /* 3. We store the appropriate values to the new slot. */
1381
1382 page_dir_slot_set_rec(new_slot, rec);
1383 page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2);
1384
1385 /* 4. Finally, we update the number of records field of the
1386 original slot */
1387
1388 page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2));
1389 }
1390
1391 /*************************************************************//**
1392 Tries to balance the given directory slot with too few records with the upper
1393 neighbor, so that there are at least the minimum number of records owned by
1394 the slot; this may result in the merging of two slots. */
1395 UNIV_INTERN
1396 void
page_dir_balance_slot(page_t * page,page_zip_des_t * page_zip,ulint slot_no)1397 page_dir_balance_slot(
1398 /*==================*/
1399 page_t* page, /*!< in/out: index page */
1400 page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
1401 ulint slot_no)/*!< in: the directory slot */
1402 {
1403 page_dir_slot_t* slot;
1404 page_dir_slot_t* up_slot;
1405 ulint n_owned;
1406 ulint up_n_owned;
1407 rec_t* old_rec;
1408 rec_t* new_rec;
1409
1410 ut_ad(page);
1411 ut_ad(!page_zip || page_is_comp(page));
1412 ut_ad(slot_no > 0);
1413
1414 slot = page_dir_get_nth_slot(page, slot_no);
1415
1416 /* The last directory slot cannot be balanced with the upper
1417 neighbor, as there is none. */
1418
1419 if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) {
1420
1421 return;
1422 }
1423
1424 up_slot = page_dir_get_nth_slot(page, slot_no + 1);
1425
1426 n_owned = page_dir_slot_get_n_owned(slot);
1427 up_n_owned = page_dir_slot_get_n_owned(up_slot);
1428
1429 ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
1430
1431 /* If the upper slot has the minimum value of n_owned, we will merge
1432 the two slots, therefore we assert: */
1433 ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
1434
1435 if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
1436
1437 /* In this case we can just transfer one record owned
1438 by the upper slot to the property of the lower slot */
1439 old_rec = (rec_t*) page_dir_slot_get_rec(slot);
1440
1441 if (page_is_comp(page)) {
1442 new_rec = rec_get_next_ptr(old_rec, TRUE);
1443
1444 rec_set_n_owned_new(old_rec, page_zip, 0);
1445 rec_set_n_owned_new(new_rec, page_zip, n_owned + 1);
1446 } else {
1447 new_rec = rec_get_next_ptr(old_rec, FALSE);
1448
1449 rec_set_n_owned_old(old_rec, 0);
1450 rec_set_n_owned_old(new_rec, n_owned + 1);
1451 }
1452
1453 page_dir_slot_set_rec(slot, new_rec);
1454
1455 page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1);
1456 } else {
1457 /* In this case we may merge the two slots */
1458 page_dir_delete_slot(page, page_zip, slot_no);
1459 }
1460 }
1461
1462 /************************************************************//**
1463 Returns the nth record of the record list.
1464 This is the inverse function of page_rec_get_n_recs_before().
1465 @return nth record */
1466 UNIV_INTERN
1467 const rec_t*
page_rec_get_nth_const(const page_t * page,ulint nth)1468 page_rec_get_nth_const(
1469 /*===================*/
1470 const page_t* page, /*!< in: page */
1471 ulint nth) /*!< in: nth record */
1472 {
1473 const page_dir_slot_t* slot;
1474 ulint i;
1475 ulint n_owned;
1476 const rec_t* rec;
1477
1478 if (nth == 0) {
1479 return(page_get_infimum_rec(page));
1480 }
1481
1482 ut_ad(nth < UNIV_PAGE_SIZE / (REC_N_NEW_EXTRA_BYTES + 1));
1483
1484 for (i = 0;; i++) {
1485
1486 slot = page_dir_get_nth_slot(page, i);
1487 n_owned = page_dir_slot_get_n_owned(slot);
1488
1489 if (n_owned > nth) {
1490 break;
1491 } else {
1492 nth -= n_owned;
1493 }
1494 }
1495
1496 ut_ad(i > 0);
1497 slot = page_dir_get_nth_slot(page, i - 1);
1498 rec = page_dir_slot_get_rec(slot);
1499
1500 if (page_is_comp(page)) {
1501 do {
1502 rec = page_rec_get_next_low(rec, TRUE);
1503 ut_ad(rec);
1504 } while (nth--);
1505 } else {
1506 do {
1507 rec = page_rec_get_next_low(rec, FALSE);
1508 ut_ad(rec);
1509 } while (nth--);
1510 }
1511
1512 return(rec);
1513 }
1514
1515 /***************************************************************//**
1516 Returns the number of records before the given record in chain.
1517 The number includes infimum and supremum records.
1518 @return number of records */
1519 UNIV_INTERN
1520 ulint
page_rec_get_n_recs_before(const rec_t * rec)1521 page_rec_get_n_recs_before(
1522 /*=======================*/
1523 const rec_t* rec) /*!< in: the physical record */
1524 {
1525 const page_dir_slot_t* slot;
1526 const rec_t* slot_rec;
1527 const page_t* page;
1528 ulint i;
1529 lint n = 0;
1530
1531 ut_ad(page_rec_check(rec));
1532
1533 page = page_align(rec);
1534 if (page_is_comp(page)) {
1535 while (rec_get_n_owned_new(rec) == 0) {
1536
1537 rec = rec_get_next_ptr_const(rec, TRUE);
1538 n--;
1539 }
1540
1541 for (i = 0; ; i++) {
1542 slot = page_dir_get_nth_slot(page, i);
1543 slot_rec = page_dir_slot_get_rec(slot);
1544
1545 n += rec_get_n_owned_new(slot_rec);
1546
1547 if (rec == slot_rec) {
1548
1549 break;
1550 }
1551 }
1552 } else {
1553 while (rec_get_n_owned_old(rec) == 0) {
1554
1555 rec = rec_get_next_ptr_const(rec, FALSE);
1556 n--;
1557 }
1558
1559 for (i = 0; ; i++) {
1560 slot = page_dir_get_nth_slot(page, i);
1561 slot_rec = page_dir_slot_get_rec(slot);
1562
1563 n += rec_get_n_owned_old(slot_rec);
1564
1565 if (rec == slot_rec) {
1566
1567 break;
1568 }
1569 }
1570 }
1571
1572 n--;
1573
1574 ut_ad(n >= 0);
1575 ut_ad(n < UNIV_PAGE_SIZE / (REC_N_NEW_EXTRA_BYTES + 1));
1576
1577 return((ulint) n);
1578 }
1579
1580 #ifndef UNIV_HOTBACKUP
1581 /************************************************************//**
1582 Prints record contents including the data relevant only in
1583 the index page context. */
1584 UNIV_INTERN
1585 void
page_rec_print(const rec_t * rec,const ulint * offsets)1586 page_rec_print(
1587 /*===========*/
1588 const rec_t* rec, /*!< in: physical record */
1589 const ulint* offsets)/*!< in: record descriptor */
1590 {
1591 ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
1592 rec_print_new(stderr, rec, offsets);
1593 if (page_rec_is_comp(rec)) {
1594 fprintf(stderr,
1595 " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1596 (ulong) rec_get_n_owned_new(rec),
1597 (ulong) rec_get_heap_no_new(rec),
1598 (ulong) rec_get_next_offs(rec, TRUE));
1599 } else {
1600 fprintf(stderr,
1601 " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1602 (ulong) rec_get_n_owned_old(rec),
1603 (ulong) rec_get_heap_no_old(rec),
1604 (ulong) rec_get_next_offs(rec, FALSE));
1605 }
1606
1607 page_rec_check(rec);
1608 rec_validate(rec, offsets);
1609 }
1610
1611 # ifdef UNIV_BTR_PRINT
1612 /***************************************************************//**
1613 This is used to print the contents of the directory for
1614 debugging purposes. */
1615 UNIV_INTERN
1616 void
page_dir_print(page_t * page,ulint pr_n)1617 page_dir_print(
1618 /*===========*/
1619 page_t* page, /*!< in: index page */
1620 ulint pr_n) /*!< in: print n first and n last entries */
1621 {
1622 ulint n;
1623 ulint i;
1624 page_dir_slot_t* slot;
1625
1626 n = page_dir_get_n_slots(page);
1627
1628 fprintf(stderr, "--------------------------------\n"
1629 "PAGE DIRECTORY\n"
1630 "Page address %p\n"
1631 "Directory stack top at offs: %lu; number of slots: %lu\n",
1632 page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)),
1633 (ulong) n);
1634 for (i = 0; i < n; i++) {
1635 slot = page_dir_get_nth_slot(page, i);
1636 if ((i == pr_n) && (i < n - pr_n)) {
1637 fputs(" ... \n", stderr);
1638 }
1639 if ((i < pr_n) || (i >= n - pr_n)) {
1640 fprintf(stderr,
1641 "Contents of slot: %lu: n_owned: %lu,"
1642 " rec offs: %lu\n",
1643 (ulong) i,
1644 (ulong) page_dir_slot_get_n_owned(slot),
1645 (ulong)
1646 page_offset(page_dir_slot_get_rec(slot)));
1647 }
1648 }
1649 fprintf(stderr, "Total of %lu records\n"
1650 "--------------------------------\n",
1651 (ulong) (PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page)));
1652 }
1653
1654 /***************************************************************//**
1655 This is used to print the contents of the page record list for
1656 debugging purposes. */
1657 UNIV_INTERN
1658 void
page_print_list(buf_block_t * block,dict_index_t * index,ulint pr_n)1659 page_print_list(
1660 /*============*/
1661 buf_block_t* block, /*!< in: index page */
1662 dict_index_t* index, /*!< in: dictionary index of the page */
1663 ulint pr_n) /*!< in: print n first and n last entries */
1664 {
1665 page_t* page = block->frame;
1666 page_cur_t cur;
1667 ulint count;
1668 ulint n_recs;
1669 mem_heap_t* heap = NULL;
1670 ulint offsets_[REC_OFFS_NORMAL_SIZE];
1671 ulint* offsets = offsets_;
1672 rec_offs_init(offsets_);
1673
1674 ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table));
1675
1676 fprintf(stderr,
1677 "--------------------------------\n"
1678 "PAGE RECORD LIST\n"
1679 "Page address %p\n", page);
1680
1681 n_recs = page_get_n_recs(page);
1682
1683 page_cur_set_before_first(block, &cur);
1684 count = 0;
1685 for (;;) {
1686 offsets = rec_get_offsets(cur.rec, index, offsets,
1687 ULINT_UNDEFINED, &heap);
1688 page_rec_print(cur.rec, offsets);
1689
1690 if (count == pr_n) {
1691 break;
1692 }
1693 if (page_cur_is_after_last(&cur)) {
1694 break;
1695 }
1696 page_cur_move_to_next(&cur);
1697 count++;
1698 }
1699
1700 if (n_recs > 2 * pr_n) {
1701 fputs(" ... \n", stderr);
1702 }
1703
1704 while (!page_cur_is_after_last(&cur)) {
1705 page_cur_move_to_next(&cur);
1706
1707 if (count + pr_n >= n_recs) {
1708 offsets = rec_get_offsets(cur.rec, index, offsets,
1709 ULINT_UNDEFINED, &heap);
1710 page_rec_print(cur.rec, offsets);
1711 }
1712 count++;
1713 }
1714
1715 fprintf(stderr,
1716 "Total of %lu records \n"
1717 "--------------------------------\n",
1718 (ulong) (count + 1));
1719
1720 if (UNIV_LIKELY_NULL(heap)) {
1721 mem_heap_free(heap);
1722 }
1723 }
1724
1725 /***************************************************************//**
1726 Prints the info in a page header. */
1727 UNIV_INTERN
1728 void
page_header_print(const page_t * page)1729 page_header_print(
1730 /*==============*/
1731 const page_t* page)
1732 {
1733 fprintf(stderr,
1734 "--------------------------------\n"
1735 "PAGE HEADER INFO\n"
1736 "Page address %p, n records %lu (%s)\n"
1737 "n dir slots %lu, heap top %lu\n"
1738 "Page n heap %lu, free %lu, garbage %lu\n"
1739 "Page last insert %lu, direction %lu, n direction %lu\n",
1740 page, (ulong) page_header_get_field(page, PAGE_N_RECS),
1741 page_is_comp(page) ? "compact format" : "original format",
1742 (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
1743 (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1744 (ulong) page_dir_get_n_heap(page),
1745 (ulong) page_header_get_field(page, PAGE_FREE),
1746 (ulong) page_header_get_field(page, PAGE_GARBAGE),
1747 (ulong) page_header_get_field(page, PAGE_LAST_INSERT),
1748 (ulong) page_header_get_field(page, PAGE_DIRECTION),
1749 (ulong) page_header_get_field(page, PAGE_N_DIRECTION));
1750 }
1751
1752 /***************************************************************//**
1753 This is used to print the contents of the page for
1754 debugging purposes. */
1755 UNIV_INTERN
1756 void
page_print(buf_block_t * block,dict_index_t * index,ulint dn,ulint rn)1757 page_print(
1758 /*=======*/
1759 buf_block_t* block, /*!< in: index page */
1760 dict_index_t* index, /*!< in: dictionary index of the page */
1761 ulint dn, /*!< in: print dn first and last entries
1762 in directory */
1763 ulint rn) /*!< in: print rn first and last records
1764 in directory */
1765 {
1766 page_t* page = block->frame;
1767
1768 page_header_print(page);
1769 page_dir_print(page, dn);
1770 page_print_list(block, index, rn);
1771 }
1772 # endif /* UNIV_BTR_PRINT */
1773 #endif /* !UNIV_HOTBACKUP */
1774
1775 /***************************************************************//**
1776 The following is used to validate a record on a page. This function
1777 differs from rec_validate as it can also check the n_owned field and
1778 the heap_no field.
1779 @return TRUE if ok */
1780 UNIV_INTERN
1781 ibool
page_rec_validate(const rec_t * rec,const ulint * offsets)1782 page_rec_validate(
1783 /*==============*/
1784 const rec_t* rec, /*!< in: physical record */
1785 const ulint* offsets)/*!< in: array returned by rec_get_offsets() */
1786 {
1787 ulint n_owned;
1788 ulint heap_no;
1789 const page_t* page;
1790
1791 page = page_align(rec);
1792 ut_a(!page_is_comp(page) == !rec_offs_comp(offsets));
1793
1794 page_rec_check(rec);
1795 rec_validate(rec, offsets);
1796
1797 if (page_rec_is_comp(rec)) {
1798 n_owned = rec_get_n_owned_new(rec);
1799 heap_no = rec_get_heap_no_new(rec);
1800 } else {
1801 n_owned = rec_get_n_owned_old(rec);
1802 heap_no = rec_get_heap_no_old(rec);
1803 }
1804
1805 if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) {
1806 fprintf(stderr,
1807 "InnoDB: Dir slot of rec %lu, n owned too big %lu\n",
1808 (ulong) page_offset(rec), (ulong) n_owned);
1809 return(FALSE);
1810 }
1811
1812 if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) {
1813 fprintf(stderr,
1814 "InnoDB: Heap no of rec %lu too big %lu %lu\n",
1815 (ulong) page_offset(rec), (ulong) heap_no,
1816 (ulong) page_dir_get_n_heap(page));
1817 return(FALSE);
1818 }
1819
1820 return(TRUE);
1821 }
1822
1823 #ifndef UNIV_HOTBACKUP
1824 /***************************************************************//**
1825 Checks that the first directory slot points to the infimum record and
1826 the last to the supremum. This function is intended to track if the
1827 bug fixed in 4.0.14 has caused corruption to users' databases. */
1828 UNIV_INTERN
1829 void
page_check_dir(const page_t * page)1830 page_check_dir(
1831 /*===========*/
1832 const page_t* page) /*!< in: index page */
1833 {
1834 ulint n_slots;
1835 ulint infimum_offs;
1836 ulint supremum_offs;
1837
1838 n_slots = page_dir_get_n_slots(page);
1839 infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0));
1840 supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page,
1841 n_slots - 1));
1842
1843 if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) {
1844
1845 fprintf(stderr,
1846 "InnoDB: Page directory corruption:"
1847 " infimum not pointed to\n");
1848 buf_page_print(page, 0, 0);
1849 }
1850
1851 if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) {
1852
1853 fprintf(stderr,
1854 "InnoDB: Page directory corruption:"
1855 " supremum not pointed to\n");
1856 buf_page_print(page, 0, 0);
1857 }
1858 }
1859 #endif /* !UNIV_HOTBACKUP */
1860
1861 /***************************************************************//**
1862 This function checks the consistency of an index page when we do not
1863 know the index. This is also resilient so that this should never crash
1864 even if the page is total garbage.
1865 @return TRUE if ok */
1866 UNIV_INTERN
1867 ibool
page_simple_validate_old(const page_t * page)1868 page_simple_validate_old(
1869 /*=====================*/
1870 const page_t* page) /*!< in: index page in ROW_FORMAT=REDUNDANT */
1871 {
1872 const page_dir_slot_t* slot;
1873 ulint slot_no;
1874 ulint n_slots;
1875 const rec_t* rec;
1876 const byte* rec_heap_top;
1877 ulint count;
1878 ulint own_count;
1879 ibool ret = FALSE;
1880
1881 ut_a(!page_is_comp(page));
1882
1883 /* Check first that the record heap and the directory do not
1884 overlap. */
1885
1886 n_slots = page_dir_get_n_slots(page);
1887
1888 if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
1889 fprintf(stderr,
1890 "InnoDB: Nonsensical number %lu of page dir slots\n",
1891 (ulong) n_slots);
1892
1893 goto func_exit;
1894 }
1895
1896 rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
1897
1898 if (UNIV_UNLIKELY(rec_heap_top
1899 > page_dir_get_nth_slot(page, n_slots - 1))) {
1900
1901 fprintf(stderr,
1902 "InnoDB: Record heap and dir overlap on a page,"
1903 " heap top %lu, dir %lu\n",
1904 (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1905 (ulong)
1906 page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
1907
1908 goto func_exit;
1909 }
1910
1911 /* Validate the record list in a loop checking also that it is
1912 consistent with the page record directory. */
1913
1914 count = 0;
1915 own_count = 1;
1916 slot_no = 0;
1917 slot = page_dir_get_nth_slot(page, slot_no);
1918
1919 rec = page_get_infimum_rec(page);
1920
1921 for (;;) {
1922 if (UNIV_UNLIKELY(rec > rec_heap_top)) {
1923 fprintf(stderr,
1924 "InnoDB: Record %lu is above"
1925 " rec heap top %lu\n",
1926 (ulong)(rec - page),
1927 (ulong)(rec_heap_top - page));
1928
1929 goto func_exit;
1930 }
1931
1932 if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) {
1933 /* This is a record pointed to by a dir slot */
1934 if (UNIV_UNLIKELY(rec_get_n_owned_old(rec)
1935 != own_count)) {
1936
1937 fprintf(stderr,
1938 "InnoDB: Wrong owned count %lu, %lu,"
1939 " rec %lu\n",
1940 (ulong) rec_get_n_owned_old(rec),
1941 (ulong) own_count,
1942 (ulong)(rec - page));
1943
1944 goto func_exit;
1945 }
1946
1947 if (UNIV_UNLIKELY
1948 (page_dir_slot_get_rec(slot) != rec)) {
1949 fprintf(stderr,
1950 "InnoDB: Dir slot does not point"
1951 " to right rec %lu\n",
1952 (ulong)(rec - page));
1953
1954 goto func_exit;
1955 }
1956
1957 own_count = 0;
1958
1959 if (!page_rec_is_supremum(rec)) {
1960 slot_no++;
1961 slot = page_dir_get_nth_slot(page, slot_no);
1962 }
1963 }
1964
1965 if (page_rec_is_supremum(rec)) {
1966
1967 break;
1968 }
1969
1970 if (UNIV_UNLIKELY
1971 (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA
1972 || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) {
1973 fprintf(stderr,
1974 "InnoDB: Next record offset"
1975 " nonsensical %lu for rec %lu\n",
1976 (ulong) rec_get_next_offs(rec, FALSE),
1977 (ulong) (rec - page));
1978
1979 goto func_exit;
1980 }
1981
1982 count++;
1983
1984 if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
1985 fprintf(stderr,
1986 "InnoDB: Page record list appears"
1987 " to be circular %lu\n",
1988 (ulong) count);
1989 goto func_exit;
1990 }
1991
1992 rec = page_rec_get_next_const(rec);
1993 own_count++;
1994 }
1995
1996 if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
1997 fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n");
1998
1999 goto func_exit;
2000 }
2001
2002 if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2003 fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
2004 (ulong) slot_no, (ulong) (n_slots - 1));
2005 goto func_exit;
2006 }
2007
2008 if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2009 + PAGE_HEAP_NO_USER_LOW
2010 != count + 1)) {
2011 fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2012 (ulong) page_header_get_field(page, PAGE_N_RECS)
2013 + PAGE_HEAP_NO_USER_LOW,
2014 (ulong) (count + 1));
2015
2016 goto func_exit;
2017 }
2018
2019 /* Check then the free list */
2020 rec = page_header_get_ptr(page, PAGE_FREE);
2021
2022 while (rec != NULL) {
2023 if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
2024 || rec >= page + UNIV_PAGE_SIZE)) {
2025 fprintf(stderr,
2026 "InnoDB: Free list record has"
2027 " a nonsensical offset %lu\n",
2028 (ulong) (rec - page));
2029
2030 goto func_exit;
2031 }
2032
2033 if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2034 fprintf(stderr,
2035 "InnoDB: Free list record %lu"
2036 " is above rec heap top %lu\n",
2037 (ulong) (rec - page),
2038 (ulong) (rec_heap_top - page));
2039
2040 goto func_exit;
2041 }
2042
2043 count++;
2044
2045 if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2046 fprintf(stderr,
2047 "InnoDB: Page free list appears"
2048 " to be circular %lu\n",
2049 (ulong) count);
2050 goto func_exit;
2051 }
2052
2053 rec = page_rec_get_next_const(rec);
2054 }
2055
2056 if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2057
2058 fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
2059 (ulong) page_dir_get_n_heap(page),
2060 (ulong) (count + 1));
2061
2062 goto func_exit;
2063 }
2064
2065 ret = TRUE;
2066
2067 func_exit:
2068 return(ret);
2069 }
2070
2071 /***************************************************************//**
2072 This function checks the consistency of an index page when we do not
2073 know the index. This is also resilient so that this should never crash
2074 even if the page is total garbage.
2075 @return TRUE if ok */
2076 UNIV_INTERN
2077 ibool
page_simple_validate_new(const page_t * page)2078 page_simple_validate_new(
2079 /*=====================*/
2080 const page_t* page) /*!< in: index page in ROW_FORMAT!=REDUNDANT */
2081 {
2082 const page_dir_slot_t* slot;
2083 ulint slot_no;
2084 ulint n_slots;
2085 const rec_t* rec;
2086 const byte* rec_heap_top;
2087 ulint count;
2088 ulint own_count;
2089 ibool ret = FALSE;
2090
2091 ut_a(page_is_comp(page));
2092
2093 /* Check first that the record heap and the directory do not
2094 overlap. */
2095
2096 n_slots = page_dir_get_n_slots(page);
2097
2098 if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
2099 fprintf(stderr,
2100 "InnoDB: Nonsensical number %lu"
2101 " of page dir slots\n", (ulong) n_slots);
2102
2103 goto func_exit;
2104 }
2105
2106 rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
2107
2108 if (UNIV_UNLIKELY(rec_heap_top
2109 > page_dir_get_nth_slot(page, n_slots - 1))) {
2110
2111 fprintf(stderr,
2112 "InnoDB: Record heap and dir overlap on a page,"
2113 " heap top %lu, dir %lu\n",
2114 (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
2115 (ulong)
2116 page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
2117
2118 goto func_exit;
2119 }
2120
2121 /* Validate the record list in a loop checking also that it is
2122 consistent with the page record directory. */
2123
2124 count = 0;
2125 own_count = 1;
2126 slot_no = 0;
2127 slot = page_dir_get_nth_slot(page, slot_no);
2128
2129 rec = page_get_infimum_rec(page);
2130
2131 for (;;) {
2132 if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2133 fprintf(stderr,
2134 "InnoDB: Record %lu is above rec"
2135 " heap top %lu\n",
2136 (ulong) page_offset(rec),
2137 (ulong) page_offset(rec_heap_top));
2138
2139 goto func_exit;
2140 }
2141
2142 if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) {
2143 /* This is a record pointed to by a dir slot */
2144 if (UNIV_UNLIKELY(rec_get_n_owned_new(rec)
2145 != own_count)) {
2146
2147 fprintf(stderr,
2148 "InnoDB: Wrong owned count %lu, %lu,"
2149 " rec %lu\n",
2150 (ulong) rec_get_n_owned_new(rec),
2151 (ulong) own_count,
2152 (ulong) page_offset(rec));
2153
2154 goto func_exit;
2155 }
2156
2157 if (UNIV_UNLIKELY
2158 (page_dir_slot_get_rec(slot) != rec)) {
2159 fprintf(stderr,
2160 "InnoDB: Dir slot does not point"
2161 " to right rec %lu\n",
2162 (ulong) page_offset(rec));
2163
2164 goto func_exit;
2165 }
2166
2167 own_count = 0;
2168
2169 if (!page_rec_is_supremum(rec)) {
2170 slot_no++;
2171 slot = page_dir_get_nth_slot(page, slot_no);
2172 }
2173 }
2174
2175 if (page_rec_is_supremum(rec)) {
2176
2177 break;
2178 }
2179
2180 if (UNIV_UNLIKELY
2181 (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA
2182 || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) {
2183 fprintf(stderr,
2184 "InnoDB: Next record offset nonsensical %lu"
2185 " for rec %lu\n",
2186 (ulong) rec_get_next_offs(rec, TRUE),
2187 (ulong) page_offset(rec));
2188
2189 goto func_exit;
2190 }
2191
2192 count++;
2193
2194 if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2195 fprintf(stderr,
2196 "InnoDB: Page record list appears"
2197 " to be circular %lu\n",
2198 (ulong) count);
2199 goto func_exit;
2200 }
2201
2202 rec = page_rec_get_next_const(rec);
2203 own_count++;
2204 }
2205
2206 if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2207 fprintf(stderr, "InnoDB: n owned is zero"
2208 " in a supremum rec\n");
2209
2210 goto func_exit;
2211 }
2212
2213 if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2214 fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
2215 (ulong) slot_no, (ulong) (n_slots - 1));
2216 goto func_exit;
2217 }
2218
2219 if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2220 + PAGE_HEAP_NO_USER_LOW
2221 != count + 1)) {
2222 fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2223 (ulong) page_header_get_field(page, PAGE_N_RECS)
2224 + PAGE_HEAP_NO_USER_LOW,
2225 (ulong) (count + 1));
2226
2227 goto func_exit;
2228 }
2229
2230 /* Check then the free list */
2231 rec = page_header_get_ptr(page, PAGE_FREE);
2232
2233 while (rec != NULL) {
2234 if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
2235 || rec >= page + UNIV_PAGE_SIZE)) {
2236 fprintf(stderr,
2237 "InnoDB: Free list record has"
2238 " a nonsensical offset %lu\n",
2239 (ulong) page_offset(rec));
2240
2241 goto func_exit;
2242 }
2243
2244 if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2245 fprintf(stderr,
2246 "InnoDB: Free list record %lu"
2247 " is above rec heap top %lu\n",
2248 (ulong) page_offset(rec),
2249 (ulong) page_offset(rec_heap_top));
2250
2251 goto func_exit;
2252 }
2253
2254 count++;
2255
2256 if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2257 fprintf(stderr,
2258 "InnoDB: Page free list appears"
2259 " to be circular %lu\n",
2260 (ulong) count);
2261 goto func_exit;
2262 }
2263
2264 rec = page_rec_get_next_const(rec);
2265 }
2266
2267 if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2268
2269 fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
2270 (ulong) page_dir_get_n_heap(page),
2271 (ulong) (count + 1));
2272
2273 goto func_exit;
2274 }
2275
2276 ret = TRUE;
2277
2278 func_exit:
2279 return(ret);
2280 }
2281
2282 /***************************************************************//**
2283 This function checks the consistency of an index page.
2284 @return TRUE if ok */
2285 UNIV_INTERN
2286 ibool
page_validate(const page_t * page,dict_index_t * index)2287 page_validate(
2288 /*==========*/
2289 const page_t* page, /*!< in: index page */
2290 dict_index_t* index) /*!< in: data dictionary index containing
2291 the page record type definition */
2292 {
2293 const page_dir_slot_t* slot;
2294 mem_heap_t* heap;
2295 byte* buf;
2296 ulint count;
2297 ulint own_count;
2298 ulint rec_own_count;
2299 ulint slot_no;
2300 ulint data_size;
2301 const rec_t* rec;
2302 const rec_t* old_rec = NULL;
2303 ulint offs;
2304 ulint n_slots;
2305 ibool ret = FALSE;
2306 ulint i;
2307 ulint* offsets = NULL;
2308 ulint* old_offsets = NULL;
2309
2310 if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
2311 != dict_table_is_comp(index->table))) {
2312 fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
2313 goto func_exit2;
2314 }
2315 if (page_is_comp(page)) {
2316 if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
2317 goto func_exit2;
2318 }
2319 } else {
2320 if (UNIV_UNLIKELY(!page_simple_validate_old(page))) {
2321 goto func_exit2;
2322 }
2323 }
2324
2325 heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
2326
2327 /* The following buffer is used to check that the
2328 records in the page record heap do not overlap */
2329
2330 buf = mem_heap_zalloc(heap, UNIV_PAGE_SIZE);
2331
2332 /* Check first that the record heap and the directory do not
2333 overlap. */
2334
2335 n_slots = page_dir_get_n_slots(page);
2336
2337 if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
2338 <= page_dir_get_nth_slot(page, n_slots - 1)))) {
2339
2340 fprintf(stderr,
2341 "InnoDB: Record heap and dir overlap"
2342 " on space %lu page %lu index %s, %p, %p\n",
2343 (ulong) page_get_space_id(page),
2344 (ulong) page_get_page_no(page), index->name,
2345 page_header_get_ptr(page, PAGE_HEAP_TOP),
2346 page_dir_get_nth_slot(page, n_slots - 1));
2347
2348 goto func_exit;
2349 }
2350
2351 /* Validate the record list in a loop checking also that
2352 it is consistent with the directory. */
2353 count = 0;
2354 data_size = 0;
2355 own_count = 1;
2356 slot_no = 0;
2357 slot = page_dir_get_nth_slot(page, slot_no);
2358
2359 rec = page_get_infimum_rec(page);
2360
2361 for (;;) {
2362 offsets = rec_get_offsets(rec, index, offsets,
2363 ULINT_UNDEFINED, &heap);
2364
2365 if (page_is_comp(page) && page_rec_is_user_rec(rec)
2366 && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
2367 == page_is_leaf(page))) {
2368 fputs("InnoDB: node_ptr flag mismatch\n", stderr);
2369 goto func_exit;
2370 }
2371
2372 if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2373 goto func_exit;
2374 }
2375
2376 #ifndef UNIV_HOTBACKUP
2377 /* Check that the records are in the ascending order */
2378 if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW)
2379 && !page_rec_is_supremum(rec)) {
2380 if (UNIV_UNLIKELY
2381 (1 != cmp_rec_rec(rec, old_rec,
2382 offsets, old_offsets, index))) {
2383 fprintf(stderr,
2384 "InnoDB: Records in wrong order"
2385 " on space %lu page %lu index %s\n",
2386 (ulong) page_get_space_id(page),
2387 (ulong) page_get_page_no(page),
2388 index->name);
2389 fputs("\nInnoDB: previous record ", stderr);
2390 rec_print_new(stderr, old_rec, old_offsets);
2391 fputs("\nInnoDB: record ", stderr);
2392 rec_print_new(stderr, rec, offsets);
2393 putc('\n', stderr);
2394
2395 goto func_exit;
2396 }
2397 }
2398 #endif /* !UNIV_HOTBACKUP */
2399
2400 if (page_rec_is_user_rec(rec)) {
2401
2402 data_size += rec_offs_size(offsets);
2403 }
2404
2405 offs = page_offset(rec_get_start(rec, offsets));
2406 i = rec_offs_size(offsets);
2407 if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
2408 fputs("InnoDB: record offset out of bounds\n", stderr);
2409 goto func_exit;
2410 }
2411
2412 while (i--) {
2413 if (UNIV_UNLIKELY(buf[offs + i])) {
2414 /* No other record may overlap this */
2415
2416 fputs("InnoDB: Record overlaps another\n",
2417 stderr);
2418 goto func_exit;
2419 }
2420
2421 buf[offs + i] = 1;
2422 }
2423
2424 if (page_is_comp(page)) {
2425 rec_own_count = rec_get_n_owned_new(rec);
2426 } else {
2427 rec_own_count = rec_get_n_owned_old(rec);
2428 }
2429
2430 if (UNIV_UNLIKELY(rec_own_count)) {
2431 /* This is a record pointed to by a dir slot */
2432 if (UNIV_UNLIKELY(rec_own_count != own_count)) {
2433 fprintf(stderr,
2434 "InnoDB: Wrong owned count %lu, %lu\n",
2435 (ulong) rec_own_count,
2436 (ulong) own_count);
2437 goto func_exit;
2438 }
2439
2440 if (page_dir_slot_get_rec(slot) != rec) {
2441 fputs("InnoDB: Dir slot does not"
2442 " point to right rec\n",
2443 stderr);
2444 goto func_exit;
2445 }
2446
2447 page_dir_slot_check(slot);
2448
2449 own_count = 0;
2450 if (!page_rec_is_supremum(rec)) {
2451 slot_no++;
2452 slot = page_dir_get_nth_slot(page, slot_no);
2453 }
2454 }
2455
2456 if (page_rec_is_supremum(rec)) {
2457 break;
2458 }
2459
2460 count++;
2461 own_count++;
2462 old_rec = rec;
2463 rec = page_rec_get_next_const(rec);
2464
2465 /* set old_offsets to offsets; recycle offsets */
2466 {
2467 ulint* offs = old_offsets;
2468 old_offsets = offsets;
2469 offsets = offs;
2470 }
2471 }
2472
2473 if (page_is_comp(page)) {
2474 if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2475
2476 goto n_owned_zero;
2477 }
2478 } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
2479 n_owned_zero:
2480 fputs("InnoDB: n owned is zero\n", stderr);
2481 goto func_exit;
2482 }
2483
2484 if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2485 fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
2486 (ulong) slot_no, (ulong) (n_slots - 1));
2487 goto func_exit;
2488 }
2489
2490 if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2491 + PAGE_HEAP_NO_USER_LOW
2492 != count + 1)) {
2493 fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2494 (ulong) page_header_get_field(page, PAGE_N_RECS)
2495 + PAGE_HEAP_NO_USER_LOW,
2496 (ulong) (count + 1));
2497 goto func_exit;
2498 }
2499
2500 if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
2501 fprintf(stderr,
2502 "InnoDB: Summed data size %lu, returned by func %lu\n",
2503 (ulong) data_size, (ulong) page_get_data_size(page));
2504 goto func_exit;
2505 }
2506
2507 /* Check then the free list */
2508 rec = page_header_get_ptr(page, PAGE_FREE);
2509
2510 while (rec != NULL) {
2511 offsets = rec_get_offsets(rec, index, offsets,
2512 ULINT_UNDEFINED, &heap);
2513 if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2514
2515 goto func_exit;
2516 }
2517
2518 count++;
2519 offs = page_offset(rec_get_start(rec, offsets));
2520 i = rec_offs_size(offsets);
2521 if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
2522 fputs("InnoDB: record offset out of bounds\n", stderr);
2523 goto func_exit;
2524 }
2525
2526 while (i--) {
2527
2528 if (UNIV_UNLIKELY(buf[offs + i])) {
2529 fputs("InnoDB: Record overlaps another"
2530 " in free list\n", stderr);
2531 goto func_exit;
2532 }
2533
2534 buf[offs + i] = 1;
2535 }
2536
2537 rec = page_rec_get_next_const(rec);
2538 }
2539
2540 if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2541 fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
2542 (ulong) page_dir_get_n_heap(page),
2543 (ulong) count + 1);
2544 goto func_exit;
2545 }
2546
2547 ret = TRUE;
2548
2549 func_exit:
2550 mem_heap_free(heap);
2551
2552 if (UNIV_UNLIKELY(ret == FALSE)) {
2553 func_exit2:
2554 fprintf(stderr,
2555 "InnoDB: Apparent corruption"
2556 " in space %lu page %lu index %s\n",
2557 (ulong) page_get_space_id(page),
2558 (ulong) page_get_page_no(page),
2559 index->name);
2560 buf_page_print(page, 0, 0);
2561 }
2562
2563 return(ret);
2564 }
2565
2566 #ifndef UNIV_HOTBACKUP
2567 /***************************************************************//**
2568 Looks in the page record list for a record with the given heap number.
2569 @return record, NULL if not found */
2570 UNIV_INTERN
2571 const rec_t*
page_find_rec_with_heap_no(const page_t * page,ulint heap_no)2572 page_find_rec_with_heap_no(
2573 /*=======================*/
2574 const page_t* page, /*!< in: index page */
2575 ulint heap_no)/*!< in: heap number */
2576 {
2577 const rec_t* rec;
2578
2579 if (page_is_comp(page)) {
2580 rec = page + PAGE_NEW_INFIMUM;
2581
2582 for(;;) {
2583 ulint rec_heap_no = rec_get_heap_no_new(rec);
2584
2585 if (rec_heap_no == heap_no) {
2586
2587 return(rec);
2588 } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2589
2590 return(NULL);
2591 }
2592
2593 rec = page + rec_get_next_offs(rec, TRUE);
2594 }
2595 } else {
2596 rec = page + PAGE_OLD_INFIMUM;
2597
2598 for (;;) {
2599 ulint rec_heap_no = rec_get_heap_no_old(rec);
2600
2601 if (rec_heap_no == heap_no) {
2602
2603 return(rec);
2604 } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2605
2606 return(NULL);
2607 }
2608
2609 rec = page + rec_get_next_offs(rec, FALSE);
2610 }
2611 }
2612 }
2613 #endif /* !UNIV_HOTBACKUP */
2614