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