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