/*****************************************************************************

Copyright (c) 1995, 2012, 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 include/mtr0mtr.ic
Mini-transaction buffer

Created 11/26/1995 Heikki Tuuri
*******************************************************/

#ifndef UNIV_HOTBACKUP
# include "sync0sync.h"
# include "sync0rw.h"
#endif /* !UNIV_HOTBACKUP */
#include "mach0data.h"

/***************************************************************//**
Starts a mini-transaction. */
UNIV_INLINE
void
mtr_start(
/*======*/
	mtr_t*	mtr)	/*!< out: mini-transaction */
{
	UNIV_MEM_INVALID(mtr, sizeof *mtr);

	dyn_array_create(&(mtr->memo));
	dyn_array_create(&(mtr->log));

	mtr->log_mode = MTR_LOG_ALL;
	mtr->modifications = FALSE;
	mtr->inside_ibuf = FALSE;
	mtr->n_log_recs = 0;
	mtr->n_freed_pages = 0;

	ut_d(mtr->state = MTR_ACTIVE);
	ut_d(mtr->magic_n = MTR_MAGIC_N);
}

/***************************************************//**
Pushes an object to an mtr memo stack. */
UNIV_INLINE
void
mtr_memo_push(
/*==========*/
	mtr_t*	mtr,	/*!< in: mtr */
	void*	object,	/*!< in: object */
	ulint	type)	/*!< in: object type: MTR_MEMO_S_LOCK, ... */
{
	dyn_array_t*		memo;
	mtr_memo_slot_t*	slot;

	ut_ad(object);
	ut_ad(type >= MTR_MEMO_PAGE_S_FIX);
	ut_ad(type <= MTR_MEMO_X_LOCK);
	ut_ad(mtr);
	ut_ad(mtr->magic_n == MTR_MAGIC_N);
	ut_ad(mtr->state == MTR_ACTIVE);

	memo = &(mtr->memo);

	slot = (mtr_memo_slot_t*) dyn_array_push(memo, sizeof *slot);

	slot->object = object;
	slot->type = type;
}

/**********************************************************//**
Sets and returns a savepoint in mtr.
@return	savepoint */
UNIV_INLINE
ulint
mtr_set_savepoint(
/*==============*/
	mtr_t*	mtr)	/*!< in: mtr */
{
	dyn_array_t*	memo;

	ut_ad(mtr);
	ut_ad(mtr->magic_n == MTR_MAGIC_N);
	ut_ad(mtr->state == MTR_ACTIVE);

	memo = &(mtr->memo);

	return(dyn_array_get_data_size(memo));
}

#ifndef UNIV_HOTBACKUP
/**********************************************************//**
Releases the (index tree) s-latch stored in an mtr memo after a
savepoint. */
UNIV_INLINE
void
mtr_release_s_latch_at_savepoint(
/*=============================*/
	mtr_t*		mtr,		/*!< in: mtr */
	ulint		savepoint,	/*!< in: savepoint */
	rw_lock_t*	lock)		/*!< in: latch to release */
{
	mtr_memo_slot_t* slot;
	dyn_array_t*	memo;

	ut_ad(mtr);
	ut_ad(mtr->magic_n == MTR_MAGIC_N);
	ut_ad(mtr->state == MTR_ACTIVE);

	memo = &(mtr->memo);

	ut_ad(dyn_array_get_data_size(memo) > savepoint);

	slot = (mtr_memo_slot_t*) dyn_array_get_element(memo, savepoint);

	ut_ad(slot->object == lock);
	ut_ad(slot->type == MTR_MEMO_S_LOCK);

	rw_lock_s_unlock(lock);

	slot->object = NULL;
}

# ifdef UNIV_DEBUG
/**********************************************************//**
Checks if memo contains the given item.
@return	TRUE if contains */
UNIV_INLINE
ibool
mtr_memo_contains(
/*==============*/
	mtr_t*		mtr,	/*!< in: mtr */
	const void*	object,	/*!< in: object to search */
	ulint		type)	/*!< in: type of object */
{
	mtr_memo_slot_t* slot;
	dyn_array_t*	memo;
	ulint		offset;

	ut_ad(mtr);
	ut_ad(mtr->magic_n == MTR_MAGIC_N);
	ut_ad(mtr->state == MTR_ACTIVE || mtr->state == MTR_COMMITTING);

	memo = &(mtr->memo);

	offset = dyn_array_get_data_size(memo);

	while (offset > 0) {
		offset -= sizeof(mtr_memo_slot_t);

		slot = (mtr_memo_slot_t*) dyn_array_get_element(memo, offset);

		if ((object == slot->object) && (type == slot->type)) {

			return(TRUE);
		}
	}

	return(FALSE);
}
# endif /* UNIV_DEBUG */
#endif /* !UNIV_HOTBACKUP */

/***************************************************************//**
Returns the log object of a mini-transaction buffer.
@return	log */
UNIV_INLINE
dyn_array_t*
mtr_get_log(
/*========*/
	mtr_t*	mtr)	/*!< in: mini-transaction */
{
	ut_ad(mtr);
	ut_ad(mtr->magic_n == MTR_MAGIC_N);

	return(&(mtr->log));
}

/***************************************************************//**
Gets the logging mode of a mini-transaction.
@return	logging mode: MTR_LOG_NONE, ... */
UNIV_INLINE
ulint
mtr_get_log_mode(
/*=============*/
	mtr_t*	mtr)	/*!< in: mtr */
{
	ut_ad(mtr);
	ut_ad(mtr->log_mode >= MTR_LOG_ALL);
	ut_ad(mtr->log_mode <= MTR_LOG_SHORT_INSERTS);

	return(mtr->log_mode);
}

/***************************************************************//**
Changes the logging mode of a mini-transaction.
@return	old mode */
UNIV_INLINE
ulint
mtr_set_log_mode(
/*=============*/
	mtr_t*	mtr,	/*!< in: mtr */
	ulint	mode)	/*!< in: logging mode: MTR_LOG_NONE, ... */
{
	ulint	old_mode;

	ut_ad(mtr);
	ut_ad(mode >= MTR_LOG_ALL);
	ut_ad(mode <= MTR_LOG_SHORT_INSERTS);

	old_mode = mtr->log_mode;

	if ((mode == MTR_LOG_SHORT_INSERTS) && (old_mode == MTR_LOG_NONE)) {
		/* Do nothing */
	} else {
		mtr->log_mode = mode;
	}

	ut_ad(old_mode >= MTR_LOG_ALL);
	ut_ad(old_mode <= MTR_LOG_SHORT_INSERTS);

	return(old_mode);
}

#ifndef UNIV_HOTBACKUP
/*********************************************************************//**
Locks a lock in s-mode. */
UNIV_INLINE
void
mtr_s_lock_func(
/*============*/
	rw_lock_t*	lock,	/*!< in: rw-lock */
	const char*	file,	/*!< in: file name */
	ulint		line,	/*!< in: line number */
	mtr_t*		mtr)	/*!< in: mtr */
{
	ut_ad(mtr);
	ut_ad(lock);

	rw_lock_s_lock_func(lock, 0, file, line);

	mtr_memo_push(mtr, lock, MTR_MEMO_S_LOCK);
}

/*********************************************************************//**
Locks a lock in x-mode. */
UNIV_INLINE
void
mtr_x_lock_func(
/*============*/
	rw_lock_t*	lock,	/*!< in: rw-lock */
	const char*	file,	/*!< in: file name */
	ulint		line,	/*!< in: line number */
	mtr_t*		mtr)	/*!< in: mtr */
{
	ut_ad(mtr);
	ut_ad(lock);

	rw_lock_x_lock_func(lock, 0, file, line);

	mtr_memo_push(mtr, lock, MTR_MEMO_X_LOCK);
}
#endif /* !UNIV_HOTBACKUP */