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