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