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