1 /***************************************************************************** 2 3 Copyright (c) 2016, 2019, Oracle and/or its affiliates. All Rights Reserved. 4 5 This program is free software; you can redistribute it and/or modify it under 6 the terms of the GNU General Public License, version 2.0, as published by the 7 Free Software Foundation. 8 9 This program is also distributed with certain software (including but not 10 limited to OpenSSL) that is licensed under separate terms, as designated in a 11 particular file or component or in included license documentation. The authors 12 of MySQL hereby grant you an additional permission to link the program and 13 your derivative works with the separately licensed software that they have 14 included with MySQL. 15 16 This program is distributed in the hope that it will be useful, but WITHOUT 17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, 19 for more details. 20 21 You should have received a copy of the GNU General Public License along with 22 this program; if not, write to the Free Software Foundation, Inc., 23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 25 *****************************************************************************/ 26 27 #ifndef lob0first_h 28 #define lob0first_h 29 30 #include "btr0btr.h" 31 #include "buf0buf.h" 32 #include "dict0dict.h" 33 #include "fut0lst.h" 34 #include "lob0index.h" 35 #include "lob0lob.h" 36 #include "lob0util.h" 37 #include "mtr0log.h" 38 39 namespace lob { 40 41 /** The first page of an uncompressed LOB. */ 42 struct first_page_t : public basic_page_t { 43 /** Version information. One byte. */ 44 static const ulint OFFSET_VERSION = FIL_PAGE_DATA; 45 46 /** One byte of flag bits. Currently only one bit (the least 47 significant bit) is used, other 7 bits are available for future use.*/ 48 static const ulint OFFSET_FLAGS = FIL_PAGE_DATA + 1; 49 50 /** LOB version. 4 bytes.*/ 51 static const uint32_t OFFSET_LOB_VERSION = OFFSET_FLAGS + 1; 52 53 /** The latest transaction that modified this LOB. */ 54 static const ulint OFFSET_LAST_TRX_ID = OFFSET_LOB_VERSION + 4; 55 56 /** The latest transaction undo_no that modified this LOB. */ 57 static const ulint OFFSET_LAST_UNDO_NO = OFFSET_LAST_TRX_ID + 6; 58 59 /** Length of data stored in this page. 4 bytes. */ 60 static const ulint OFFSET_DATA_LEN = OFFSET_LAST_UNDO_NO + 4; 61 62 /** The trx that created the data stored in this page. */ 63 static const ulint OFFSET_TRX_ID = OFFSET_DATA_LEN + 4; 64 65 /** The offset where the list base node is located. This is the list 66 of LOB pages. */ 67 static const ulint OFFSET_INDEX_LIST = OFFSET_TRX_ID + 6; 68 69 /** The offset where the list base node is located. This is the list 70 of free nodes. */ 71 static const ulint OFFSET_INDEX_FREE_NODES = 72 OFFSET_INDEX_LIST + FLST_BASE_NODE_SIZE; 73 74 /** The offset where the contents of the first page begins. */ 75 static const ulint LOB_PAGE_DATA = 76 OFFSET_INDEX_FREE_NODES + FLST_BASE_NODE_SIZE; 77 78 static const ulint LOB_PAGE_TRAILER_LEN = FIL_PAGE_DATA_END; 79 80 /** The default constructor. */ first_page_tfirst_page_t81 first_page_t() {} 82 83 /** Constructor. 84 @param[in] block the buffer block of the first page. 85 @param[in] mtr the mini-transaction context. */ first_page_tfirst_page_t86 first_page_t(buf_block_t *block, mtr_t *mtr) : basic_page_t(block, mtr) {} 87 88 /** Constructor. 89 @param[in] block the buffer block of the first page.*/ first_page_tfirst_page_t90 first_page_t(buf_block_t *block) : basic_page_t(block, nullptr) {} 91 92 /** Constructor. 93 @param[in] block the buffer block of the first page. 94 @param[in] mtr the mini-transaction context. 95 @param[in] index the clustered index containing the LOB. */ first_page_tfirst_page_t96 first_page_t(buf_block_t *block, mtr_t *mtr, dict_index_t *index) 97 : basic_page_t(block, mtr, index) {} 98 99 /** Constructor. 100 @param[in] mtr the mini-transaction context. 101 @param[in] index the clustered index containing the LOB. */ first_page_tfirst_page_t102 first_page_t(mtr_t *mtr, dict_index_t *index) 103 : basic_page_t(nullptr, mtr, index) {} 104 105 /** Set the LOB format version number to 0. */ set_version_0first_page_t106 void set_version_0() { 107 mlog_write_ulint(frame() + OFFSET_VERSION, 0, MLOG_1BYTE, m_mtr); 108 } 109 110 /** Obtain the flags value. This has 8 bits of which only the first 111 bit is used. 112 @return one byte flag */ get_flagsfirst_page_t113 uint8_t get_flags() { return (mach_read_from_1(frame() + OFFSET_FLAGS)); } 114 115 /** When the bit is set, the LOB is not partially updatable anymore. 116 @return true, if partially updatable. 117 @return false, if partially NOT updatable. */ can_be_partially_updatedfirst_page_t118 bool can_be_partially_updated() { 119 uint8_t flags = get_flags(); 120 return (!(flags & 0x01)); 121 } 122 123 /** Do tablespace import. */ 124 void import(trx_id_t trx_id); 125 126 /** When the bit is set, the LOB is not partially updatable anymore. 127 Enable the bit. 128 @param[in] trx the current transaction. */ 129 void mark_cannot_be_partially_updated(trx_t *trx); 130 131 /** Allocate the first page for uncompressed LOB. 132 @param[in,out] alloc_mtr the allocation mtr. 133 @param[in] is_bulk true if it is bulk operation 134 (OPCODE_INSERT_BULK) 135 @return the allocated buffer block.*/ 136 buf_block_t *alloc(mtr_t *alloc_mtr, bool is_bulk); 137 138 /** Free all the index pages. The list of index pages can be accessed 139 by traversing via the FIL_PAGE_NEXT field.*/ 140 void free_all_index_pages(); 141 142 /** Free all the data pages. The data pages can be accessed through 143 index entry. */ 144 void free_all_data_pages(); 145 146 /** Load the first page of LOB with s-latch. 147 @param[in] page_id the page identifier of the first page. 148 @param[in] page_size the page size information. 149 @return the buffer block of the first page. */ load_sfirst_page_t150 buf_block_t *load_s(page_id_t page_id, page_size_t page_size) { 151 m_block = buf_page_get(page_id, page_size, RW_S_LATCH, m_mtr); 152 return (m_block); 153 } 154 155 /** Load the first page of LOB with x-latch. 156 @param[in] page_id the page identifier of the first page. 157 @param[in] page_size the page size information. 158 @param[in] mtr the mini transaction context for latch. 159 @return the buffer block of the first page. */ 160 buf_block_t *load_x(const page_id_t &page_id, const page_size_t &page_size, 161 mtr_t *mtr); 162 163 /** Load the first page of LOB with x-latch in the given mtr context. 164 The first page must already be x-latched by the m_mtr. 165 @param[in] mtr the mini transaction context for latch. 166 @return the buffer block of the first page. */ load_xfirst_page_t167 buf_block_t *load_x(mtr_t *mtr) const { 168 ut_ad(mtr_memo_contains(m_mtr, m_block, MTR_MEMO_PAGE_X_FIX)); 169 buf_block_t *tmp = buf_page_get(m_block->page.id, m_index->get_page_size(), 170 RW_X_LATCH, mtr); 171 ut_ad(tmp == m_block); 172 return (tmp); 173 } 174 175 /** Load the first page of LOB with x-latch. 176 @param[in] page_id the page identifier of the first page. 177 @param[in] page_size the page size information. 178 @return the buffer block of the first page. */ load_xfirst_page_t179 buf_block_t *load_x(const page_id_t &page_id, const page_size_t &page_size) { 180 return (load_x(page_id, page_size, m_mtr)); 181 } 182 183 /** Get the buffer block of the LOB first page. 184 @return the buffer block. */ get_blockfirst_page_t185 buf_block_t *get_block() { return (m_block); } 186 187 /** Load the file list node from the given location. An x-latch is taken 188 on the page containing the file list node. 189 @param[in] addr the location of file list node. 190 @param[in] mtr the mini transaction context to be used. 191 @return the file list node.*/ addr2ptr_xfirst_page_t192 flst_node_t *addr2ptr_x(fil_addr_t &addr, mtr_t *mtr) const { 193 space_id_t space = dict_index_get_space(m_index); 194 const page_size_t page_size = dict_table_page_size(m_index->table); 195 return (fut_get_ptr(space, page_size, addr, RW_X_LATCH, mtr)); 196 } 197 198 /** Load the file list node from the given location. An x-latch is taken 199 on the page containing the file list node. 200 @param[in] addr the location of file list node. 201 @return the file list node.*/ addr2ptr_xfirst_page_t202 flst_node_t *addr2ptr_x(fil_addr_t &addr) const { 203 return (addr2ptr_x(addr, m_mtr)); 204 } 205 206 /** Load the file list node from the given location, assuming that it 207 exists in the first page itself. 208 @param[in] addr the location of file list node. 209 @return the file list node.*/ addr2ptrfirst_page_t210 flst_node_t *addr2ptr(const fil_addr_t &addr) { 211 ut_ad(m_block->page.id.page_no() == addr.page); 212 return (buf_block_get_frame(m_block) + addr.boffset); 213 } 214 215 /** Load the file list node from the given location. An s-latch is taken 216 on the page containing the file list node. 217 @param[in] addr the location of file list node. 218 @return the file list node.*/ addr2ptr_sfirst_page_t219 flst_node_t *addr2ptr_s(fil_addr_t &addr) { 220 space_id_t space = dict_index_get_space(m_index); 221 const page_size_t page_size = dict_table_page_size(m_index->table); 222 return (fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr)); 223 } 224 225 /** Load the file list node from the given location. An s-latch is taken 226 on the page containing the file list node. The given cache is checked to 227 see if the page is already loaded. 228 @param[in] cache cache of loaded buffer blocks. 229 @param[in] addr the location of file list node. 230 @return the file list node.*/ addr2ptr_s_cachefirst_page_t231 flst_node_t *addr2ptr_s_cache(std::map<page_no_t, buf_block_t *> &cache, 232 fil_addr_t &addr) const { 233 byte *result; 234 space_id_t space = dict_index_get_space(m_index); 235 const page_size_t page_size = dict_table_page_size(m_index->table); 236 237 auto iter = cache.find(addr.page); 238 239 if (iter == cache.end()) { 240 /* Not there in cached blocks. Add the loaded block to cache. */ 241 buf_block_t *block = nullptr; 242 result = fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr, &block); 243 cache.insert(std::make_pair(addr.page, block)); 244 } else { 245 buf_block_t *block = iter->second; 246 ut_ad(block->page.id.page_no() == addr.page); 247 result = buf_block_get_frame(block) + addr.boffset; 248 } 249 return (result); 250 } 251 252 /** Free the first page. This is done when all other LOB pages have 253 been freed. */ 254 void dealloc(); 255 256 /** Free all the pages associated with this LOB. */ 257 void destroy(); 258 259 /** Check if the index list is empty or not. 260 @return true if empty, false otherwise. */ is_emptyfirst_page_t261 bool is_empty() const { 262 flst_base_node_t *base = index_list(); 263 ut_ad(base != nullptr); 264 return (flst_get_len(base) == 0); 265 } 266 267 /** Allocate one index entry. If required an index page (of type 268 FIL_PAGE_TYPE_LOB_INDEX) will be allocated. 269 @param[in] bulk true if it is a bulk operation 270 (OPCODE_INSERT_BULK), false otherwise. 271 @return the file list node of the index entry. */ 272 flst_node_t *alloc_index_entry(bool bulk); 273 274 /** Get a pointer to the beginning of the index entry nodes in the 275 first part of the page. 276 @return the first index entry node. */ nodes_beginfirst_page_t277 byte *nodes_begin() const { return (frame() + LOB_PAGE_DATA); } 278 279 /** Calculate and return the payload. 280 @return the payload possible in this page. */ payloadfirst_page_t281 static ulint payload() { 282 return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN); 283 } 284 285 /** Set the transaction identifier in the first page header without 286 generating redo logs. 287 @param[in] id the transaction identifier. */ set_trx_id_no_redofirst_page_t288 void set_trx_id_no_redo(trx_id_t id) { 289 byte *ptr = frame() + OFFSET_TRX_ID; 290 mach_write_to_6(ptr, id); 291 } 292 293 /** Set the transaction identifier in the first page header. 294 @param[in] id the transaction identifier. */ set_trx_idfirst_page_t295 void set_trx_id(trx_id_t id) { 296 byte *ptr = frame() + OFFSET_TRX_ID; 297 mach_write_to_6(ptr, id); 298 mlog_log_string(ptr, 6, m_mtr); 299 } 300 301 /** Initialize the LOB version to 1. */ init_lob_versionfirst_page_t302 void init_lob_version() { 303 ut_ad(m_mtr != nullptr); 304 305 mlog_write_ulint(frame() + OFFSET_LOB_VERSION, 1, MLOG_4BYTES, m_mtr); 306 } 307 308 /** Get the lob version number. 309 @return the lob version. */ get_lob_versionfirst_page_t310 uint32_t get_lob_version() { 311 return (mach_read_from_4(frame() + OFFSET_LOB_VERSION)); 312 } 313 314 /** Increment the lob version by 1. */ 315 uint32_t incr_lob_version(); 316 317 /** Set the last transaction identifier, without generating redo log 318 records. 319 @param[in] id the trx identifier. */ set_last_trx_id_no_redofirst_page_t320 void set_last_trx_id_no_redo(trx_id_t id) { 321 byte *ptr = frame() + OFFSET_LAST_TRX_ID; 322 mach_write_to_6(ptr, id); 323 } 324 325 /** Set the last transaction identifier. 326 @param[in] id the trx identifier. */ set_last_trx_idfirst_page_t327 void set_last_trx_id(trx_id_t id) { 328 byte *ptr = frame() + OFFSET_LAST_TRX_ID; 329 mach_write_to_6(ptr, id); 330 mlog_log_string(ptr, 6, m_mtr); 331 } 332 333 /** Set the last transaction undo number. 334 @param[in] undo_no the trx undo number. */ set_last_trx_undo_nofirst_page_t335 void set_last_trx_undo_no(undo_no_t undo_no) { 336 ut_ad(m_mtr != nullptr); 337 338 byte *ptr = frame() + OFFSET_LAST_UNDO_NO; 339 mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr); 340 } 341 342 /** Get the last transaction identifier. 343 @return the transaction identifier. */ get_last_trx_idfirst_page_t344 trx_id_t get_last_trx_id() const { 345 byte *ptr = frame() + OFFSET_LAST_TRX_ID; 346 return (mach_read_from_6(ptr)); 347 } 348 349 /** Get the last transaction undo number. 350 @return the transaction undo number. */ get_last_trx_undo_nofirst_page_t351 undo_no_t get_last_trx_undo_no() const { 352 byte *ptr = frame() + OFFSET_LAST_UNDO_NO; 353 return (mach_read_from_4(ptr)); 354 } 355 356 /** Set the length of data stored in bytes. 357 @param[in] len amount of data stored in bytes. */ set_data_lenfirst_page_t358 void set_data_len(ulint len) { 359 ut_ad(m_mtr != nullptr); 360 361 mlog_write_ulint(frame() + OFFSET_DATA_LEN, len, MLOG_4BYTES, m_mtr); 362 } 363 364 /** Write as much as possible of the given data into the page. 365 @param[in] trxid the current transaction. 366 @param[in] data the data to be written. 367 @param[in] len the length of the given data. 368 @return number of bytes actually written. */ 369 ulint write(trx_id_t trxid, const byte *&data, ulint &len); 370 371 /** Replace data in the page by making a copy-on-write. 372 @param[in] trx the current transaction. 373 @param[in] offset the location where replace operation starts. 374 @param[in,out] ptr the buffer containing new data. after the 375 call it will point to remaining data. 376 @param[in,out] want requested amount of data to be replaced. 377 after the call it will contain amount of 378 data yet to be replaced. 379 @param[in] mtr the mini-transaction context. 380 @return the newly allocated buffer block. */ 381 buf_block_t *replace(trx_t *trx, ulint offset, const byte *&ptr, ulint &want, 382 mtr_t *mtr); 383 384 /** Replace data in the page inline. 385 @param[in] trx the current transaction. 386 @param[in] offset the location where replace operation starts. 387 @param[in,out] ptr the buffer containing new data. after the 388 call it will point to remaining data. 389 @param[in,out] want requested amount of data to be replaced. 390 after the call it will contain amount of 391 data yet to be replaced. 392 @param[in] mtr the mini-transaction context.*/ 393 void replace_inline(trx_t *trx, ulint offset, const byte *&ptr, ulint &want, 394 mtr_t *mtr); 395 get_data_lenfirst_page_t396 ulint get_data_len() const { 397 return (mach_read_from_4(frame() + OFFSET_DATA_LEN)); 398 } 399 400 /** Read data from the first page. 401 @param[in] offset the offset from where read starts. 402 @param[out] ptr the output buffer 403 @param[in] want number of bytes to read. 404 @return number of bytes read. */ 405 ulint read(ulint offset, byte *ptr, ulint want); 406 set_page_typefirst_page_t407 void set_page_type() { 408 ut_ad(m_mtr != nullptr); 409 410 mlog_write_ulint(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_FIRST, 411 MLOG_2BYTES, m_mtr); 412 } 413 index_listfirst_page_t414 flst_base_node_t *index_list() const { return (frame() + OFFSET_INDEX_LIST); } 415 free_listfirst_page_t416 flst_base_node_t *free_list() const { 417 return (frame() + OFFSET_INDEX_FREE_NODES); 418 } 419 420 /** Get the number of bytes used to store LOB data in the first page 421 of uncompressed LOB. 422 @return Number of bytes available for LOB data. */ max_space_availablefirst_page_t423 static ulint max_space_available() { 424 const ulint index_array_size = node_count() * index_entry_t::SIZE; 425 426 return (payload() - index_array_size); 427 } 428 429 /** Get the number of index entries this page can hold. 430 @return Number of index entries this page can hold. */ node_countfirst_page_t431 constexpr static ulint node_count() { 432 /* Each index entry is of size 60 bytes. We store only 10 433 index entries in the first page of the LOB. This means that 434 only 600 bytes are used for index data in the first page of 435 LOB. This will help to reserve more space in the first page 436 for the LOB data.*/ 437 return (10); 438 } 439 440 std::ostream &print_index_entries(std::ostream &out) const; 441 442 std::ostream &print_index_entries_cache_s(std::ostream &out, 443 BlockCache &cache) const; 444 445 /** Obtain the location where the data begins. 446 @return pointer to location within page where data begins. */ data_beginfirst_page_t447 byte *data_begin() const { 448 ut_ad(buf_block_get_page_zip(m_block) == NULL); 449 450 constexpr ulint index_array_size = node_count() * index_entry_t::SIZE; 451 452 return (frame() + LOB_PAGE_DATA + index_array_size); 453 } 454 455 /** Append data into a LOB first page. */ 456 ulint append(trx_id_t trxid, byte *&data, ulint &len); 457 458 #ifdef UNIV_DEBUG 459 /** Validate the first page. */ 460 bool validate() const; 461 #endif /* UNIV_DEBUG */ 462 get_page_typefirst_page_t463 page_type_t get_page_type() { return (basic_page_t::get_page_type()); } 464 get_page_typefirst_page_t465 static page_type_t get_page_type(dict_index_t *index, 466 const page_id_t &page_id, 467 const page_size_t &page_size) { 468 mtr_t local_mtr; 469 mtr_start(&local_mtr); 470 first_page_t first(&local_mtr, index); 471 first.load_x(page_id, page_size); 472 page_type_t page_type = first.get_page_type(); 473 mtr_commit(&local_mtr); 474 return (page_type); 475 } 476 477 public: 478 /** Restart the given mtr. The first page must already be x-latched by 479 the m_mtr. 480 @param[in] mtr the mini transaction context which is to be restarted. */ restart_mtrfirst_page_t481 void restart_mtr(mtr_t *mtr) { 482 ut_ad(mtr != m_mtr); 483 mtr_commit(mtr); 484 mtr_start(mtr); 485 mtr->set_log_mode(m_mtr->get_log_mode()); 486 load_x(mtr); 487 } 488 }; 489 490 } /* namespace lob */ 491 492 #endif /* lob0first_h */ 493