1 /***************************************************************************** 2 3 Copyright (c) 2014, 2021, Oracle and/or its affiliates. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License, version 2.0, 7 as published by the Free Software Foundation. 8 9 This program is also distributed with certain software (including 10 but not limited to OpenSSL) that is licensed under separate terms, 11 as designated in a particular file or component or in included license 12 documentation. The authors of MySQL hereby grant you an additional 13 permission to link the program and your derivative works with the 14 separately licensed software that they have included with MySQL. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License, version 2.0, 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 Street, Suite 500, Boston, MA 02110-1335 USA 24 25 *****************************************************************************/ 26 27 /********************************************************************//** 28 @file include/btr0bulk.h 29 The B-tree bulk load 30 31 Created 03/11/2014 Shaohua Wang 32 *************************************************************************/ 33 34 #ifndef btr0bulk_h 35 #define btr0bulk_h 36 37 #include "dict0dict.h" 38 #include "page0cur.h" 39 #include "ut0new.h" 40 41 #include <vector> 42 43 /** Innodb B-tree index fill factor for bulk load. */ 44 extern long innobase_fill_factor; 45 46 /* 47 The proper function call sequence of PageBulk is as below: 48 -- PageBulk::init 49 -- PageBulk::insert 50 -- PageBulk::finish 51 -- PageBulk::compress(COMPRESSED table only) 52 -- PageBulk::pageSplit(COMPRESSED table only) 53 -- PageBulk::commit 54 */ 55 56 class PageBulk 57 { 58 public: 59 /** Constructor 60 @param[in] index B-tree index 61 @param[in] page_no page number 62 @param[in] level page level 63 @param[in] trx_id transaction id 64 @param[in] observer flush observer */ PageBulk(dict_index_t * index,trx_id_t trx_id,ulint page_no,ulint level,FlushObserver * observer)65 PageBulk( 66 dict_index_t* index, 67 trx_id_t trx_id, 68 ulint page_no, 69 ulint level, 70 FlushObserver* observer) 71 : 72 m_heap(NULL), 73 m_index(index), 74 m_mtr(NULL), 75 m_trx_id(trx_id), 76 m_block(NULL), 77 m_page(NULL), 78 m_page_zip(NULL), 79 m_cur_rec(NULL), 80 m_page_no(page_no), 81 m_level(level), 82 m_is_comp(dict_table_is_comp(index->table)), 83 m_heap_top(NULL), 84 m_rec_no(0), 85 m_free_space(0), 86 m_reserved_space(0), 87 #ifdef UNIV_DEBUG 88 m_total_data(0), 89 #endif /* UNIV_DEBUG */ 90 m_modify_clock(0), 91 m_flush_observer(observer) 92 { 93 ut_ad(!dict_index_is_spatial(m_index)); 94 } 95 96 /** Deconstructor */ ~PageBulk()97 ~PageBulk() 98 { 99 mem_heap_free(m_heap); 100 } 101 102 /** Initialize members and allocate page if needed and start mtr. 103 Note: must be called and only once right after constructor. 104 @return error code */ 105 dberr_t init(); 106 107 /** Insert a record in the page. 108 @param[in] rec record 109 @param[in] offsets record offsets */ 110 void insert(const rec_t* rec, ulint* offsets); 111 112 /** Mark end of insertion to the page. Scan all records to set page 113 dirs, and set page header members. */ 114 void finish(); 115 116 /** Commit mtr for a page 117 @param[in] success Flag whether all inserts succeed. */ 118 void commit(bool success); 119 120 /** Compress if it is compressed table 121 @return true compress successfully or no need to compress 122 @return false compress failed. */ 123 bool compress(); 124 125 /** Check whether the record needs to be stored externally. 126 @return true 127 @return false */ 128 bool needExt(const dtuple_t* tuple, ulint rec_size); 129 130 /** Store external record 131 @param[in] big_rec external recrod 132 @param[in] offsets record offsets 133 @return error code */ 134 dberr_t storeExt(const big_rec_t* big_rec, ulint* offsets); 135 136 /** Get node pointer 137 @return node pointer */ 138 dtuple_t* getNodePtr(); 139 140 /** Get split rec in the page. We split a page in half when compresssion 141 fails, and the split rec should be copied to the new page. 142 @return split rec */ 143 rec_t* getSplitRec(); 144 145 /** Copy all records after split rec including itself. 146 @param[in] rec split rec */ 147 void copyIn(rec_t* split_rec); 148 149 /** Remove all records after split rec including itself. 150 @param[in] rec split rec */ 151 void copyOut(rec_t* split_rec); 152 153 /** Set next page 154 @param[in] next_page_no next page no */ 155 void setNext(ulint next_page_no); 156 157 /** Set previous page 158 @param[in] prev_page_no previous page no */ 159 void setPrev(ulint prev_page_no); 160 161 /** Release block by commiting mtr */ 162 inline void release(); 163 164 /** Start mtr and latch block */ 165 inline void latch(); 166 167 /** Check if required space is available in the page for the rec 168 to be inserted. We check fill factor & padding here. 169 @param[in] length required length 170 @return true if space is available */ 171 inline bool isSpaceAvailable(ulint rec_size); 172 173 /** Get page no */ getPageNo()174 ulint getPageNo() 175 { 176 return(m_page_no); 177 } 178 179 /** Get page level */ getLevel()180 ulint getLevel() 181 { 182 return(m_level); 183 } 184 185 /** Get record no */ getRecNo()186 ulint getRecNo() 187 { 188 return(m_rec_no); 189 } 190 191 /** Get page */ getPage()192 page_t* getPage() 193 { 194 return(m_page); 195 } 196 197 /** Get page zip */ getPageZip()198 page_zip_des_t* getPageZip() 199 { 200 return(m_page_zip); 201 } 202 203 #ifdef UNIV_DEBUG 204 /** Check if index is X locked */ 205 bool isIndexXLocked(); 206 #endif // UNIV_DEBUG 207 208 /* Memory heap for internal allocation */ 209 mem_heap_t* m_heap; 210 211 private: 212 /** The index B-tree */ 213 dict_index_t* m_index; 214 215 /** The min-transaction */ 216 mtr_t* m_mtr; 217 218 /** The transaction id */ 219 trx_id_t m_trx_id; 220 221 /** The buffer block */ 222 buf_block_t* m_block; 223 224 /** The page */ 225 page_t* m_page; 226 227 /** The page zip descriptor */ 228 page_zip_des_t* m_page_zip; 229 230 /** The current rec, just before the next insert rec */ 231 rec_t* m_cur_rec; 232 233 /** The page no */ 234 ulint m_page_no; 235 236 /** The page level in B-tree */ 237 ulint m_level; 238 239 /** Flag: is page in compact format */ 240 const bool m_is_comp; 241 242 /** The heap top in page for next insert */ 243 byte* m_heap_top; 244 245 /** User record no */ 246 ulint m_rec_no; 247 248 /** The free space left in the page */ 249 ulint m_free_space; 250 251 /** The reserved space for fill factor */ 252 ulint m_reserved_space; 253 254 /** The padding space for compressed page */ 255 ulint m_padding_space; 256 257 #ifdef UNIV_DEBUG 258 /** Total data in the page */ 259 ulint m_total_data; 260 #endif /* UNIV_DEBUG */ 261 262 /** The modify clock value of the buffer block 263 when the block is re-pinned */ 264 ib_uint64_t m_modify_clock; 265 266 /** Flush observer */ 267 FlushObserver* m_flush_observer; 268 }; 269 270 typedef std::vector<PageBulk*, ut_allocator<PageBulk*> > 271 page_bulk_vector; 272 273 class BtrBulk 274 { 275 public: 276 /** Constructor 277 @param[in] index B-tree index 278 @param[in] trx_id transaction id 279 @param[in] observer flush observer */ BtrBulk(dict_index_t * index,trx_id_t trx_id,FlushObserver * observer)280 BtrBulk( 281 dict_index_t* index, 282 trx_id_t trx_id, 283 FlushObserver* observer) 284 : 285 m_heap(NULL), 286 m_index(index), 287 m_trx_id(trx_id), 288 m_flush_observer(observer) 289 { 290 ut_ad(m_flush_observer != NULL); 291 #ifdef UNIV_DEBUG 292 fil_space_inc_redo_skipped_count(m_index->space); 293 m_index_online = m_index->online_status; 294 #endif /* UNIV_DEBUG */ 295 } 296 297 /** Destructor */ ~BtrBulk()298 ~BtrBulk() 299 { 300 mem_heap_free(m_heap); 301 UT_DELETE(m_page_bulks); 302 303 #ifdef UNIV_DEBUG 304 fil_space_dec_redo_skipped_count(m_index->space); 305 #endif /* UNIV_DEBUG */ 306 } 307 308 /** Initialization 309 Note: must be called right after constructor. */ init()310 void init() 311 { 312 ut_ad(m_heap == NULL); 313 m_heap = mem_heap_create(1000); 314 315 m_page_bulks = UT_NEW_NOKEY(page_bulk_vector()); 316 } 317 318 /** Insert a tuple 319 @param[in] tuple tuple to insert. 320 @return error code */ insert(dtuple_t * tuple)321 dberr_t insert(dtuple_t* tuple) 322 { 323 return(insert(tuple, 0)); 324 } 325 326 /** Btree bulk load finish. We commit the last page in each level 327 and copy the last page in top level to the root page of the index 328 if no error occurs. 329 @param[in] err whether bulk load was successful until now 330 @return error code */ 331 dberr_t finish(dberr_t err); 332 333 /** Release all latches */ 334 void release(); 335 336 /** Re-latch all latches */ 337 void latch(); 338 339 private: 340 /** Insert a tuple to a page in a level 341 @param[in] tuple tuple to insert 342 @param[in] level B-tree level 343 @return error code */ 344 dberr_t insert(dtuple_t* tuple, ulint level); 345 346 /** Split a page 347 @param[in] page_bulk page to split 348 @param[in] next_page_bulk next page 349 @return error code */ 350 dberr_t pageSplit(PageBulk* page_bulk, 351 PageBulk* next_page_bulk); 352 353 /** Commit(finish) a page. We set next/prev page no, compress a page of 354 compressed table and split the page if compression fails, insert a node 355 pointer to father page if needed, and commit mini-transaction. 356 @param[in] page_bulk page to commit 357 @param[in] next_page_bulk next page 358 @param[in] insert_father flag whether need to insert node ptr 359 @return error code */ 360 dberr_t pageCommit(PageBulk* page_bulk, 361 PageBulk* next_page_bulk, 362 bool insert_father); 363 364 /** Abort a page when an error occurs 365 @param[in] page_bulk page bulk object 366 Note: we should call pageAbort for a PageBulk object, which is not in 367 m_page_bulks after pageCommit, and we will commit or abort PageBulk 368 objects in function "finish". */ pageAbort(PageBulk * page_bulk)369 void pageAbort(PageBulk* page_bulk) 370 { 371 page_bulk->commit(false); 372 } 373 374 /** Log free check */ 375 void logFreeCheck(); 376 377 private: 378 /** Memory heap for allocation */ 379 mem_heap_t* m_heap; 380 381 /** B-tree index */ 382 dict_index_t* m_index; 383 384 /** Transaction id */ 385 trx_id_t m_trx_id; 386 387 /** Root page level */ 388 ulint m_root_level; 389 390 /** Flush observer */ 391 FlushObserver* m_flush_observer; 392 393 /** Page cursor vector for all level */ 394 page_bulk_vector* m_page_bulks; 395 396 #ifdef UNIV_DEBUG 397 /** State of the index. Used for asserting at the end of a 398 bulk load operation to ensure that the online status of the 399 index does not change */ 400 unsigned m_index_online; 401 #endif // UNIV_DEBUG 402 }; 403 404 #endif 405