1 /***************************************************************************** 2 3 Copyright (c) 2016, 2018, 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 #ifndef _lob0int_h_ 27 #define _lob0int_h_ 28 29 #include <string.h> 30 31 #include "buf0buf.h" 32 #include "fut0fut.h" 33 #include "fut0lst.h" 34 #include "lot0buf.h" 35 #include "lot0types.h" 36 #include "trx0types.h" 37 38 namespace lob { 39 const ulint LOB_HDR_PART_LEN = 0; 40 const ulint LOB_HDR_TRX_ID = 4; 41 const ulint LOB_HDR_SIZE = 10; 42 const ulint LOB_PAGE_DATA = FIL_PAGE_DATA + LOB_HDR_SIZE; 43 44 /** The offset where the list base node is located. This is the list 45 of LOB pages. */ 46 const ulint LOB_INDEX_LIST = LOB_PAGE_DATA; 47 48 /** The offset where the list base node is located. This is the list 49 of free pages. */ 50 const ulint LOB_INDEX_FREE_NODES = LOB_PAGE_DATA + 16; 51 52 const ulint LOB_PAGE_TRAILER_LEN = 8; 53 54 /** This page type refers to the first page of an LOB, which contains 55 half data and half meta data. */ 56 #define FIL_PAGE_TYPE_LOB_BASE 21 57 58 struct base_node_page_t; 59 60 /** An index entry pointing to an LOB page. */ 61 struct index_entry_t { index_entry_tindex_entry_t62 index_entry_t(flst_node_t *node) : m_node(node) {} resetindex_entry_t63 void reset(flst_node_t *node) { m_node = node; } 64 65 /** Index entry offsets within node. */ 66 static const ulint OFFSET_PREV = 0; 67 static const ulint OFFSET_NEXT = 6; 68 69 /** Points to base node of the list of versions. The size of base node is 70 16 bytes. */ 71 static const ulint OFFSET_VERSIONS = 12; 72 static const ulint OFFSET_TRXID = 28; 73 static const ulint OFFSET_PAGE_NO = 34; 74 static const ulint OFFSET_DATA_LEN = 38; 75 76 /** Total length of an index node. */ 77 static const ulint SIZE = 40; 78 79 /** The versions base node is set to NULL. */ set_versions_nullindex_entry_t80 void set_versions_null() { 81 byte *base_node = get_versions_ptr(); 82 flst_init(base_node); 83 } 84 85 /** Can this index entry be purged. 86 @param[in] trxid the transaction that is being purged. 87 @return true if this entry can be purged, false otherwise. */ can_be_purgedindex_entry_t88 bool can_be_purged(trx_id_t trxid) { return (trxid == get_trx_id()); } 89 90 /* The given entry becomes the old version of the current entry. 91 Move the version base node from old entry to current entry. 92 @param[in] entry the old entry */ set_old_versionindex_entry_t93 void set_old_version(index_entry_t &entry) { 94 flst_node_t *node = entry.get_node_ptr(); 95 flst_base_node_t *version_list = get_versions_ptr(); 96 ut_ad(flst_get_len(version_list) == 0); 97 98 entry.move_version_base_node(*this); 99 flst_add_first(version_list, node); 100 } 101 102 /** The current index entry points to a latest LOB page. It may or may 103 not have older versions. If older version is there, bring it back to the 104 index list from the versions list. Then remove the current entry from 105 the index list. Move the versions list from current entry to older entry. 106 @param[in] trxid The transaction identifier. 107 @param[in] first_page The first lob page containing index list and free 108 list. */ 109 void make_old_version_current(trx_id_t trxid, base_node_page_t &first_page); 110 111 fil_addr_t purge_version(trx_id_t trxid, flst_base_node_t *ver_list, 112 flst_base_node_t *free_list); 113 add_versionindex_entry_t114 void add_version(index_entry_t &entry) const { 115 flst_node_t *node = entry.get_node_ptr(); 116 flst_base_node_t *version_list = get_versions_ptr(); 117 flst_add_first(version_list, node); 118 } 119 get_versions_listindex_entry_t120 flst_base_node_t *get_versions_list() const { return (get_versions_ptr()); } 121 get_trx_idindex_entry_t122 trx_id_t get_trx_id() const { 123 byte *ptr = get_trxid_ptr(); 124 return (mach_read_from_6(ptr)); 125 } 126 set_trx_idindex_entry_t127 void set_trx_id(trx_id_t id) { 128 byte *ptr = get_trxid_ptr(); 129 return (mach_write_to_6(ptr, id)); 130 } 131 set_page_noindex_entry_t132 void set_page_no(page_no_t num) { 133 byte *ptr = get_pageno_ptr(); 134 return (mach_write_to_4(ptr, num)); 135 } 136 set_prev_nullindex_entry_t137 void set_prev_null() { flst_write_addr(m_node + OFFSET_PREV, fil_addr_null); } 138 set_next_nullindex_entry_t139 void set_next_null() { flst_write_addr(m_node + OFFSET_NEXT, fil_addr_null); } 140 get_page_noindex_entry_t141 page_no_t get_page_no() { 142 byte *ptr = get_pageno_ptr(); 143 return (mach_read_from_4(ptr)); 144 } 145 set_data_lenindex_entry_t146 void set_data_len(ulint len) { 147 byte *ptr = get_datalen_ptr(); 148 return (mach_write_to_2(ptr, len)); 149 } 150 get_data_lenindex_entry_t151 ulint get_data_len() { 152 byte *ptr = get_datalen_ptr(); 153 return (mach_read_from_2(ptr)); 154 } 155 printindex_entry_t156 std::ostream &print(std::ostream &out) { 157 out << "[index_entry_t: trxid=" << get_trx_id() 158 << ", page_no=" << get_page_no() << ", data_len=" << get_data_len() 159 << "]"; 160 return (out); 161 } 162 is_sameindex_entry_t163 bool is_same(const index_entry_t &that) { return (m_node == that.m_node); } 164 165 private: 166 /** Move the version base node from current entry to the given entry. 167 @param[in] entry The index entry to which the version base node is moved 168 to. */ 169 void move_version_base_node(index_entry_t &entry); 170 171 /** Purge the current index entry. An index entry points to either a FIRST 172 page or DATA page. That LOB page will be freed if it is DATA page. A FIRST 173 page should not be freed. */ 174 void purge(); 175 get_versions_ptrindex_entry_t176 byte *get_versions_ptr() const { return (m_node + OFFSET_VERSIONS); } get_trxid_ptrindex_entry_t177 byte *get_trxid_ptr() const { return (m_node + OFFSET_TRXID); } get_pageno_ptrindex_entry_t178 byte *get_pageno_ptr() const { return (m_node + OFFSET_PAGE_NO); } get_datalen_ptrindex_entry_t179 byte *get_datalen_ptr() const { return (m_node + OFFSET_DATA_LEN); } get_node_ptrindex_entry_t180 byte *get_node_ptr() const { return (m_node); } 181 182 byte *m_node; 183 }; 184 185 struct page_t { page_tpage_t186 page_t() : m_block(nullptr) {} page_tpage_t187 page_t(buf_block_t *block) : m_block(block) {} 188 get_trx_idpage_t189 trx_id_t get_trx_id() const { 190 return (mach_read_from_6(frame() + FIL_PAGE_DATA + LOB_HDR_TRX_ID)); 191 } 192 get_page_nopage_t193 page_no_t get_page_no() const { 194 return (mach_read_from_4(frame() + FIL_PAGE_OFFSET)); 195 } 196 set_data_lenpage_t197 void set_data_len(ulint len) { 198 mach_write_to_4(frame() + FIL_PAGE_DATA + LOB_HDR_PART_LEN, len); 199 } 200 set_next_pagepage_t201 void set_next_page(page_no_t page_no) { 202 mach_write_to_4(frame() + FIL_PAGE_NEXT, page_no); 203 } 204 set_next_page_nullpage_t205 void set_next_page_null() { 206 mach_write_to_4(frame() + FIL_PAGE_NEXT, FIL_NULL); 207 } 208 get_next_pagepage_t209 page_no_t get_next_page() { 210 return (mach_read_from_4(frame() + FIL_PAGE_NEXT)); 211 } 212 get_data_lenpage_t213 ulint get_data_len() { 214 return (mach_read_from_4(frame() + FIL_PAGE_DATA + LOB_HDR_PART_LEN)); 215 } 216 framepage_t217 byte *frame() const { return (buf_block_get_frame(m_block)); } 218 219 static ulint payload(); 220 ulint max_space_available(); 221 222 protected: 223 buf_block_t *m_block; 224 }; 225 226 struct data_page_t : public page_t { data_page_tdata_page_t227 data_page_t() {} data_page_tdata_page_t228 data_page_t(buf_block_t *block) : page_t(block) {} 229 allocdata_page_t230 buf_block_t *alloc() { 231 ut_ad(m_block == nullptr); 232 m_block = btr_page_alloc(); 233 set_page_type(); 234 set_next_page_null(); 235 return (m_block); 236 } 237 set_page_typedata_page_t238 void set_page_type() { 239 mach_write_to_2(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_DATA); 240 } 241 set_trx_iddata_page_t242 void set_trx_id(trx_id_t id) { 243 mach_write_to_6(frame() + FIL_PAGE_DATA + LOB_HDR_TRX_ID, id); 244 } 245 payloaddata_page_t246 static ulint payload() { 247 return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN); 248 } 249 data_begindata_page_t250 byte *data_begin() const { return (frame() + LOB_PAGE_DATA); } 251 252 buf_block_t *replace(trx_id_t trxid, ulint offset, byte *&ptr, ulint &want); 253 readdata_page_t254 ulint read(trx_id_t trxid, ulint offset, byte *ptr, ulint want) { 255 byte *start = data_begin(); 256 start += offset; 257 ulint avail_data = get_data_len() - offset; 258 259 ulint copy_len = want < avail_data ? want : avail_data; 260 memcpy(ptr, start, copy_len); 261 return (copy_len); 262 } 263 writedata_page_t264 ulint write(trx_id_t trxid, byte *&data, ulint &len) { 265 byte *ptr = data_begin(); 266 ulint written = (len > payload()) ? payload() : len; 267 memcpy(ptr, data, written); 268 set_data_len(written); 269 270 data += written; 271 len -= written; 272 273 return (written); 274 } 275 276 ulint append(trx_id_t trxid, byte *&data, ulint &len); 277 278 std::pair<ulint, byte *> insert_middle(trx_id_t trxid, ulint offset, 279 byte *&data, ulint &len, 280 buf_block_t *&new_block); 281 282 buf_block_t *remove_middle(trx_id_t trxid, ulint offset, ulint &len); 283 max_space_availabledata_page_t284 ulint max_space_available() const { return (payload()); } 285 }; 286 287 struct base_node_page_t : public page_t { base_node_page_tbase_node_page_t288 base_node_page_t() {} base_node_page_tbase_node_page_t289 base_node_page_t(buf_block_t *block) : page_t(block) {} 290 291 buf_block_t *alloc(); 292 deallocbase_node_page_t293 void dealloc() { 294 btr_page_free(m_block); 295 m_block = nullptr; 296 } 297 emptybase_node_page_t298 bool empty() const { 299 flst_base_node_t *base = index_list(); 300 return (flst_get_len(base) == 0); 301 } 302 303 /** Allocate one index entry. */ 304 flst_node_t *alloc_index_entry(); 305 nodes_beginbase_node_page_t306 byte *nodes_begin() const { 307 return (frame() + LOB_PAGE_DATA + FLST_BASE_NODE_SIZE + 308 FLST_BASE_NODE_SIZE); 309 } 310 payloadbase_node_page_t311 static ulint payload() { 312 return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN - 313 FLST_BASE_NODE_SIZE - FLST_BASE_NODE_SIZE); 314 } 315 set_trx_idbase_node_page_t316 void set_trx_id(trx_id_t id) { 317 mach_write_to_6(frame() + FIL_PAGE_DATA + LOB_HDR_TRX_ID, id); 318 } 319 320 std::pair<ulint, byte *> insert_middle(trx_id_t trxid, ulint offset, 321 byte *&data, ulint &len, 322 buf_block_t *&new_block); 323 324 buf_block_t *remove_middle(trx_id_t trxid, ulint offset, ulint &len); 325 326 /** Write as much as possible of the given data into the page. */ writebase_node_page_t327 ulint write(trx_id_t trxid, byte *&data, ulint &len) { 328 byte *ptr = data_begin(); 329 ulint space_available = payload() / 2; 330 ulint written = (len > space_available) ? space_available : len; 331 memcpy(ptr, data, written); 332 set_data_len(written); 333 set_trx_id(trxid); 334 335 data += written; 336 len -= written; 337 338 return (written); 339 } 340 341 buf_block_t *replace(trx_id_t trxid, ulint offset, byte *&ptr, ulint &want); 342 readbase_node_page_t343 ulint read(trx_id_t trxid, ulint offset, byte *ptr, ulint want) { 344 byte *start = data_begin(); 345 start += offset; 346 ulint avail_data = get_data_len() - offset; 347 348 ulint copy_len = want < avail_data ? want : avail_data; 349 memcpy(ptr, start, copy_len); 350 return (copy_len); 351 } 352 set_page_typebase_node_page_t353 void set_page_type() { 354 mach_write_to_2(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_FIRST); 355 } 356 index_listbase_node_page_t357 flst_base_node_t *index_list() const { return (frame() + LOB_INDEX_LIST); } 358 free_listbase_node_page_t359 flst_base_node_t *free_list() const { 360 return (frame() + LOB_INDEX_FREE_NODES); 361 } 362 max_space_availablebase_node_page_t363 static ulint max_space_available() { return (payload() / 2); } 364 365 /** Get the number of index entries this page can hold. 366 @return Number of index entries this page can hold. */ node_countbase_node_page_t367 static ulint node_count() { 368 return (max_space_available() / index_entry_t::SIZE); 369 } 370 print_index_entriesbase_node_page_t371 std::ostream &print_index_entries(std::ostream &out) const { 372 flst_base_node_t *base = index_list(); 373 fil_addr_t node_loc = flst_get_first(base); 374 375 while (!fil_addr_is_null(node_loc)) { 376 flst_node_t *node = fut_get_ptr(node_loc); 377 out << (void *)node << std::endl; 378 index_entry_t entry(node); 379 entry.print(out) << std::endl; 380 node_loc = flst_get_next_addr(node); 381 } 382 return (out); 383 } 384 data_beginbase_node_page_t385 byte *data_begin() const { 386 ulint space_available = payload() / 2; 387 return (frame() + UNIV_PAGE_SIZE - LOB_PAGE_TRAILER_LEN - space_available); 388 } 389 390 /** Append data into a LOB first page. */ 391 ulint append(trx_id_t trxid, byte *&data, ulint &len); 392 }; 393 394 struct node_page_t : public page_t { node_page_tnode_page_t395 node_page_t() {} node_page_tnode_page_t396 node_page_t(buf_block_t *block) : page_t(block) {} 397 398 buf_block_t *alloc(base_node_page_t &first_page); 399 payloadnode_page_t400 static ulint payload() { 401 return (UNIV_PAGE_SIZE - FIL_PAGE_DATA - FIL_PAGE_DATA_END); 402 } 403 max_space_availablenode_page_t404 static ulint max_space_available() { return (payload()); } 405 406 /** Get the number of index entries this page can hold. 407 @return Number of index entries this page can hold. */ node_countnode_page_t408 static ulint node_count() { 409 return (max_space_available() / index_entry_t::SIZE); 410 } 411 set_page_typenode_page_t412 void set_page_type() { 413 mach_write_to_2(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_INDEX); 414 } 415 nodes_beginnode_page_t416 byte *nodes_begin() const { return (frame() + LOB_PAGE_DATA); } 417 }; 418 419 } // namespace lob 420 #endif // _lob0int_h_ 421