1/***************************************************************************** 2 3Copyright (c) 1995, 2019, Oracle and/or its affiliates. All Rights Reserved. 4 5This program is free software; you can redistribute it and/or modify it under 6the terms of the GNU General Public License, version 2.0, as published by the 7Free Software Foundation. 8 9This program is also distributed with certain software (including but not 10limited to OpenSSL) that is licensed under separate terms, as designated in a 11particular file or component or in included license documentation. The authors 12of MySQL hereby grant you an additional permission to link the program and 13your derivative works with the separately licensed software that they have 14included with MySQL. 15 16This program is distributed in the hope that it will be useful, but WITHOUT 17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, 19for more details. 20 21You should have received a copy of the GNU General Public License along with 22this program; if not, write to the Free Software Foundation, Inc., 2351 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 25*****************************************************************************/ 26 27/** @file include/fsp0fsp.ic 28 File space management 29 30 Created 12/18/1995 Heikki Tuuri 31 *******************************************************/ 32 33/** Checks if a page address is an extent descriptor page address. 34@param[in] page_id page id 35@param[in] page_size page size 36@return true if a descriptor page */ 37UNIV_INLINE 38ibool fsp_descr_page(const page_id_t &page_id, const page_size_t &page_size) { 39 return ((page_id.page_no() & (page_size.physical() - 1)) == FSP_XDES_OFFSET); 40} 41 42/** Determine if the tablespace is compressed from tablespace flags. 43@param[in] flags Tablespace flags 44@return true if compressed, false if not compressed */ 45UNIV_INLINE 46bool fsp_flags_is_compressed(uint32_t flags) { 47 return (FSP_FLAGS_GET_ZIP_SSIZE(flags) != 0); 48} 49 50#define ACTUAL_SSIZE(ssize) (0 == ssize ? UNIV_PAGE_SSIZE_ORIG : ssize) 51 52/** Determine if two tablespaces are equivalent or compatible. 53@param[in] flags1 First tablespace flags 54@param[in] flags2 Second tablespace flags 55@return true the flags are compatible, false if not */ 56UNIV_INLINE 57bool fsp_flags_are_equal(uint32_t flags1, uint32_t flags2) { 58 /* If either one of these flags is UINT32_UNDEFINED, 59 then they are not equal */ 60 if (flags1 == UINT32_UNDEFINED || flags2 == UINT32_UNDEFINED) { 61 return (false); 62 } 63 64 if (!fsp_is_shared_tablespace(flags1) || !fsp_is_shared_tablespace(flags2)) { 65 /* At least one of these is a single-table tablespaces so all 66 flags must match. */ 67 return (flags1 == flags2); 68 } 69 70 /* Both are shared tablespaces which can contain all formats. 71 But they must have the same logical and physical page size. 72 Once InnoDB can support multiple page sizes together, 73 the logical page size will not matter. */ 74 ulint zip_ssize1 = ACTUAL_SSIZE(FSP_FLAGS_GET_ZIP_SSIZE(flags1)); 75 ulint zip_ssize2 = ACTUAL_SSIZE(FSP_FLAGS_GET_ZIP_SSIZE(flags2)); 76 ulint page_ssize1 = ACTUAL_SSIZE(FSP_FLAGS_GET_PAGE_SSIZE(flags1)); 77 ulint page_ssize2 = ACTUAL_SSIZE(FSP_FLAGS_GET_PAGE_SSIZE(flags2)); 78 79 return (zip_ssize1 == zip_ssize2 && page_ssize1 == page_ssize2); 80} 81 82/** Convert a page size, which is a power of 2, to an ssize, which is 83the number of bit shifts from 512 to make that page size. 84@param[in] page_size compressed page size in bytes 85@return an ssize created from the page size provided. */ 86UNIV_INLINE 87uint32_t page_size_to_ssize(ulint page_size) { 88 uint32_t ssize; 89 90 for (ssize = UNIV_ZIP_SIZE_SHIFT_MIN; 91 static_cast<uint32_t>(1 << ssize) < page_size; ssize++) { 92 } 93 94 return (ssize - UNIV_ZIP_SIZE_SHIFT_MIN + 1); 95} 96 97/** Add the compressed page size to the tablespace flags. 98@param[in] flags Tablespace flags 99@param[in] page_size page sizes in bytes and compression flag. 100@return tablespace flags after zip size is added */ 101UNIV_INLINE 102uint32_t fsp_flags_set_zip_size(uint32_t flags, const page_size_t &page_size) { 103 if (!page_size.is_compressed()) { 104 return (flags); 105 } 106 107 /* Zip size should be a power of 2 between UNIV_ZIP_SIZE_MIN 108 and UNIV_ZIP_SIZE_MAX */ 109 ut_ad(page_size.physical() >= UNIV_ZIP_SIZE_MIN); 110 ut_ad(page_size.physical() <= UNIV_ZIP_SIZE_MAX); 111 ut_ad(ut_is_2pow(page_size.physical())); 112 113 uint32_t ssize = page_size_to_ssize(page_size.physical()); 114 115 ut_ad(ssize > 0); 116 ut_ad(ssize <= UNIV_PAGE_SSIZE_MAX); 117 118 flags |= (ssize << FSP_FLAGS_POS_ZIP_SSIZE); 119 120 ut_ad(fsp_flags_is_valid(flags)); 121 122 return (flags); 123} 124 125/** Add the page size to the tablespace flags. 126@param[in] flags Tablespace flags 127@param[in] page_size page sizes in bytes and compression flag. 128@return tablespace flags after page size is added */ 129UNIV_INLINE 130uint32_t fsp_flags_set_page_size(uint32_t flags, const page_size_t &page_size) { 131 /* Page size should be a power of two between UNIV_PAGE_SIZE_MIN 132 and UNIV_PAGE_SIZE */ 133 ut_ad(page_size.logical() >= UNIV_PAGE_SIZE_MIN); 134 ut_ad(page_size.logical() <= UNIV_PAGE_SIZE_MAX); 135 ut_ad(ut_is_2pow(page_size.logical())); 136 137 /* Remove this assert once we add support for different 138 page size per tablespace. Currently all tablespaces must 139 have a page size that is equal to innodb-page-size */ 140 ut_ad(page_size.logical() == UNIV_PAGE_SIZE); 141 142 if (page_size.logical() == UNIV_PAGE_SIZE_ORIG) { 143 ut_ad(0 == FSP_FLAGS_GET_PAGE_SSIZE(flags)); 144 145 } else { 146 uint32_t ssize = page_size_to_ssize(page_size.logical()); 147 148 ut_ad(ssize); 149 ut_ad(ssize <= UNIV_PAGE_SSIZE_MAX); 150 151 flags |= (ssize << FSP_FLAGS_POS_PAGE_SSIZE); 152 } 153 154 ut_ad(fsp_flags_is_valid(flags)); 155 156 return (flags); 157} 158 159/** Initialize an FSP flags integer. 160@param[in] page_size page sizes in bytes and compression flag. 161@param[in] atomic_blobs Used by Dynammic and Compressed. 162@param[in] has_data_dir This tablespace is in a remote location. 163@param[in] is_shared This tablespace can be shared by many tables. 164@param[in] is_temporary This tablespace is temporary. 165@param[in] is_encrypted This tablespace is encrypted. 166@return tablespace flags after initialization */ 167UNIV_INLINE 168uint32_t fsp_flags_init(const page_size_t &page_size, bool atomic_blobs, 169 bool has_data_dir, bool is_shared, bool is_temporary, 170 bool is_encrypted) { 171 ut_ad(page_size.physical() <= page_size.logical()); 172 ut_ad(!page_size.is_compressed() || atomic_blobs); 173 174 /* Page size should be a power of two between UNIV_PAGE_SIZE_MIN 175 and UNIV_PAGE_SIZE, but zip_size may be 0 if not compressed. */ 176 uint32_t flags = fsp_flags_set_page_size(0, page_size); 177 178 if (atomic_blobs) { 179 flags |= FSP_FLAGS_MASK_POST_ANTELOPE | FSP_FLAGS_MASK_ATOMIC_BLOBS; 180 } 181 182 /* If the zip_size is explicit and different from the default, 183 compressed row format is implied. */ 184 flags = fsp_flags_set_zip_size(flags, page_size); 185 186 if (has_data_dir) { 187 flags |= FSP_FLAGS_MASK_DATA_DIR; 188 } 189 190 /* Shared tablespaces can hold all row formats, so we only mark the 191 POST_ANTELOPE and ATOMIC_BLOB bits if it is compressed. */ 192 if (is_shared) { 193 ut_ad(!has_data_dir); 194 flags |= FSP_FLAGS_MASK_SHARED; 195 } 196 197 if (is_temporary) { 198 ut_ad(!has_data_dir); 199 flags |= FSP_FLAGS_MASK_TEMPORARY; 200 } 201 202 if (is_encrypted) { 203 flags |= FSP_FLAGS_MASK_ENCRYPTION; 204 } 205 206 return (flags); 207} 208 209/** Calculates the descriptor index within a descriptor page. 210@param[in] page_size page size 211@param[in] offset page offset 212@return descriptor index */ 213UNIV_INLINE 214ulint xdes_calc_descriptor_index(const page_size_t &page_size, ulint offset) { 215 return (ut_2pow_remainder(offset, page_size.physical()) / FSP_EXTENT_SIZE); 216} 217 218/** Gets a descriptor bit of a page. 219 @return true if free */ 220UNIV_INLINE 221ibool xdes_get_bit(const xdes_t *descr, /*!< in: descriptor */ 222 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */ 223 page_no_t offset) /*!< in: page offset within extent: 224 0 ... FSP_EXTENT_SIZE - 1 */ 225{ 226 ut_ad(offset < FSP_EXTENT_SIZE); 227 ut_ad(bit == XDES_FREE_BIT || bit == XDES_CLEAN_BIT); 228 229 ulint index = bit + XDES_BITS_PER_PAGE * offset; 230 231 ulint bit_index = index % 8; 232 ulint byte_index = index / 8; 233 234 return (ut_bit_get_nth( 235 mach_read_ulint(descr + XDES_BITMAP + byte_index, MLOG_1BYTE), 236 bit_index)); 237} 238 239/** Calculates the page where the descriptor of a page resides. 240@param[in] page_size page size 241@param[in] offset page offset 242@return descriptor page offset */ 243UNIV_INLINE 244page_no_t xdes_calc_descriptor_page(const page_size_t &page_size, 245 page_no_t offset) { 246 static_assert(UNIV_PAGE_SIZE_MAX > XDES_ARR_OFFSET + (UNIV_PAGE_SIZE_MAX / 247 FSP_EXTENT_SIZE_MAX) * 248 XDES_SIZE_MAX, 249 "Extent descriptor won't fit on a page"); 250 251 static_assert(UNIV_ZIP_SIZE_MIN > XDES_ARR_OFFSET + (UNIV_ZIP_SIZE_MIN / 252 FSP_EXTENT_SIZE_MIN) * 253 XDES_SIZE_MIN, 254 "Extent descriptor won't fit on a page"); 255 256 ut_ad(UNIV_PAGE_SIZE > 257 XDES_ARR_OFFSET + (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE) * XDES_SIZE); 258 ut_ad(UNIV_ZIP_SIZE_MIN > 259 XDES_ARR_OFFSET + (UNIV_ZIP_SIZE_MIN / FSP_EXTENT_SIZE) * XDES_SIZE); 260 261#ifdef UNIV_DEBUG 262 if (page_size.is_compressed()) { 263 ut_a(page_size.physical() > 264 XDES_ARR_OFFSET + 265 (page_size.physical() / FSP_EXTENT_SIZE) * XDES_SIZE); 266 } 267#endif /* UNIV_DEBUG */ 268 269 return (ut_2pow_round(offset, page_size.physical())); 270} 271 272/** Calculates the descriptor array size. 273@param[in] page_size page size 274@return size of descriptor array */ 275UNIV_INLINE 276ulint xdes_arr_size(const page_size_t &page_size) { 277 return (page_size.physical() / FSP_EXTENT_SIZE); 278} 279 280/** Check if a specified page is inode page or not. This is used for 281index root pages of hard-coded DD tables, we can safely assume that the passed 282in page number is in the range of pages which are only either index root page 283or inode page 284@param[in] page Page number to check 285@return true if it's inode page, otherwise false */ 286inline bool fsp_is_inode_page(page_no_t page) { 287 static const uint inode_per_page = FSP_SEG_INODES_PER_PAGE(univ_page_size); 288 289 /* Every two inode would be allocated for one index, and all inodes in 290 two inode pages are needed exactly for FSP_SEG_INODES_PER_PAGE indexes. 291 One inode page is at the beginning of one cycle, the other is in the 292 middle. If FSP_SEG_INODES_PER_PAGE is even, the second inode page 293 is at cycle / 2, if odd, it should be (cycle + 1) / 2 in the cycle. */ 294 static const uint cycle = inode_per_page + 2; 295 296 /* Number of all hard-coded DD table indexes. Please sync it with 297 innodb_dd_table array. */ 298 static const uint indexes = 98; 299 300 /* Max page number for index root pages of hard-coded DD tables. */ 301 static const uint max_page_no = 302 FSP_FIRST_INODE_PAGE_NO + 1 /* SDI Index page */ 303 + (indexes / inode_per_page) * cycle + (indexes % inode_per_page) + 304 ((indexes % inode_per_page) / ((inode_per_page + 1) / 2)); 305 306 /* The page range should be determinate for different page sizes. */ 307 ut_a(page >= FSP_FIRST_INODE_PAGE_NO); 308 ut_a(page <= max_page_no); 309 310 uint step = (page - FSP_FIRST_INODE_PAGE_NO) % cycle; 311 312 return (step == 0 || step == (cycle + 1) / 2); 313} 314 315/** Validate the tablespace flags. 316These flags are stored in the tablespace header at offset FSP_SPACE_FLAGS. 317They should be 0 for ROW_FORMAT=COMPACT and ROW_FORMAT=REDUNDANT. 318The newer row formats, COMPRESSED and DYNAMIC, will have at least 319the DICT_TF_COMPACT bit set. 320@param[in] flags Tablespace flags 321@return true if valid, false if not */ 322inline bool fsp_flags_is_valid(uint32_t flags) { 323 bool post_antelope = FSP_FLAGS_GET_POST_ANTELOPE(flags); 324 ulint zip_ssize = FSP_FLAGS_GET_ZIP_SSIZE(flags); 325 bool atomic_blobs = FSP_FLAGS_HAS_ATOMIC_BLOBS(flags); 326 ulint page_ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags); 327 bool has_data_dir = FSP_FLAGS_HAS_DATA_DIR(flags); 328 bool is_shared = FSP_FLAGS_GET_SHARED(flags); 329 bool is_temp = FSP_FLAGS_GET_TEMPORARY(flags); 330 331 ulint unused = FSP_FLAGS_GET_UNUSED(flags); 332 333 DBUG_EXECUTE_IF("fsp_flags_is_valid_failure", return (false);); 334 335 /* The Antelope row formats REDUNDANT and COMPACT did 336 not use tablespace flags, so the entire 4-byte field 337 is zero for Antelope row formats. */ 338 if (flags == 0) { 339 return (true); 340 } 341 342 /* Row_FORMAT=COMPRESSED and ROW_FORMAT=DYNAMIC use a feature called 343 ATOMIC_BLOBS which builds on the page structure introduced for the 344 COMPACT row format by allowing long fields to be broken into prefix 345 and externally stored parts. So if it is Post_antelope, it uses 346 Atomic BLOBs. */ 347 if (post_antelope != atomic_blobs) { 348 return (false); 349 } 350 351 /* Make sure there are no bits that we do not know about. */ 352 if (unused != 0) { 353 return (false); 354 } 355 356 /* The zip ssize can be zero if it is other than compressed row format, 357 or it could be from 1 to the max. */ 358 if (zip_ssize > PAGE_ZIP_SSIZE_MAX) { 359 return (false); 360 } 361 362 /* The actual page size must be within 4k and 64K (3 =< ssize =< 7). */ 363 if (page_ssize != 0 && 364 (page_ssize < UNIV_PAGE_SSIZE_MIN || page_ssize > UNIV_PAGE_SSIZE_MAX)) { 365 return (false); 366 } 367 368 /* Only single-table tablespaces use the DATA DIRECTORY clause. 369 It is not compatible with the TABLESPACE clause. Nor is it 370 compatible with the TEMPORARY clause. */ 371 if (has_data_dir && (is_shared || is_temp)) { 372 return (false); 373 } 374 375#if FSP_FLAGS_POS_UNUSED != 15 376#error You have added a new FSP_FLAG without adding a validation check. 377#endif 378 379 return (true); 380} 381 382/** Get the offset of SDI root page number in page 0. 383@param[in] page_size page size. 384@return offset on success, otherwise 0. */ 385inline ulint fsp_header_get_sdi_offset(const page_size_t &page_size) { 386 ulint offset; 387#ifdef UNIV_DEBUG 388 ulint left_size; 389#endif 390 391 offset = XDES_ARR_OFFSET + XDES_SIZE * xdes_arr_size(page_size) + 392 Encryption::INFO_MAX_SIZE; 393#ifdef UNIV_DEBUG 394 left_size = 395 page_size.physical() - FSP_HEADER_OFFSET - offset - FIL_PAGE_DATA_END; 396 397 ut_ad(left_size >= FSP_SDI_HEADER_LEN); 398#endif 399 return (offset); 400} 401 402/** Get the offset of encrytion progress information in page 0. 403@param[in] page_size page size. 404@return offset on success, otherwise 0. */ 405inline ulint fsp_header_get_encryption_progress_offset( 406 const page_size_t &page_size) { 407 ulint offset; 408#ifdef UNIV_DEBUG 409 ulint left_size; 410#endif 411 412 offset = fsp_header_get_sdi_offset(page_size) + FSP_SDI_HEADER_LEN; 413#ifdef UNIV_DEBUG 414 left_size = 415 page_size.physical() - FSP_HEADER_OFFSET - offset - FIL_PAGE_DATA_END; 416 417 ut_ad(left_size >= 418 Encryption::OPERATION_INFO_SIZE + Encryption::PROGRESS_INFO_SIZE); 419#endif 420 421 return (offset); 422} 423 424/** Reads the server space version from the first page of a tablespace. 425@param[in] page first page of a tablespace 426@return space server version */ 427UNIV_INLINE 428uint32 fsp_header_get_server_version(const page_t *page) { 429 uint32 version; 430 431 version = mach_read_from_4(page + FIL_PAGE_SRV_VERSION); 432 433 return (version); 434} 435 436/** Reads the server space version from the first page of a tablespace. 437@param[in] page first page of a tablespace 438@return space server version */ 439UNIV_INLINE 440uint32 fsp_header_get_space_version(const page_t *page) { 441 uint32 version; 442 443 version = mach_read_from_4(page + FIL_PAGE_SPACE_VERSION); 444 445 return (version); 446} 447