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