mariadb/storage/innobase/mtr/mtr0mtr.cc
Marko Mäkelä c868acdf65 MDEV-11487 Revert InnoDB internal temporary tables from WL#7682
WL#7682 in MySQL 5.7 introduced the possibility to create light-weight
temporary tables in InnoDB. These are called 'intrinsic temporary tables'
in InnoDB, and in MySQL 5.7, they can be created by the optimizer for
sorting or buffering data in query processing.

In MariaDB 10.2, the optimizer temporary tables cannot be created in
InnoDB, so we should remove the dead code and related data structures.
2016-12-09 12:05:07 +02:00

1103 lines
25 KiB
C++

/*****************************************************************************
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/
/**************************************************//**
@file mtr/mtr0mtr.cc
Mini-transaction buffer
Created 11/26/1995 Heikki Tuuri
*******************************************************/
#include "mtr0mtr.h"
#include "buf0buf.h"
#include "buf0flu.h"
#include "fsp0sysspace.h"
#include "page0types.h"
#include "mtr0log.h"
#include "log0log.h"
#include "row0trunc.h"
#include "log0recv.h"
#ifdef UNIV_NONINL
#include "mtr0mtr.ic"
#endif /* UNIV_NONINL */
/** Iterate over a memo block in reverse. */
template <typename Functor>
struct Iterate {
/** Release specific object */
explicit Iterate(Functor& functor)
:
m_functor(functor)
{
/* Do nothing */
}
/** @return false if the functor returns false. */
bool operator()(mtr_buf_t::block_t* block)
{
const mtr_memo_slot_t* start =
reinterpret_cast<const mtr_memo_slot_t*>(
block->begin());
mtr_memo_slot_t* slot =
reinterpret_cast<mtr_memo_slot_t*>(
block->end());
ut_ad(!(block->used() % sizeof(*slot)));
while (slot-- != start) {
if (!m_functor(slot)) {
return(false);
}
}
return(true);
}
Functor& m_functor;
};
/** Find specific object */
struct Find {
/** Constructor */
Find(const void* object, ulint type)
:
m_slot(),
m_type(type),
m_object(object)
{
ut_a(object != NULL);
}
/** @return false if the object was found. */
bool operator()(mtr_memo_slot_t* slot)
{
if (m_object == slot->object && m_type == slot->type) {
m_slot = slot;
return(false);
}
return(true);
}
/** Slot if found */
mtr_memo_slot_t*m_slot;
/** Type of the object to look for */
ulint m_type;
/** The object instance to look for */
const void* m_object;
};
/** Find a page frame */
struct FindPage
{
/** Constructor
@param[in] ptr pointer to within a page frame
@param[in] flags MTR_MEMO flags to look for */
FindPage(const void* ptr, ulint flags)
: m_ptr(ptr), m_flags(flags), m_slot(NULL)
{
/* We can only look for page-related flags. */
ut_ad(!(flags & ~(MTR_MEMO_PAGE_S_FIX
| MTR_MEMO_PAGE_X_FIX
| MTR_MEMO_PAGE_SX_FIX
| MTR_MEMO_BUF_FIX
| MTR_MEMO_MODIFY)));
}
/** Visit a memo entry.
@param[in] slot memo entry to visit
@retval false if a page was found
@retval true if the iteration should continue */
bool operator()(mtr_memo_slot_t* slot)
{
ut_ad(m_slot == NULL);
if (!(m_flags & slot->type) || slot->object == NULL) {
return(true);
}
buf_block_t* block = reinterpret_cast<buf_block_t*>(
slot->object);
if (m_ptr < block->frame
|| m_ptr >= block->frame + block->page.size.logical()) {
return(true);
}
m_slot = slot;
return(false);
}
/** @return the slot that was found */
mtr_memo_slot_t* get_slot() const
{
ut_ad(m_slot != NULL);
return(m_slot);
}
/** @return the block that was found */
buf_block_t* get_block() const
{
return(reinterpret_cast<buf_block_t*>(get_slot()->object));
}
private:
/** Pointer inside a page frame to look for */
const void*const m_ptr;
/** MTR_MEMO flags to look for */
const ulint m_flags;
/** The slot corresponding to m_ptr */
mtr_memo_slot_t* m_slot;
};
/** Release latches and decrement the buffer fix count.
@param slot memo slot */
static
void
memo_slot_release(mtr_memo_slot_t* slot)
{
switch (slot->type) {
case MTR_MEMO_BUF_FIX:
case MTR_MEMO_PAGE_S_FIX:
case MTR_MEMO_PAGE_SX_FIX:
case MTR_MEMO_PAGE_X_FIX: {
buf_block_t* block;
block = reinterpret_cast<buf_block_t*>(slot->object);
buf_block_unfix(block);
buf_page_release_latch(block, slot->type);
break;
}
case MTR_MEMO_S_LOCK:
rw_lock_s_unlock(reinterpret_cast<rw_lock_t*>(slot->object));
break;
case MTR_MEMO_SX_LOCK:
rw_lock_sx_unlock(reinterpret_cast<rw_lock_t*>(slot->object));
break;
case MTR_MEMO_X_LOCK:
rw_lock_x_unlock(reinterpret_cast<rw_lock_t*>(slot->object));
break;
#ifdef UNIV_DEBUG
default:
ut_ad(slot->type == MTR_MEMO_MODIFY);
#endif /* UNIV_DEBUG */
}
slot->object = NULL;
}
/** Unfix a page, do not release the latches on the page.
@param slot memo slot */
static
void
memo_block_unfix(mtr_memo_slot_t* slot)
{
switch (slot->type) {
case MTR_MEMO_BUF_FIX:
case MTR_MEMO_PAGE_S_FIX:
case MTR_MEMO_PAGE_X_FIX:
case MTR_MEMO_PAGE_SX_FIX: {
buf_block_unfix(reinterpret_cast<buf_block_t*>(slot->object));
break;
}
case MTR_MEMO_S_LOCK:
case MTR_MEMO_X_LOCK:
case MTR_MEMO_SX_LOCK:
break;
#ifdef UNIV_DEBUG
default:
#endif /* UNIV_DEBUG */
break;
}
}
/** Release latches represented by a slot.
@param slot memo slot */
static
void
memo_latch_release(mtr_memo_slot_t* slot)
{
switch (slot->type) {
case MTR_MEMO_BUF_FIX:
case MTR_MEMO_PAGE_S_FIX:
case MTR_MEMO_PAGE_SX_FIX:
case MTR_MEMO_PAGE_X_FIX: {
buf_block_t* block;
block = reinterpret_cast<buf_block_t*>(slot->object);
memo_block_unfix(slot);
buf_page_release_latch(block, slot->type);
slot->object = NULL;
break;
}
case MTR_MEMO_S_LOCK:
rw_lock_s_unlock(reinterpret_cast<rw_lock_t*>(slot->object));
slot->object = NULL;
break;
case MTR_MEMO_X_LOCK:
rw_lock_x_unlock(reinterpret_cast<rw_lock_t*>(slot->object));
slot->object = NULL;
break;
case MTR_MEMO_SX_LOCK:
rw_lock_sx_unlock(reinterpret_cast<rw_lock_t*>(slot->object));
slot->object = NULL;
break;
#ifdef UNIV_DEBUG
default:
ut_ad(slot->type == MTR_MEMO_MODIFY);
slot->object = NULL;
#endif /* UNIV_DEBUG */
}
}
/** Release the latches acquired by the mini-transaction. */
struct ReleaseLatches {
/** @return true always. */
bool operator()(mtr_memo_slot_t* slot) const
{
if (slot->object != NULL) {
memo_latch_release(slot);
}
return(true);
}
};
/** Release the latches and blocks acquired by the mini-transaction. */
struct ReleaseAll {
/** @return true always. */
bool operator()(mtr_memo_slot_t* slot) const
{
if (slot->object != NULL) {
memo_slot_release(slot);
}
return(true);
}
};
/** Check that all slots have been handled. */
struct DebugCheck {
/** @return true always. */
bool operator()(const mtr_memo_slot_t* slot) const
{
ut_a(slot->object == NULL);
return(true);
}
};
/** Release a resource acquired by the mini-transaction. */
struct ReleaseBlocks {
/** Release specific object */
ReleaseBlocks(lsn_t start_lsn, lsn_t end_lsn, FlushObserver* observer)
:
m_end_lsn(end_lsn),
m_start_lsn(start_lsn),
m_flush_observer(observer)
{
/* Do nothing */
}
/** Add the modified page to the buffer flush list. */
void add_dirty_page_to_flush_list(mtr_memo_slot_t* slot) const
{
ut_ad(m_end_lsn > 0);
ut_ad(m_start_lsn > 0);
buf_block_t* block;
block = reinterpret_cast<buf_block_t*>(slot->object);
buf_flush_note_modification(block, m_start_lsn,
m_end_lsn, m_flush_observer);
}
/** @return true always. */
bool operator()(mtr_memo_slot_t* slot) const
{
if (slot->object != NULL) {
if (slot->type == MTR_MEMO_PAGE_X_FIX
|| slot->type == MTR_MEMO_PAGE_SX_FIX) {
add_dirty_page_to_flush_list(slot);
}
}
return(true);
}
/** Mini-transaction REDO start LSN */
lsn_t m_end_lsn;
/** Mini-transaction REDO end LSN */
lsn_t m_start_lsn;
/** Flush observer */
FlushObserver* m_flush_observer;
};
class mtr_t::Command {
public:
/** Constructor.
Takes ownership of the mtr->m_impl, is responsible for deleting it.
@param[in,out] mtr mini-transaction */
explicit Command(mtr_t* mtr)
:
m_locks_released()
{
init(mtr);
}
void init(mtr_t* mtr)
{
m_impl = &mtr->m_impl;
m_sync = mtr->m_sync;
}
/** Destructor */
~Command()
{
ut_ad(m_impl == 0);
}
/** Write the redo log record, add dirty pages to the flush list and
release the resources. */
void execute();
/** Release the blocks used in this mini-transaction. */
void release_blocks();
/** Release the latches acquired by the mini-transaction. */
void release_latches();
/** Release both the latches and blocks used in the mini-transaction. */
void release_all();
/** Release the resources */
void release_resources();
/** Append the redo log records to the redo log buffer.
@param[in] len number of bytes to write */
void finish_write(ulint len);
private:
/** Prepare to write the mini-transaction log to the redo log buffer.
@return number of bytes to write in finish_write() */
ulint prepare_write();
/** true if it is a sync mini-transaction. */
bool m_sync;
/** The mini-transaction state. */
mtr_t::Impl* m_impl;
/** Set to 1 after the user thread releases the latches. The log
writer thread must wait for this to be set to 1. */
volatile ulint m_locks_released;
/** Start lsn of the possible log entry for this mtr */
lsn_t m_start_lsn;
/** End lsn of the possible log entry for this mtr */
lsn_t m_end_lsn;
};
/** Check if a mini-transaction is dirtying a clean page.
@return true if the mtr is dirtying a clean page. */
bool
mtr_t::is_block_dirtied(const buf_block_t* block)
{
ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE);
ut_ad(block->page.buf_fix_count > 0);
/* It is OK to read oldest_modification because no
other thread can be performing a write of it and it
is only during write that the value is reset to 0. */
return(block->page.oldest_modification == 0);
}
/** Write the block contents to the REDO log */
struct mtr_write_log_t {
/** Append a block to the redo log buffer.
@return whether the appending should continue */
bool operator()(const mtr_buf_t::block_t* block) const
{
log_write_low(block->begin(), block->used());
return(true);
}
};
/** Append records to the system-wide redo log buffer.
@param[in] log redo log records */
void
mtr_write_log(
const mtr_buf_t* log)
{
const ulint len = log->size();
mtr_write_log_t write_log;
DBUG_PRINT("ib_log",
(ULINTPF " extra bytes written at " LSN_PF,
len, log_sys->lsn));
log_reserve_and_open(len);
log->for_each_block(write_log);
log_close();
}
/** Start a mini-transaction.
@param sync true if it is a synchronous mini-transaction
@param read_only true if read only mini-transaction */
void
mtr_t::start(bool sync, bool read_only)
{
start(NULL, sync, read_only);
}
/** Start a mini-transaction.
@param sync true if it is a synchronous mini-transaction
@param read_only true if read only mini-transaction */
void
mtr_t::start(trx_t* trx, bool sync, bool read_only)
{
UNIV_MEM_INVALID(this, sizeof(*this));
UNIV_MEM_INVALID(&m_impl, sizeof(m_impl));
m_sync = sync;
m_commit_lsn = 0;
new(&m_impl.m_log) mtr_buf_t();
new(&m_impl.m_memo) mtr_buf_t();
m_impl.m_mtr = this;
m_impl.m_log_mode = MTR_LOG_ALL;
m_impl.m_inside_ibuf = false;
m_impl.m_modifications = false;
m_impl.m_made_dirty = false;
m_impl.m_n_log_recs = 0;
m_impl.m_state = MTR_STATE_ACTIVE;
ut_d(m_impl.m_user_space_id = TRX_SYS_SPACE);
m_impl.m_user_space = NULL;
m_impl.m_undo_space = NULL;
m_impl.m_sys_space = NULL;
m_impl.m_flush_observer = NULL;
m_impl.m_trx = trx;
ut_d(m_impl.m_magic_n = MTR_MAGIC_N);
}
/** Release the resources */
void
mtr_t::Command::release_resources()
{
ut_ad(m_impl->m_magic_n == MTR_MAGIC_N);
/* Currently only used in commit */
ut_ad(m_impl->m_state == MTR_STATE_COMMITTING);
#ifdef UNIV_DEBUG
DebugCheck release;
Iterate<DebugCheck> iterator(release);
m_impl->m_memo.for_each_block_in_reverse(iterator);
#endif /* UNIV_DEBUG */
/* Reset the mtr buffers */
m_impl->m_log.erase();
m_impl->m_memo.erase();
m_impl->m_state = MTR_STATE_COMMITTED;
m_impl = 0;
}
/** Commit a mini-transaction. */
void
mtr_t::commit()
{
ut_ad(is_active());
ut_ad(!is_inside_ibuf());
ut_ad(m_impl.m_magic_n == MTR_MAGIC_N);
m_impl.m_state = MTR_STATE_COMMITTING;
/* This is a dirty read, for debugging. */
ut_ad(!recv_no_log_write);
Command cmd(this);
if (m_impl.m_modifications
&& (m_impl.m_n_log_recs > 0
|| m_impl.m_log_mode == MTR_LOG_NO_REDO)) {
ut_ad(!srv_read_only_mode
|| m_impl.m_log_mode == MTR_LOG_NO_REDO);
cmd.execute();
} else {
cmd.release_all();
cmd.release_resources();
}
}
/** Commit a mini-transaction that did not modify any pages,
but generated some redo log on a higher level, such as
MLOG_FILE_NAME records and a MLOG_CHECKPOINT marker.
The caller must invoke log_mutex_enter() and log_mutex_exit().
This is to be used at log_checkpoint().
@param[in] checkpoint_lsn the LSN of the log checkpoint */
void
mtr_t::commit_checkpoint(lsn_t checkpoint_lsn)
{
ut_ad(log_mutex_own());
ut_ad(is_active());
ut_ad(!is_inside_ibuf());
ut_ad(m_impl.m_magic_n == MTR_MAGIC_N);
ut_ad(get_log_mode() == MTR_LOG_ALL);
ut_ad(!m_impl.m_made_dirty);
ut_ad(m_impl.m_memo.size() == 0);
ut_ad(!srv_read_only_mode);
ut_d(m_impl.m_state = MTR_STATE_COMMITTING);
/* This is a dirty read, for debugging. */
ut_ad(!recv_no_log_write);
switch (m_impl.m_n_log_recs) {
case 0:
break;
case 1:
*m_impl.m_log.front()->begin() |= MLOG_SINGLE_REC_FLAG;
break;
default:
mlog_catenate_ulint(
&m_impl.m_log, MLOG_MULTI_REC_END, MLOG_1BYTE);
}
byte* ptr = m_impl.m_log.push<byte*>(SIZE_OF_MLOG_CHECKPOINT);
#if SIZE_OF_MLOG_CHECKPOINT != 9
# error SIZE_OF_MLOG_CHECKPOINT != 9
#endif
*ptr = MLOG_CHECKPOINT;
mach_write_to_8(ptr + 1, checkpoint_lsn);
Command cmd(this);
cmd.finish_write(m_impl.m_log.size());
cmd.release_resources();
DBUG_PRINT("ib_log",
("MLOG_CHECKPOINT(" LSN_PF ") written at " LSN_PF,
checkpoint_lsn, log_sys->lsn));
}
#ifdef UNIV_DEBUG
/** Check if a tablespace is associated with the mini-transaction
(needed for generating a MLOG_FILE_NAME record)
@param[in] space tablespace
@return whether the mini-transaction is associated with the space */
bool
mtr_t::is_named_space(ulint space) const
{
ut_ad(!m_impl.m_sys_space
|| m_impl.m_sys_space->id == TRX_SYS_SPACE);
ut_ad(!m_impl.m_undo_space
|| m_impl.m_undo_space->id != TRX_SYS_SPACE);
ut_ad(!m_impl.m_user_space
|| m_impl.m_user_space->id != TRX_SYS_SPACE);
ut_ad(!m_impl.m_sys_space
|| m_impl.m_sys_space != m_impl.m_user_space);
ut_ad(!m_impl.m_sys_space
|| m_impl.m_sys_space != m_impl.m_undo_space);
ut_ad(!m_impl.m_user_space
|| m_impl.m_user_space != m_impl.m_undo_space);
switch (get_log_mode()) {
case MTR_LOG_NONE:
case MTR_LOG_NO_REDO:
return(true);
case MTR_LOG_ALL:
case MTR_LOG_SHORT_INSERTS:
return(m_impl.m_user_space_id == space
|| is_predefined_tablespace(space));
}
ut_error;
return(false);
}
#endif /* UNIV_DEBUG */
/** Acquire a tablespace X-latch.
NOTE: use mtr_x_lock_space().
@param[in] space_id tablespace ID
@param[in] file file name from where called
@param[in] line line number in file
@return the tablespace object (never NULL) */
fil_space_t*
mtr_t::x_lock_space(ulint space_id, const char* file, ulint line)
{
fil_space_t* space;
ut_ad(m_impl.m_magic_n == MTR_MAGIC_N);
ut_ad(is_active());
if (space_id == TRX_SYS_SPACE) {
space = m_impl.m_sys_space;
if (!space) {
space = m_impl.m_sys_space = fil_space_get(space_id);
}
} else if ((space = m_impl.m_user_space) && space_id == space->id) {
} else if ((space = m_impl.m_undo_space) && space_id == space->id) {
} else if (get_log_mode() == MTR_LOG_NO_REDO) {
space = fil_space_get(space_id);
ut_ad(space->purpose == FIL_TYPE_TEMPORARY
|| space->purpose == FIL_TYPE_IMPORT
|| space->redo_skipped_count > 0
|| srv_is_tablespace_truncated(space->id));
} else {
/* called from trx_rseg_create() */
space = m_impl.m_undo_space = fil_space_get(space_id);
}
ut_ad(space);
ut_ad(space->id == space_id);
x_lock(&space->latch, file, line);
ut_ad(space->purpose == FIL_TYPE_TEMPORARY
|| space->purpose == FIL_TYPE_IMPORT
|| space->purpose == FIL_TYPE_TABLESPACE);
return(space);
}
/** Look up the system tablespace. */
void
mtr_t::lookup_sys_space()
{
ut_ad(!m_impl.m_sys_space);
m_impl.m_sys_space = fil_space_get(TRX_SYS_SPACE);
ut_ad(m_impl.m_sys_space);
}
/** Look up the user tablespace.
@param[in] space_id tablespace ID */
void
mtr_t::lookup_user_space(ulint space_id)
{
ut_ad(space_id != TRX_SYS_SPACE);
ut_ad(m_impl.m_user_space_id == space_id);
ut_ad(!m_impl.m_user_space);
m_impl.m_user_space = fil_space_get(space_id);
ut_ad(m_impl.m_user_space);
}
/** Set the tablespace associated with the mini-transaction
(needed for generating a MLOG_FILE_NAME record)
@param[in] space user or system tablespace */
void
mtr_t::set_named_space(fil_space_t* space)
{
ut_ad(m_impl.m_user_space_id == TRX_SYS_SPACE);
ut_d(m_impl.m_user_space_id = space->id);
if (space->id == TRX_SYS_SPACE) {
ut_ad(m_impl.m_sys_space == NULL
|| m_impl.m_sys_space == space);
m_impl.m_sys_space = space;
} else {
m_impl.m_user_space = space;
}
}
/** Release an object in the memo stack.
@return true if released */
bool
mtr_t::memo_release(const void* object, ulint type)
{
ut_ad(m_impl.m_magic_n == MTR_MAGIC_N);
ut_ad(is_active());
/* We cannot release a page that has been written to in the
middle of a mini-transaction. */
ut_ad(!m_impl.m_modifications || type != MTR_MEMO_PAGE_X_FIX);
Find find(object, type);
Iterate<Find> iterator(find);
if (!m_impl.m_memo.for_each_block_in_reverse(iterator)) {
memo_slot_release(find.m_slot);
return(true);
}
return(false);
}
/** Release a page latch.
@param[in] ptr pointer to within a page frame
@param[in] type object type: MTR_MEMO_PAGE_X_FIX, ... */
void
mtr_t::release_page(const void* ptr, mtr_memo_type_t type)
{
ut_ad(m_impl.m_magic_n == MTR_MAGIC_N);
ut_ad(is_active());
/* We cannot release a page that has been written to in the
middle of a mini-transaction. */
ut_ad(!m_impl.m_modifications || type != MTR_MEMO_PAGE_X_FIX);
FindPage find(ptr, type);
Iterate<FindPage> iterator(find);
if (!m_impl.m_memo.for_each_block_in_reverse(iterator)) {
memo_slot_release(find.get_slot());
return;
}
/* The page was not found! */
ut_ad(0);
}
/** Prepare to write the mini-transaction log to the redo log buffer.
@return number of bytes to write in finish_write() */
ulint
mtr_t::Command::prepare_write()
{
switch (m_impl->m_log_mode) {
case MTR_LOG_SHORT_INSERTS:
ut_ad(0);
/* fall through (write no redo log) */
case MTR_LOG_NO_REDO:
case MTR_LOG_NONE:
ut_ad(m_impl->m_log.size() == 0);
log_mutex_enter();
m_end_lsn = m_start_lsn = log_sys->lsn;
return(0);
case MTR_LOG_ALL:
break;
}
ulint len = m_impl->m_log.size();
ulint n_recs = m_impl->m_n_log_recs;
ut_ad(len > 0);
ut_ad(n_recs > 0);
if (len > log_sys->buf_size / 2) {
log_buffer_extend((len + 1) * 2);
}
ut_ad(m_impl->m_n_log_recs == n_recs);
fil_space_t* space = m_impl->m_user_space;
if (space != NULL && space->id <= srv_undo_tablespaces_open) {
/* Omit MLOG_FILE_NAME for predefined tablespaces. */
space = NULL;
}
log_mutex_enter();
if (fil_names_write_if_was_clean(space, m_impl->m_mtr)) {
/* This mini-transaction was the first one to modify
this tablespace since the latest checkpoint, so
some MLOG_FILE_NAME records were appended to m_log. */
ut_ad(m_impl->m_n_log_recs > n_recs);
mlog_catenate_ulint(
&m_impl->m_log, MLOG_MULTI_REC_END, MLOG_1BYTE);
len = m_impl->m_log.size();
} else {
/* This was not the first time of dirtying a
tablespace since the latest checkpoint. */
ut_ad(n_recs == m_impl->m_n_log_recs);
if (n_recs <= 1) {
ut_ad(n_recs == 1);
/* Flag the single log record as the
only record in this mini-transaction. */
*m_impl->m_log.front()->begin()
|= MLOG_SINGLE_REC_FLAG;
} else {
/* Because this mini-transaction comprises
multiple log records, append MLOG_MULTI_REC_END
at the end. */
mlog_catenate_ulint(
&m_impl->m_log, MLOG_MULTI_REC_END,
MLOG_1BYTE);
len++;
}
}
/* check and attempt a checkpoint if exceeding capacity */
log_margin_checkpoint_age(len);
return(len);
}
/** Append the redo log records to the redo log buffer
@param[in] len number of bytes to write */
void
mtr_t::Command::finish_write(
ulint len)
{
ut_ad(m_impl->m_log_mode == MTR_LOG_ALL);
ut_ad(log_mutex_own());
ut_ad(m_impl->m_log.size() == len);
ut_ad(len > 0);
if (m_impl->m_log.is_small()) {
const mtr_buf_t::block_t* front = m_impl->m_log.front();
ut_ad(len <= front->used());
m_end_lsn = log_reserve_and_write_fast(
front->begin(), len, &m_start_lsn);
if (m_end_lsn > 0) {
return;
}
}
/* Open the database log for log_write_low */
m_start_lsn = log_reserve_and_open(len);
mtr_write_log_t write_log;
m_impl->m_log.for_each_block(write_log);
m_end_lsn = log_close();
}
/** Release the latches and blocks acquired by this mini-transaction */
void
mtr_t::Command::release_all()
{
ReleaseAll release;
Iterate<ReleaseAll> iterator(release);
m_impl->m_memo.for_each_block_in_reverse(iterator);
/* Note that we have released the latches. */
m_locks_released = 1;
}
/** Release the latches acquired by this mini-transaction */
void
mtr_t::Command::release_latches()
{
ReleaseLatches release;
Iterate<ReleaseLatches> iterator(release);
m_impl->m_memo.for_each_block_in_reverse(iterator);
/* Note that we have released the latches. */
m_locks_released = 1;
}
/** Release the blocks used in this mini-transaction */
void
mtr_t::Command::release_blocks()
{
ReleaseBlocks release(m_start_lsn, m_end_lsn, m_impl->m_flush_observer);
Iterate<ReleaseBlocks> iterator(release);
m_impl->m_memo.for_each_block_in_reverse(iterator);
}
/** Write the redo log record, add dirty pages to the flush list and release
the resources. */
void
mtr_t::Command::execute()
{
ut_ad(m_impl->m_log_mode != MTR_LOG_NONE);
if (const ulint len = prepare_write()) {
finish_write(len);
}
if (m_impl->m_made_dirty) {
log_flush_order_mutex_enter();
}
/* It is now safe to release the log mutex because the
flush_order mutex will ensure that we are the first one
to insert into the flush list. */
log_mutex_exit();
m_impl->m_mtr->m_commit_lsn = m_end_lsn;
release_blocks();
if (m_impl->m_made_dirty) {
log_flush_order_mutex_exit();
}
release_latches();
release_resources();
}
/** Release the free extents that was reserved using
fsp_reserve_free_extents(). This is equivalent to calling
fil_space_release_free_extents(). This is intended for use
with index pages.
@param[in] n_reserved number of reserved extents */
void
mtr_t::release_free_extents(ulint n_reserved)
{
fil_space_t* space;
ut_ad(m_impl.m_undo_space == NULL);
if (m_impl.m_user_space != NULL) {
ut_ad(m_impl.m_user_space->id
== m_impl.m_user_space_id);
ut_ad(memo_contains(get_memo(), &m_impl.m_user_space->latch,
MTR_MEMO_X_LOCK));
space = m_impl.m_user_space;
} else {
ut_ad(m_impl.m_sys_space->id == TRX_SYS_SPACE);
ut_ad(memo_contains(get_memo(), &m_impl.m_sys_space->latch,
MTR_MEMO_X_LOCK));
space = m_impl.m_sys_space;
}
space->release_free_extents(n_reserved);
}
#ifdef UNIV_DEBUG
/** Check if memo contains the given item.
@return true if contains */
bool
mtr_t::memo_contains(
mtr_buf_t* memo,
const void* object,
ulint type)
{
Find find(object, type);
Iterate<Find> iterator(find);
return(!memo->for_each_block_in_reverse(iterator));
}
/** Debug check for flags */
struct FlaggedCheck {
FlaggedCheck(const void* ptr, ulint flags)
:
m_ptr(ptr),
m_flags(flags)
{
// Do nothing
}
bool operator()(const mtr_memo_slot_t* slot) const
{
if (m_ptr == slot->object && (m_flags & slot->type)) {
return(false);
}
return(true);
}
const void* m_ptr;
ulint m_flags;
};
/** Check if memo contains the given item.
@param object object to search
@param flags specify types of object (can be ORred) of
MTR_MEMO_PAGE_S_FIX ... values
@return true if contains */
bool
mtr_t::memo_contains_flagged(const void* ptr, ulint flags) const
{
ut_ad(m_impl.m_magic_n == MTR_MAGIC_N);
ut_ad(is_committing() || is_active());
FlaggedCheck check(ptr, flags);
Iterate<FlaggedCheck> iterator(check);
return(!m_impl.m_memo.for_each_block_in_reverse(iterator));
}
/** Check if memo contains the given page.
@param[in] ptr pointer to within buffer frame
@param[in] flags specify types of object with OR of
MTR_MEMO_PAGE_S_FIX... values
@return the block
@retval NULL if not found */
buf_block_t*
mtr_t::memo_contains_page_flagged(
const byte* ptr,
ulint flags) const
{
FindPage check(ptr, flags);
Iterate<FindPage> iterator(check);
return(m_impl.m_memo.for_each_block_in_reverse(iterator)
? NULL : check.get_block());
}
/** Mark the given latched page as modified.
@param[in] ptr pointer to within buffer frame */
void
mtr_t::memo_modify_page(const byte* ptr)
{
buf_block_t* block = memo_contains_page_flagged(
ptr, MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX);
ut_ad(block != NULL);
if (!memo_contains(get_memo(), block, MTR_MEMO_MODIFY)) {
memo_push(block, MTR_MEMO_MODIFY);
}
}
/** Print info of an mtr handle. */
void
mtr_t::print() const
{
ib::info() << "Mini-transaction handle: memo size "
<< m_impl.m_memo.size() << " bytes log size "
<< get_log()->size() << " bytes";
}
#endif /* UNIV_DEBUG */