1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
|
/*****************************************************************************
Copyright (c) 1995, 2025, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.
This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
/** @file include/fsp0fsp.h
File space management
Created 12/18/1995 Heikki Tuuri
*******************************************************/
#ifndef fsp0fsp_h
#define fsp0fsp_h
#include "univ.i"
#include "fsp0space.h"
#include "fut0lst.h"
#include "mtr0mtr.h"
#include "mysql/components/services/bits/mysql_cond_bits.h"
#include "mysql/components/services/bits/mysql_mutex_bits.h"
#include "page0types.h"
#include "rem0types.h"
#include "ut0byte.h"
#include <random>
#include "fsp0types.h"
#ifdef UNIV_HOTBACKUP
#include "buf0buf.h"
#endif /* UNIV_HOTBACKUP */
class DDL_Record;
extern std::vector<DDL_Record *> ts_encrypt_ddl_records;
extern mysql_cond_t resume_encryption_cond;
extern mysql_mutex_t resume_encryption_cond_m;
/** @defgroup Tablespace Header Constants (moved from fsp0fsp.c)
@{ */
#ifdef UNIV_DEBUG
extern std::vector<space_id_t> flag_mismatch_spaces;
#endif
/** Offset of the space header within a file page */
constexpr uint32_t FSP_HEADER_OFFSET = FIL_PAGE_DATA;
/** The number of bytes required to store SDI root page number(4)
and SDI version(4) at Page 0 */
constexpr uint32_t FSP_SDI_HEADER_LEN = 8;
/* The data structures in files are defined just as byte strings in C */
typedef byte fsp_header_t;
typedef byte xdes_t;
#ifdef UNIV_DEBUG
/** Check if the state of extent descriptor is valid.
@param[in] state the extent descriptor state
@return true if state is valid, false otherwise */
bool xdes_state_is_valid(ulint state);
#endif /* UNIV_DEBUG */
#ifdef UNIV_DEBUG
struct xdes_mem_t {
xdes_mem_t(const xdes_t *xdes) : m_xdes(xdes) {}
const char *state_name() const;
bool is_valid() const;
const xdes_t *m_xdes;
std::ostream &print(std::ostream &out) const;
};
inline std::ostream &operator<<(std::ostream &out, const xdes_mem_t &obj) {
return (obj.print(out));
}
/** In-memory representation of the fsp_header_t file structure. */
struct fsp_header_mem_t {
fsp_header_mem_t(const fsp_header_t *header, mtr_t *mtr);
ulint m_space_id;
ulint m_notused;
ulint m_fsp_size;
ulint m_free_limit;
ulint m_flags;
ulint m_fsp_frag_n_used;
flst_bnode_t m_fsp_free;
flst_bnode_t m_free_frag;
flst_bnode_t m_full_frag;
ib_id_t m_segid;
flst_bnode_t m_inodes_full;
flst_bnode_t m_inodes_free;
std::ostream &print(std::ostream &out) const;
};
inline std::ostream &operator<<(std::ostream &out,
const fsp_header_mem_t &obj) {
return (obj.print(out));
}
#endif /* UNIV_DEBUG */
/* SPACE HEADER
============
File space header data structure: this data structure is contained in the
first page of a space. The space for this header is reserved in every extent
descriptor page, but used only in the first. */
/*-------------------------------------*/
/** space id */
constexpr uint32_t FSP_SPACE_ID = 0;
/** this field contained a value up to which we know that the modifications in
the database have been flushed to the file space; not used now */
constexpr uint32_t FSP_NOT_USED = 4;
/** Current size of the space in pages */
constexpr uint32_t FSP_SIZE = 8;
/** Minimum page number for which the free list has not been initialized: the
pages >= this limit are, bydefinition, free; note that in a single-table
tablespace where size < 64 pages, this number is 64, i.e.,we have initialized
the space about the first extent, but have not physically allocated those
pages to thefile */
constexpr uint32_t FSP_FREE_LIMIT = 12;
/** fsp_space_t.flags, similar to dict_table_t::flags */
constexpr uint32_t FSP_SPACE_FLAGS = 16;
/** number of used pages in the FSP_FREE_FRAG list */
constexpr uint32_t FSP_FRAG_N_USED = 20;
/** list of free extents */
constexpr uint32_t FSP_FREE = 24;
/** list of partially free extents not belonging to any segment */
constexpr uint32_t FSP_FREE_FRAG = 24 + FLST_BASE_NODE_SIZE;
/** list of full extents not belonging to any segment */
constexpr uint32_t FSP_FULL_FRAG = 24 + 2 * FLST_BASE_NODE_SIZE;
/** 8 bytes which give the first unused segment id */
constexpr uint32_t FSP_SEG_ID = 24 + 3 * FLST_BASE_NODE_SIZE;
/** list of pages containing segment headers, where all the segment inode slots
are reserved */
constexpr uint32_t FSP_SEG_INODES_FULL = 32 + 3 * FLST_BASE_NODE_SIZE;
/** list of pages containing segment headers, where not all the segment header
slots are reserved */
constexpr uint32_t FSP_SEG_INODES_FREE = 32 + 4 * FLST_BASE_NODE_SIZE;
/*-------------------------------------*/
/* File space header size */
constexpr uint32_t FSP_HEADER_SIZE = 32 + 5 * FLST_BASE_NODE_SIZE;
/** This many free extents are added to the free list from above FSP_FREE_LIMIT
at a time */
constexpr uint32_t FSP_FREE_ADD = 4;
/** @} */
/* Maximum allowed value of AUTOEXTEND_SIZE attribute */
constexpr uint64_t FSP_MAX_AUTOEXTEND_SIZE = 4 * 1024 * 1024 * 1024ULL;
/** @defgroup File Segment Inode Constants (moved from fsp0fsp.c)
@{ */
/* FILE SEGMENT INODE
==================
Segment inode which is created for each segment in a tablespace. NOTE: in
purge we assume that a segment having only one currently used page can be
freed in a few steps, so that the freeing cannot fill the file buffer with
bufferfixed file pages. */
typedef byte fseg_inode_t;
constexpr uint32_t FSEG_INODE_PAGE_NODE = FSEG_PAGE_DATA;
/* the list node for linking
segment inode pages */
constexpr uint32_t FSEG_ARR_OFFSET = FSEG_PAGE_DATA + FLST_NODE_SIZE;
/*-------------------------------------*/
/* 8 bytes of segment id: if this is 0, it means that the header is unused */
constexpr uint32_t FSEG_ID = 0;
/** number of used segment pages in the FSEG_NOT_FULL list */
constexpr uint32_t FSEG_NOT_FULL_N_USED = 8;
/** list of free extents of this segment */
constexpr uint32_t FSEG_FREE = 12;
/** list of partially free extents */
constexpr uint32_t FSEG_NOT_FULL = 12 + FLST_BASE_NODE_SIZE;
/** list of full extents */
constexpr uint32_t FSEG_FULL = 12 + 2 * FLST_BASE_NODE_SIZE;
/** magic number used in debugging */
constexpr uint32_t FSEG_MAGIC_N = 12 + 3 * FLST_BASE_NODE_SIZE;
/** array of individual pages belonging to this segment in fsp fragment extent
lists */
constexpr uint32_t FSEG_FRAG_ARR = 16 + 3 * FLST_BASE_NODE_SIZE;
/* number of slots in the array for the fragment pages */
#define FSEG_FRAG_ARR_N_SLOTS (FSP_EXTENT_SIZE / 2)
/** a fragment page slot contains its page number within space, FIL_NULL means
that the slot is not in use */
constexpr uint32_t FSEG_FRAG_SLOT_SIZE = 4;
/*-------------------------------------*/
#define FSEG_INODE_SIZE \
(16 + 3 * FLST_BASE_NODE_SIZE + FSEG_FRAG_ARR_N_SLOTS * FSEG_FRAG_SLOT_SIZE)
static inline uint32_t FSP_SEG_INODES_PER_PAGE(page_size_t page_size) {
return (page_size.physical() - FSEG_ARR_OFFSET - 10) / FSEG_INODE_SIZE;
}
/* Number of segment inodes which fit on a
single page */
constexpr uint32_t FSEG_MAGIC_N_VALUE = 97937874;
/** The segment_reserve_factor is the ratio x/y expressed in percentage,
where x is the number of free pages in the segment, and y is the total number
of pages in the segment. The number of used pages in the segment is given by
(y-x). The number of free pages in the segment (x) will be maintained such
that the actual segment_reserve_factor will be >= the requested
segment_reserve_factor, which is contained in this variable. */
extern double fseg_reserve_pct;
/* Various constants related to segment reserve factor */
constexpr double FSEG_RESERVE_PCT_DFLT = 12.50;
constexpr double FSEG_RESERVE_PCT_MIN = 0.03;
constexpr double FSEG_RESERVE_PCT_MAX = 40.00;
#define FSEG_FRAG_LIMIT FSEG_FRAG_ARR_N_SLOTS
/** If the reserved size of a segment is at least this many extents, we allow
extents to be put to the free list of the extent: at most
FSEG_FREE_LIST_MAX_LEN many */
constexpr uint32_t FSEG_FREE_LIST_LIMIT = 40;
constexpr uint32_t FSEG_FREE_LIST_MAX_LEN = 4;
/** @} */
/** @defgroup Extent Descriptor Constants (moved from fsp0fsp.c)
@{ */
/* EXTENT DESCRIPTOR
=================
File extent descriptor data structure: contains bits to tell which pages in
the extent are free and which contain old tuple version to clean. */
/*-------------------------------------*/
/** The identifier of the segment to which this extent belongs */
constexpr uint32_t XDES_ID = 0;
/** The list node data structure for the descriptors */
constexpr uint32_t XDES_FLST_NODE = 8;
/** contains state information of the extent */
constexpr uint32_t XDES_STATE = FLST_NODE_SIZE + 8;
/** Descriptor bitmap of the pages in the extent */
constexpr uint32_t XDES_BITMAP = FLST_NODE_SIZE + 12;
/*-------------------------------------*/
/** How many bits are there per page */
constexpr uint32_t XDES_BITS_PER_PAGE = 2;
/** Index of the bit which tells if the page is free */
constexpr uint32_t XDES_FREE_BIT = 0;
/** NOTE: currently not used! Index of the bit which tells if there are old
versions of tuples on the page */
constexpr uint32_t XDES_CLEAN_BIT = 1;
/** States of a descriptor */
enum xdes_state_t {
/** extent descriptor is not initialized */
XDES_NOT_INITED = 0,
/** extent is in free list of space */
XDES_FREE = 1,
/** extent is in free fragment list of space */
XDES_FREE_FRAG = 2,
/** extent is in full fragment list of space */
XDES_FULL_FRAG = 3,
/** extent belongs to a segment */
XDES_FSEG = 4,
/** fragment extent leased to segment */
XDES_FSEG_FRAG = 5
};
/** File extent data structure size in bytes. */
#define XDES_SIZE \
(XDES_BITMAP + UT_BITS_IN_BYTES(FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE))
/** File extent data structure size in bytes for MAX page size. */
#define XDES_SIZE_MAX \
(XDES_BITMAP + UT_BITS_IN_BYTES(FSP_EXTENT_SIZE_MAX * XDES_BITS_PER_PAGE))
/** File extent data structure size in bytes for MIN page size. */
#define XDES_SIZE_MIN \
(XDES_BITMAP + UT_BITS_IN_BYTES(FSP_EXTENT_SIZE_MIN * XDES_BITS_PER_PAGE))
/** Offset of the descriptor array on a descriptor page */
constexpr uint32_t XDES_ARR_OFFSET = FSP_HEADER_OFFSET + FSP_HEADER_SIZE;
/** The number of reserved pages in a fragment extent. */
const ulint XDES_FRAG_N_USED = 2;
/** @} */
/** Initializes the file space system. */
void fsp_init(void);
/** Gets the size of the system tablespace from the tablespace header. If
we do not have an auto-extending data file, this should be equal to
the size of the data files. If there is an auto-extending data file,
this can be smaller.
@return size in pages */
page_no_t fsp_header_get_tablespace_size(void);
/** Calculate the number of pages to extend a datafile.
We extend single-table and general tablespaces first one extent at a time,
but 4 at a time for bigger tablespaces. It is not enough to extend always
by one extent, because we need to add at least one extent to FSP_FREE.
A single extent descriptor page will track many extents. And the extent
that uses its extent descriptor page is put onto the FSP_FREE_FRAG list.
Extents that do not use their extent descriptor page are added to FSP_FREE.
The physical page size is used to determine how many extents are tracked
on one extent descriptor page. See xdes_calc_descriptor_page().
@param[in] page_size page_size of the datafile
@param[in] size current number of pages in the datafile
@return number of pages to extend the file. */
page_no_t fsp_get_pages_to_extend_ibd(const page_size_t &page_size,
page_no_t size);
/** Calculate the number of physical pages in an extent for this file.
@param[in] page_size page_size of the datafile
@return number of pages in an extent for this file. */
static inline page_no_t fsp_get_extent_size_in_pages(
const page_size_t &page_size) {
return (static_cast<page_no_t>(FSP_EXTENT_SIZE * UNIV_PAGE_SIZE /
page_size.physical()));
}
/** Reads the space id from the first page of a tablespace.
@return space id, ULINT UNDEFINED if error */
space_id_t fsp_header_get_space_id(
const page_t *page); /*!< in: first page of a tablespace */
/** Read the server version number from the DD tablespace header.
@param[out] version server version from tablespace header
@return false if success. */
bool fsp_header_dict_get_server_version(uint *version);
/** Read a tablespace header field.
@param[in] page first page of a tablespace
@param[in] field the header field
@return the contents of the header field */
inline uint32_t fsp_header_get_field(const page_t *page, uint32_t field) {
return (mach_read_from_4(FSP_HEADER_OFFSET + field + page));
}
/** Update a tablespace header field.
@param[in] page first page of a tablespace
@param[in] field the header field
@param[in] val field value */
inline void fsp_header_set_field(page_t *page, uint32_t field, uint32_t val) {
mach_write_to_4(page + FSP_HEADER_OFFSET + field, val);
}
/** Read the flags from the tablespace header page.
@param[in] page first page of a tablespace
@return the contents of FSP_SPACE_FLAGS */
inline uint32_t fsp_header_get_flags(const page_t *page) {
return (fsp_header_get_field(page, FSP_SPACE_FLAGS));
}
/** Reads the page size from the first page of a tablespace.
@param[in] page first page of a tablespace
@return page size */
page_size_t fsp_header_get_page_size(const page_t *page);
/** Reads the encryption key from the first page of a tablespace.
@param[in] fsp_flags tablespace flags
@param[in,out] e_key tablespace key, iv
@param[in] page first page of a tablespace
@return true if success */
bool fsp_header_get_encryption_key(uint32_t fsp_flags, Encryption_key &e_key,
page_t *page);
/** Get encryption operation type in progress from the first
page of a tablespace.
@param[in] page first page of a tablespace
@param[in] page_size tablespace page size
@return operation type
*/
Encryption::Progress fsp_header_encryption_op_type_in_progress(
const page_t *page, page_size_t page_size);
/** Check if the tablespace size information is valid.
@param[in] space_id the tablespace identifier
@return true if valid, false if invalid. */
bool fsp_check_tablespace_size(space_id_t space_id);
/** Writes the space id and flags to a tablespace header. The flags contain
row type, physical/compressed page size, and logical/uncompressed page
size of the tablespace. */
void fsp_header_init_fields(
page_t *page, /*!< in/out: first page in the space */
space_id_t space_id, /*!< in: space id */
uint32_t flags); /*!< in: tablespace flags
(FSP_SPACE_FLAGS): 0, or
table->flags if newer than COMPACT */
/** Get the offset of encryption information in page 0.
@param[in] page_size page size.
@return offset on success, otherwise 0. */
ulint fsp_header_get_encryption_offset(const page_size_t &page_size);
/** Write the encryption info into the space header.
@param[in] space_id Tablespace id
@param[in] space_flags Tablespace flags
@param[in] encrypt_info Buffer for re-encrypt key
@param[in] update_fsp_flags If it need to update the space flags
@param[in] rotate_encryption If it is called during key rotation
@param[in,out] mtr Mini-transaction
@return true if success. */
bool fsp_header_write_encryption(space_id_t space_id, uint32_t space_flags,
byte *encrypt_info, bool update_fsp_flags,
bool rotate_encryption, mtr_t *mtr);
/** Write the encryption progress info into the space header.
@param[in] space_id Tablespace id
@param[in] space_flags Tablespace flags
@param[in] progress_info Max pages (un)encrypted
@param[in] operation_type Type of operation
@param[in] update_operation_type Is operation to be updated
@param[in,out] mtr Mini-transaction
@return true if success. */
bool fsp_header_write_encryption_progress(
space_id_t space_id, ulint space_flags, ulint progress_info,
byte operation_type, bool update_operation_type, mtr_t *mtr);
/** Rotate the encryption info in the space header.
@param[in] space Tablespace
@param[in] encrypt_info Buffer for re-encrypt key.
@param[in,out] mtr Mini-transaction
@return true if success. */
bool fsp_header_rotate_encryption(fil_space_t *space, byte *encrypt_info,
mtr_t *mtr);
/** Initializes the space header of a new created space and creates also the
insert buffer tree root if space == 0.
@param[in] space_id Space id
@param[in] size Current size in blocks
@param[in,out] mtr Mini-transaction
@return true on success, otherwise false. */
bool fsp_header_init(space_id_t space_id, page_no_t size, mtr_t *mtr);
/** Increases the space size field of a space.
@param[in] space_id Space id
@param[in] size_inc Size increment in pages
@param[in,out] mtr Mini-transaction */
void fsp_header_inc_size(space_id_t space_id, page_no_t size_inc, mtr_t *mtr);
/** Creates a new segment.
@return the block where the segment header is placed, x-latched, NULL
if could not create segment because of lack of space */
buf_block_t *fseg_create(
space_id_t space, /*!< in: space id */
page_no_t page, /*!< in: page where the segment header is
placed: if this is != 0, the page must belong
to another segment, if this is 0, a new page
will be allocated and it will belong to the
created segment */
ulint byte_offset, /*!< in: byte offset of the created
segment header on the page */
mtr_t *mtr); /*!< in/out: mini-transaction */
/** Creates a new segment.
@param[in] space_id Space id
@param[in,out] page Page where the segment header is placed: if this is != 0,
the page must belong to another segment, if this is 0, a new page will be
allocated and it will belong to the created segment
@param[in] byte_offset Byte offset of the created segment header on the page
@param[in,out] has_done_reservation True if the caller has already done the
reservation for the pages with fsp_reserve_free_extents (at least 2 extents: one
for the inode and the other for the segment) then there is no need to do the
check for this individual operation
@param[in,out] mtr Mini-transaction
@return the block where the segment header is placed, x-latched, NULL
if could not create segment because of lack of space */
buf_block_t *fseg_create_general(space_id_t space_id, page_no_t page,
ulint byte_offset, bool has_done_reservation,
mtr_t *mtr);
/** Calculates the number of pages reserved by a segment, and how many pages are
currently used.
@return number of reserved pages */
ulint fseg_n_reserved_pages(
fseg_header_t *header, /*!< in: segment header */
ulint *used, /*!< out: number of pages used (<= reserved) */
mtr_t *mtr); /*!< in/out: mini-transaction */
/** Allocates a single free page from a segment. This function implements
the intelligent allocation strategy which tries to minimize file space
fragmentation.
@param[in,out] seg_header Segment header
@param[in] hint Hint of which page would be desirable
@param[in,out] direction If the new page is needed because of an index page
split, and records are inserted there in order, into which direction they go
alphabetically: fsp_down, fsp_up, fsp_no_dir
@param[in] has_done_reservation True if the caller has already done the
reservation for the page with fsp_reserve_free_extents, then there is no need to
do the check for this individual page
@param[in,out] mtr Mini-transaction
@param[in,out] init_mtr mtr or another mini-transaction in which
the page should be initialized. if init_mtr!=mtr, but the page is already
latched in mtr, do not initialize the page.
@retval NULL if no page could be allocated
@retval block, rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
(init_mtr == mtr, or the page was not previously freed in mtr),
returned block is not allocated nor initialized otherwise */
[[nodiscard]] buf_block_t *fseg_alloc_free_page_general(
fseg_header_t *seg_header, page_no_t hint, byte direction,
bool has_done_reservation, mtr_t *mtr, mtr_t *init_mtr);
/** Allocates a single free page from a segment. This function implements
the intelligent allocation strategy which tries to minimize
file space fragmentation.
@param[in,out] seg_header Segment header
@param[in] hint Hint of which page would be desirable
@param[in] direction If the new page is needed because
of an index page split, and records are
inserted there in order, into which
direction they go alphabetically: FSP_DOWN,
FSP_UP, FSP_NO_DIR
@param[in,out] mtr Mini-transaction
@return X-latched block, or NULL if no page could be allocated */
static inline buf_block_t *fseg_alloc_free_page(fseg_header_t *seg_header,
page_no_t hint, byte direction,
mtr_t *mtr) {
return fseg_alloc_free_page_general(seg_header, hint, direction, false, mtr,
mtr);
}
/** Reserves free pages from a tablespace. All mini-transactions which may
use several pages from the tablespace should call this function beforehand
and reserve enough free extents so that they certainly will be able
to do their operation, like a B-tree page split, fully. Reservations
must be released with function fil_space_release_free_extents!
The alloc_type below has the following meaning: FSP_NORMAL means an
operation which will probably result in more space usage, like an
insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are
deleting rows, then this allocation will in the long run result in
less space usage (after a purge); FSP_CLEANING means allocation done
in a physical record delete (like in a purge) or other cleaning operation
which will result in less space usage in the long run. We prefer the latter
two types of allocation: when space is scarce, FSP_NORMAL allocations
will not succeed, but the latter two allocations will succeed, if possible.
The purpose is to avoid dead end where the database is full but the
user cannot free any space because these freeing operations temporarily
reserve some space.
Single-table tablespaces whose size is < FSP_EXTENT_SIZE pages are a special
case. In this function we would liberally reserve several extents for
every page split or merge in a B-tree. But we do not want to waste disk space
if the table only occupies < FSP_EXTENT_SIZE pages. That is why we apply
different rules in that special case, just ensuring that there are n_pages
free pages available.
@param[out] n_reserved Number of extents actually reserved; if we
return true and the tablespace size is <
FSP_EXTENT_SIZE pages, then this can be 0,
otherwise it is n_ext
@param[in] space_id Tablespace identifier
@param[in] n_ext Number of extents to reserve
@param[in] alloc_type Page reservation type (FSP_BLOB, etc)
@param[in,out] mtr Mini-transaction
@param[in] n_pages For small tablespaces (tablespace size is
less than FSP_EXTENT_SIZE), number of free
pages to reserve.
@return true if we were able to make the reservation */
bool fsp_reserve_free_extents(ulint *n_reserved, space_id_t space_id,
ulint n_ext, fsp_reserve_t alloc_type, mtr_t *mtr,
page_no_t n_pages = 2);
/** Calculate how many KiB of new data we will be able to insert to the
tablespace without running out of space.
@param[in] space_id tablespace ID
@return available space in KiB
@retval UINTMAX_MAX if unknown */
uintmax_t fsp_get_available_space_in_free_extents(space_id_t space_id);
/** Calculate how many KiB of new data we will be able to insert to the
tablespace without running out of space. Start with a space object that has
been acquired by the caller who holds it for the calculation,
@param[in] space tablespace object from fil_space_acquire()
@return available space in KiB */
uintmax_t fsp_get_available_space_in_free_extents(const fil_space_t *space);
/** Frees a single page of a segment.
@param[in] seg_header Segment header
@param[in] space_id Space id
@param[in] page Page offset
@param[in] ahi Whether we may need to drop the adaptive hash index
@param[in,out] mtr Mini-transaction */
void fseg_free_page(fseg_header_t *seg_header, space_id_t space_id,
page_no_t page, bool ahi, mtr_t *mtr);
/** Checks if a single page of a segment is free.
@return true if free */
[[nodiscard]] bool fseg_page_is_free(
fseg_header_t *seg_header, /*!< in: segment header */
space_id_t space_id, /*!< in: space id */
page_no_t page); /*!< in: page offset */
/** Frees part of a segment. This function can be used to free a segment
by repeatedly calling this function in different mini-transactions.
Doing the freeing in a single mini-transaction might result in
too big a mini-transaction.
@return true if freeing completed */
[[nodiscard]] bool fseg_free_step(
fseg_header_t *header, /*!< in, own: segment header; NOTE: if the header
resides on the first page of the frag list
of the segment, this pointer becomes obsolete
after the last freeing step */
bool ahi, /*!< in: whether we may need to drop
the adaptive hash index */
mtr_t *mtr); /*!< in/out: mini-transaction */
/** Frees part of a segment. Differs from fseg_free_step because this function
leaves the header page unfreed.
@return true if freeing completed, except the header page */
[[nodiscard]] bool fseg_free_step_not_header(
fseg_header_t *header, /*!< in: segment header which must reside on
the first fragment page of the segment */
bool ahi, /*!< in: whether we may need to drop
the adaptive hash index */
mtr_t *mtr); /*!< in/out: mini-transaction */
/** Checks if a page address is an extent descriptor page address.
@param[in] page_id page id
@param[in] page_size page size
@return true if a descriptor page */
static inline bool fsp_descr_page(const page_id_t &page_id,
const page_size_t &page_size);
/** Parses a redo log record of a file page init.
@param[in] ptr Buffer.
@param[in] end_ptr Buffer end.
@param[in] block Block or nullptr.
@return end of log record or nullptr */
byte *fsp_parse_init_file_page(byte *ptr, byte *end_ptr, buf_block_t *block);
#ifdef UNIV_BTR_PRINT
/** Writes info of a segment. */
void fseg_print(fseg_header_t *header, /*!< in: segment header */
mtr_t *mtr); /*!< in/out: mini-transaction */
#endif /* UNIV_BTR_PRINT */
/** Check whether a space id is an undo tablespace ID
Undo tablespaces have space_id's starting 1 less than the redo logs.
They are numbered down from this. Since rseg_id=0 always refers to the
system tablespace, undo_space_num values start at 1. The current limit
is 127. The translation from an undo_space_num is:
undo space_id = log_first_space_id - undo_space_num
@param[in] space_id space id to check
@return true if it is undo tablespace else false. */
bool fsp_is_undo_tablespace(space_id_t space_id);
static inline bool fsp_is_system_tablespace(space_id_t space_id) {
return (space_id == TRX_SYS_SPACE);
}
/** Check if the space_id is for a system-tablespace (shared + temp).
@param[in] space_id tablespace ID
@return true if id is a system tablespace, false if not. */
static inline bool fsp_is_system_or_temp_tablespace(space_id_t space_id) {
return (fsp_is_system_tablespace(space_id) ||
fsp_is_system_temporary(space_id));
}
/** Determine if the space ID is an IBD tablespace, either file_per_table
or a general shared tablespace, where user tables exist.
@param[in] space_id tablespace ID
@return true if it is a user tablespace ID */
static inline bool fsp_is_ibd_tablespace(space_id_t space_id) {
return (space_id != TRX_SYS_SPACE && !fsp_is_undo_tablespace(space_id) &&
!fsp_is_system_temporary(space_id));
}
/** Check if tablespace is file-per-table.
@param[in] space_id tablespace ID
@param[in] fsp_flags tablespace flags
@return true if tablespace is file-per-table. */
static inline bool fsp_is_file_per_table(space_id_t space_id,
uint32_t fsp_flags) {
return (!fsp_is_shared_tablespace(fsp_flags) &&
fsp_is_ibd_tablespace(space_id));
}
/** Check if tablespace is dd tablespace.
@param[in] space_id tablespace ID
@return true if tablespace is dd tablespace. */
bool fsp_is_dd_tablespace(space_id_t space_id);
/** Determine if the tablespace is compressed from tablespace flags.
@param[in] flags Tablespace flags
@return true if compressed, false if not compressed */
static inline bool fsp_flags_is_compressed(uint32_t flags);
/** Determine if two tablespaces are equivalent or compatible.
@param[in] flags1 First tablespace flags
@param[in] flags2 Second tablespace flags
@return true the flags are compatible, false if not */
static inline bool fsp_flags_are_equal(uint32_t flags1, uint32_t flags2);
/** Initialize an FSP flags integer.
@param[in] page_size page sizes in bytes and compression flag.
@param[in] atomic_blobs Used by Dynamic and Compressed.
@param[in] has_data_dir This tablespace is in a remote location.
@param[in] is_shared This tablespace can be shared by many tables.
@param[in] is_temporary This tablespace is temporary.
@param[in] is_encrypted This tablespace is encrypted.
@return tablespace flags after initialization */
static inline uint32_t fsp_flags_init(const page_size_t &page_size,
bool atomic_blobs, bool has_data_dir,
bool is_shared, bool is_temporary,
bool is_encrypted = false);
/** Convert a 32 bit integer tablespace flags to the 32 bit table flags.
This can only be done for a tablespace that was built as a file-per-table
tablespace. Note that the fsp_flags cannot show the difference between a
Compact and Redundant table, so an extra Compact boolean must be supplied.
Low order bit
| REDUNDANT | COMPACT | COMPRESSED | DYNAMIC
fil_space_t::flags | 0 | 0 | 1 | 1
dict_table_t::flags | 0 | 1 | 1 | 1
@param[in] fsp_flags fil_space_t::flags
@param[in] compact true if not Redundant row format
@return tablespace flags (fil_space_t::flags) */
uint32_t fsp_flags_to_dict_tf(uint32_t fsp_flags, bool compact);
/** Calculates the descriptor index within a descriptor page.
@param[in] page_size page size
@param[in] offset page offset
@return descriptor index */
static inline ulint xdes_calc_descriptor_index(const page_size_t &page_size,
ulint offset);
/** Gets a descriptor bit of a page.
@param[in] descr descriptor
@param[in] bit XDES_FREE_BIT or XDES_CLEAN_BIT
@param[in] offset page offset within extent: 0 ... FSP_EXTENT_SIZE - 1
@return true if free */
static inline bool xdes_get_bit(const xdes_t *descr, ulint bit,
page_no_t offset);
/** Calculates the page where the descriptor of a page resides.
@param[in] page_size page size
@param[in] offset page offset
@return descriptor page offset */
static inline page_no_t xdes_calc_descriptor_page(const page_size_t &page_size,
page_no_t offset);
/** Gets a pointer to the space header and acquires a
SX lock on the page.
@param[in] id Space id
@param[in] page_size Page size
@param[in,out] mtr Mini-transaction
@param[out] block Block
@return pointer to the space header, page x-locked */
fsp_header_t *fsp_get_space_header_block(space_id_t id,
const page_size_t &page_size,
mtr_t *mtr, buf_block_t **block);
/** Gets a pointer to the space header and acquires a
SX lock on the page.
@param[in] id Space id
@param[in] page_size Page size
@param[in,out] mtr Mini-transaction
@return pointer to the space header, page x-locked */
fsp_header_t *fsp_get_space_header(space_id_t id, const page_size_t &page_size,
mtr_t *mtr);
/** Retrieve tablespace dictionary index root page number stored in the
page 0
@param[in] space Tablespace id
@param[in] page_size Page size
@param[in,out] mtr Mini-transaction
@return root page num of the tablespace dictionary index copy */
page_no_t fsp_sdi_get_root_page_num(space_id_t space,
const page_size_t &page_size, mtr_t *mtr);
/** Write SDI Index root page num to page 0 of tablespace.
@param[in,out] page Page 0 frame
@param[in] page_size Size of page
@param[in] root_page_num Root page number of SDI
@param[in,out] mtr Mini-transaction */
void fsp_sdi_write_root_to_page(page_t *page, const page_size_t &page_size,
page_no_t root_page_num, mtr_t *mtr);
#include "fsp0fsp.ic"
/** Reads the server version from the first page of a tablespace.
@param[in] page first page of a tablespace
@return space server version */
inline uint32_t fsp_header_get_server_version(const page_t *page);
/** Reads the server space version from the first page of a tablespace.
@param[in] page first page of a tablespace
@return space server version */
inline uint32_t fsp_header_get_space_version(const page_t *page);
/** Get the state of an xdes.
@param[in] descr extent descriptor
@param[in,out] mtr Mini-transaction.
@return state */
inline xdes_state_t xdes_get_state(const xdes_t *descr, mtr_t *mtr) {
ut_ad(descr && mtr);
ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
const ulint state = mach_read_from_4(descr + XDES_STATE);
ut_ad(xdes_state_is_valid(state));
return (static_cast<xdes_state_t>(state));
}
#ifdef UNIV_DEBUG
/** Print the extent descriptor page in user-friendly format.
@param[in] out The output file stream
@param[in] xdes The extent descriptor page
@param[in] page_no The page number of xdes page
@param[in] mtr Mini-transaction.
@return None. */
std::ostream &xdes_page_print(std::ostream &out, const page_t *xdes,
page_no_t page_no, mtr_t *mtr);
inline bool xdes_mem_t::is_valid() const {
const ulint state = mach_read_from_4(m_xdes + XDES_STATE);
return (xdes_state_is_valid(state));
}
inline const char *xdes_mem_t::state_name() const {
const ulint val = mach_read_from_4(m_xdes + XDES_STATE);
ut_ad(xdes_state_is_valid(val));
xdes_state_t state = static_cast<xdes_state_t>(val);
switch (state) {
case XDES_NOT_INITED:
return ("XDES_NOT_INITED");
case XDES_FREE:
return ("XDES_FREE");
case XDES_FREE_FRAG:
return ("XDES_FREE_FRAG");
case XDES_FULL_FRAG:
return ("XDES_FULL_FRAG");
case XDES_FSEG:
return ("XDES_FSEG");
case XDES_FSEG_FRAG:
return ("XDES_FSEG_FRAG");
}
return ("UNKNOWN");
}
#endif /* UNIV_DEBUG */
/** Update the tablespace size information and generate redo log for it.
@param[in] header Tablespace header.
@param[in] size New tablespace size in pages.
@param[in] mtr Mini-transaction context. */
inline void fsp_header_size_update(fsp_header_t *header, ulint size,
mtr_t *mtr) {
DBUG_TRACE;
DBUG_LOG("ib_log", "old_size=" << mach_read_from_4(header + FSP_SIZE)
<< ", new_size=" << size);
mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
}
/** Check if a specified page is inode page or not. This is used for
index root pages of hard-coded DD tables, we can safely assume that the passed
in page number is in the range of pages which are only either index root page
or inode page
@param[in] page Page number to check
@return true if it's inode page, otherwise false */
inline bool fsp_is_inode_page(page_no_t page);
/** Get the offset of SDI root page number in page 0.
@param[in] page_size Page size.
@return offset on success, else 0 */
inline ulint fsp_header_get_sdi_offset(const page_size_t &page_size);
/** Get the offset of encryption progress information in page 0.
@param[in] page_size page size.
@return offset on success, otherwise 0. */
inline ulint fsp_header_get_encryption_progress_offset(
const page_size_t &page_size);
/** Determine if the tablespace has SDI.
@param[in] space_id Tablespace id
@return DB_SUCCESS if SDI is present else DB_ERROR
or DB_TABLESPACE_NOT_FOUND */
dberr_t fsp_has_sdi(space_id_t space_id);
/** Encrypt/Unencrypt a tablespace.
@param[in] thd current thread
@param[in] space_id Tablespace id
@param[in] to_encrypt true if to encrypt, false if to decrypt
@param[in,out] dd_space_in dd tablespace object
@return 0 for success, otherwise error code */
dberr_t fsp_alter_encrypt_tablespace(THD *thd, space_id_t space_id,
bool to_encrypt, void *dd_space_in);
/** Initiate roll-forward of alter encrypt in background thread */
void fsp_init_resume_alter_encrypt_tablespace();
/** A wrapper class to operate on a file segment inode pointer (fseg_inode_t*)
*/
class File_segment_inode {
public:
/** Constructor
@param[in] space_id Table space identifier
@param[in] page_size Size of each page in the tablespace.
@param[in] inode File segment inode pointer
@param[in] mtr Mini-transaction context. */
File_segment_inode(space_id_t space_id, const page_size_t &page_size,
fseg_inode_t *inode, mtr_t *mtr)
: m_space_id(space_id),
m_page_size(page_size),
m_fseg_inode(inode),
m_mtr(mtr) IF_DEBUG(, m_random_engine(m_rd()), m_dist(1, 100)) {}
/** Update the value of FSEG_NOT_FULL_N_USED.
@param[in] n_used the new value of FSEG_NOT_FULL_N_USED. */
void write_not_full_n_used(uint32_t n_used);
/** Get the current value of FSEG_NOT_FULL_N_USED.
@return the current value of FSEG_NOT_FULL_N_USED. */
uint32_t read_not_full_n_used() const;
/** Get the segment identifier value.
@return the segment identifier value. */
uint64_t get_seg_id() const {
return (mach_read_from_8(m_fseg_inode + FSEG_ID));
}
/** Print the current object into the given output stream.
@return the output stream. */
std::ostream &print(std::ostream &out) const;
private:
/** Unique tablespace identifier */
space_id_t m_space_id;
/** The page size used in this tablespace. */
const page_size_t &m_page_size;
/** file segment inode pointer that is being wrapped by this object. */
fseg_inode_t *m_fseg_inode;
/** The mini-transaction operation context. */
mtr_t *m_mtr;
#ifdef UNIV_DEBUG
public:
/** Verify the stored FSEG_NOT_FULL_N_USED value.
@return true if correct value, false if incorrect. */
bool verify_not_full_n_used();
private:
/** Calculate the value of FSEG_NOT_FULL_N_USED by traversing
the FSEG_NOT_FULL list.
@return the calculated value of FSEG_NOT_FULL_N_USED. */
page_no_t calculate_not_full_n_used();
std::random_device m_rd;
std::default_random_engine m_random_engine;
std::uniform_int_distribution<int> m_dist;
/** To reduce the cost of verification of FSEG_NOT_FULL_N_USED, do it
only when this function returns true.
@return true for 10% of the time. */
bool do_verify() { return (m_dist(m_random_engine) > 90); }
#endif /* UNIV_DEBUG */
};
/** The global output stream operator is overloaded to work with an object
of type File_segment_inode.
@param[in] out the output stream.
@param[in] obj an object of type File_segment_inode.
@return the output stream. */
inline std::ostream &operator<<(std::ostream &out,
const File_segment_inode &obj) {
return (obj.print(out));
}
/** Returns the file segment inode, page x-latched.
@param[in] header Segment header
@param[in] space Space id
@param[in] page_size Page size
@param[in,out] mtr Mini-transaction
@param[out] block Inode block
@return segment inode, page x-latched */
fseg_inode_t *fseg_inode_get(const fseg_header_t *header, space_id_t space,
const page_size_t &page_size, mtr_t *mtr,
buf_block_t **block = nullptr);
#endif
|