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