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