1 /*****************************************************************************
2 
3 Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2012, Facebook Inc.
5 Copyright (c) 2013, 2021, MariaDB Corporation.
6 
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free Software
9 Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License along with
16 this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
18 
19 *****************************************************************************/
20 
21 /**************************************************//**
22 @file include/mtr0mtr.h
23 Mini-transaction buffer
24 
25 Created 11/26/1995 Heikki Tuuri
26 *******************************************************/
27 
28 #ifndef mtr0mtr_h
29 #define mtr0mtr_h
30 
31 #include "fil0fil.h"
32 #include "dyn0buf.h"
33 
34 /** Start a mini-transaction. */
35 #define mtr_start(m)		(m)->start()
36 
37 /** Commit a mini-transaction. */
38 #define mtr_commit(m)		(m)->commit()
39 
40 /** Set and return a savepoint in mtr.
41 @return	savepoint */
42 #define mtr_set_savepoint(m)	(m)->get_savepoint()
43 
44 /** Release the (index tree) s-latch stored in an mtr memo after a
45 savepoint. */
46 #define mtr_release_s_latch_at_savepoint(m, s, l)			\
47 				(m)->release_s_latch_at_savepoint((s), (l))
48 
49 /** Get the logging mode of a mini-transaction.
50 @return	logging mode: MTR_LOG_NONE, ... */
51 #define mtr_get_log_mode(m)	(m)->get_log_mode()
52 
53 /** Change the logging mode of a mini-transaction.
54 @return	old mode */
55 #define mtr_set_log_mode(m, d)	(m)->set_log_mode((d))
56 
57 /** Release an object in the memo stack.
58 @return true if released */
59 #define mtr_memo_release(m, o, t)					\
60 				(m)->memo_release((o), (t))
61 
62 #ifdef UNIV_DEBUG
63 /** Check if memo contains the given item.
64 @return	TRUE if contains */
65 #define mtr_memo_contains(m, o, t)					\
66 				(m)->memo_contains((m)->get_memo(), (o), (t))
67 
68 /** Check if memo contains the given page.
69 @return	TRUE if contains */
70 #define mtr_memo_contains_page(m, p, t)					\
71 	(m)->memo_contains_page_flagged((p), (t))
72 #endif /* UNIV_DEBUG */
73 
74 /** Print info of an mtr handle. */
75 #define mtr_print(m)		(m)->print()
76 
77 /** Return the log object of a mini-transaction buffer.
78 @return	log */
79 #define mtr_get_log(m)		(m)->get_log()
80 
81 /** Push an object to an mtr memo stack. */
82 #define mtr_memo_push(m, o, t)	(m)->memo_push(o, t)
83 
84 #define mtr_s_lock_space(s, m)	(m)->s_lock_space((s), __FILE__, __LINE__)
85 #define mtr_x_lock_space(s, m)	(m)->x_lock_space((s), __FILE__, __LINE__)
86 
87 #define mtr_s_lock_index(i, m)	(m)->s_lock(&(i)->lock, __FILE__, __LINE__)
88 #define mtr_x_lock_index(i, m)	(m)->x_lock(&(i)->lock, __FILE__, __LINE__)
89 #define mtr_sx_lock_index(i, m)	(m)->sx_lock(&(i)->lock, __FILE__, __LINE__)
90 
91 #define mtr_memo_contains_flagged(m, p, l)				\
92 				(m)->memo_contains_flagged((p), (l))
93 
94 #define mtr_memo_contains_page_flagged(m, p, l)				\
95 				(m)->memo_contains_page_flagged((p), (l))
96 
97 #define mtr_release_block_at_savepoint(m, s, b)				\
98 				(m)->release_block_at_savepoint((s), (b))
99 
100 #define mtr_block_sx_latch_at_savepoint(m, s, b)			\
101 				(m)->sx_latch_at_savepoint((s), (b))
102 
103 #define mtr_block_x_latch_at_savepoint(m, s, b)				\
104 				(m)->x_latch_at_savepoint((s), (b))
105 
106 /** Check if a mini-transaction is dirtying a clean page.
107 @param b	block being x-fixed
108 @return true if the mtr is dirtying a clean page. */
109 #define mtr_block_dirtied(b)	mtr_t::is_block_dirtied((b))
110 
111 /** Append records to the system-wide redo log buffer.
112 @param[in]	log	redo log records */
113 void
114 mtr_write_log(
115 	const mtr_buf_t*	log);
116 
117 /** Mini-transaction memo stack slot. */
118 struct mtr_memo_slot_t {
119 	/** pointer to the object */
120 	void*		object;
121 
122 	/** type of the stored object (MTR_MEMO_S_LOCK, ...) */
123 	ulint		type;
124 };
125 
126 /** Mini-transaction handle and buffer */
127 struct mtr_t {
mtr_tmtr_t128 	mtr_t() : m_state(MTR_STATE_INIT) {}
129 
130 	/** Start a mini-transaction. */
131 	void start();
132 
133 	/** Commit the mini-transaction. */
134 	void commit();
135 
136 	/** Commit a mini-transaction that is shrinking a tablespace.
137 	@param space   tablespace that is being shrunk */
138 	ATTRIBUTE_COLD void commit_shrink(fil_space_t &space);
139 
140 	/** Commit a mini-transaction that did not modify any pages,
141 	but generated some redo log on a higher level, such as
142 	MLOG_FILE_NAME records and a MLOG_CHECKPOINT marker.
143 	The caller must invoke log_mutex_enter() and log_mutex_exit().
144 	This is to be used at log_checkpoint().
145 	@param[in]	checkpoint_lsn		the LSN of the log checkpoint
146 	@param[in]	write_mlog_checkpoint	Write MLOG_CHECKPOINT marker
147 						if it is enabled. */
148 	void commit_checkpoint(
149 		lsn_t	checkpoint_lsn,
150 		bool	write_mlog_checkpoint);
151 
152 	/** Return current size of the buffer.
153 	@return	savepoint */
get_savepointmtr_t154 	ulint get_savepoint() const {ut_ad(is_active()); return m_memo.size();}
155 
156 	/** Release the (index tree) s-latch stored in an mtr memo after a
157 	savepoint.
158 	@param savepoint	value returned by @see set_savepoint.
159 	@param lock		latch to release */
160 	inline void release_s_latch_at_savepoint(
161 		ulint		savepoint,
162 		rw_lock_t*	lock);
163 
164 	/** Release the block in an mtr memo after a savepoint. */
165 	inline void release_block_at_savepoint(
166 		ulint		savepoint,
167 		buf_block_t*	block);
168 
169 	/** SX-latch a not yet latched block after a savepoint. */
170 	inline void sx_latch_at_savepoint(ulint savepoint, buf_block_t* block);
171 
172 	/** X-latch a not yet latched block after a savepoint. */
173 	inline void x_latch_at_savepoint(ulint savepoint, buf_block_t*	block);
174 
175 	/** Get the logging mode.
176 	@return	logging mode */
177 	inline mtr_log_t get_log_mode() const
178 		MY_ATTRIBUTE((warn_unused_result));
179 
180 	/** Change the logging mode.
181 	@param mode	 logging mode
182 	@return	old mode */
183 	inline mtr_log_t set_log_mode(mtr_log_t mode);
184 
185 	/** Copy the tablespaces associated with the mini-transaction
186 	(needed for generating MLOG_FILE_NAME records)
187 	@param[in]	mtr	mini-transaction that may modify
188 	the same set of tablespaces as this one */
set_spacesmtr_t189 	void set_spaces(const mtr_t& mtr)
190 	{
191 		ut_ad(!m_user_space_id);
192 		ut_ad(!m_user_space);
193 
194 		ut_d(m_user_space_id = mtr.m_user_space_id);
195 		m_user_space = mtr.m_user_space;
196 	}
197 
198 	/** Set the tablespace associated with the mini-transaction
199 	(needed for generating a MLOG_FILE_NAME record)
200 	@param[in]	space_id	user or system tablespace ID
201 	@return	the tablespace */
set_named_space_idmtr_t202 	fil_space_t* set_named_space_id(ulint space_id)
203 	{
204 		ut_ad(!m_user_space_id);
205 		ut_d(m_user_space_id = space_id);
206 		if (!space_id) {
207 			return fil_system.sys_space;
208 		} else {
209 			ut_ad(m_user_space_id == space_id);
210 			ut_ad(!m_user_space);
211 			m_user_space = fil_space_get(space_id);
212 			ut_ad(m_user_space);
213 			return m_user_space;
214 		}
215 	}
216 
217 	/** Set the tablespace associated with the mini-transaction
218 	(needed for generating a MLOG_FILE_NAME record)
219 	@param[in]	space	user or system tablespace */
set_named_spacemtr_t220 	void set_named_space(fil_space_t* space)
221 	{
222 		ut_ad(!m_user_space_id);
223 		ut_d(m_user_space_id = space->id);
224 		if (space->id) {
225 			m_user_space = space;
226 		}
227 	}
228 
229 #ifdef UNIV_DEBUG
230 	/** Check the tablespace associated with the mini-transaction
231 	(needed for generating a MLOG_FILE_NAME record)
232 	@param[in]	space	tablespace
233 	@return whether the mini-transaction is associated with the space */
234 	bool is_named_space(ulint space) const;
235 	/** Check the tablespace associated with the mini-transaction
236 	(needed for generating a MLOG_FILE_NAME record)
237 	@param[in]	space	tablespace
238 	@return whether the mini-transaction is associated with the space */
239 	bool is_named_space(const fil_space_t* space) const;
240 #endif /* UNIV_DEBUG */
241 
242 	/** Acquire a tablespace X-latch.
243 	@param[in]	space_id	tablespace ID
244 	@param[in]	file		file name from where called
245 	@param[in]	line		line number in file
246 	@return the tablespace object (never NULL) */
247 	fil_space_t* x_lock_space(
248 		ulint		space_id,
249 		const char*	file,
250 		unsigned	line);
251 
252 	/** Acquire a shared rw-latch.
253 	@param[in]	lock	rw-latch
254 	@param[in]	file	file name from where called
255 	@param[in]	line	line number in file */
s_lockmtr_t256 	void s_lock(rw_lock_t* lock, const char* file, unsigned line)
257 	{
258 		rw_lock_s_lock_inline(lock, 0, file, line);
259 		memo_push(lock, MTR_MEMO_S_LOCK);
260 	}
261 
262 	/** Acquire an exclusive rw-latch.
263 	@param[in]	lock	rw-latch
264 	@param[in]	file	file name from where called
265 	@param[in]	line	line number in file */
x_lockmtr_t266 	void x_lock(rw_lock_t* lock, const char* file, unsigned line)
267 	{
268 		rw_lock_x_lock_inline(lock, 0, file, line);
269 		memo_push(lock, MTR_MEMO_X_LOCK);
270 	}
271 
272 	/** Acquire an shared/exclusive rw-latch.
273 	@param[in]	lock	rw-latch
274 	@param[in]	file	file name from where called
275 	@param[in]	line	line number in file */
sx_lockmtr_t276 	void sx_lock(rw_lock_t* lock, const char* file, unsigned line)
277 	{
278 		rw_lock_sx_lock_inline(lock, 0, file, line);
279 		memo_push(lock, MTR_MEMO_SX_LOCK);
280 	}
281 
282 	/** Acquire a tablespace S-latch.
283 	@param[in]	space	tablespace
284 	@param[in]	file	file name from where called
285 	@param[in]	line	line number in file */
s_lock_spacemtr_t286 	void s_lock_space(fil_space_t* space, const char* file, unsigned line)
287 	{
288 		ut_ad(space->purpose == FIL_TYPE_TEMPORARY
289 		      || space->purpose == FIL_TYPE_IMPORT
290 		      || space->purpose == FIL_TYPE_TABLESPACE);
291 		s_lock(&space->latch, file, line);
292 	}
293 
294 	/** Acquire a tablespace X-latch.
295 	@param[in]	space	tablespace
296 	@param[in]	file	file name from where called
297 	@param[in]	line	line number in file */
x_lock_spacemtr_t298 	void x_lock_space(fil_space_t* space, const char* file, unsigned line)
299 	{
300 		ut_ad(space->purpose == FIL_TYPE_TEMPORARY
301 		      || space->purpose == FIL_TYPE_IMPORT
302 		      || space->purpose == FIL_TYPE_TABLESPACE);
303 		memo_push(space, MTR_MEMO_SPACE_X_LOCK);
304 		rw_lock_x_lock_inline(&space->latch, 0, file, line);
305 	}
306 
307 	/** Release an object in the memo stack.
308 	@param object	object
309 	@param type	object type: MTR_MEMO_S_LOCK, ...
310 	@return bool if lock released */
311 	bool memo_release(const void* object, ulint type);
312 	/** Release a page latch.
313 	@param[in]	ptr	pointer to within a page frame
314 	@param[in]	type	object type: MTR_MEMO_PAGE_X_FIX, ... */
315 	void release_page(const void* ptr, mtr_memo_type_t type);
316 
317 	/** Note that the mini-transaction has modified data. */
set_modifiedmtr_t318 	void set_modified() { m_modifications = true; }
319 
320 	/** Set the state to not-modified. This will not log the
321 	changes.  This is only used during redo log apply, to avoid
322 	logging the changes. */
discard_modificationsmtr_t323 	void discard_modifications() { m_modifications = false; }
324 
325 	/** Get the LSN of commit().
326 	@return the commit LSN
327 	@retval 0 if the transaction only modified temporary tablespaces */
commit_lsnmtr_t328 	lsn_t commit_lsn() const
329 	{
330 		ut_ad(has_committed());
331 		return(m_commit_lsn);
332 	}
333 
334 	/** Note that we are inside the change buffer code. */
enter_ibufmtr_t335 	void enter_ibuf() { m_inside_ibuf = true; }
336 
337 	/** Note that we have exited from the change buffer code. */
exit_ibufmtr_t338 	void exit_ibuf() { m_inside_ibuf = false; }
339 
340 	/** @return true if we are inside the change buffer code */
is_inside_ibufmtr_t341 	bool is_inside_ibuf() const { return m_inside_ibuf; }
342 
343 	/*
344 	@return true if the mini-transaction is active */
is_activemtr_t345 	bool is_active() const { return m_state == MTR_STATE_ACTIVE; }
346 
347 	/** Get flush observer
348 	@return flush observer */
get_flush_observermtr_t349 	FlushObserver* get_flush_observer() const { return m_flush_observer; }
350 
351 	/** Set flush observer
352 	@param[in]	observer	flush observer */
set_flush_observermtr_t353 	void set_flush_observer(FlushObserver*	observer)
354 	{
355 		ut_ad(observer == NULL || m_log_mode == MTR_LOG_NO_REDO);
356 		m_flush_observer = observer;
357 	}
358 
359 #ifdef UNIV_DEBUG
360 	/** Check if memo contains the given item.
361 	@param memo	memo stack
362 	@param object,	object to search
363 	@param type	type of object
364 	@return	true if contains */
365 	static bool memo_contains(
366 		const mtr_buf_t*	memo,
367 		const void*		object,
368 		ulint			type)
369 		MY_ATTRIBUTE((warn_unused_result));
370 
371 	/** Check if memo contains the given item.
372 	@param object		object to search
373 	@param flags		specify types of object (can be ORred) of
374 				MTR_MEMO_PAGE_S_FIX ... values
375 	@return true if contains */
376 	bool memo_contains_flagged(const void* ptr, ulint flags) const;
377 
378 	/** Check if memo contains the given page.
379 	@param[in]	ptr	pointer to within buffer frame
380 	@param[in]	flags	specify types of object with OR of
381 				MTR_MEMO_PAGE_S_FIX... values
382 	@return	the block
383 	@retval	NULL	if not found */
384 	buf_block_t* memo_contains_page_flagged(
385 		const byte*	ptr,
386 		ulint		flags) const;
387 
388 	/** Mark the given latched page as modified.
389 	@param[in]	ptr	pointer to within buffer frame */
390 	void memo_modify_page(const byte* ptr);
391 
392 	/** Print info of an mtr handle. */
393 	void print() const;
394 
395 	/** @return true if the mini-transaction has committed */
has_committedmtr_t396 	bool has_committed() const { return m_state == MTR_STATE_COMMITTED; }
397 
398 	/** @return true if mini-transaction contains modifications. */
has_modificationsmtr_t399 	bool has_modifications() const { return m_modifications; }
400 
401 	/** @return the memo stack */
get_memomtr_t402 	const mtr_buf_t* get_memo() const { return &m_memo; }
403 
404 	/** @return the memo stack */
get_memomtr_t405 	mtr_buf_t* get_memo() { return &m_memo; }
406 #endif /* UNIV_DEBUG */
407 
408 	/** @return true if a record was added to the mini-transaction */
is_dirtymtr_t409 	bool is_dirty() const { return m_made_dirty; }
410 
411 	/** Note that a record has been added to the log */
added_recmtr_t412 	void added_rec() { ++m_n_log_recs; }
413 
414 	/** Get the buffered redo log of this mini-transaction.
415 	@return	redo log */
get_logmtr_t416 	const mtr_buf_t* get_log() const { return &m_log; }
417 
418 	/** Get the buffered redo log of this mini-transaction.
419 	@return	redo log */
get_logmtr_t420 	mtr_buf_t* get_log() { return &m_log; }
421 
422 	/** Push an object to an mtr memo stack.
423 	@param object	object
424 	@param type	object type: MTR_MEMO_S_LOCK, ... */
425 	inline void memo_push(void* object, mtr_memo_type_t type);
426 
427 	/** Check if this mini-transaction is dirtying a clean page.
428 	@param block	block being x-fixed
429 	@return true if the mtr is dirtying a clean page. */
430 	static inline bool is_block_dirtied(const buf_block_t* block)
431 		MY_ATTRIBUTE((warn_unused_result));
432 
433 	/** Check if we are holding a block latch in exclusive mode
434 	@param block  buffer pool block to search for */
435 	bool have_x_latch(const buf_block_t& block) const;
436 private:
437 	/** Prepare to write the mini-transaction log to the redo log buffer.
438 	@return number of bytes to write in finish_write() */
439 	inline ulint prepare_write();
440 
441 	/** Append the redo log records to the redo log buffer.
442 	@param[in]	len	number of bytes to write
443 	@return start_lsn */
444 	inline lsn_t finish_write(ulint len);
445 
446 	/** Release the resources */
447 	inline void release_resources();
448 
449 	/** memo stack for locks etc. */
450 	mtr_buf_t	m_memo;
451 
452 	/** mini-transaction log */
453 	mtr_buf_t	m_log;
454 
455 	/** true if mtr has made at least one buffer pool page dirty */
456 	bool		m_made_dirty;
457 
458 	/** true if inside ibuf changes */
459 	bool		m_inside_ibuf;
460 
461 	/** true if the mini-transaction modified buffer pool pages */
462 	bool		m_modifications;
463 
464 	/** Count of how many page initial log records have been
465 	written to the mtr log */
466 	ib_uint32_t	m_n_log_recs;
467 
468 	/** specifies which operations should be logged; default
469 	value MTR_LOG_ALL */
470 	mtr_log_t	m_log_mode;
471 #ifdef UNIV_DEBUG
472 	/** Persistent user tablespace associated with the
473 	mini-transaction, or 0 (TRX_SYS_SPACE) if none yet */
474 	ulint		m_user_space_id;
475 #endif /* UNIV_DEBUG */
476 	/** User tablespace that is being modified by the mini-transaction */
477 	fil_space_t*	m_user_space;
478 
479 	/** State of the transaction */
480 	mtr_state_t	m_state;
481 
482 	/** Flush Observer */
483 	FlushObserver*	m_flush_observer;
484 
485 	/** LSN at commit time */
486 	lsn_t		m_commit_lsn;
487 };
488 
489 #include "mtr0mtr.inl"
490 
491 #endif /* mtr0mtr_h */
492