1/*****************************************************************************
2
3Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file mtr/mtr0mtr.cc
22Mini-transaction buffer
23
24Created 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. */
40template <typename Functor>
41struct 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 */
78struct 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 */
112struct 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 }
170private:
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 */
181static
182void
183memo_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 */
223static
224void
225memo_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 */
248static
249void
250memo_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. */
294struct 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. */
308struct 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. */
321struct 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. */
331struct 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
381class mtr_t::Command {
382public:
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
425private:
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. */
449bool
450mtr_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 */
462struct 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 */
474void
475mtr_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 */
493void
494mtr_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 */
522void
523mtr_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. */
548void
549mtr_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,
576but generated some redo log on a higher level, such as
577MLOG_FILE_NAME records and a MLOG_CHECKPOINT marker.
578The caller must invoke log_mutex_enter() and log_mutex_exit().
579This 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. */
583void
584mtr_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 */
633bool
634mtr_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 */
656bool 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.
677NOTE: 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) */
682fil_space_t*
683mtr_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 */
713bool
714mtr_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, ... */
737void
738mtr_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() */
761ulint
762mtr_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 */
841void
842mtr_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 */
872void
873mtr_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 */
885void
886mtr_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 */
898void
899mtr_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
908the resources. */
909void
910mtr_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 */
943bool
944mtr_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 */
972struct 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 */
1035bool
1036mtr_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 */
1053buf_block_t*
1054mtr_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 */
1067void
1068mtr_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. */
1080void
1081mtr_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