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