1 /*****************************************************************************
2 
3 Copyright (c) 1994, 2021, Oracle and/or its affiliates.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8 
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation.  The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License, version 2.0, for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24 
25 *****************************************************************************/
26 
27 /**************************************************//**
28 @file include/page0page.h
29 Index page routines
30 
31 Created 2/2/1994 Heikki Tuuri
32 *******************************************************/
33 
34 #ifndef page0page_h
35 #define page0page_h
36 
37 #include "univ.i"
38 
39 #include "page0types.h"
40 #ifndef UNIV_INNOCHECKSUM
41 #include "fil0fil.h"
42 #include "buf0buf.h"
43 #include "data0data.h"
44 #include "dict0dict.h"
45 #include "rem0rec.h"
46 #endif /* !UNIV_INNOCHECKSUM*/
47 #include "fsp0fsp.h"
48 #ifndef UNIV_INNOCHECKSUM
49 #include "mtr0mtr.h"
50 
51 #ifdef UNIV_MATERIALIZE
52 #undef UNIV_INLINE
53 #define UNIV_INLINE
54 #endif
55 
56 /*			PAGE HEADER
57 			===========
58 
59 Index page header starts at the first offset left free by the FIL-module */
60 
61 typedef	byte		page_header_t;
62 #endif /* !UNIV_INNOCHECKSUM */
63 
64 #define	PAGE_HEADER	FSEG_PAGE_DATA	/* index page header starts at this
65 				offset */
66 /*-----------------------------*/
67 #define PAGE_N_DIR_SLOTS 0	/* number of slots in page directory */
68 #define	PAGE_HEAP_TOP	 2	/* pointer to record heap top */
69 #define	PAGE_N_HEAP	 4	/* number of records in the heap,
70 				bit 15=flag: new-style compact page format */
71 #define	PAGE_FREE	 6	/* pointer to start of page free record list */
72 #define	PAGE_GARBAGE	 8	/* number of bytes in deleted records */
73 #define	PAGE_LAST_INSERT 10	/* pointer to the last inserted record, or
74 				NULL if this info has been reset by a delete,
75 				for example */
76 #define	PAGE_DIRECTION	 12	/* last insert direction: PAGE_LEFT, ... */
77 #define	PAGE_N_DIRECTION 14	/* number of consecutive inserts to the same
78 				direction */
79 #define	PAGE_N_RECS	 16	/* number of user records on the page */
80 #define PAGE_MAX_TRX_ID	 18	/* highest id of a trx which may have modified
81 				a record on the page; trx_id_t; defined only
82 				in secondary indexes and in the insert buffer
83 				tree */
84 #define PAGE_HEADER_PRIV_END 26	/* end of private data structure of the page
85 				header which are set in a page create */
86 /*----*/
87 #define	PAGE_LEVEL	 26	/* level of the node in an index tree; the
88 				leaf level is the level 0.  This field should
89 				not be written to after page creation. */
90 #define	PAGE_INDEX_ID	 28	/* index id where the page belongs.
91 				This field should not be written to after
92 				page creation. */
93 
94 #ifndef UNIV_INNOCHECKSUM
95 
96 #define PAGE_BTR_SEG_LEAF 36	/* file segment header for the leaf pages in
97 				a B-tree: defined only on the root page of a
98 				B-tree, but not in the root of an ibuf tree */
99 #define PAGE_BTR_IBUF_FREE_LIST	PAGE_BTR_SEG_LEAF
100 #define PAGE_BTR_IBUF_FREE_LIST_NODE PAGE_BTR_SEG_LEAF
101 				/* in the place of PAGE_BTR_SEG_LEAF and _TOP
102 				there is a free list base node if the page is
103 				the root page of an ibuf tree, and at the same
104 				place is the free list node if the page is in
105 				a free list */
106 #define PAGE_BTR_SEG_TOP (36 + FSEG_HEADER_SIZE)
107 				/* file segment header for the non-leaf pages
108 				in a B-tree: defined only on the root page of
109 				a B-tree, but not in the root of an ibuf
110 				tree */
111 /*----*/
112 #define PAGE_DATA	(PAGE_HEADER + 36 + 2 * FSEG_HEADER_SIZE)
113 				/* start of data on the page */
114 
115 #define PAGE_OLD_INFIMUM	(PAGE_DATA + 1 + REC_N_OLD_EXTRA_BYTES)
116 				/* offset of the page infimum record on an
117 				old-style page */
118 #define PAGE_OLD_SUPREMUM	(PAGE_DATA + 2 + 2 * REC_N_OLD_EXTRA_BYTES + 8)
119 				/* offset of the page supremum record on an
120 				old-style page */
121 #define PAGE_OLD_SUPREMUM_END (PAGE_OLD_SUPREMUM + 9)
122 				/* offset of the page supremum record end on
123 				an old-style page */
124 #define PAGE_NEW_INFIMUM	(PAGE_DATA + REC_N_NEW_EXTRA_BYTES)
125 				/* offset of the page infimum record on a
126 				new-style compact page */
127 #define PAGE_NEW_SUPREMUM	(PAGE_DATA + 2 * REC_N_NEW_EXTRA_BYTES + 8)
128 				/* offset of the page supremum record on a
129 				new-style compact page */
130 #define PAGE_NEW_SUPREMUM_END (PAGE_NEW_SUPREMUM + 8)
131 				/* offset of the page supremum record end on
132 				a new-style compact page */
133 /*-----------------------------*/
134 
135 /* Heap numbers */
136 #define PAGE_HEAP_NO_INFIMUM	0	/* page infimum */
137 #define PAGE_HEAP_NO_SUPREMUM	1	/* page supremum */
138 #define PAGE_HEAP_NO_USER_LOW	2	/* first user record in
139 					creation (insertion) order,
140 					not necessarily collation order;
141 					this record may have been deleted */
142 
143 /* Directions of cursor movement */
144 #define	PAGE_LEFT		1
145 #define	PAGE_RIGHT		2
146 #define	PAGE_SAME_REC		3
147 #define	PAGE_SAME_PAGE		4
148 #define	PAGE_NO_DIRECTION	5
149 
150 /*			PAGE DIRECTORY
151 			==============
152 */
153 
154 typedef	byte			page_dir_slot_t;
155 typedef page_dir_slot_t		page_dir_t;
156 
157 /* Offset of the directory start down from the page end. We call the
158 slot with the highest file address directory start, as it points to
159 the first record in the list of records. */
160 #define	PAGE_DIR		FIL_PAGE_DATA_END
161 
162 /* We define a slot in the page directory as two bytes */
163 #define	PAGE_DIR_SLOT_SIZE	2
164 
165 /* The offset of the physically lower end of the directory, counted from
166 page end, when the page is empty */
167 #define PAGE_EMPTY_DIR_START	(PAGE_DIR + 2 * PAGE_DIR_SLOT_SIZE)
168 
169 /* The maximum and minimum number of records owned by a directory slot. The
170 number may drop below the minimum in the first and the last slot in the
171 directory. */
172 #define PAGE_DIR_SLOT_MAX_N_OWNED	8
173 #define	PAGE_DIR_SLOT_MIN_N_OWNED	4
174 
175 /************************************************************//**
176 Gets the start of a page.
177 @return start of the page */
178 UNIV_INLINE
179 page_t*
180 page_align(
181 /*=======*/
182 	const void*	ptr)	/*!< in: pointer to page frame */
183 		MY_ATTRIBUTE((const));
184 /************************************************************//**
185 Gets the offset within a page.
186 @return offset from the start of the page */
187 UNIV_INLINE
188 ulint
189 page_offset(
190 /*========*/
191 	const void*	ptr)	/*!< in: pointer to page frame */
192 		MY_ATTRIBUTE((const));
193 /*************************************************************//**
194 Returns the max trx id field value. */
195 UNIV_INLINE
196 trx_id_t
197 page_get_max_trx_id(
198 /*================*/
199 	const page_t*	page);	/*!< in: page */
200 /*************************************************************//**
201 Sets the max trx id field value. */
202 void
203 page_set_max_trx_id(
204 /*================*/
205 	buf_block_t*	block,	/*!< in/out: page */
206 	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
207 	trx_id_t	trx_id,	/*!< in: transaction id */
208 	mtr_t*		mtr);	/*!< in/out: mini-transaction, or NULL */
209 /*************************************************************//**
210 Sets the max trx id field value if trx_id is bigger than the previous
211 value. */
212 UNIV_INLINE
213 void
214 page_update_max_trx_id(
215 /*===================*/
216 	buf_block_t*	block,	/*!< in/out: page */
217 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
218 				uncompressed part will be updated, or NULL */
219 	trx_id_t	trx_id,	/*!< in: transaction id */
220 	mtr_t*		mtr);	/*!< in/out: mini-transaction */
221 /*************************************************************//**
222 Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM).
223 @return SPLIT SEQUENCE NUMBER */
224 UNIV_INLINE
225 node_seq_t
226 page_get_ssn_id(
227 /*============*/
228 	const page_t*	page);	/*!< in: page */
229 /*************************************************************//**
230 Sets the RTREE SPLIT SEQUENCE NUMBER field value */
231 UNIV_INLINE
232 void
233 page_set_ssn_id(
234 /*============*/
235 	buf_block_t*	block,	/*!< in/out: page */
236 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
237 				uncompressed part will be updated, or NULL */
238 	node_seq_t	ssn_id,	/*!< in: split sequence id */
239 	mtr_t*		mtr);	/*!< in/out: mini-transaction */
240 
241 #endif /* !UNIV_INNOCHECKSUM */
242 /*************************************************************//**
243 Reads the given header field. */
244 UNIV_INLINE
245 ulint
246 page_header_get_field(
247 /*==================*/
248 	const page_t*	page,	/*!< in: page */
249 	ulint		field);	/*!< in: PAGE_N_DIR_SLOTS, ... */
250 
251 #ifndef UNIV_INNOCHECKSUM
252 /*************************************************************//**
253 Sets the given header field. */
254 UNIV_INLINE
255 void
256 page_header_set_field(
257 /*==================*/
258 	page_t*		page,	/*!< in/out: page */
259 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
260 				uncompressed part will be updated, or NULL */
261 	ulint		field,	/*!< in: PAGE_N_DIR_SLOTS, ... */
262 	ulint		val);	/*!< in: value */
263 /*************************************************************//**
264 Returns the offset stored in the given header field.
265 @return offset from the start of the page, or 0 */
266 UNIV_INLINE
267 ulint
268 page_header_get_offs(
269 /*=================*/
270 	const page_t*	page,	/*!< in: page */
271 	ulint		field)	/*!< in: PAGE_FREE, ... */
272 	MY_ATTRIBUTE((warn_unused_result));
273 
274 /*************************************************************//**
275 Returns the pointer stored in the given header field, or NULL. */
276 #define page_header_get_ptr(page, field)			\
277 	(page_header_get_offs(page, field)			\
278 	 ? page + page_header_get_offs(page, field) : NULL)
279 /*************************************************************//**
280 Sets the pointer stored in the given header field. */
281 UNIV_INLINE
282 void
283 page_header_set_ptr(
284 /*================*/
285 	page_t*		page,	/*!< in/out: page */
286 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
287 				uncompressed part will be updated, or NULL */
288 	ulint		field,	/*!< in/out: PAGE_FREE, ... */
289 	const byte*	ptr);	/*!< in: pointer or NULL*/
290 #ifndef UNIV_HOTBACKUP
291 /*************************************************************//**
292 Resets the last insert info field in the page header. Writes to mlog
293 about this operation. */
294 UNIV_INLINE
295 void
296 page_header_reset_last_insert(
297 /*==========================*/
298 	page_t*		page,	/*!< in: page */
299 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
300 				uncompressed part will be updated, or NULL */
301 	mtr_t*		mtr);	/*!< in: mtr */
302 #endif /* !UNIV_HOTBACKUP */
303 /************************************************************//**
304 Gets the offset of the first record on the page.
305 @return offset of the first record in record list, relative from page */
306 UNIV_INLINE
307 ulint
308 page_get_infimum_offset(
309 /*====================*/
310 	const page_t*	page);	/*!< in: page which must have record(s) */
311 /************************************************************//**
312 Gets the offset of the last record on the page.
313 @return offset of the last record in record list, relative from page */
314 UNIV_INLINE
315 ulint
316 page_get_supremum_offset(
317 /*=====================*/
318 	const page_t*	page);	/*!< in: page which must have record(s) */
319 #define page_get_infimum_rec(page) ((page) + page_get_infimum_offset(page))
320 #define page_get_supremum_rec(page) ((page) + page_get_supremum_offset(page))
321 
322 /************************************************************//**
323 Returns the nth record of the record list.
324 This is the inverse function of page_rec_get_n_recs_before().
325 @return nth record */
326 const rec_t*
327 page_rec_get_nth_const(
328 /*===================*/
329 	const page_t*	page,	/*!< in: page */
330 	ulint		nth)	/*!< in: nth record */
331 	MY_ATTRIBUTE((warn_unused_result));
332 /************************************************************//**
333 Returns the nth record of the record list.
334 This is the inverse function of page_rec_get_n_recs_before().
335 @return nth record */
336 UNIV_INLINE
337 rec_t*
338 page_rec_get_nth(
339 /*=============*/
340 	page_t*	page,	/*< in: page */
341 	ulint	nth)	/*!< in: nth record */
342 	MY_ATTRIBUTE((warn_unused_result));
343 
344 #ifndef UNIV_HOTBACKUP
345 /************************************************************//**
346 Returns the middle record of the records on the page. If there is an
347 even number of records in the list, returns the first record of the
348 upper half-list.
349 @return middle record */
350 UNIV_INLINE
351 rec_t*
352 page_get_middle_rec(
353 /*================*/
354 	page_t*	page)	/*!< in: page */
355 	MY_ATTRIBUTE((warn_unused_result));
356 #endif /* !UNIV_HOTBACKUP */
357 /*************************************************************//**
358 Gets the page number.
359 @return page number */
360 UNIV_INLINE
361 ulint
362 page_get_page_no(
363 /*=============*/
364 	const page_t*	page);	/*!< in: page */
365 /*************************************************************//**
366 Gets the tablespace identifier.
367 @return space id */
368 UNIV_INLINE
369 ulint
370 page_get_space_id(
371 /*==============*/
372 	const page_t*	page);	/*!< in: page */
373 /*************************************************************//**
374 Gets the number of user records on page (the infimum and supremum records
375 are not user records).
376 @return number of user records */
377 UNIV_INLINE
378 ulint
379 page_get_n_recs(
380 /*============*/
381 	const page_t*	page);	/*!< in: index page */
382 /***************************************************************//**
383 Returns the number of records before the given record in chain.
384 The number includes infimum and supremum records.
385 This is the inverse function of page_rec_get_nth().
386 @return number of records */
387 ulint
388 page_rec_get_n_recs_before(
389 /*=======================*/
390 	const rec_t*	rec);	/*!< in: the physical record */
391 /*************************************************************//**
392 Gets the number of records in the heap.
393 @return number of user records */
394 UNIV_INLINE
395 ulint
396 page_dir_get_n_heap(
397 /*================*/
398 	const page_t*	page);	/*!< in: index page */
399 /*************************************************************//**
400 Sets the number of records in the heap. */
401 UNIV_INLINE
402 void
403 page_dir_set_n_heap(
404 /*================*/
405 	page_t*		page,	/*!< in/out: index page */
406 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
407 				uncompressed part will be updated, or NULL.
408 				Note that the size of the dense page directory
409 				in the compressed page trailer is
410 				n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */
411 	ulint		n_heap);/*!< in: number of records */
412 /*************************************************************//**
413 Gets the number of dir slots in directory.
414 @return number of slots */
415 UNIV_INLINE
416 ulint
417 page_dir_get_n_slots(
418 /*=================*/
419 	const page_t*	page);	/*!< in: index page */
420 /*************************************************************//**
421 Sets the number of dir slots in directory. */
422 UNIV_INLINE
423 void
424 page_dir_set_n_slots(
425 /*=================*/
426 	page_t*		page,	/*!< in/out: page */
427 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
428 				uncompressed part will be updated, or NULL */
429 	ulint		n_slots);/*!< in: number of slots */
430 #ifdef UNIV_DEBUG
431 /*************************************************************//**
432 Gets pointer to nth directory slot.
433 @return pointer to dir slot */
434 UNIV_INLINE
435 page_dir_slot_t*
436 page_dir_get_nth_slot(
437 /*==================*/
438 	const page_t*	page,	/*!< in: index page */
439 	ulint		n);	/*!< in: position */
440 #else /* UNIV_DEBUG */
441 # define page_dir_get_nth_slot(page, n)			\
442 	((page) + (UNIV_PAGE_SIZE - PAGE_DIR		\
443 		   - (n + 1) * PAGE_DIR_SLOT_SIZE))
444 #endif /* UNIV_DEBUG */
445 /**************************************************************//**
446 Used to check the consistency of a record on a page.
447 @return TRUE if succeed */
448 UNIV_INLINE
449 ibool
450 page_rec_check(
451 /*===========*/
452 	const rec_t*	rec);	/*!< in: record */
453 /***************************************************************//**
454 Gets the record pointed to by a directory slot.
455 @return pointer to record */
456 UNIV_INLINE
457 const rec_t*
458 page_dir_slot_get_rec(
459 /*==================*/
460 	const page_dir_slot_t*	slot);	/*!< in: directory slot */
461 /***************************************************************//**
462 This is used to set the record offset in a directory slot. */
463 UNIV_INLINE
464 void
465 page_dir_slot_set_rec(
466 /*==================*/
467 	page_dir_slot_t* slot,	/*!< in: directory slot */
468 	rec_t*		 rec);	/*!< in: record on the page */
469 /***************************************************************//**
470 Gets the number of records owned by a directory slot.
471 @return number of records */
472 UNIV_INLINE
473 ulint
474 page_dir_slot_get_n_owned(
475 /*======================*/
476 	const page_dir_slot_t*	slot);	/*!< in: page directory slot */
477 /***************************************************************//**
478 This is used to set the owned records field of a directory slot. */
479 UNIV_INLINE
480 void
481 page_dir_slot_set_n_owned(
482 /*======================*/
483 	page_dir_slot_t*slot,	/*!< in/out: directory slot */
484 	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
485 	ulint		n);	/*!< in: number of records owned by the slot */
486 /************************************************************//**
487 Calculates the space reserved for directory slots of a given
488 number of records. The exact value is a fraction number
489 n * PAGE_DIR_SLOT_SIZE / PAGE_DIR_SLOT_MIN_N_OWNED, and it is
490 rounded upwards to an integer. */
491 UNIV_INLINE
492 ulint
493 page_dir_calc_reserved_space(
494 /*=========================*/
495 	ulint	n_recs);	/*!< in: number of records */
496 /***************************************************************//**
497 Looks for the directory slot which owns the given record.
498 @return the directory slot number */
499 ulint
500 page_dir_find_owner_slot(
501 /*=====================*/
502 	const rec_t*	rec);	/*!< in: the physical record */
503 /************************************************************//**
504 Determine whether the page is in new-style compact format.
505 @return nonzero if the page is in compact format, zero if it is in
506 old-style format */
507 UNIV_INLINE
508 ulint
509 page_is_comp(
510 /*=========*/
511 	const page_t*	page);	/*!< in: index page */
512 /************************************************************//**
513 TRUE if the record is on a page in compact format.
514 @return nonzero if in compact format */
515 UNIV_INLINE
516 ulint
517 page_rec_is_comp(
518 /*=============*/
519 	const rec_t*	rec);	/*!< in: record */
520 /***************************************************************//**
521 Returns the heap number of a record.
522 @return heap number */
523 UNIV_INLINE
524 ulint
525 page_rec_get_heap_no(
526 /*=================*/
527 	const rec_t*	rec);	/*!< in: the physical record */
528 /************************************************************//**
529 Determine whether the page is a B-tree leaf.
530 @return true if the page is a B-tree leaf (PAGE_LEVEL = 0) */
531 UNIV_INLINE
532 bool
533 page_is_leaf(
534 /*=========*/
535 	const page_t*	page)	/*!< in: page */
536 	MY_ATTRIBUTE((warn_unused_result));
537 /************************************************************//**
538 Determine whether the page is empty.
539 @return true if the page is empty (PAGE_N_RECS = 0) */
540 UNIV_INLINE
541 bool
542 page_is_empty(
543 /*==========*/
544 	const page_t*	page)	/*!< in: page */
545 	MY_ATTRIBUTE((warn_unused_result));
546 /** Determine whether a page is an index root page.
547 @param[in]	page	page frame
548 @return true if the page is a root page of an index */
549 UNIV_INLINE
550 bool
551 page_is_root(
552 	const page_t*	page)
553 	MY_ATTRIBUTE((warn_unused_result));
554 /************************************************************//**
555 Determine whether the page contains garbage.
556 @return true if the page contains garbage (PAGE_GARBAGE is not 0) */
557 UNIV_INLINE
558 bool
559 page_has_garbage(
560 /*=============*/
561 	const page_t*	page)	/*!< in: page */
562 	MY_ATTRIBUTE((warn_unused_result));
563 /************************************************************//**
564 Gets the pointer to the next record on the page.
565 @return pointer to next record */
566 UNIV_INLINE
567 const rec_t*
568 page_rec_get_next_low(
569 /*==================*/
570 	const rec_t*	rec,	/*!< in: pointer to record */
571 	ulint		comp);	/*!< in: nonzero=compact page layout */
572 /************************************************************//**
573 Gets the pointer to the next record on the page.
574 @return pointer to next record */
575 UNIV_INLINE
576 rec_t*
577 page_rec_get_next(
578 /*==============*/
579 	rec_t*	rec);	/*!< in: pointer to record */
580 /************************************************************//**
581 Gets the pointer to the next record on the page.
582 @return pointer to next record */
583 UNIV_INLINE
584 const rec_t*
585 page_rec_get_next_const(
586 /*====================*/
587 	const rec_t*	rec);	/*!< in: pointer to record */
588 /************************************************************//**
589 Gets the pointer to the next non delete-marked record on the page.
590 If all subsequent records are delete-marked, then this function
591 will return the supremum record.
592 @return pointer to next non delete-marked record or pointer to supremum */
593 UNIV_INLINE
594 const rec_t*
595 page_rec_get_next_non_del_marked(
596 /*=============================*/
597 	const rec_t*	rec);	/*!< in: pointer to record */
598 /************************************************************//**
599 Sets the pointer to the next record on the page. */
600 UNIV_INLINE
601 void
602 page_rec_set_next(
603 /*==============*/
604 	rec_t*		rec,	/*!< in: pointer to record,
605 				must not be page supremum */
606 	const rec_t*	next);	/*!< in: pointer to next record,
607 				must not be page infimum */
608 /************************************************************//**
609 Gets the pointer to the previous record.
610 @return pointer to previous record */
611 UNIV_INLINE
612 const rec_t*
613 page_rec_get_prev_const(
614 /*====================*/
615 	const rec_t*	rec);	/*!< in: pointer to record, must not be page
616 				infimum */
617 /************************************************************//**
618 Gets the pointer to the previous record.
619 @return pointer to previous record */
620 UNIV_INLINE
621 rec_t*
622 page_rec_get_prev(
623 /*==============*/
624 	rec_t*		rec);	/*!< in: pointer to record,
625 				must not be page infimum */
626 /************************************************************//**
627 TRUE if the record is a user record on the page.
628 @return TRUE if a user record */
629 UNIV_INLINE
630 ibool
631 page_rec_is_user_rec_low(
632 /*=====================*/
633 	ulint	offset)	/*!< in: record offset on page */
634 	MY_ATTRIBUTE((const));
635 /************************************************************//**
636 TRUE if the record is the supremum record on a page.
637 @return TRUE if the supremum record */
638 UNIV_INLINE
639 ibool
640 page_rec_is_supremum_low(
641 /*=====================*/
642 	ulint	offset)	/*!< in: record offset on page */
643 	MY_ATTRIBUTE((const));
644 /************************************************************//**
645 TRUE if the record is the infimum record on a page.
646 @return TRUE if the infimum record */
647 UNIV_INLINE
648 ibool
649 page_rec_is_infimum_low(
650 /*====================*/
651 	ulint	offset)	/*!< in: record offset on page */
652 	MY_ATTRIBUTE((const));
653 
654 /************************************************************//**
655 TRUE if the record is a user record on the page.
656 @return TRUE if a user record */
657 UNIV_INLINE
658 ibool
659 page_rec_is_user_rec(
660 /*=================*/
661 	const rec_t*	rec)	/*!< in: record */
662 	MY_ATTRIBUTE((warn_unused_result));
663 /************************************************************//**
664 TRUE if the record is the supremum record on a page.
665 @return TRUE if the supremum record */
666 UNIV_INLINE
667 ibool
668 page_rec_is_supremum(
669 /*=================*/
670 	const rec_t*	rec)	/*!< in: record */
671 	MY_ATTRIBUTE((warn_unused_result));
672 
673 /************************************************************//**
674 TRUE if the record is the infimum record on a page.
675 @return TRUE if the infimum record */
676 UNIV_INLINE
677 ibool
678 page_rec_is_infimum(
679 /*================*/
680 	const rec_t*	rec)	/*!< in: record */
681 	MY_ATTRIBUTE((warn_unused_result));
682 
683 /************************************************************//**
684 true if the record is the first user record on a page.
685 @return true if the first user record */
686 UNIV_INLINE
687 bool
688 page_rec_is_first(
689 /*==============*/
690 	const rec_t*	rec,	/*!< in: record */
691 	const page_t*	page)	/*!< in: page */
692 	MY_ATTRIBUTE((warn_unused_result));
693 
694 /************************************************************//**
695 true if the record is the second user record on a page.
696 @return true if the second user record */
697 UNIV_INLINE
698 bool
699 page_rec_is_second(
700 /*===============*/
701 	const rec_t*	rec,	/*!< in: record */
702 	const page_t*	page)	/*!< in: page */
703 	MY_ATTRIBUTE((warn_unused_result));
704 
705 /************************************************************//**
706 true if the record is the last user record on a page.
707 @return true if the last user record */
708 UNIV_INLINE
709 bool
710 page_rec_is_last(
711 /*=============*/
712 	const rec_t*	rec,	/*!< in: record */
713 	const page_t*	page)	/*!< in: page */
714 	MY_ATTRIBUTE((warn_unused_result));
715 
716 /************************************************************//**
717 true if distance between the records (measured in number of times we have to
718 move to the next record) is at most the specified value
719 @param[in]	left_rec	lefter record
720 @param[in]	right_rec	righter record
721 @param[in]	val		specified value to compare
722 @return true if the distance is smaller than the value */
723 UNIV_INLINE
724 bool
725 page_rec_distance_is_at_most(
726 /*=========================*/
727 	const rec_t*	left_rec,
728 	const rec_t*	right_rec,
729 	ulint		val)
730 	MY_ATTRIBUTE((warn_unused_result));
731 
732 /************************************************************//**
733 true if the record is the second last user record on a page.
734 @return true if the second last user record */
735 UNIV_INLINE
736 bool
737 page_rec_is_second_last(
738 /*====================*/
739 	const rec_t*	rec,	/*!< in: record */
740 	const page_t*	page)	/*!< in: page */
741 	MY_ATTRIBUTE((warn_unused_result));
742 
743 /***************************************************************//**
744 Looks for the record which owns the given record.
745 @return the owner record */
746 UNIV_INLINE
747 rec_t*
748 page_rec_find_owner_rec(
749 /*====================*/
750 	rec_t*	rec);	/*!< in: the physical record */
751 #ifndef UNIV_HOTBACKUP
752 /***********************************************************************//**
753 Write a 32-bit field in a data dictionary record. */
754 UNIV_INLINE
755 void
756 page_rec_write_field(
757 /*=================*/
758 	rec_t*	rec,	/*!< in/out: record to update */
759 	ulint	i,	/*!< in: index of the field to update */
760 	ulint	val,	/*!< in: value to write */
761 	mtr_t*	mtr);	/*!< in/out: mini-transaction */
762 #endif /* !UNIV_HOTBACKUP */
763 /************************************************************//**
764 Returns the maximum combined size of records which can be inserted on top
765 of record heap.
766 @return maximum combined size for inserted records */
767 UNIV_INLINE
768 ulint
769 page_get_max_insert_size(
770 /*=====================*/
771 	const page_t*	page,	/*!< in: index page */
772 	ulint		n_recs);/*!< in: number of records */
773 /************************************************************//**
774 Returns the maximum combined size of records which can be inserted on top
775 of record heap if page is first reorganized.
776 @return maximum combined size for inserted records */
777 UNIV_INLINE
778 ulint
779 page_get_max_insert_size_after_reorganize(
780 /*======================================*/
781 	const page_t*	page,	/*!< in: index page */
782 	ulint		n_recs);/*!< in: number of records */
783 /*************************************************************//**
784 Calculates free space if a page is emptied.
785 @return free space */
786 UNIV_INLINE
787 ulint
788 page_get_free_space_of_empty(
789 /*=========================*/
790 	ulint	comp)	/*!< in: nonzero=compact page format */
791 		MY_ATTRIBUTE((const));
792 /**********************************************************//**
793 Returns the base extra size of a physical record.  This is the
794 size of the fixed header, independent of the record size.
795 @return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */
796 UNIV_INLINE
797 ulint
798 page_rec_get_base_extra_size(
799 /*=========================*/
800 	const rec_t*	rec);	/*!< in: physical record */
801 /************************************************************//**
802 Returns the sum of the sizes of the records in the record list
803 excluding the infimum and supremum records.
804 @return data in bytes */
805 UNIV_INLINE
806 ulint
807 page_get_data_size(
808 /*===============*/
809 	const page_t*	page);	/*!< in: index page */
810 /************************************************************//**
811 Allocates a block of memory from the head of the free list
812 of an index page. */
813 UNIV_INLINE
814 void
815 page_mem_alloc_free(
816 /*================*/
817 	page_t*		page,	/*!< in/out: index page */
818 	page_zip_des_t*	page_zip,/*!< in/out: compressed page with enough
819 				space available for inserting the record,
820 				or NULL */
821 	rec_t*		next_rec,/*!< in: pointer to the new head of the
822 				free record list */
823 	ulint		need);	/*!< in: number of bytes allocated */
824 /************************************************************//**
825 Allocates a block of memory from the heap of an index page.
826 @return pointer to start of allocated buffer, or NULL if allocation fails */
827 byte*
828 page_mem_alloc_heap(
829 /*================*/
830 	page_t*		page,	/*!< in/out: index page */
831 	page_zip_des_t*	page_zip,/*!< in/out: compressed page with enough
832 				space available for inserting the record,
833 				or NULL */
834 	ulint		need,	/*!< in: total number of bytes needed */
835 	ulint*		heap_no);/*!< out: this contains the heap number
836 				of the allocated record
837 				if allocation succeeds */
838 /************************************************************//**
839 Puts a record to free list. */
840 UNIV_INLINE
841 void
842 page_mem_free(
843 /*==========*/
844 	page_t*			page,	/*!< in/out: index page */
845 	page_zip_des_t*		page_zip,/*!< in/out: compressed page,
846 					 or NULL */
847 	rec_t*			rec,	/*!< in: pointer to the (origin of)
848 					record */
849 	const dict_index_t*	index,	/*!< in: index of rec */
850 	const ulint*		offsets);/*!< in: array returned by
851 					 rec_get_offsets() */
852 /**********************************************************//**
853 Create an uncompressed B-tree index page.
854 @return pointer to the page */
855 page_t*
856 page_create(
857 /*========*/
858 	buf_block_t*	block,		/*!< in: a buffer block where the
859 					page is created */
860 	mtr_t*		mtr,		/*!< in: mini-transaction handle */
861 	ulint		comp,		/*!< in: nonzero=compact page format */
862 	bool		is_rtree);	/*!< in: if creating R-tree page */
863 /**********************************************************//**
864 Create a compressed B-tree index page.
865 @return pointer to the page */
866 page_t*
867 page_create_zip(
868 /*============*/
869 	buf_block_t*		block,		/*!< in/out: a buffer frame
870 						where the page is created */
871 	dict_index_t*		index,		/*!< in: the index of the
872 						page, or NULL when applying
873 						TRUNCATE log
874 						record during recovery */
875 	ulint			level,		/*!< in: the B-tree level of
876 						the page */
877 	trx_id_t		max_trx_id,	/*!< in: PAGE_MAX_TRX_ID */
878 	const redo_page_compress_t* page_comp_info,
879 						/*!< in: used for applying
880 						TRUNCATE log
881 						record during recovery */
882 	mtr_t*			mtr);		/*!< in/out: mini-transaction
883 						handle */
884 /**********************************************************//**
885 Empty a previously created B-tree index page. */
886 void
887 page_create_empty(
888 /*==============*/
889 	buf_block_t*	block,	/*!< in/out: B-tree block */
890 	dict_index_t*	index,	/*!< in: the index of the page */
891 	mtr_t*		mtr);	/*!< in/out: mini-transaction */
892 /*************************************************************//**
893 Differs from page_copy_rec_list_end, because this function does not
894 touch the lock table and max trx id on page or compress the page.
895 
896 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
897 if new_block is a compressed leaf page in a secondary index.
898 This has to be done either within the same mini-transaction,
899 or by invoking ibuf_reset_free_bits() before mtr_commit(). */
900 void
901 page_copy_rec_list_end_no_locks(
902 /*============================*/
903 	buf_block_t*	new_block,	/*!< in: index page to copy to */
904 	buf_block_t*	block,		/*!< in: index page of rec */
905 	rec_t*		rec,		/*!< in: record on page */
906 	dict_index_t*	index,		/*!< in: record descriptor */
907 	mtr_t*		mtr);		/*!< in: mtr */
908 /*************************************************************//**
909 Copies records from page to new_page, from the given record onward,
910 including that record. Infimum and supremum records are not copied.
911 The records are copied to the start of the record list on new_page.
912 
913 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
914 if new_block is a compressed leaf page in a secondary index.
915 This has to be done either within the same mini-transaction,
916 or by invoking ibuf_reset_free_bits() before mtr_commit().
917 
918 @return pointer to the original successor of the infimum record on
919 new_page, or NULL on zip overflow (new_block will be decompressed) */
920 rec_t*
921 page_copy_rec_list_end(
922 /*===================*/
923 	buf_block_t*	new_block,	/*!< in/out: index page to copy to */
924 	buf_block_t*	block,		/*!< in: index page containing rec */
925 	rec_t*		rec,		/*!< in: record on page */
926 	dict_index_t*	index,		/*!< in: record descriptor */
927 	mtr_t*		mtr);		/*!< in: mtr */
928 /*************************************************************//**
929 Copies records from page to new_page, up to the given record, NOT
930 including that record. Infimum and supremum records are not copied.
931 The records are copied to the end of the record list on new_page.
932 
933 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
934 if new_block is a compressed leaf page in a secondary index.
935 This has to be done either within the same mini-transaction,
936 or by invoking ibuf_reset_free_bits() before mtr_commit().
937 
938 @return pointer to the original predecessor of the supremum record on
939 new_page, or NULL on zip overflow (new_block will be decompressed) */
940 rec_t*
941 page_copy_rec_list_start(
942 /*=====================*/
943 	buf_block_t*	new_block,	/*!< in/out: index page to copy to */
944 	buf_block_t*	block,		/*!< in: index page containing rec */
945 	rec_t*		rec,		/*!< in: record on page */
946 	dict_index_t*	index,		/*!< in: record descriptor */
947 	mtr_t*		mtr);		/*!< in: mtr */
948 /*************************************************************//**
949 Deletes records from a page from a given record onward, including that record.
950 The infimum and supremum records are not deleted. */
951 void
952 page_delete_rec_list_end(
953 /*=====================*/
954 	rec_t*		rec,	/*!< in: pointer to record on page */
955 	buf_block_t*	block,	/*!< in: buffer block of the page */
956 	dict_index_t*	index,	/*!< in: record descriptor */
957 	ulint		n_recs,	/*!< in: number of records to delete,
958 				or ULINT_UNDEFINED if not known */
959 	ulint		size,	/*!< in: the sum of the sizes of the
960 				records in the end of the chain to
961 				delete, or ULINT_UNDEFINED if not known */
962 	mtr_t*		mtr);	/*!< in: mtr */
963 /*************************************************************//**
964 Deletes records from page, up to the given record, NOT including
965 that record. Infimum and supremum records are not deleted. */
966 void
967 page_delete_rec_list_start(
968 /*=======================*/
969 	rec_t*		rec,	/*!< in: record on page */
970 	buf_block_t*	block,	/*!< in: buffer block of the page */
971 	dict_index_t*	index,	/*!< in: record descriptor */
972 	mtr_t*		mtr);	/*!< in: mtr */
973 /*************************************************************//**
974 Moves record list end to another page. Moved records include
975 split_rec.
976 
977 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
978 if new_block is a compressed leaf page in a secondary index.
979 This has to be done either within the same mini-transaction,
980 or by invoking ibuf_reset_free_bits() before mtr_commit().
981 
982 @return TRUE on success; FALSE on compression failure (new_block will
983 be decompressed) */
984 ibool
985 page_move_rec_list_end(
986 /*===================*/
987 	buf_block_t*	new_block,	/*!< in/out: index page where to move */
988 	buf_block_t*	block,		/*!< in: index page from where to move */
989 	rec_t*		split_rec,	/*!< in: first record to move */
990 	dict_index_t*	index,		/*!< in: record descriptor */
991 	mtr_t*		mtr);		/*!< in: mtr */
992 /*************************************************************//**
993 Moves record list start to another page. Moved records do not include
994 split_rec.
995 
996 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
997 if new_block is a compressed leaf page in a secondary index.
998 This has to be done either within the same mini-transaction,
999 or by invoking ibuf_reset_free_bits() before mtr_commit().
1000 
1001 @return TRUE on success; FALSE on compression failure */
1002 ibool
1003 page_move_rec_list_start(
1004 /*=====================*/
1005 	buf_block_t*	new_block,	/*!< in/out: index page where to move */
1006 	buf_block_t*	block,		/*!< in/out: page containing split_rec */
1007 	rec_t*		split_rec,	/*!< in: first record not to move */
1008 	dict_index_t*	index,		/*!< in: record descriptor */
1009 	mtr_t*		mtr);		/*!< in: mtr */
1010 /****************************************************************//**
1011 Splits a directory slot which owns too many records. */
1012 void
1013 page_dir_split_slot(
1014 /*================*/
1015 	page_t*		page,	/*!< in: index page */
1016 	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
1017 				uncompressed part will be written, or NULL */
1018 	ulint		slot_no);/*!< in: the directory slot */
1019 /*************************************************************//**
1020 Tries to balance the given directory slot with too few records
1021 with the upper neighbor, so that there are at least the minimum number
1022 of records owned by the slot; this may result in the merging of
1023 two slots. */
1024 void
1025 page_dir_balance_slot(
1026 /*==================*/
1027 	page_t*		page,	/*!< in/out: index page */
1028 	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
1029 	ulint		slot_no);/*!< in: the directory slot */
1030 /**********************************************************//**
1031 Parses a log record of a record list end or start deletion.
1032 @return end of log record or NULL */
1033 byte*
1034 page_parse_delete_rec_list(
1035 /*=======================*/
1036 	mlog_id_t	type,	/*!< in: MLOG_LIST_END_DELETE,
1037 				MLOG_LIST_START_DELETE,
1038 				MLOG_COMP_LIST_END_DELETE or
1039 				MLOG_COMP_LIST_START_DELETE */
1040 	byte*		ptr,	/*!< in: buffer */
1041 	byte*		end_ptr,/*!< in: buffer end */
1042 	buf_block_t*	block,	/*!< in/out: buffer block or NULL */
1043 	dict_index_t*	index,	/*!< in: record descriptor */
1044 	mtr_t*		mtr);	/*!< in: mtr or NULL */
1045 /** Parses a redo log record of creating a page.
1046 @param[in,out]	block	buffer block, or NULL
1047 @param[in]	comp	nonzero=compact page format
1048 @param[in]	is_rtree whether it is rtree page */
1049 void
1050 page_parse_create(
1051 	buf_block_t*	block,
1052 	ulint		comp,
1053 	bool		is_rtree);
1054 #ifndef UNIV_HOTBACKUP
1055 /************************************************************//**
1056 Prints record contents including the data relevant only in
1057 the index page context. */
1058 void
1059 page_rec_print(
1060 /*===========*/
1061 	const rec_t*	rec,	/*!< in: physical record */
1062 	const ulint*	offsets);/*!< in: record descriptor */
1063 # ifdef UNIV_BTR_PRINT
1064 /***************************************************************//**
1065 This is used to print the contents of the directory for
1066 debugging purposes. */
1067 void
1068 page_dir_print(
1069 /*===========*/
1070 	page_t*	page,	/*!< in: index page */
1071 	ulint	pr_n);	/*!< in: print n first and n last entries */
1072 /***************************************************************//**
1073 This is used to print the contents of the page record list for
1074 debugging purposes. */
1075 void
1076 page_print_list(
1077 /*============*/
1078 	buf_block_t*	block,	/*!< in: index page */
1079 	dict_index_t*	index,	/*!< in: dictionary index of the page */
1080 	ulint		pr_n);	/*!< in: print n first and n last entries */
1081 /***************************************************************//**
1082 Prints the info in a page header. */
1083 void
1084 page_header_print(
1085 /*==============*/
1086 	const page_t*	page);	/*!< in: index page */
1087 /***************************************************************//**
1088 This is used to print the contents of the page for
1089 debugging purposes. */
1090 void
1091 page_print(
1092 /*=======*/
1093 	buf_block_t*	block,	/*!< in: index page */
1094 	dict_index_t*	index,	/*!< in: dictionary index of the page */
1095 	ulint		dn,	/*!< in: print dn first and last entries
1096 				in directory */
1097 	ulint		rn);	/*!< in: print rn first and last records
1098 				in directory */
1099 # endif /* UNIV_BTR_PRINT */
1100 #endif /* !UNIV_HOTBACKUP */
1101 /***************************************************************//**
1102 The following is used to validate a record on a page. This function
1103 differs from rec_validate as it can also check the n_owned field and
1104 the heap_no field.
1105 @return TRUE if ok */
1106 ibool
1107 page_rec_validate(
1108 /*==============*/
1109 	const rec_t*	rec,	/*!< in: physical record */
1110 	const ulint*	offsets);/*!< in: array returned by rec_get_offsets() */
1111 #ifdef UNIV_DEBUG
1112 /***************************************************************//**
1113 Checks that the first directory slot points to the infimum record and
1114 the last to the supremum. This function is intended to track if the
1115 bug fixed in 4.0.14 has caused corruption to users' databases. */
1116 void
1117 page_check_dir(
1118 /*===========*/
1119 	const page_t*	page);	/*!< in: index page */
1120 #endif /* UNIV_DEBUG */
1121 /***************************************************************//**
1122 This function checks the consistency of an index page when we do not
1123 know the index. This is also resilient so that this should never crash
1124 even if the page is total garbage.
1125 @return TRUE if ok */
1126 ibool
1127 page_simple_validate_old(
1128 /*=====================*/
1129 	const page_t*	page);	/*!< in: index page in ROW_FORMAT=REDUNDANT */
1130 /***************************************************************//**
1131 This function checks the consistency of an index page when we do not
1132 know the index. This is also resilient so that this should never crash
1133 even if the page is total garbage.
1134 @return TRUE if ok */
1135 ibool
1136 page_simple_validate_new(
1137 /*=====================*/
1138 	const page_t*	page);	/*!< in: index page in ROW_FORMAT!=REDUNDANT */
1139 /***************************************************************//**
1140 This function checks the consistency of an index page.
1141 @return TRUE if ok */
1142 ibool
1143 page_validate(
1144 /*==========*/
1145 	const page_t*	page,	/*!< in: index page */
1146 	dict_index_t*	index);	/*!< in: data dictionary index containing
1147 				the page record type definition */
1148 /***************************************************************//**
1149 Looks in the page record list for a record with the given heap number.
1150 @return record, NULL if not found */
1151 const rec_t*
1152 page_find_rec_with_heap_no(
1153 /*=======================*/
1154 	const page_t*	page,	/*!< in: index page */
1155 	ulint		heap_no);/*!< in: heap number */
1156 /** Get the last non-delete-marked record on a page.
1157 @param[in]	page	index tree leaf page
1158 @return the last record, not delete-marked
1159 @retval infimum record if all records are delete-marked */
1160 const rec_t*
1161 page_find_rec_max_not_deleted(
1162 	const page_t*	page);
1163 
1164 /** Issue a warning when the checksum that is stored in the page is valid,
1165 but different than the global setting innodb_checksum_algorithm.
1166 @param[in]	current_algo	current checksum algorithm
1167 @param[in]	page_checksum	page valid checksum
1168 @param[in]	page_id		page identifier */
1169 void
1170 page_warn_strict_checksum(
1171 	srv_checksum_algorithm_t	curr_algo,
1172 	srv_checksum_algorithm_t	page_checksum,
1173 	const page_id_t&		page_id);
1174 
1175 /** This function checks if the page in which record is present is a
1176 non-leaf node of a spatial index.
1177 param[in]       rec     Btree record
1178 param[in]       index   index
1179 @return TRUE if ok */
1180 bool page_is_spatial_non_leaf(
1181 /*==========================*/
1182 	const rec_t* rec,/*!< in: Btree record */
1183 	dict_index_t* index);/*!< in: index */
1184 
1185 #ifdef UNIV_MATERIALIZE
1186 #undef UNIV_INLINE
1187 #define UNIV_INLINE  UNIV_INLINE_ORIGINAL
1188 #endif
1189 
1190 #endif /* !UNIV_INNOCHECKSUM */
1191 #ifndef UNIV_NONINL
1192 #include "page0page.ic"
1193 #endif
1194 
1195 #endif
1196