1/*****************************************************************************
2
3Copyright (c) 1994, 2021, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License, version 2.0,
7as published by the Free Software Foundation.
8
9This program is also distributed with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation.  The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have included with MySQL.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License, version 2.0, for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25*****************************************************************************/
26
27/**************************************************//**
28@file include/page0page.ic
29Index page routines
30
31Created 2/2/1994 Heikki Tuuri
32*******************************************************/
33
34#include "mach0data.h"
35#ifndef UNIV_INNOCHECKSUM
36#ifdef UNIV_DEBUG
37# include "log0recv.h"
38#endif /* !UNIV_DEBUG */
39#ifndef UNIV_HOTBACKUP
40# include "rem0cmp.h"
41#endif /* !UNIV_HOTBACKUP */
42#include "mtr0log.h"
43#include "page0zip.h"
44
45#ifdef UNIV_MATERIALIZE
46#undef UNIV_INLINE
47#define UNIV_INLINE
48#endif
49
50/************************************************************//**
51Gets the start of a page.
52@return start of the page */
53UNIV_INLINE
54page_t*
55page_align(
56/*=======*/
57	const void*	ptr)	/*!< in: pointer to page frame */
58{
59	return((page_t*) ut_align_down(ptr, UNIV_PAGE_SIZE));
60}
61/************************************************************//**
62Gets the offset within a page.
63@return offset from the start of the page */
64UNIV_INLINE
65ulint
66page_offset(
67/*========*/
68	const void*	ptr)	/*!< in: pointer to page frame */
69{
70	return(ut_align_offset(ptr, UNIV_PAGE_SIZE));
71}
72/*************************************************************//**
73Returns the max trx id field value. */
74UNIV_INLINE
75trx_id_t
76page_get_max_trx_id(
77/*================*/
78	const page_t*	page)	/*!< in: page */
79{
80	ut_ad(page);
81
82	return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID));
83}
84
85/*************************************************************//**
86Sets the max trx id field value if trx_id is bigger than the previous
87value. */
88UNIV_INLINE
89void
90page_update_max_trx_id(
91/*===================*/
92	buf_block_t*	block,	/*!< in/out: page */
93	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
94				uncompressed part will be updated, or NULL */
95	trx_id_t	trx_id,	/*!< in: transaction id */
96	mtr_t*		mtr)	/*!< in/out: mini-transaction */
97{
98	ut_ad(block);
99	ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
100	/* During crash recovery, this function may be called on
101	something else than a leaf page of a secondary index or the
102	insert buffer index tree (dict_index_is_sec_or_ibuf() returns
103	TRUE for the dummy indexes constructed during redo log
104	application).  In that case, PAGE_MAX_TRX_ID is unused,
105	and trx_id is usually zero. */
106	ut_ad(trx_id || recv_recovery_is_on());
107	ut_ad(page_is_leaf(buf_block_get_frame(block)));
108
109	if (page_get_max_trx_id(buf_block_get_frame(block)) < trx_id) {
110
111		page_set_max_trx_id(block, page_zip, trx_id, mtr);
112	}
113}
114
115/*************************************************************//**
116Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM).
117@return	SPLIT SEQUENCE NUMBER */
118UNIV_INLINE
119node_seq_t
120page_get_ssn_id(
121/*============*/
122	const page_t*	page)	/*!< in: page */
123{
124	ut_ad(page);
125
126	return(static_cast<node_seq_t>(
127		mach_read_from_8(page + FIL_RTREE_SPLIT_SEQ_NUM)));
128}
129
130/*************************************************************//**
131Sets the RTREE SPLIT SEQUENCE NUMBER field value */
132UNIV_INLINE
133void
134page_set_ssn_id(
135/*============*/
136	buf_block_t*	block,	/*!< in/out: page */
137	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
138				uncompressed part will be updated, or NULL */
139	node_seq_t	ssn_id,	/*!< in: transaction id */
140	mtr_t*		mtr)	/*!< in/out: mini-transaction */
141{
142	page_t*	page = buf_block_get_frame(block);
143#ifndef UNIV_HOTBACKUP
144	ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_SX_FIX)
145	      || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
146#endif /* !UNIV_HOTBACKUP */
147
148	if (page_zip) {
149		mach_write_to_8(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id);
150		page_zip_write_header(page_zip,
151				      page + FIL_RTREE_SPLIT_SEQ_NUM,
152				      8, mtr);
153#ifndef UNIV_HOTBACKUP
154	} else if (mtr) {
155		mlog_write_ull(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id, mtr);
156#endif /* !UNIV_HOTBACKUP */
157	} else {
158		mach_write_to_8(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id);
159	}
160}
161
162#endif /* !UNIV_INNOCHECKSUM */
163
164/*************************************************************//**
165Reads the given header field. */
166UNIV_INLINE
167ulint
168page_header_get_field(
169/*==================*/
170	const page_t*	page,	/*!< in: page */
171	ulint		field)	/*!< in: PAGE_LEVEL, ... */
172{
173	ut_ad(page);
174	ut_ad(field <= PAGE_INDEX_ID);
175
176	return(mach_read_from_2(page + PAGE_HEADER + field));
177}
178
179#ifndef UNIV_INNOCHECKSUM
180
181/*************************************************************//**
182Sets the given header field. */
183UNIV_INLINE
184void
185page_header_set_field(
186/*==================*/
187	page_t*		page,	/*!< in/out: page */
188	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
189				uncompressed part will be updated, or NULL */
190	ulint		field,	/*!< in: PAGE_N_DIR_SLOTS, ... */
191	ulint		val)	/*!< in: value */
192{
193	ut_ad(page);
194	ut_ad(field <= PAGE_N_RECS);
195	ut_ad(field == PAGE_N_HEAP || val < UNIV_PAGE_SIZE);
196	ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < UNIV_PAGE_SIZE);
197
198	mach_write_to_2(page + PAGE_HEADER + field, val);
199	if (page_zip) {
200		page_zip_write_header(page_zip,
201				      page + PAGE_HEADER + field, 2, NULL);
202	}
203}
204
205/*************************************************************//**
206Returns the offset stored in the given header field.
207@return offset from the start of the page, or 0 */
208UNIV_INLINE
209ulint
210page_header_get_offs(
211/*=================*/
212	const page_t*	page,	/*!< in: page */
213	ulint		field)	/*!< in: PAGE_FREE, ... */
214{
215	ulint	offs;
216
217	ut_ad(page);
218	ut_ad((field == PAGE_FREE)
219	      || (field == PAGE_LAST_INSERT)
220	      || (field == PAGE_HEAP_TOP));
221
222	offs = page_header_get_field(page, field);
223
224	ut_ad((field != PAGE_HEAP_TOP) || offs);
225
226	return(offs);
227}
228
229/*************************************************************//**
230Sets the pointer stored in the given header field. */
231UNIV_INLINE
232void
233page_header_set_ptr(
234/*================*/
235	page_t*		page,	/*!< in: page */
236	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
237				uncompressed part will be updated, or NULL */
238	ulint		field,	/*!< in: PAGE_FREE, ... */
239	const byte*	ptr)	/*!< in: pointer or NULL*/
240{
241	ulint	offs;
242
243	ut_ad(page);
244	ut_ad((field == PAGE_FREE)
245	      || (field == PAGE_LAST_INSERT)
246	      || (field == PAGE_HEAP_TOP));
247
248	if (ptr == NULL) {
249		offs = 0;
250	} else {
251		offs = ptr - page;
252	}
253
254	ut_ad((field != PAGE_HEAP_TOP) || offs);
255
256	page_header_set_field(page, page_zip, field, offs);
257}
258
259#ifndef UNIV_HOTBACKUP
260/*************************************************************//**
261Resets the last insert info field in the page header. Writes to mlog
262about this operation. */
263UNIV_INLINE
264void
265page_header_reset_last_insert(
266/*==========================*/
267	page_t*		page,	/*!< in/out: page */
268	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
269				uncompressed part will be updated, or NULL */
270	mtr_t*		mtr)	/*!< in: mtr */
271{
272	ut_ad(page != NULL);
273	ut_ad(mtr != NULL);
274
275	if (page_zip) {
276		mach_write_to_2(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0);
277		page_zip_write_header(page_zip,
278				      page + (PAGE_HEADER + PAGE_LAST_INSERT),
279				      2, mtr);
280	} else {
281		mlog_write_ulint(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0,
282				 MLOG_2BYTES, mtr);
283	}
284}
285#endif /* !UNIV_HOTBACKUP */
286
287/************************************************************//**
288Determine whether the page is in new-style compact format.
289@return nonzero if the page is in compact format, zero if it is in
290old-style format */
291UNIV_INLINE
292ulint
293page_is_comp(
294/*=========*/
295	const page_t*	page)	/*!< in: index page */
296{
297	return(page_header_get_field(page, PAGE_N_HEAP) & 0x8000);
298}
299
300/************************************************************//**
301TRUE if the record is on a page in compact format.
302@return nonzero if in compact format */
303UNIV_INLINE
304ulint
305page_rec_is_comp(
306/*=============*/
307	const rec_t*	rec)	/*!< in: record */
308{
309	return(page_is_comp(page_align(rec)));
310}
311
312/***************************************************************//**
313Returns the heap number of a record.
314@return heap number */
315UNIV_INLINE
316ulint
317page_rec_get_heap_no(
318/*=================*/
319	const rec_t*	rec)	/*!< in: the physical record */
320{
321	if (page_rec_is_comp(rec)) {
322		return(rec_get_heap_no_new(rec));
323	} else {
324		return(rec_get_heap_no_old(rec));
325	}
326}
327
328/************************************************************//**
329Determine whether the page is a B-tree leaf.
330@return true if the page is a B-tree leaf (PAGE_LEVEL = 0) */
331UNIV_INLINE
332bool
333page_is_leaf(
334/*=========*/
335	const page_t*	page)	/*!< in: page */
336{
337	if (!page) {
338		return(false);
339	}
340	return(!*(const uint16*) (page + (PAGE_HEADER + PAGE_LEVEL)));
341}
342
343/************************************************************//**
344Determine whether the page is empty.
345@return true if the page is empty (PAGE_N_RECS = 0) */
346UNIV_INLINE
347bool
348page_is_empty(
349/*==========*/
350	const page_t*	page)	/*!< in: page */
351{
352	return(!*(const uint16*) (page + (PAGE_HEADER + PAGE_N_RECS)));
353}
354
355/** Determine whether a page is an index root page.
356@param[in]	page	page frame
357@return true if the page is a root page of an index */
358UNIV_INLINE
359bool
360page_is_root(
361	const page_t*	page)
362{
363#if FIL_PAGE_PREV % 8
364# error FIL_PAGE_PREV must be 64-bit aligned
365#endif
366#if FIL_PAGE_NEXT != FIL_PAGE_PREV + 4
367# error FIL_PAGE_NEXT must be adjacent to FIL_PAGE_PREV
368#endif
369#if FIL_NULL != 0xffffffff
370# error FIL_NULL != 0xffffffff
371#endif
372	/* Check that this is an index page and both the PREV and NEXT
373	pointers are FIL_NULL, because the root page does not have any
374	siblings. */
375	return(fil_page_index_page_check(page)
376	       && *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_PREV)
377	       == IB_UINT64_MAX);
378}
379
380/************************************************************//**
381Determine whether the page contains garbage.
382@return true if the page contains garbage (PAGE_GARBAGE is not 0) */
383UNIV_INLINE
384bool
385page_has_garbage(
386/*=============*/
387	const page_t*	page)	/*!< in: page */
388{
389	return(!!*(const uint16*) (page + (PAGE_HEADER + PAGE_GARBAGE)));
390}
391
392/************************************************************//**
393Gets the offset of the first record on the page.
394@return offset of the first record in record list, relative from page */
395UNIV_INLINE
396ulint
397page_get_infimum_offset(
398/*====================*/
399	const page_t*	page)	/*!< in: page which must have record(s) */
400{
401	ut_ad(page);
402	ut_ad(!page_offset(page));
403
404	if (page_is_comp(page)) {
405		return(PAGE_NEW_INFIMUM);
406	} else {
407		return(PAGE_OLD_INFIMUM);
408	}
409}
410
411/************************************************************//**
412Gets the offset of the last record on the page.
413@return offset of the last record in record list, relative from page */
414UNIV_INLINE
415ulint
416page_get_supremum_offset(
417/*=====================*/
418	const page_t*	page)	/*!< in: page which must have record(s) */
419{
420	ut_ad(page);
421	ut_ad(!page_offset(page));
422
423	if (page_is_comp(page)) {
424		return(PAGE_NEW_SUPREMUM);
425	} else {
426		return(PAGE_OLD_SUPREMUM);
427	}
428}
429
430/************************************************************//**
431TRUE if the record is a user record on the page.
432@return TRUE if a user record */
433UNIV_INLINE
434ibool
435page_rec_is_user_rec_low(
436/*=====================*/
437	ulint	offset)	/*!< in: record offset on page */
438{
439	ut_ad(offset >= PAGE_NEW_INFIMUM);
440#if PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM
441# error "PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM"
442#endif
443#if PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM
444# error "PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM"
445#endif
446#if PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM
447# error "PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM"
448#endif
449#if PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM
450# error "PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM"
451#endif
452#if PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END
453# error "PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END"
454#endif
455#if PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END
456# error "PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END"
457#endif
458	ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
459
460	return(offset != PAGE_NEW_SUPREMUM
461	       && offset != PAGE_NEW_INFIMUM
462	       && offset != PAGE_OLD_INFIMUM
463	       && offset != PAGE_OLD_SUPREMUM);
464}
465
466/************************************************************//**
467TRUE if the record is the supremum record on a page.
468@return TRUE if the supremum record */
469UNIV_INLINE
470ibool
471page_rec_is_supremum_low(
472/*=====================*/
473	ulint	offset)	/*!< in: record offset on page */
474{
475	ut_ad(offset >= PAGE_NEW_INFIMUM);
476	ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
477
478	return(offset == PAGE_NEW_SUPREMUM
479	       || offset == PAGE_OLD_SUPREMUM);
480}
481
482/************************************************************//**
483TRUE if the record is the infimum record on a page.
484@return TRUE if the infimum record */
485UNIV_INLINE
486ibool
487page_rec_is_infimum_low(
488/*====================*/
489	ulint	offset)	/*!< in: record offset on page */
490{
491	ut_ad(offset >= PAGE_NEW_INFIMUM);
492	ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
493
494	return(offset == PAGE_NEW_INFIMUM || offset == PAGE_OLD_INFIMUM);
495}
496
497/************************************************************//**
498TRUE if the record is a user record on the page.
499@return TRUE if a user record */
500UNIV_INLINE
501ibool
502page_rec_is_user_rec(
503/*=================*/
504	const rec_t*	rec)	/*!< in: record */
505{
506	ut_ad(page_rec_check(rec));
507
508	return(page_rec_is_user_rec_low(page_offset(rec)));
509}
510
511/************************************************************//**
512TRUE if the record is the supremum record on a page.
513@return TRUE if the supremum record */
514UNIV_INLINE
515ibool
516page_rec_is_supremum(
517/*=================*/
518	const rec_t*	rec)	/*!< in: record */
519{
520	ut_ad(page_rec_check(rec));
521
522	return(page_rec_is_supremum_low(page_offset(rec)));
523}
524
525/************************************************************//**
526TRUE if the record is the infimum record on a page.
527@return TRUE if the infimum record */
528UNIV_INLINE
529ibool
530page_rec_is_infimum(
531/*================*/
532	const rec_t*	rec)	/*!< in: record */
533{
534	ut_ad(page_rec_check(rec));
535
536	return(page_rec_is_infimum_low(page_offset(rec)));
537}
538
539/************************************************************//**
540true if the record is the first user record on a page.
541@return true if the first user record */
542UNIV_INLINE
543bool
544page_rec_is_first(
545/*==============*/
546	const rec_t*	rec,	/*!< in: record */
547	const page_t*	page)	/*!< in: page */
548{
549	ut_ad(page_get_n_recs(page) > 0);
550
551	return(page_rec_get_next_const(page_get_infimum_rec(page)) == rec);
552}
553
554/************************************************************//**
555true if the record is the second user record on a page.
556@return true if the second user record */
557UNIV_INLINE
558bool
559page_rec_is_second(
560/*===============*/
561	const rec_t*	rec,	/*!< in: record */
562	const page_t*	page)	/*!< in: page */
563{
564	ut_ad(page_get_n_recs(page) > 1);
565
566	return(page_rec_get_next_const(
567		page_rec_get_next_const(page_get_infimum_rec(page))) == rec);
568}
569
570/************************************************************//**
571true if the record is the last user record on a page.
572@return true if the last user record */
573UNIV_INLINE
574bool
575page_rec_is_last(
576/*=============*/
577	const rec_t*	rec,	/*!< in: record */
578	const page_t*	page)	/*!< in: page */
579{
580	ut_ad(page_get_n_recs(page) > 0);
581
582	return(page_rec_get_next_const(rec) == page_get_supremum_rec(page));
583}
584
585/************************************************************//**
586true if distance between the records (measured in number of times we have to
587move to the next record) is at most the specified value */
588UNIV_INLINE
589bool
590page_rec_distance_is_at_most(
591/*=========================*/
592	const rec_t*	left_rec,
593	const rec_t*	right_rec,
594	ulint		val)
595{
596	for (ulint i = 0; i <= val; i++) {
597		if (left_rec == right_rec) {
598			return (true);
599		}
600		left_rec = page_rec_get_next_const(left_rec);
601	}
602	return (false);
603}
604
605/************************************************************//**
606true if the record is the second last user record on a page.
607@return true if the second last user record */
608UNIV_INLINE
609bool
610page_rec_is_second_last(
611/*====================*/
612	const rec_t*	rec,	/*!< in: record */
613	const page_t*	page)	/*!< in: page */
614{
615	ut_ad(page_get_n_recs(page) > 1);
616	ut_ad(!page_rec_is_last(rec, page));
617
618	return(page_rec_get_next_const(
619		page_rec_get_next_const(rec)) == page_get_supremum_rec(page));
620}
621
622/************************************************************//**
623Returns the nth record of the record list.
624This is the inverse function of page_rec_get_n_recs_before().
625@return nth record */
626UNIV_INLINE
627rec_t*
628page_rec_get_nth(
629/*=============*/
630	page_t*	page,	/*!< in: page */
631	ulint	nth)	/*!< in: nth record */
632{
633	return((rec_t*) page_rec_get_nth_const(page, nth));
634}
635
636#ifndef UNIV_HOTBACKUP
637/************************************************************//**
638Returns the middle record of the records on the page. If there is an
639even number of records in the list, returns the first record of the
640upper half-list.
641@return middle record */
642UNIV_INLINE
643rec_t*
644page_get_middle_rec(
645/*================*/
646	page_t*	page)	/*!< in: page */
647{
648	ulint	middle = (page_get_n_recs(page) + PAGE_HEAP_NO_USER_LOW) / 2;
649
650	return(page_rec_get_nth(page, middle));
651}
652#endif /* !UNIV_HOTBACKUP */
653
654/*************************************************************//**
655Gets the page number.
656@return page number */
657UNIV_INLINE
658ulint
659page_get_page_no(
660/*=============*/
661	const page_t*	page)	/*!< in: page */
662{
663	ut_ad(page == page_align((page_t*) page));
664	return(mach_read_from_4(page + FIL_PAGE_OFFSET));
665}
666
667/*************************************************************//**
668Gets the tablespace identifier.
669@return space id */
670UNIV_INLINE
671ulint
672page_get_space_id(
673/*==============*/
674	const page_t*	page)	/*!< in: page */
675{
676	ut_ad(page == page_align((page_t*) page));
677	return(mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID));
678}
679
680/*************************************************************//**
681Gets the number of user records on page (infimum and supremum records
682are not user records).
683@return number of user records */
684UNIV_INLINE
685ulint
686page_get_n_recs(
687/*============*/
688	const page_t*	page)	/*!< in: index page */
689{
690	return(page_header_get_field(page, PAGE_N_RECS));
691}
692
693/*************************************************************//**
694Gets the number of dir slots in directory.
695@return number of slots */
696UNIV_INLINE
697ulint
698page_dir_get_n_slots(
699/*=================*/
700	const page_t*	page)	/*!< in: index page */
701{
702	return(page_header_get_field(page, PAGE_N_DIR_SLOTS));
703}
704/*************************************************************//**
705Sets the number of dir slots in directory. */
706UNIV_INLINE
707void
708page_dir_set_n_slots(
709/*=================*/
710	page_t*		page,	/*!< in/out: page */
711	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
712				uncompressed part will be updated, or NULL */
713	ulint		n_slots)/*!< in: number of slots */
714{
715	page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots);
716}
717
718/*************************************************************//**
719Gets the number of records in the heap.
720@return number of user records */
721UNIV_INLINE
722ulint
723page_dir_get_n_heap(
724/*================*/
725	const page_t*	page)	/*!< in: index page */
726{
727	return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
728}
729
730/*************************************************************//**
731Sets the number of records in the heap. */
732UNIV_INLINE
733void
734page_dir_set_n_heap(
735/*================*/
736	page_t*		page,	/*!< in/out: index page */
737	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
738				uncompressed part will be updated, or NULL.
739				Note that the size of the dense page directory
740				in the compressed page trailer is
741				n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */
742	ulint		n_heap)	/*!< in: number of records */
743{
744	ut_ad(n_heap < 0x8000);
745	ut_ad(!page_zip || n_heap
746	      == (page_header_get_field(page, PAGE_N_HEAP) & 0x7fff) + 1);
747
748	page_header_set_field(page, page_zip, PAGE_N_HEAP, n_heap
749			      | (0x8000
750				 & page_header_get_field(page, PAGE_N_HEAP)));
751}
752
753#ifdef UNIV_DEBUG
754/*************************************************************//**
755Gets pointer to nth directory slot.
756@return pointer to dir slot */
757UNIV_INLINE
758page_dir_slot_t*
759page_dir_get_nth_slot(
760/*==================*/
761	const page_t*	page,	/*!< in: index page */
762	ulint		n)	/*!< in: position */
763{
764	ut_ad(page_dir_get_n_slots(page) > n);
765
766	return((page_dir_slot_t*)
767	       page + UNIV_PAGE_SIZE - PAGE_DIR
768	       - (n + 1) * PAGE_DIR_SLOT_SIZE);
769}
770#endif /* UNIV_DEBUG */
771
772/**************************************************************//**
773Used to check the consistency of a record on a page.
774@return TRUE if succeed */
775UNIV_INLINE
776ibool
777page_rec_check(
778/*===========*/
779	const rec_t*	rec)	/*!< in: record */
780{
781	const page_t*	page = page_align(rec);
782
783	ut_a(rec);
784
785	ut_a(page_offset(rec) <= page_header_get_field(page, PAGE_HEAP_TOP));
786	ut_a(page_offset(rec) >= PAGE_DATA);
787
788	return(TRUE);
789}
790
791/***************************************************************//**
792Gets the record pointed to by a directory slot.
793@return pointer to record */
794UNIV_INLINE
795const rec_t*
796page_dir_slot_get_rec(
797/*==================*/
798	const page_dir_slot_t*	slot)	/*!< in: directory slot */
799{
800	return(page_align(slot) + mach_read_from_2(slot));
801}
802
803/***************************************************************//**
804This is used to set the record offset in a directory slot. */
805UNIV_INLINE
806void
807page_dir_slot_set_rec(
808/*==================*/
809	page_dir_slot_t* slot,	/*!< in: directory slot */
810	rec_t*		 rec)	/*!< in: record on the page */
811{
812	ut_ad(page_rec_check(rec));
813
814	mach_write_to_2(slot, page_offset(rec));
815}
816
817/***************************************************************//**
818Gets the number of records owned by a directory slot.
819@return number of records */
820UNIV_INLINE
821ulint
822page_dir_slot_get_n_owned(
823/*======================*/
824	const page_dir_slot_t*	slot)	/*!< in: page directory slot */
825{
826	const rec_t*	rec	= page_dir_slot_get_rec(slot);
827	if (page_rec_is_comp(slot)) {
828		return(rec_get_n_owned_new(rec));
829	} else {
830		return(rec_get_n_owned_old(rec));
831	}
832}
833
834/***************************************************************//**
835This is used to set the owned records field of a directory slot. */
836UNIV_INLINE
837void
838page_dir_slot_set_n_owned(
839/*======================*/
840	page_dir_slot_t*slot,	/*!< in/out: directory slot */
841	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
842	ulint		n)	/*!< in: number of records owned by the slot */
843{
844	rec_t*	rec	= (rec_t*) page_dir_slot_get_rec(slot);
845	if (page_rec_is_comp(slot)) {
846		rec_set_n_owned_new(rec, page_zip, n);
847	} else {
848		ut_ad(!page_zip);
849		rec_set_n_owned_old(rec, n);
850	}
851}
852
853/************************************************************//**
854Calculates the space reserved for directory slots of a given number of
855records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE /
856PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */
857UNIV_INLINE
858ulint
859page_dir_calc_reserved_space(
860/*=========================*/
861	ulint	n_recs)		/*!< in: number of records */
862{
863	return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1)
864	       / PAGE_DIR_SLOT_MIN_N_OWNED);
865}
866
867/************************************************************//**
868Gets the pointer to the next record on the page.
869@return pointer to next record */
870UNIV_INLINE
871const rec_t*
872page_rec_get_next_low(
873/*==================*/
874	const rec_t*	rec,	/*!< in: pointer to record */
875	ulint		comp)	/*!< in: nonzero=compact page layout */
876{
877	ulint		offs;
878	const page_t*	page;
879
880	ut_ad(page_rec_check(rec));
881
882	page = page_align(rec);
883
884	offs = rec_get_next_offs(rec, comp);
885
886	if (offs >= UNIV_PAGE_SIZE) {
887		fprintf(stderr,
888			"InnoDB: Next record offset is nonsensical %lu"
889			" in record at offset %lu\n"
890			"InnoDB: rec address %p, space id %lu, page %lu\n",
891			(ulong) offs, (ulong) page_offset(rec),
892			(void*) rec,
893			(ulong) page_get_space_id(page),
894			(ulong) page_get_page_no(page));
895		ut_error;
896	} else if (offs == 0) {
897
898		return(NULL);
899	}
900
901	return(page + offs);
902}
903
904/************************************************************//**
905Gets the pointer to the next record on the page.
906@return pointer to next record */
907UNIV_INLINE
908rec_t*
909page_rec_get_next(
910/*==============*/
911	rec_t*	rec)	/*!< in: pointer to record */
912{
913	return((rec_t*) page_rec_get_next_low(rec, page_rec_is_comp(rec)));
914}
915
916/************************************************************//**
917Gets the pointer to the next record on the page.
918@return pointer to next record */
919UNIV_INLINE
920const rec_t*
921page_rec_get_next_const(
922/*====================*/
923	const rec_t*	rec)	/*!< in: pointer to record */
924{
925	return(page_rec_get_next_low(rec, page_rec_is_comp(rec)));
926}
927
928/************************************************************//**
929Gets the pointer to the next non delete-marked record on the page.
930If all subsequent records are delete-marked, then this function
931will return the supremum record.
932@return pointer to next non delete-marked record or pointer to supremum */
933UNIV_INLINE
934const rec_t*
935page_rec_get_next_non_del_marked(
936/*=============================*/
937	const rec_t*	rec)	/*!< in: pointer to record */
938{
939	const rec_t*	r;
940	ulint		page_is_compact = page_rec_is_comp(rec);
941
942	for (r = page_rec_get_next_const(rec);
943	     !page_rec_is_supremum(r)
944	     && rec_get_deleted_flag(r, page_is_compact);
945	     r = page_rec_get_next_const(r)) {
946		/* noop */
947	}
948
949	return(r);
950}
951
952/************************************************************//**
953Sets the pointer to the next record on the page. */
954UNIV_INLINE
955void
956page_rec_set_next(
957/*==============*/
958	rec_t*		rec,	/*!< in: pointer to record,
959				must not be page supremum */
960	const rec_t*	next)	/*!< in: pointer to next record,
961				must not be page infimum */
962{
963	ulint	offs;
964
965	ut_ad(page_rec_check(rec));
966	ut_ad(!page_rec_is_supremum(rec));
967	ut_ad(rec != next);
968
969	ut_ad(!next || !page_rec_is_infimum(next));
970	ut_ad(!next || page_align(rec) == page_align(next));
971
972	offs = next != NULL ? page_offset(next) : 0;
973
974	if (page_rec_is_comp(rec)) {
975		rec_set_next_offs_new(rec, offs);
976	} else {
977		rec_set_next_offs_old(rec, offs);
978	}
979}
980
981/************************************************************//**
982Gets the pointer to the previous record.
983@return pointer to previous record */
984UNIV_INLINE
985const rec_t*
986page_rec_get_prev_const(
987/*====================*/
988	const rec_t*	rec)	/*!< in: pointer to record, must not be page
989				infimum */
990{
991	const page_dir_slot_t*	slot;
992	ulint			slot_no;
993	const rec_t*		rec2;
994	const rec_t*		prev_rec = NULL;
995	const page_t*		page;
996
997	ut_ad(page_rec_check(rec));
998
999	page = page_align(rec);
1000
1001	ut_ad(!page_rec_is_infimum(rec));
1002
1003	slot_no = page_dir_find_owner_slot(rec);
1004
1005	ut_a(slot_no != 0);
1006
1007	slot = page_dir_get_nth_slot(page, slot_no - 1);
1008
1009	rec2 = page_dir_slot_get_rec(slot);
1010
1011	if (page_is_comp(page)) {
1012		while (rec != rec2) {
1013			prev_rec = rec2;
1014			rec2 = page_rec_get_next_low(rec2, TRUE);
1015		}
1016	} else {
1017		while (rec != rec2) {
1018			prev_rec = rec2;
1019			rec2 = page_rec_get_next_low(rec2, FALSE);
1020		}
1021	}
1022
1023	ut_a(prev_rec);
1024
1025	return(prev_rec);
1026}
1027
1028/************************************************************//**
1029Gets the pointer to the previous record.
1030@return pointer to previous record */
1031UNIV_INLINE
1032rec_t*
1033page_rec_get_prev(
1034/*==============*/
1035	rec_t*	rec)	/*!< in: pointer to record, must not be page
1036			infimum */
1037{
1038	return((rec_t*) page_rec_get_prev_const(rec));
1039}
1040
1041/***************************************************************//**
1042Looks for the record which owns the given record.
1043@return the owner record */
1044UNIV_INLINE
1045rec_t*
1046page_rec_find_owner_rec(
1047/*====================*/
1048	rec_t*	rec)	/*!< in: the physical record */
1049{
1050	ut_ad(page_rec_check(rec));
1051
1052	if (page_rec_is_comp(rec)) {
1053		while (rec_get_n_owned_new(rec) == 0) {
1054			rec = page_rec_get_next(rec);
1055		}
1056	} else {
1057		while (rec_get_n_owned_old(rec) == 0) {
1058			rec = page_rec_get_next(rec);
1059		}
1060	}
1061
1062	return(rec);
1063}
1064
1065/**********************************************************//**
1066Returns the base extra size of a physical record.  This is the
1067size of the fixed header, independent of the record size.
1068@return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */
1069UNIV_INLINE
1070ulint
1071page_rec_get_base_extra_size(
1072/*=========================*/
1073	const rec_t*	rec)	/*!< in: physical record */
1074{
1075#if REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES
1076# error "REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES"
1077#endif
1078	return(REC_N_NEW_EXTRA_BYTES + (ulint) !page_rec_is_comp(rec));
1079}
1080
1081/************************************************************//**
1082Returns the sum of the sizes of the records in the record list, excluding
1083the infimum and supremum records.
1084@return data in bytes */
1085UNIV_INLINE
1086ulint
1087page_get_data_size(
1088/*===============*/
1089	const page_t*	page)	/*!< in: index page */
1090{
1091	ulint	ret;
1092
1093	ret = (ulint)(page_header_get_field(page, PAGE_HEAP_TOP)
1094		      - (page_is_comp(page)
1095			 ? PAGE_NEW_SUPREMUM_END
1096			 : PAGE_OLD_SUPREMUM_END)
1097		      - page_header_get_field(page, PAGE_GARBAGE));
1098
1099	ut_ad(ret < UNIV_PAGE_SIZE);
1100
1101	return(ret);
1102}
1103
1104
1105/************************************************************//**
1106Allocates a block of memory from the free list of an index page. */
1107UNIV_INLINE
1108void
1109page_mem_alloc_free(
1110/*================*/
1111	page_t*		page,	/*!< in/out: index page */
1112	page_zip_des_t*	page_zip,/*!< in/out: compressed page with enough
1113				space available for inserting the record,
1114				or NULL */
1115	rec_t*		next_rec,/*!< in: pointer to the new head of the
1116				free record list */
1117	ulint		need)	/*!< in: number of bytes allocated */
1118{
1119	ulint		garbage;
1120
1121#ifdef UNIV_DEBUG
1122	const rec_t*	old_rec	= page_header_get_ptr(page, PAGE_FREE);
1123	ulint		next_offs;
1124
1125	ut_ad(old_rec);
1126	next_offs = rec_get_next_offs(old_rec, page_is_comp(page));
1127	ut_ad(next_rec == (next_offs ? page + next_offs : NULL));
1128#endif
1129
1130	page_header_set_ptr(page, page_zip, PAGE_FREE, next_rec);
1131
1132	garbage = page_header_get_field(page, PAGE_GARBAGE);
1133	ut_ad(garbage >= need);
1134
1135	page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage - need);
1136}
1137
1138/*************************************************************//**
1139Calculates free space if a page is emptied.
1140@return free space */
1141UNIV_INLINE
1142ulint
1143page_get_free_space_of_empty(
1144/*=========================*/
1145	ulint	comp)		/*!< in: nonzero=compact page layout */
1146{
1147	if (comp) {
1148		return((ulint)(UNIV_PAGE_SIZE
1149			       - PAGE_NEW_SUPREMUM_END
1150			       - PAGE_DIR
1151			       - 2 * PAGE_DIR_SLOT_SIZE));
1152	}
1153
1154	return((ulint)(UNIV_PAGE_SIZE
1155		       - PAGE_OLD_SUPREMUM_END
1156		       - PAGE_DIR
1157		       - 2 * PAGE_DIR_SLOT_SIZE));
1158}
1159
1160#ifndef UNIV_HOTBACKUP
1161/***********************************************************************//**
1162Write a 32-bit field in a data dictionary record. */
1163UNIV_INLINE
1164void
1165page_rec_write_field(
1166/*=================*/
1167	rec_t*	rec,	/*!< in/out: record to update */
1168	ulint	i,	/*!< in: index of the field to update */
1169	ulint	val,	/*!< in: value to write */
1170	mtr_t*	mtr)	/*!< in/out: mini-transaction */
1171{
1172	byte*	data;
1173	ulint	len;
1174
1175	data = rec_get_nth_field_old(rec, i, &len);
1176
1177	ut_ad(len == 4);
1178
1179	mlog_write_ulint(data, val, MLOG_4BYTES, mtr);
1180}
1181#endif /* !UNIV_HOTBACKUP */
1182
1183/************************************************************//**
1184Each user record on a page, and also the deleted user records in the heap
1185takes its size plus the fraction of the dir cell size /
1186PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the
1187value of page_get_free_space_of_empty, the insert is impossible, otherwise
1188it is allowed. This function returns the maximum combined size of records
1189which can be inserted on top of the record heap.
1190@return maximum combined size for inserted records */
1191UNIV_INLINE
1192ulint
1193page_get_max_insert_size(
1194/*=====================*/
1195	const page_t*	page,	/*!< in: index page */
1196	ulint		n_recs)	/*!< in: number of records */
1197{
1198	ulint	occupied;
1199	ulint	free_space;
1200
1201	if (page_is_comp(page)) {
1202		occupied = page_header_get_field(page, PAGE_HEAP_TOP)
1203			- PAGE_NEW_SUPREMUM_END
1204			+ page_dir_calc_reserved_space(
1205				n_recs + page_dir_get_n_heap(page) - 2);
1206
1207		free_space = page_get_free_space_of_empty(TRUE);
1208	} else {
1209		occupied = page_header_get_field(page, PAGE_HEAP_TOP)
1210			- PAGE_OLD_SUPREMUM_END
1211			+ page_dir_calc_reserved_space(
1212				n_recs + page_dir_get_n_heap(page) - 2);
1213
1214		free_space = page_get_free_space_of_empty(FALSE);
1215	}
1216
1217	/* Above the 'n_recs +' part reserves directory space for the new
1218	inserted records; the '- 2' excludes page infimum and supremum
1219	records */
1220
1221	if (occupied > free_space) {
1222
1223		return(0);
1224	}
1225
1226	return(free_space - occupied);
1227}
1228
1229/************************************************************//**
1230Returns the maximum combined size of records which can be inserted on top
1231of the record heap if a page is first reorganized.
1232@return maximum combined size for inserted records */
1233UNIV_INLINE
1234ulint
1235page_get_max_insert_size_after_reorganize(
1236/*======================================*/
1237	const page_t*	page,	/*!< in: index page */
1238	ulint		n_recs)	/*!< in: number of records */
1239{
1240	ulint	occupied;
1241	ulint	free_space;
1242
1243	occupied = page_get_data_size(page)
1244		+ page_dir_calc_reserved_space(n_recs + page_get_n_recs(page));
1245
1246	free_space = page_get_free_space_of_empty(page_is_comp(page));
1247
1248	if (occupied > free_space) {
1249
1250		return(0);
1251	}
1252
1253	return(free_space - occupied);
1254}
1255
1256/************************************************************//**
1257Puts a record to free list. */
1258UNIV_INLINE
1259void
1260page_mem_free(
1261/*==========*/
1262	page_t*			page,		/*!< in/out: index page */
1263	page_zip_des_t*		page_zip,	/*!< in/out: compressed page,
1264						or NULL */
1265	rec_t*			rec,		/*!< in: pointer to the
1266						(origin of) record */
1267	const dict_index_t*	index,		/*!< in: index of rec */
1268	const ulint*		offsets)	/*!< in: array returned by
1269						rec_get_offsets() */
1270{
1271	rec_t*		free;
1272	ulint		garbage;
1273
1274	ut_ad(rec_offs_validate(rec, index, offsets));
1275	free = page_header_get_ptr(page, PAGE_FREE);
1276
1277	page_rec_set_next(rec, free);
1278	page_header_set_ptr(page, page_zip, PAGE_FREE, rec);
1279
1280	garbage = page_header_get_field(page, PAGE_GARBAGE);
1281
1282	page_header_set_field(page, page_zip, PAGE_GARBAGE,
1283			      garbage + rec_offs_size(offsets));
1284
1285	if (page_zip) {
1286		page_zip_dir_delete(page_zip, rec, index, offsets, free);
1287	} else {
1288		page_header_set_field(page, page_zip, PAGE_N_RECS,
1289				      page_get_n_recs(page) - 1);
1290	}
1291}
1292
1293#endif /* !UNIV_INNOCHECKSUM */
1294#ifdef UNIV_MATERIALIZE
1295#undef UNIV_INLINE
1296#define UNIV_INLINE	UNIV_INLINE_ORIGINAL
1297#endif
1298