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