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, 2017, 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, Suite 500, 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 "univ.i" |
32 | #include "fil0fil.h" |
33 | #include "dyn0buf.h" |
34 | |
35 | /** Start a mini-transaction. */ |
36 | #define mtr_start(m) (m)->start() |
37 | |
38 | /** Start a synchronous mini-transaction */ |
39 | #define mtr_start_sync(m) (m)->start(true) |
40 | |
41 | /** Start an asynchronous read-only mini-transaction */ |
42 | #define mtr_start_ro(m) (m)->start(true, true) |
43 | |
44 | /** Commit a mini-transaction. */ |
45 | #define mtr_commit(m) (m)->commit() |
46 | |
47 | /** Set and return a savepoint in mtr. |
48 | @return savepoint */ |
49 | #define mtr_set_savepoint(m) (m)->get_savepoint() |
50 | |
51 | /** Release the (index tree) s-latch stored in an mtr memo after a |
52 | savepoint. */ |
53 | #define mtr_release_s_latch_at_savepoint(m, s, l) \ |
54 | (m)->release_s_latch_at_savepoint((s), (l)) |
55 | |
56 | /** Get the logging mode of a mini-transaction. |
57 | @return logging mode: MTR_LOG_NONE, ... */ |
58 | #define mtr_get_log_mode(m) (m)->get_log_mode() |
59 | |
60 | /** Change the logging mode of a mini-transaction. |
61 | @return old mode */ |
62 | #define mtr_set_log_mode(m, d) (m)->set_log_mode((d)) |
63 | |
64 | /** Get the flush observer of a mini-transaction. |
65 | @return flush observer object */ |
66 | #define mtr_get_flush_observer(m) (m)->get_flush_observer() |
67 | |
68 | /** Set the flush observer of a mini-transaction. */ |
69 | #define mtr_set_flush_observer(m, d) (m)->set_flush_observer((d)) |
70 | |
71 | /** Read 1 - 4 bytes from a file page buffered in the buffer pool. |
72 | @return value read */ |
73 | #define mtr_read_ulint(p, t, m) (m)->read_ulint((p), (t)) |
74 | |
75 | /** Release an object in the memo stack. |
76 | @return true if released */ |
77 | #define mtr_memo_release(m, o, t) \ |
78 | (m)->memo_release((o), (t)) |
79 | |
80 | #ifdef UNIV_DEBUG |
81 | /** Check if memo contains the given item. |
82 | @return TRUE if contains */ |
83 | #define mtr_memo_contains(m, o, t) \ |
84 | (m)->memo_contains((m)->get_memo(), (o), (t)) |
85 | |
86 | /** Check if memo contains the given page. |
87 | @return TRUE if contains */ |
88 | #define mtr_memo_contains_page(m, p, t) \ |
89 | (m)->memo_contains_page_flagged((p), (t)) |
90 | #endif /* UNIV_DEBUG */ |
91 | |
92 | /** Print info of an mtr handle. */ |
93 | #define mtr_print(m) (m)->print() |
94 | |
95 | /** Return the log object of a mini-transaction buffer. |
96 | @return log */ |
97 | #define mtr_get_log(m) (m)->get_log() |
98 | |
99 | /** Push an object to an mtr memo stack. */ |
100 | #define mtr_memo_push(m, o, t) (m)->memo_push(o, t) |
101 | |
102 | /** Lock an rw-lock in s-mode. */ |
103 | #define mtr_s_lock(l, m) (m)->s_lock((l), __FILE__, __LINE__) |
104 | |
105 | /** Lock an rw-lock in x-mode. */ |
106 | #define mtr_x_lock(l, m) (m)->x_lock((l), __FILE__, __LINE__) |
107 | |
108 | /** Lock a tablespace in x-mode. */ |
109 | #define mtr_x_lock_space(s, m) (m)->x_lock_space((s), __FILE__, __LINE__) |
110 | |
111 | /** Lock an rw-lock in sx-mode. */ |
112 | #define mtr_sx_lock(l, m) (m)->sx_lock((l), __FILE__, __LINE__) |
113 | |
114 | #define mtr_memo_contains_flagged(m, p, l) \ |
115 | (m)->memo_contains_flagged((p), (l)) |
116 | |
117 | #define mtr_memo_contains_page_flagged(m, p, l) \ |
118 | (m)->memo_contains_page_flagged((p), (l)) |
119 | |
120 | #define mtr_release_block_at_savepoint(m, s, b) \ |
121 | (m)->release_block_at_savepoint((s), (b)) |
122 | |
123 | #define mtr_block_sx_latch_at_savepoint(m, s, b) \ |
124 | (m)->sx_latch_at_savepoint((s), (b)) |
125 | |
126 | #define mtr_block_x_latch_at_savepoint(m, s, b) \ |
127 | (m)->x_latch_at_savepoint((s), (b)) |
128 | |
129 | /** Check if a mini-transaction is dirtying a clean page. |
130 | @param b block being x-fixed |
131 | @return true if the mtr is dirtying a clean page. */ |
132 | #define mtr_block_dirtied(b) mtr_t::is_block_dirtied((b)) |
133 | |
134 | /** Append records to the system-wide redo log buffer. |
135 | @param[in] log redo log records */ |
136 | void |
137 | mtr_write_log( |
138 | const mtr_buf_t* log); |
139 | |
140 | /** Mini-transaction memo stack slot. */ |
141 | struct mtr_memo_slot_t { |
142 | /** pointer to the object */ |
143 | void* object; |
144 | |
145 | /** type of the stored object (MTR_MEMO_S_LOCK, ...) */ |
146 | ulint type; |
147 | }; |
148 | |
149 | /** Mini-transaction handle and buffer */ |
150 | struct mtr_t { |
151 | |
152 | /** State variables of the mtr */ |
153 | struct Impl { |
154 | |
155 | /** memo stack for locks etc. */ |
156 | mtr_buf_t m_memo; |
157 | |
158 | /** mini-transaction log */ |
159 | mtr_buf_t m_log; |
160 | |
161 | /** true if mtr has made at least one buffer pool page dirty */ |
162 | bool m_made_dirty; |
163 | |
164 | /** true if inside ibuf changes */ |
165 | bool m_inside_ibuf; |
166 | |
167 | /** true if the mini-transaction modified buffer pool pages */ |
168 | bool m_modifications; |
169 | |
170 | /** Count of how many page initial log records have been |
171 | written to the mtr log */ |
172 | ib_uint32_t m_n_log_recs; |
173 | |
174 | /** specifies which operations should be logged; default |
175 | value MTR_LOG_ALL */ |
176 | mtr_log_t m_log_mode; |
177 | #ifdef UNIV_DEBUG |
178 | /** Persistent user tablespace associated with the |
179 | mini-transaction, or 0 (TRX_SYS_SPACE) if none yet */ |
180 | ulint m_user_space_id; |
181 | #endif /* UNIV_DEBUG */ |
182 | /** User tablespace that is being modified by the |
183 | mini-transaction */ |
184 | fil_space_t* m_user_space; |
185 | |
186 | /** State of the transaction */ |
187 | mtr_state_t m_state; |
188 | |
189 | /** Flush Observer */ |
190 | FlushObserver* m_flush_observer; |
191 | |
192 | #ifdef UNIV_DEBUG |
193 | /** For checking corruption. */ |
194 | ulint m_magic_n; |
195 | #endif /* UNIV_DEBUG */ |
196 | |
197 | /** Owning mini-transaction */ |
198 | mtr_t* m_mtr; |
199 | }; |
200 | |
201 | mtr_t() |
202 | { |
203 | m_impl.m_state = MTR_STATE_INIT; |
204 | } |
205 | |
206 | ~mtr_t() { } |
207 | |
208 | /** Start a mini-transaction. |
209 | @param sync true if it is a synchronous mini-transaction */ |
210 | void start(bool sync = true); |
211 | |
212 | /** @return whether this is an asynchronous mini-transaction. */ |
213 | bool is_async() const |
214 | { |
215 | return(!m_sync); |
216 | } |
217 | |
218 | /** Request a future commit to be synchronous. */ |
219 | void set_sync() |
220 | { |
221 | m_sync = true; |
222 | } |
223 | |
224 | /** Commit the mini-transaction. */ |
225 | void commit(); |
226 | |
227 | /** Commit a mini-transaction that did not modify any pages, |
228 | but generated some redo log on a higher level, such as |
229 | MLOG_FILE_NAME records and a MLOG_CHECKPOINT marker. |
230 | The caller must invoke log_mutex_enter() and log_mutex_exit(). |
231 | This is to be used at log_checkpoint(). |
232 | @param[in] checkpoint_lsn the LSN of the log checkpoint |
233 | @param[in] write_mlog_checkpoint Write MLOG_CHECKPOINT marker |
234 | if it is enabled. */ |
235 | void commit_checkpoint( |
236 | lsn_t checkpoint_lsn, |
237 | bool write_mlog_checkpoint); |
238 | |
239 | /** Return current size of the buffer. |
240 | @return savepoint */ |
241 | ulint get_savepoint() const |
242 | MY_ATTRIBUTE((warn_unused_result)) |
243 | { |
244 | ut_ad(is_active()); |
245 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
246 | |
247 | return(m_impl.m_memo.size()); |
248 | } |
249 | |
250 | /** Release the (index tree) s-latch stored in an mtr memo after a |
251 | savepoint. |
252 | @param savepoint value returned by @see set_savepoint. |
253 | @param lock latch to release */ |
254 | inline void release_s_latch_at_savepoint( |
255 | ulint savepoint, |
256 | rw_lock_t* lock); |
257 | |
258 | /** Release the block in an mtr memo after a savepoint. */ |
259 | inline void release_block_at_savepoint( |
260 | ulint savepoint, |
261 | buf_block_t* block); |
262 | |
263 | /** SX-latch a not yet latched block after a savepoint. */ |
264 | inline void sx_latch_at_savepoint(ulint savepoint, buf_block_t* block); |
265 | |
266 | /** X-latch a not yet latched block after a savepoint. */ |
267 | inline void x_latch_at_savepoint(ulint savepoint, buf_block_t* block); |
268 | |
269 | /** Get the logging mode. |
270 | @return logging mode */ |
271 | inline mtr_log_t get_log_mode() const |
272 | MY_ATTRIBUTE((warn_unused_result)); |
273 | |
274 | /** Change the logging mode. |
275 | @param mode logging mode |
276 | @return old mode */ |
277 | inline mtr_log_t set_log_mode(mtr_log_t mode); |
278 | |
279 | /** Copy the tablespaces associated with the mini-transaction |
280 | (needed for generating MLOG_FILE_NAME records) |
281 | @param[in] mtr mini-transaction that may modify |
282 | the same set of tablespaces as this one */ |
283 | void set_spaces(const mtr_t& mtr) |
284 | { |
285 | ut_ad(!m_impl.m_user_space_id); |
286 | ut_ad(!m_impl.m_user_space); |
287 | |
288 | ut_d(m_impl.m_user_space_id = mtr.m_impl.m_user_space_id); |
289 | m_impl.m_user_space = mtr.m_impl.m_user_space; |
290 | } |
291 | |
292 | /** Set the tablespace associated with the mini-transaction |
293 | (needed for generating a MLOG_FILE_NAME record) |
294 | @param[in] space_id user or system tablespace ID |
295 | @return the tablespace */ |
296 | fil_space_t* set_named_space_id(ulint space_id) |
297 | { |
298 | ut_ad(!m_impl.m_user_space_id); |
299 | ut_d(m_impl.m_user_space_id = space_id); |
300 | if (!space_id) { |
301 | return fil_system.sys_space; |
302 | } else { |
303 | ut_ad(m_impl.m_user_space_id == space_id); |
304 | ut_ad(!m_impl.m_user_space); |
305 | m_impl.m_user_space = fil_space_get(space_id); |
306 | ut_ad(m_impl.m_user_space); |
307 | return m_impl.m_user_space; |
308 | } |
309 | } |
310 | |
311 | /** Set the tablespace associated with the mini-transaction |
312 | (needed for generating a MLOG_FILE_NAME record) |
313 | @param[in] space user or system tablespace */ |
314 | void set_named_space(fil_space_t* space) |
315 | { |
316 | ut_ad(!m_impl.m_user_space_id); |
317 | ut_d(m_impl.m_user_space_id = space->id); |
318 | if (space->id) { |
319 | m_impl.m_user_space = space; |
320 | } |
321 | } |
322 | |
323 | #ifdef UNIV_DEBUG |
324 | /** Check the tablespace associated with the mini-transaction |
325 | (needed for generating a MLOG_FILE_NAME record) |
326 | @param[in] space tablespace |
327 | @return whether the mini-transaction is associated with the space */ |
328 | bool is_named_space(ulint space) const; |
329 | /** Check the tablespace associated with the mini-transaction |
330 | (needed for generating a MLOG_FILE_NAME record) |
331 | @param[in] space tablespace |
332 | @return whether the mini-transaction is associated with the space */ |
333 | bool is_named_space(const fil_space_t* space) const; |
334 | #endif /* UNIV_DEBUG */ |
335 | |
336 | /** Read 1 - 4 bytes from a file page buffered in the buffer pool. |
337 | @param ptr pointer from where to read |
338 | @param type) MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES |
339 | @return value read */ |
340 | inline ulint read_ulint(const byte* ptr, mlog_id_t type) const |
341 | MY_ATTRIBUTE((warn_unused_result)); |
342 | |
343 | /** Locks a rw-latch in S mode. |
344 | NOTE: use mtr_s_lock(). |
345 | @param lock rw-lock |
346 | @param file file name from where called |
347 | @param line line number in file */ |
348 | inline void s_lock(rw_lock_t* lock, const char* file, unsigned line); |
349 | |
350 | /** Locks a rw-latch in X mode. |
351 | NOTE: use mtr_x_lock(). |
352 | @param lock rw-lock |
353 | @param file file name from where called |
354 | @param line line number in file */ |
355 | inline void x_lock(rw_lock_t* lock, const char* file, unsigned line); |
356 | |
357 | /** Locks a rw-latch in X mode. |
358 | NOTE: use mtr_sx_lock(). |
359 | @param lock rw-lock |
360 | @param file file name from where called |
361 | @param line line number in file */ |
362 | inline void sx_lock(rw_lock_t* lock, const char* file, unsigned line); |
363 | |
364 | /** Acquire a tablespace X-latch. |
365 | NOTE: use mtr_x_lock_space(). |
366 | @param[in] space_id tablespace ID |
367 | @param[in] file file name from where called |
368 | @param[in] line line number in file |
369 | @return the tablespace object (never NULL) */ |
370 | fil_space_t* x_lock_space( |
371 | ulint space_id, |
372 | const char* file, |
373 | unsigned line); |
374 | |
375 | /** Release an object in the memo stack. |
376 | @param object object |
377 | @param type object type: MTR_MEMO_S_LOCK, ... |
378 | @return bool if lock released */ |
379 | bool memo_release(const void* object, ulint type); |
380 | /** Release a page latch. |
381 | @param[in] ptr pointer to within a page frame |
382 | @param[in] type object type: MTR_MEMO_PAGE_X_FIX, ... */ |
383 | void release_page(const void* ptr, mtr_memo_type_t type); |
384 | |
385 | /** Note that the mini-transaction has modified data. */ |
386 | void set_modified() |
387 | { |
388 | m_impl.m_modifications = true; |
389 | } |
390 | |
391 | /** Set the state to not-modified. This will not log the |
392 | changes. This is only used during redo log apply, to avoid |
393 | logging the changes. */ |
394 | void discard_modifications() |
395 | { |
396 | m_impl.m_modifications = false; |
397 | } |
398 | |
399 | /** Get the LSN of commit(). |
400 | @return the commit LSN |
401 | @retval 0 if the transaction only modified temporary tablespaces */ |
402 | lsn_t commit_lsn() const |
403 | { |
404 | ut_ad(has_committed()); |
405 | return(m_commit_lsn); |
406 | } |
407 | |
408 | /** Note that we are inside the change buffer code. */ |
409 | void enter_ibuf() |
410 | { |
411 | m_impl.m_inside_ibuf = true; |
412 | } |
413 | |
414 | /** Note that we have exited from the change buffer code. */ |
415 | void exit_ibuf() |
416 | { |
417 | m_impl.m_inside_ibuf = false; |
418 | } |
419 | |
420 | /** @return true if we are inside the change buffer code */ |
421 | bool is_inside_ibuf() const |
422 | { |
423 | return(m_impl.m_inside_ibuf); |
424 | } |
425 | |
426 | /* |
427 | @return true if the mini-transaction is active */ |
428 | bool is_active() const |
429 | { |
430 | return(m_impl.m_state == MTR_STATE_ACTIVE); |
431 | } |
432 | |
433 | /** Get flush observer |
434 | @return flush observer */ |
435 | FlushObserver* get_flush_observer() const |
436 | { |
437 | return(m_impl.m_flush_observer); |
438 | } |
439 | |
440 | /** Set flush observer |
441 | @param[in] observer flush observer */ |
442 | void set_flush_observer(FlushObserver* observer) |
443 | { |
444 | ut_ad(observer == NULL |
445 | || m_impl.m_log_mode == MTR_LOG_NO_REDO); |
446 | |
447 | m_impl.m_flush_observer = observer; |
448 | } |
449 | |
450 | #ifdef UNIV_DEBUG |
451 | /** Check if memo contains the given item. |
452 | @param memo memo stack |
453 | @param object, object to search |
454 | @param type type of object |
455 | @return true if contains */ |
456 | static bool memo_contains( |
457 | const mtr_buf_t* memo, |
458 | const void* object, |
459 | ulint type) |
460 | MY_ATTRIBUTE((warn_unused_result)); |
461 | |
462 | /** Check if memo contains the given item. |
463 | @param object object to search |
464 | @param flags specify types of object (can be ORred) of |
465 | MTR_MEMO_PAGE_S_FIX ... values |
466 | @return true if contains */ |
467 | bool memo_contains_flagged(const void* ptr, ulint flags) const; |
468 | |
469 | /** Check if memo contains the given page. |
470 | @param[in] ptr pointer to within buffer frame |
471 | @param[in] flags specify types of object with OR of |
472 | MTR_MEMO_PAGE_S_FIX... values |
473 | @return the block |
474 | @retval NULL if not found */ |
475 | buf_block_t* memo_contains_page_flagged( |
476 | const byte* ptr, |
477 | ulint flags) const; |
478 | |
479 | /** Mark the given latched page as modified. |
480 | @param[in] ptr pointer to within buffer frame */ |
481 | void memo_modify_page(const byte* ptr); |
482 | |
483 | /** Print info of an mtr handle. */ |
484 | void print() const; |
485 | |
486 | /** @return true if the mini-transaction has committed */ |
487 | bool has_committed() const |
488 | { |
489 | return(m_impl.m_state == MTR_STATE_COMMITTED); |
490 | } |
491 | |
492 | /** @return true if the mini-transaction is committing */ |
493 | bool is_committing() const |
494 | { |
495 | return(m_impl.m_state == MTR_STATE_COMMITTING); |
496 | } |
497 | |
498 | /** @return true if mini-transaction contains modifications. */ |
499 | bool has_modifications() const |
500 | { |
501 | return(m_impl.m_modifications); |
502 | } |
503 | |
504 | /** @return the memo stack */ |
505 | const mtr_buf_t* get_memo() const |
506 | { |
507 | return(&m_impl.m_memo); |
508 | } |
509 | |
510 | /** @return the memo stack */ |
511 | mtr_buf_t* get_memo() |
512 | { |
513 | return(&m_impl.m_memo); |
514 | } |
515 | #endif /* UNIV_DEBUG */ |
516 | |
517 | /** @return true if a record was added to the mini-transaction */ |
518 | bool is_dirty() const |
519 | { |
520 | return(m_impl.m_made_dirty); |
521 | } |
522 | |
523 | /** Note that a record has been added to the log */ |
524 | void added_rec() |
525 | { |
526 | ++m_impl.m_n_log_recs; |
527 | } |
528 | |
529 | /** Get the buffered redo log of this mini-transaction. |
530 | @return redo log */ |
531 | const mtr_buf_t* get_log() const |
532 | { |
533 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
534 | |
535 | return(&m_impl.m_log); |
536 | } |
537 | |
538 | /** Get the buffered redo log of this mini-transaction. |
539 | @return redo log */ |
540 | mtr_buf_t* get_log() |
541 | { |
542 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
543 | |
544 | return(&m_impl.m_log); |
545 | } |
546 | |
547 | /** Push an object to an mtr memo stack. |
548 | @param object object |
549 | @param type object type: MTR_MEMO_S_LOCK, ... */ |
550 | inline void memo_push(void* object, mtr_memo_type_t type); |
551 | |
552 | /** Check if this mini-transaction is dirtying a clean page. |
553 | @param block block being x-fixed |
554 | @return true if the mtr is dirtying a clean page. */ |
555 | static bool is_block_dirtied(const buf_block_t* block) |
556 | MY_ATTRIBUTE((warn_unused_result)); |
557 | |
558 | private: |
559 | class Command; |
560 | |
561 | friend class Command; |
562 | |
563 | private: |
564 | Impl m_impl; |
565 | |
566 | /** LSN at commit time */ |
567 | volatile lsn_t m_commit_lsn; |
568 | |
569 | /** true if it is synchronous mini-transaction */ |
570 | bool m_sync; |
571 | }; |
572 | |
573 | #include "mtr0mtr.ic" |
574 | |
575 | #endif /* mtr0mtr_h */ |
576 | |