1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /**************************************************//** |
21 | @file mtr/mtr0mtr.cc |
22 | Mini-transaction buffer |
23 | |
24 | Created 11/26/1995 Heikki Tuuri |
25 | *******************************************************/ |
26 | |
27 | #include "mtr0mtr.h" |
28 | |
29 | #include "buf0buf.h" |
30 | #include "buf0flu.h" |
31 | #include "fsp0sysspace.h" |
32 | #include "page0types.h" |
33 | #include "mtr0log.h" |
34 | #include "log0log.h" |
35 | #include "row0trunc.h" |
36 | |
37 | #include "log0recv.h" |
38 | |
39 | /** Iterate over a memo block in reverse. */ |
40 | template <typename Functor> |
41 | struct Iterate { |
42 | |
43 | /** Release specific object */ |
44 | explicit Iterate(Functor& functor) |
45 | : |
46 | m_functor(functor) |
47 | { |
48 | /* Do nothing */ |
49 | } |
50 | |
51 | /** @return false if the functor returns false. */ |
52 | bool operator()(mtr_buf_t::block_t* block) |
53 | { |
54 | const mtr_memo_slot_t* start = |
55 | reinterpret_cast<const mtr_memo_slot_t*>( |
56 | block->begin()); |
57 | |
58 | mtr_memo_slot_t* slot = |
59 | reinterpret_cast<mtr_memo_slot_t*>( |
60 | block->end()); |
61 | |
62 | ut_ad(!(block->used() % sizeof(*slot))); |
63 | |
64 | while (slot-- != start) { |
65 | |
66 | if (!m_functor(slot)) { |
67 | return(false); |
68 | } |
69 | } |
70 | |
71 | return(true); |
72 | } |
73 | |
74 | Functor& m_functor; |
75 | }; |
76 | |
77 | /** Find specific object */ |
78 | struct Find { |
79 | |
80 | /** Constructor */ |
81 | Find(const void* object, ulint type) |
82 | : |
83 | m_slot(), |
84 | m_type(type), |
85 | m_object(object) |
86 | { |
87 | ut_a(object != NULL); |
88 | } |
89 | |
90 | /** @return false if the object was found. */ |
91 | bool operator()(mtr_memo_slot_t* slot) |
92 | { |
93 | if (m_object == slot->object && m_type == slot->type) { |
94 | m_slot = slot; |
95 | return(false); |
96 | } |
97 | |
98 | return(true); |
99 | } |
100 | |
101 | /** Slot if found */ |
102 | mtr_memo_slot_t*m_slot; |
103 | |
104 | /** Type of the object to look for */ |
105 | ulint m_type; |
106 | |
107 | /** The object instance to look for */ |
108 | const void* m_object; |
109 | }; |
110 | |
111 | /** Find a page frame */ |
112 | struct FindPage |
113 | { |
114 | /** Constructor |
115 | @param[in] ptr pointer to within a page frame |
116 | @param[in] flags MTR_MEMO flags to look for */ |
117 | FindPage(const void* ptr, ulint flags) |
118 | : m_ptr(ptr), m_flags(flags), m_slot(NULL) |
119 | { |
120 | /* There must be some flags to look for. */ |
121 | ut_ad(flags); |
122 | /* We can only look for page-related flags. */ |
123 | ut_ad(!(flags & ulint(~(MTR_MEMO_PAGE_S_FIX |
124 | | MTR_MEMO_PAGE_X_FIX |
125 | | MTR_MEMO_PAGE_SX_FIX |
126 | | MTR_MEMO_BUF_FIX |
127 | | MTR_MEMO_MODIFY)))); |
128 | } |
129 | |
130 | /** Visit a memo entry. |
131 | @param[in] slot memo entry to visit |
132 | @retval false if a page was found |
133 | @retval true if the iteration should continue */ |
134 | bool operator()(mtr_memo_slot_t* slot) |
135 | { |
136 | ut_ad(m_slot == NULL); |
137 | |
138 | if (!(m_flags & slot->type) || slot->object == NULL) { |
139 | return(true); |
140 | } |
141 | |
142 | buf_block_t* block = reinterpret_cast<buf_block_t*>( |
143 | slot->object); |
144 | |
145 | if (m_ptr < block->frame |
146 | || m_ptr >= block->frame + block->page.size.logical()) { |
147 | return(true); |
148 | } |
149 | |
150 | ut_ad(!(m_flags & (MTR_MEMO_PAGE_S_FIX |
151 | | MTR_MEMO_PAGE_SX_FIX |
152 | | MTR_MEMO_PAGE_X_FIX)) |
153 | || rw_lock_own_flagged(&block->lock, m_flags)); |
154 | |
155 | m_slot = slot; |
156 | return(false); |
157 | } |
158 | |
159 | /** @return the slot that was found */ |
160 | mtr_memo_slot_t* get_slot() const |
161 | { |
162 | ut_ad(m_slot != NULL); |
163 | return(m_slot); |
164 | } |
165 | /** @return the block that was found */ |
166 | buf_block_t* get_block() const |
167 | { |
168 | return(reinterpret_cast<buf_block_t*>(get_slot()->object)); |
169 | } |
170 | private: |
171 | /** Pointer inside a page frame to look for */ |
172 | const void*const m_ptr; |
173 | /** MTR_MEMO flags to look for */ |
174 | const ulint m_flags; |
175 | /** The slot corresponding to m_ptr */ |
176 | mtr_memo_slot_t* m_slot; |
177 | }; |
178 | |
179 | /** Release latches and decrement the buffer fix count. |
180 | @param slot memo slot */ |
181 | static |
182 | void |
183 | memo_slot_release(mtr_memo_slot_t* slot) |
184 | { |
185 | switch (slot->type) { |
186 | case MTR_MEMO_BUF_FIX: |
187 | case MTR_MEMO_PAGE_S_FIX: |
188 | case MTR_MEMO_PAGE_SX_FIX: |
189 | case MTR_MEMO_PAGE_X_FIX: { |
190 | |
191 | buf_block_t* block; |
192 | |
193 | block = reinterpret_cast<buf_block_t*>(slot->object); |
194 | |
195 | buf_block_unfix(block); |
196 | buf_page_release_latch(block, slot->type); |
197 | break; |
198 | } |
199 | |
200 | case MTR_MEMO_S_LOCK: |
201 | rw_lock_s_unlock(reinterpret_cast<rw_lock_t*>(slot->object)); |
202 | break; |
203 | |
204 | case MTR_MEMO_SX_LOCK: |
205 | rw_lock_sx_unlock(reinterpret_cast<rw_lock_t*>(slot->object)); |
206 | break; |
207 | |
208 | case MTR_MEMO_X_LOCK: |
209 | rw_lock_x_unlock(reinterpret_cast<rw_lock_t*>(slot->object)); |
210 | break; |
211 | |
212 | #ifdef UNIV_DEBUG |
213 | default: |
214 | ut_ad(slot->type == MTR_MEMO_MODIFY); |
215 | #endif /* UNIV_DEBUG */ |
216 | } |
217 | |
218 | slot->object = NULL; |
219 | } |
220 | |
221 | /** Unfix a page, do not release the latches on the page. |
222 | @param slot memo slot */ |
223 | static |
224 | void |
225 | memo_block_unfix(mtr_memo_slot_t* slot) |
226 | { |
227 | switch (slot->type) { |
228 | case MTR_MEMO_BUF_FIX: |
229 | case MTR_MEMO_PAGE_S_FIX: |
230 | case MTR_MEMO_PAGE_X_FIX: |
231 | case MTR_MEMO_PAGE_SX_FIX: { |
232 | buf_block_unfix(reinterpret_cast<buf_block_t*>(slot->object)); |
233 | break; |
234 | } |
235 | |
236 | case MTR_MEMO_S_LOCK: |
237 | case MTR_MEMO_X_LOCK: |
238 | case MTR_MEMO_SX_LOCK: |
239 | break; |
240 | #ifdef UNIV_DEBUG |
241 | default: |
242 | #endif /* UNIV_DEBUG */ |
243 | break; |
244 | } |
245 | } |
246 | /** Release latches represented by a slot. |
247 | @param slot memo slot */ |
248 | static |
249 | void |
250 | memo_latch_release(mtr_memo_slot_t* slot) |
251 | { |
252 | switch (slot->type) { |
253 | case MTR_MEMO_BUF_FIX: |
254 | case MTR_MEMO_PAGE_S_FIX: |
255 | case MTR_MEMO_PAGE_SX_FIX: |
256 | case MTR_MEMO_PAGE_X_FIX: { |
257 | buf_block_t* block; |
258 | |
259 | block = reinterpret_cast<buf_block_t*>(slot->object); |
260 | |
261 | memo_block_unfix(slot); |
262 | |
263 | buf_page_release_latch(block, slot->type); |
264 | |
265 | slot->object = NULL; |
266 | break; |
267 | } |
268 | |
269 | case MTR_MEMO_S_LOCK: |
270 | rw_lock_s_unlock(reinterpret_cast<rw_lock_t*>(slot->object)); |
271 | slot->object = NULL; |
272 | break; |
273 | |
274 | case MTR_MEMO_X_LOCK: |
275 | rw_lock_x_unlock(reinterpret_cast<rw_lock_t*>(slot->object)); |
276 | slot->object = NULL; |
277 | break; |
278 | |
279 | case MTR_MEMO_SX_LOCK: |
280 | rw_lock_sx_unlock(reinterpret_cast<rw_lock_t*>(slot->object)); |
281 | slot->object = NULL; |
282 | break; |
283 | |
284 | #ifdef UNIV_DEBUG |
285 | default: |
286 | ut_ad(slot->type == MTR_MEMO_MODIFY); |
287 | |
288 | slot->object = NULL; |
289 | #endif /* UNIV_DEBUG */ |
290 | } |
291 | } |
292 | |
293 | /** Release the latches acquired by the mini-transaction. */ |
294 | struct ReleaseLatches { |
295 | |
296 | /** @return true always. */ |
297 | bool operator()(mtr_memo_slot_t* slot) const |
298 | { |
299 | if (slot->object != NULL) { |
300 | memo_latch_release(slot); |
301 | } |
302 | |
303 | return(true); |
304 | } |
305 | }; |
306 | |
307 | /** Release the latches and blocks acquired by the mini-transaction. */ |
308 | struct ReleaseAll { |
309 | /** @return true always. */ |
310 | bool operator()(mtr_memo_slot_t* slot) const |
311 | { |
312 | if (slot->object != NULL) { |
313 | memo_slot_release(slot); |
314 | } |
315 | |
316 | return(true); |
317 | } |
318 | }; |
319 | |
320 | /** Check that all slots have been handled. */ |
321 | struct DebugCheck { |
322 | /** @return true always. */ |
323 | bool operator()(const mtr_memo_slot_t* slot) const |
324 | { |
325 | ut_a(slot->object == NULL); |
326 | return(true); |
327 | } |
328 | }; |
329 | |
330 | /** Release a resource acquired by the mini-transaction. */ |
331 | struct ReleaseBlocks { |
332 | /** Release specific object */ |
333 | ReleaseBlocks(lsn_t start_lsn, lsn_t end_lsn, FlushObserver* observer) |
334 | : |
335 | m_end_lsn(end_lsn), |
336 | m_start_lsn(start_lsn), |
337 | m_flush_observer(observer) |
338 | { |
339 | /* Do nothing */ |
340 | } |
341 | |
342 | /** Add the modified page to the buffer flush list. */ |
343 | void add_dirty_page_to_flush_list(mtr_memo_slot_t* slot) const |
344 | { |
345 | ut_ad(m_end_lsn > 0); |
346 | ut_ad(m_start_lsn > 0); |
347 | |
348 | buf_block_t* block; |
349 | |
350 | block = reinterpret_cast<buf_block_t*>(slot->object); |
351 | |
352 | buf_flush_note_modification(block, m_start_lsn, |
353 | m_end_lsn, m_flush_observer); |
354 | } |
355 | |
356 | /** @return true always. */ |
357 | bool operator()(mtr_memo_slot_t* slot) const |
358 | { |
359 | if (slot->object != NULL) { |
360 | |
361 | if (slot->type == MTR_MEMO_PAGE_X_FIX |
362 | || slot->type == MTR_MEMO_PAGE_SX_FIX) { |
363 | |
364 | add_dirty_page_to_flush_list(slot); |
365 | } |
366 | } |
367 | |
368 | return(true); |
369 | } |
370 | |
371 | /** Mini-transaction REDO start LSN */ |
372 | lsn_t m_end_lsn; |
373 | |
374 | /** Mini-transaction REDO end LSN */ |
375 | lsn_t m_start_lsn; |
376 | |
377 | /** Flush observer */ |
378 | FlushObserver* m_flush_observer; |
379 | }; |
380 | |
381 | class mtr_t::Command { |
382 | public: |
383 | /** Constructor. |
384 | Takes ownership of the mtr->m_impl, is responsible for deleting it. |
385 | @param[in,out] mtr mini-transaction */ |
386 | explicit Command(mtr_t* mtr) |
387 | : |
388 | m_locks_released() |
389 | { |
390 | init(mtr); |
391 | } |
392 | |
393 | void init(mtr_t* mtr) |
394 | { |
395 | m_impl = &mtr->m_impl; |
396 | m_sync = mtr->m_sync; |
397 | } |
398 | |
399 | /** Destructor */ |
400 | ~Command() |
401 | { |
402 | ut_ad(m_impl == 0); |
403 | } |
404 | |
405 | /** Write the redo log record, add dirty pages to the flush list and |
406 | release the resources. */ |
407 | void execute(); |
408 | |
409 | /** Release the blocks used in this mini-transaction. */ |
410 | void release_blocks(); |
411 | |
412 | /** Release the latches acquired by the mini-transaction. */ |
413 | void release_latches(); |
414 | |
415 | /** Release both the latches and blocks used in the mini-transaction. */ |
416 | void release_all(); |
417 | |
418 | /** Release the resources */ |
419 | void release_resources(); |
420 | |
421 | /** Append the redo log records to the redo log buffer. |
422 | @param[in] len number of bytes to write */ |
423 | void finish_write(ulint len); |
424 | |
425 | private: |
426 | /** Prepare to write the mini-transaction log to the redo log buffer. |
427 | @return number of bytes to write in finish_write() */ |
428 | ulint prepare_write(); |
429 | |
430 | /** true if it is a sync mini-transaction. */ |
431 | bool m_sync; |
432 | |
433 | /** The mini-transaction state. */ |
434 | mtr_t::Impl* m_impl; |
435 | |
436 | /** Set to 1 after the user thread releases the latches. The log |
437 | writer thread must wait for this to be set to 1. */ |
438 | volatile ulint m_locks_released; |
439 | |
440 | /** Start lsn of the possible log entry for this mtr */ |
441 | lsn_t m_start_lsn; |
442 | |
443 | /** End lsn of the possible log entry for this mtr */ |
444 | lsn_t m_end_lsn; |
445 | }; |
446 | |
447 | /** Check if a mini-transaction is dirtying a clean page. |
448 | @return true if the mtr is dirtying a clean page. */ |
449 | bool |
450 | mtr_t::is_block_dirtied(const buf_block_t* block) |
451 | { |
452 | ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); |
453 | ut_ad(block->page.buf_fix_count > 0); |
454 | |
455 | /* It is OK to read oldest_modification because no |
456 | other thread can be performing a write of it and it |
457 | is only during write that the value is reset to 0. */ |
458 | return(block->page.oldest_modification == 0); |
459 | } |
460 | |
461 | /** Write the block contents to the REDO log */ |
462 | struct mtr_write_log_t { |
463 | /** Append a block to the redo log buffer. |
464 | @return whether the appending should continue */ |
465 | bool operator()(const mtr_buf_t::block_t* block) const |
466 | { |
467 | log_write_low(block->begin(), block->used()); |
468 | return(true); |
469 | } |
470 | }; |
471 | |
472 | /** Append records to the system-wide redo log buffer. |
473 | @param[in] log redo log records */ |
474 | void |
475 | mtr_write_log( |
476 | const mtr_buf_t* log) |
477 | { |
478 | const ulint len = log->size(); |
479 | mtr_write_log_t write_log; |
480 | |
481 | ut_ad(!recv_no_log_write); |
482 | DBUG_PRINT("ib_log" , |
483 | (ULINTPF " extra bytes written at " LSN_PF, |
484 | len, log_sys.lsn)); |
485 | |
486 | log_reserve_and_open(len); |
487 | log->for_each_block(write_log); |
488 | log_close(); |
489 | } |
490 | |
491 | /** Start a mini-transaction. |
492 | @param sync true if it is a synchronous mini-transaction */ |
493 | void |
494 | mtr_t::start(bool sync) |
495 | { |
496 | UNIV_MEM_INVALID(this, sizeof(*this)); |
497 | |
498 | UNIV_MEM_INVALID(&m_impl, sizeof(m_impl)); |
499 | |
500 | m_sync = sync; |
501 | |
502 | m_commit_lsn = 0; |
503 | |
504 | new(&m_impl.m_log) mtr_buf_t(); |
505 | new(&m_impl.m_memo) mtr_buf_t(); |
506 | |
507 | m_impl.m_mtr = this; |
508 | m_impl.m_log_mode = MTR_LOG_ALL; |
509 | m_impl.m_inside_ibuf = false; |
510 | m_impl.m_modifications = false; |
511 | m_impl.m_made_dirty = false; |
512 | m_impl.m_n_log_recs = 0; |
513 | m_impl.m_state = MTR_STATE_ACTIVE; |
514 | ut_d(m_impl.m_user_space_id = TRX_SYS_SPACE); |
515 | m_impl.m_user_space = NULL; |
516 | m_impl.m_flush_observer = NULL; |
517 | |
518 | ut_d(m_impl.m_magic_n = MTR_MAGIC_N); |
519 | } |
520 | |
521 | /** Release the resources */ |
522 | void |
523 | mtr_t::Command::release_resources() |
524 | { |
525 | ut_ad(m_impl->m_magic_n == MTR_MAGIC_N); |
526 | |
527 | /* Currently only used in commit */ |
528 | ut_ad(m_impl->m_state == MTR_STATE_COMMITTING); |
529 | |
530 | #ifdef UNIV_DEBUG |
531 | DebugCheck release; |
532 | Iterate<DebugCheck> iterator(release); |
533 | |
534 | m_impl->m_memo.for_each_block_in_reverse(iterator); |
535 | #endif /* UNIV_DEBUG */ |
536 | |
537 | /* Reset the mtr buffers */ |
538 | m_impl->m_log.erase(); |
539 | |
540 | m_impl->m_memo.erase(); |
541 | |
542 | m_impl->m_state = MTR_STATE_COMMITTED; |
543 | |
544 | m_impl = 0; |
545 | } |
546 | |
547 | /** Commit a mini-transaction. */ |
548 | void |
549 | mtr_t::commit() |
550 | { |
551 | ut_ad(is_active()); |
552 | ut_ad(!is_inside_ibuf()); |
553 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
554 | m_impl.m_state = MTR_STATE_COMMITTING; |
555 | |
556 | /* This is a dirty read, for debugging. */ |
557 | ut_ad(!m_impl.m_modifications || !recv_no_log_write); |
558 | |
559 | Command cmd(this); |
560 | |
561 | if (m_impl.m_modifications |
562 | && (m_impl.m_n_log_recs > 0 |
563 | || m_impl.m_log_mode == MTR_LOG_NO_REDO)) { |
564 | |
565 | ut_ad(!srv_read_only_mode |
566 | || m_impl.m_log_mode == MTR_LOG_NO_REDO); |
567 | |
568 | cmd.execute(); |
569 | } else { |
570 | cmd.release_all(); |
571 | cmd.release_resources(); |
572 | } |
573 | } |
574 | |
575 | /** Commit a mini-transaction that did not modify any pages, |
576 | but generated some redo log on a higher level, such as |
577 | MLOG_FILE_NAME records and a MLOG_CHECKPOINT marker. |
578 | The caller must invoke log_mutex_enter() and log_mutex_exit(). |
579 | This is to be used at log_checkpoint(). |
580 | @param[in] checkpoint_lsn the LSN of the log checkpoint |
581 | @param[in] write_mlog_checkpoint Write MLOG_CHECKPOINT marker |
582 | if it is enabled. */ |
583 | void |
584 | mtr_t::commit_checkpoint( |
585 | lsn_t checkpoint_lsn, |
586 | bool write_mlog_checkpoint) |
587 | { |
588 | ut_ad(log_mutex_own()); |
589 | ut_ad(is_active()); |
590 | ut_ad(!is_inside_ibuf()); |
591 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
592 | ut_ad(get_log_mode() == MTR_LOG_ALL); |
593 | ut_ad(!m_impl.m_made_dirty); |
594 | ut_ad(m_impl.m_memo.size() == 0); |
595 | ut_ad(!srv_read_only_mode); |
596 | ut_d(m_impl.m_state = MTR_STATE_COMMITTING); |
597 | ut_ad(write_mlog_checkpoint || m_impl.m_n_log_recs > 1); |
598 | |
599 | switch (m_impl.m_n_log_recs) { |
600 | case 0: |
601 | break; |
602 | case 1: |
603 | *m_impl.m_log.front()->begin() |= MLOG_SINGLE_REC_FLAG; |
604 | break; |
605 | default: |
606 | mlog_catenate_ulint( |
607 | &m_impl.m_log, MLOG_MULTI_REC_END, MLOG_1BYTE); |
608 | } |
609 | |
610 | if (write_mlog_checkpoint) { |
611 | byte* ptr = m_impl.m_log.push<byte*>(SIZE_OF_MLOG_CHECKPOINT); |
612 | compile_time_assert(SIZE_OF_MLOG_CHECKPOINT == 1 + 8); |
613 | *ptr = MLOG_CHECKPOINT; |
614 | mach_write_to_8(ptr + 1, checkpoint_lsn); |
615 | } |
616 | |
617 | Command cmd(this); |
618 | cmd.finish_write(m_impl.m_log.size()); |
619 | cmd.release_resources(); |
620 | |
621 | if (write_mlog_checkpoint) { |
622 | DBUG_PRINT("ib_log" , |
623 | ("MLOG_CHECKPOINT(" LSN_PF ") written at " LSN_PF, |
624 | checkpoint_lsn, log_sys.lsn)); |
625 | } |
626 | } |
627 | |
628 | #ifdef UNIV_DEBUG |
629 | /** Check if a tablespace is associated with the mini-transaction |
630 | (needed for generating a MLOG_FILE_NAME record) |
631 | @param[in] space tablespace |
632 | @return whether the mini-transaction is associated with the space */ |
633 | bool |
634 | mtr_t::is_named_space(ulint space) const |
635 | { |
636 | ut_ad(!m_impl.m_user_space |
637 | || m_impl.m_user_space->id != TRX_SYS_SPACE); |
638 | |
639 | switch (get_log_mode()) { |
640 | case MTR_LOG_NONE: |
641 | case MTR_LOG_NO_REDO: |
642 | return(true); |
643 | case MTR_LOG_ALL: |
644 | case MTR_LOG_SHORT_INSERTS: |
645 | return(m_impl.m_user_space_id == space |
646 | || is_predefined_tablespace(space)); |
647 | } |
648 | |
649 | ut_error; |
650 | return(false); |
651 | } |
652 | /** Check if a tablespace is associated with the mini-transaction |
653 | (needed for generating a MLOG_FILE_NAME record) |
654 | @param[in] space tablespace |
655 | @return whether the mini-transaction is associated with the space */ |
656 | bool mtr_t::is_named_space(const fil_space_t* space) const |
657 | { |
658 | ut_ad(!m_impl.m_user_space |
659 | || m_impl.m_user_space->id != TRX_SYS_SPACE); |
660 | |
661 | switch (get_log_mode()) { |
662 | case MTR_LOG_NONE: |
663 | case MTR_LOG_NO_REDO: |
664 | return true; |
665 | case MTR_LOG_ALL: |
666 | case MTR_LOG_SHORT_INSERTS: |
667 | return(m_impl.m_user_space == space |
668 | || is_predefined_tablespace(space->id)); |
669 | } |
670 | |
671 | ut_error; |
672 | return false; |
673 | } |
674 | #endif /* UNIV_DEBUG */ |
675 | |
676 | /** Acquire a tablespace X-latch. |
677 | NOTE: use mtr_x_lock_space(). |
678 | @param[in] space_id tablespace ID |
679 | @param[in] file file name from where called |
680 | @param[in] line line number in file |
681 | @return the tablespace object (never NULL) */ |
682 | fil_space_t* |
683 | mtr_t::x_lock_space(ulint space_id, const char* file, unsigned line) |
684 | { |
685 | fil_space_t* space; |
686 | |
687 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
688 | ut_ad(is_active()); |
689 | |
690 | if (space_id == TRX_SYS_SPACE) { |
691 | space = fil_system.sys_space; |
692 | } else if ((space = m_impl.m_user_space) && space_id == space->id) { |
693 | } else { |
694 | space = fil_space_get(space_id); |
695 | ut_ad(get_log_mode() != MTR_LOG_NO_REDO |
696 | || space->purpose == FIL_TYPE_TEMPORARY |
697 | || space->purpose == FIL_TYPE_IMPORT |
698 | || my_atomic_loadlint(&space->redo_skipped_count) > 0 |
699 | || srv_is_tablespace_truncated(space->id)); |
700 | } |
701 | |
702 | ut_ad(space); |
703 | ut_ad(space->id == space_id); |
704 | x_lock(&space->latch, file, line); |
705 | ut_ad(space->purpose == FIL_TYPE_TEMPORARY |
706 | || space->purpose == FIL_TYPE_IMPORT |
707 | || space->purpose == FIL_TYPE_TABLESPACE); |
708 | return(space); |
709 | } |
710 | |
711 | /** Release an object in the memo stack. |
712 | @return true if released */ |
713 | bool |
714 | mtr_t::memo_release(const void* object, ulint type) |
715 | { |
716 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
717 | ut_ad(is_active()); |
718 | |
719 | /* We cannot release a page that has been written to in the |
720 | middle of a mini-transaction. */ |
721 | ut_ad(!m_impl.m_modifications || type != MTR_MEMO_PAGE_X_FIX); |
722 | |
723 | Find find(object, type); |
724 | Iterate<Find> iterator(find); |
725 | |
726 | if (!m_impl.m_memo.for_each_block_in_reverse(iterator)) { |
727 | memo_slot_release(find.m_slot); |
728 | return(true); |
729 | } |
730 | |
731 | return(false); |
732 | } |
733 | |
734 | /** Release a page latch. |
735 | @param[in] ptr pointer to within a page frame |
736 | @param[in] type object type: MTR_MEMO_PAGE_X_FIX, ... */ |
737 | void |
738 | mtr_t::release_page(const void* ptr, mtr_memo_type_t type) |
739 | { |
740 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
741 | ut_ad(is_active()); |
742 | |
743 | /* We cannot release a page that has been written to in the |
744 | middle of a mini-transaction. */ |
745 | ut_ad(!m_impl.m_modifications || type != MTR_MEMO_PAGE_X_FIX); |
746 | |
747 | FindPage find(ptr, type); |
748 | Iterate<FindPage> iterator(find); |
749 | |
750 | if (!m_impl.m_memo.for_each_block_in_reverse(iterator)) { |
751 | memo_slot_release(find.get_slot()); |
752 | return; |
753 | } |
754 | |
755 | /* The page was not found! */ |
756 | ut_ad(0); |
757 | } |
758 | |
759 | /** Prepare to write the mini-transaction log to the redo log buffer. |
760 | @return number of bytes to write in finish_write() */ |
761 | ulint |
762 | mtr_t::Command::prepare_write() |
763 | { |
764 | ut_ad(!recv_no_log_write); |
765 | |
766 | switch (m_impl->m_log_mode) { |
767 | case MTR_LOG_SHORT_INSERTS: |
768 | ut_ad(0); |
769 | /* fall through (write no redo log) */ |
770 | case MTR_LOG_NO_REDO: |
771 | case MTR_LOG_NONE: |
772 | ut_ad(m_impl->m_log.size() == 0); |
773 | log_mutex_enter(); |
774 | m_end_lsn = m_start_lsn = log_sys.lsn; |
775 | return(0); |
776 | case MTR_LOG_ALL: |
777 | break; |
778 | } |
779 | |
780 | ulint len = m_impl->m_log.size(); |
781 | ulint n_recs = m_impl->m_n_log_recs; |
782 | ut_ad(len > 0); |
783 | ut_ad(n_recs > 0); |
784 | |
785 | if (len > srv_log_buffer_size / 2) { |
786 | log_buffer_extend(ulong((len + 1) * 2)); |
787 | } |
788 | |
789 | ut_ad(m_impl->m_n_log_recs == n_recs); |
790 | |
791 | fil_space_t* space = m_impl->m_user_space; |
792 | |
793 | if (space != NULL && is_predefined_tablespace(space->id)) { |
794 | /* Omit MLOG_FILE_NAME for predefined tablespaces. */ |
795 | space = NULL; |
796 | } |
797 | |
798 | log_mutex_enter(); |
799 | |
800 | if (fil_names_write_if_was_clean(space, m_impl->m_mtr)) { |
801 | /* This mini-transaction was the first one to modify |
802 | this tablespace since the latest checkpoint, so |
803 | some MLOG_FILE_NAME records were appended to m_log. */ |
804 | ut_ad(m_impl->m_n_log_recs > n_recs); |
805 | mlog_catenate_ulint( |
806 | &m_impl->m_log, MLOG_MULTI_REC_END, MLOG_1BYTE); |
807 | len = m_impl->m_log.size(); |
808 | } else { |
809 | /* This was not the first time of dirtying a |
810 | tablespace since the latest checkpoint. */ |
811 | |
812 | ut_ad(n_recs == m_impl->m_n_log_recs); |
813 | |
814 | if (n_recs <= 1) { |
815 | ut_ad(n_recs == 1); |
816 | |
817 | /* Flag the single log record as the |
818 | only record in this mini-transaction. */ |
819 | *m_impl->m_log.front()->begin() |
820 | |= MLOG_SINGLE_REC_FLAG; |
821 | } else { |
822 | /* Because this mini-transaction comprises |
823 | multiple log records, append MLOG_MULTI_REC_END |
824 | at the end. */ |
825 | |
826 | mlog_catenate_ulint( |
827 | &m_impl->m_log, MLOG_MULTI_REC_END, |
828 | MLOG_1BYTE); |
829 | len++; |
830 | } |
831 | } |
832 | |
833 | /* check and attempt a checkpoint if exceeding capacity */ |
834 | log_margin_checkpoint_age(len); |
835 | |
836 | return(len); |
837 | } |
838 | |
839 | /** Append the redo log records to the redo log buffer |
840 | @param[in] len number of bytes to write */ |
841 | void |
842 | mtr_t::Command::finish_write( |
843 | ulint len) |
844 | { |
845 | ut_ad(m_impl->m_log_mode == MTR_LOG_ALL); |
846 | ut_ad(log_mutex_own()); |
847 | ut_ad(m_impl->m_log.size() == len); |
848 | ut_ad(len > 0); |
849 | |
850 | if (m_impl->m_log.is_small()) { |
851 | const mtr_buf_t::block_t* front = m_impl->m_log.front(); |
852 | ut_ad(len <= front->used()); |
853 | |
854 | m_end_lsn = log_reserve_and_write_fast( |
855 | front->begin(), len, &m_start_lsn); |
856 | |
857 | if (m_end_lsn > 0) { |
858 | return; |
859 | } |
860 | } |
861 | |
862 | /* Open the database log for log_write_low */ |
863 | m_start_lsn = log_reserve_and_open(len); |
864 | |
865 | mtr_write_log_t write_log; |
866 | m_impl->m_log.for_each_block(write_log); |
867 | |
868 | m_end_lsn = log_close(); |
869 | } |
870 | |
871 | /** Release the latches and blocks acquired by this mini-transaction */ |
872 | void |
873 | mtr_t::Command::release_all() |
874 | { |
875 | ReleaseAll release; |
876 | Iterate<ReleaseAll> iterator(release); |
877 | |
878 | m_impl->m_memo.for_each_block_in_reverse(iterator); |
879 | |
880 | /* Note that we have released the latches. */ |
881 | m_locks_released = 1; |
882 | } |
883 | |
884 | /** Release the latches acquired by this mini-transaction */ |
885 | void |
886 | mtr_t::Command::release_latches() |
887 | { |
888 | ReleaseLatches release; |
889 | Iterate<ReleaseLatches> iterator(release); |
890 | |
891 | m_impl->m_memo.for_each_block_in_reverse(iterator); |
892 | |
893 | /* Note that we have released the latches. */ |
894 | m_locks_released = 1; |
895 | } |
896 | |
897 | /** Release the blocks used in this mini-transaction */ |
898 | void |
899 | mtr_t::Command::release_blocks() |
900 | { |
901 | ReleaseBlocks release(m_start_lsn, m_end_lsn, m_impl->m_flush_observer); |
902 | Iterate<ReleaseBlocks> iterator(release); |
903 | |
904 | m_impl->m_memo.for_each_block_in_reverse(iterator); |
905 | } |
906 | |
907 | /** Write the redo log record, add dirty pages to the flush list and release |
908 | the resources. */ |
909 | void |
910 | mtr_t::Command::execute() |
911 | { |
912 | ut_ad(m_impl->m_log_mode != MTR_LOG_NONE); |
913 | |
914 | if (const ulint len = prepare_write()) { |
915 | finish_write(len); |
916 | } |
917 | |
918 | if (m_impl->m_made_dirty) { |
919 | log_flush_order_mutex_enter(); |
920 | } |
921 | |
922 | /* It is now safe to release the log mutex because the |
923 | flush_order mutex will ensure that we are the first one |
924 | to insert into the flush list. */ |
925 | log_mutex_exit(); |
926 | |
927 | m_impl->m_mtr->m_commit_lsn = m_end_lsn; |
928 | |
929 | release_blocks(); |
930 | |
931 | if (m_impl->m_made_dirty) { |
932 | log_flush_order_mutex_exit(); |
933 | } |
934 | |
935 | release_latches(); |
936 | |
937 | release_resources(); |
938 | } |
939 | |
940 | #ifdef UNIV_DEBUG |
941 | /** Check if memo contains the given item. |
942 | @return true if contains */ |
943 | bool |
944 | mtr_t::memo_contains( |
945 | const mtr_buf_t* memo, |
946 | const void* object, |
947 | ulint type) |
948 | { |
949 | Find find(object, type); |
950 | Iterate<Find> iterator(find); |
951 | |
952 | if (memo->for_each_block_in_reverse(iterator)) { |
953 | return(false); |
954 | } |
955 | |
956 | switch (type) { |
957 | case MTR_MEMO_X_LOCK: |
958 | ut_ad(rw_lock_own((rw_lock_t*) object, RW_LOCK_X)); |
959 | break; |
960 | case MTR_MEMO_SX_LOCK: |
961 | ut_ad(rw_lock_own((rw_lock_t*) object, RW_LOCK_SX)); |
962 | break; |
963 | case MTR_MEMO_S_LOCK: |
964 | ut_ad(rw_lock_own((rw_lock_t*) object, RW_LOCK_S)); |
965 | break; |
966 | } |
967 | |
968 | return(true); |
969 | } |
970 | |
971 | /** Debug check for flags */ |
972 | struct FlaggedCheck { |
973 | FlaggedCheck(const void* ptr, ulint flags) |
974 | : |
975 | m_ptr(ptr), |
976 | m_flags(flags) |
977 | { |
978 | /* There must be some flags to look for. */ |
979 | ut_ad(flags); |
980 | /* Look for rw-lock-related and page-related flags. */ |
981 | ut_ad(!(flags & ulint(~(MTR_MEMO_PAGE_S_FIX |
982 | | MTR_MEMO_PAGE_X_FIX |
983 | | MTR_MEMO_PAGE_SX_FIX |
984 | | MTR_MEMO_BUF_FIX |
985 | | MTR_MEMO_MODIFY |
986 | | MTR_MEMO_X_LOCK |
987 | | MTR_MEMO_SX_LOCK |
988 | | MTR_MEMO_S_LOCK)))); |
989 | /* Either some rw-lock-related or page-related flags |
990 | must be specified, but not both at the same time. */ |
991 | ut_ad(!(flags & (MTR_MEMO_PAGE_S_FIX |
992 | | MTR_MEMO_PAGE_X_FIX |
993 | | MTR_MEMO_PAGE_SX_FIX |
994 | | MTR_MEMO_BUF_FIX |
995 | | MTR_MEMO_MODIFY)) |
996 | == !!(flags & (MTR_MEMO_X_LOCK |
997 | | MTR_MEMO_SX_LOCK |
998 | | MTR_MEMO_S_LOCK))); |
999 | } |
1000 | |
1001 | /** Visit a memo entry. |
1002 | @param[in] slot memo entry to visit |
1003 | @retval false if m_ptr was found |
1004 | @retval true if the iteration should continue */ |
1005 | bool operator()(const mtr_memo_slot_t* slot) const |
1006 | { |
1007 | if (m_ptr != slot->object || !(m_flags & slot->type)) { |
1008 | return(true); |
1009 | } |
1010 | |
1011 | if (ulint flags = m_flags & (MTR_MEMO_PAGE_S_FIX |
1012 | | MTR_MEMO_PAGE_SX_FIX |
1013 | | MTR_MEMO_PAGE_X_FIX)) { |
1014 | rw_lock_t* lock = &static_cast<buf_block_t*>( |
1015 | const_cast<void*>(m_ptr))->lock; |
1016 | ut_ad(rw_lock_own_flagged(lock, flags)); |
1017 | } else { |
1018 | rw_lock_t* lock = static_cast<rw_lock_t*>( |
1019 | const_cast<void*>(m_ptr)); |
1020 | ut_ad(rw_lock_own_flagged(lock, m_flags >> 5)); |
1021 | } |
1022 | |
1023 | return(false); |
1024 | } |
1025 | |
1026 | const void*const m_ptr; |
1027 | const ulint m_flags; |
1028 | }; |
1029 | |
1030 | /** Check if memo contains the given item. |
1031 | @param object object to search |
1032 | @param flags specify types of object (can be ORred) of |
1033 | MTR_MEMO_PAGE_S_FIX ... values |
1034 | @return true if contains */ |
1035 | bool |
1036 | mtr_t::memo_contains_flagged(const void* ptr, ulint flags) const |
1037 | { |
1038 | ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); |
1039 | ut_ad(is_committing() || is_active()); |
1040 | |
1041 | FlaggedCheck check(ptr, flags); |
1042 | Iterate<FlaggedCheck> iterator(check); |
1043 | |
1044 | return(!m_impl.m_memo.for_each_block_in_reverse(iterator)); |
1045 | } |
1046 | |
1047 | /** Check if memo contains the given page. |
1048 | @param[in] ptr pointer to within buffer frame |
1049 | @param[in] flags specify types of object with OR of |
1050 | MTR_MEMO_PAGE_S_FIX... values |
1051 | @return the block |
1052 | @retval NULL if not found */ |
1053 | buf_block_t* |
1054 | mtr_t::memo_contains_page_flagged( |
1055 | const byte* ptr, |
1056 | ulint flags) const |
1057 | { |
1058 | FindPage check(ptr, flags); |
1059 | Iterate<FindPage> iterator(check); |
1060 | |
1061 | return(m_impl.m_memo.for_each_block_in_reverse(iterator) |
1062 | ? NULL : check.get_block()); |
1063 | } |
1064 | |
1065 | /** Mark the given latched page as modified. |
1066 | @param[in] ptr pointer to within buffer frame */ |
1067 | void |
1068 | mtr_t::memo_modify_page(const byte* ptr) |
1069 | { |
1070 | buf_block_t* block = memo_contains_page_flagged( |
1071 | ptr, MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX); |
1072 | ut_ad(block != NULL); |
1073 | |
1074 | if (!memo_contains(get_memo(), block, MTR_MEMO_MODIFY)) { |
1075 | memo_push(block, MTR_MEMO_MODIFY); |
1076 | } |
1077 | } |
1078 | |
1079 | /** Print info of an mtr handle. */ |
1080 | void |
1081 | mtr_t::print() const |
1082 | { |
1083 | ib::info() << "Mini-transaction handle: memo size " |
1084 | << m_impl.m_memo.size() << " bytes log size " |
1085 | << get_log()->size() << " bytes" ; |
1086 | } |
1087 | |
1088 | #endif /* UNIV_DEBUG */ |
1089 | |