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

Copyright (c) 1996, 2009, Innobase Oy. 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., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA

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

/**************************************************//**
@file include/trx0undo.ic
Transaction undo log

Created 3/26/1996 Heikki Tuuri
*******************************************************/

#include "data0type.h"
#include "page0page.h"

#ifndef UNIV_HOTBACKUP
/***********************************************************************//**
Builds a roll pointer.
@return	roll pointer */
UNIV_INLINE
roll_ptr_t
trx_undo_build_roll_ptr(
/*====================*/
	ibool	is_insert,	/*!< in: TRUE if insert undo log */
	ulint	rseg_id,	/*!< in: rollback segment id */
	ulint	page_no,	/*!< in: page number */
	ulint	offset)		/*!< in: offset of the undo entry within page */
{
	roll_ptr_t	roll_ptr;
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
	ut_ad(is_insert == 0 || is_insert == 1);
	ut_ad(rseg_id < TRX_SYS_N_RSEGS);
	ut_ad(offset < 65536);

	roll_ptr = (roll_ptr_t) is_insert << 55
		| (roll_ptr_t) rseg_id << 48
		| (roll_ptr_t) page_no << 16
		| offset;
	return(roll_ptr);
}

/***********************************************************************//**
Decodes a roll pointer. */
UNIV_INLINE
void
trx_undo_decode_roll_ptr(
/*=====================*/
	roll_ptr_t	roll_ptr,	/*!< in: roll pointer */
	ibool*		is_insert,	/*!< out: TRUE if insert undo log */
	ulint*		rseg_id,	/*!< out: rollback segment id */
	ulint*		page_no,	/*!< out: page number */
	ulint*		offset)		/*!< out: offset of the undo
					entry within page */
{
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
#if TRUE != 1
# error "TRUE != 1"
#endif
	ut_ad(roll_ptr < (1ULL << 56));
	*offset = (ulint) roll_ptr & 0xFFFF;
	roll_ptr >>= 16;
	*page_no = (ulint) roll_ptr & 0xFFFFFFFF;
	roll_ptr >>= 32;
	*rseg_id = (ulint) roll_ptr & 0x7F;
	roll_ptr >>= 7;
	*is_insert = (ibool) roll_ptr; /* TRUE==1 */
}

/***********************************************************************//**
Returns TRUE if the roll pointer is of the insert type.
@return	TRUE if insert undo log */
UNIV_INLINE
ibool
trx_undo_roll_ptr_is_insert(
/*========================*/
	roll_ptr_t	roll_ptr)	/*!< in: roll pointer */
{
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
#if TRUE != 1
# error "TRUE != 1"
#endif
	ut_ad(roll_ptr < (1ULL << 56));
	return((ibool) (roll_ptr >> 55));
}
#endif /* !UNIV_HOTBACKUP */

/*****************************************************************//**
Writes a roll ptr to an index page. In case that the size changes in
some future version, this function should be used instead of
mach_write_... */
UNIV_INLINE
void
trx_write_roll_ptr(
/*===============*/
	byte*		ptr,		/*!< in: pointer to memory where
					written */
	roll_ptr_t	roll_ptr)	/*!< in: roll ptr */
{
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
	mach_write_to_7(ptr, roll_ptr);
}

/*****************************************************************//**
Reads a roll ptr from an index page. In case that the roll ptr size
changes in some future version, this function should be used instead of
mach_read_...
@return	roll ptr */
UNIV_INLINE
roll_ptr_t
trx_read_roll_ptr(
/*==============*/
	const byte*	ptr)	/*!< in: pointer to memory from where to read */
{
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
	return(mach_read_from_7(ptr));
}

#ifndef UNIV_HOTBACKUP
/******************************************************************//**
Gets an undo log page and x-latches it.
@return	pointer to page x-latched */
UNIV_INLINE
page_t*
trx_undo_page_get(
/*==============*/
	ulint	space,		/*!< in: space where placed */
	ulint	zip_size,	/*!< in: compressed page size in bytes
				or 0 for uncompressed pages */
	ulint	page_no,	/*!< in: page number */
	mtr_t*	mtr)		/*!< in: mtr */
{
	buf_block_t*	block = buf_page_get(space, zip_size, page_no,
					     RW_X_LATCH, mtr);
	buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);

	return(buf_block_get_frame(block));
}

/******************************************************************//**
Gets an undo log page and s-latches it.
@return	pointer to page s-latched */
UNIV_INLINE
page_t*
trx_undo_page_get_s_latched(
/*========================*/
	ulint	space,		/*!< in: space where placed */
	ulint	zip_size,	/*!< in: compressed page size in bytes
				or 0 for uncompressed pages */
	ulint	page_no,	/*!< in: page number */
	mtr_t*	mtr)		/*!< in: mtr */
{
	buf_block_t*	block = buf_page_get(space, zip_size, page_no,
					     RW_S_LATCH, mtr);
	buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);

	return(buf_block_get_frame(block));
}

/******************************************************************//**
Returns the start offset of the undo log records of the specified undo
log on the page.
@return	start offset */
UNIV_INLINE
ulint
trx_undo_page_get_start(
/*====================*/
	page_t*	undo_page,/*!< in: undo log page */
	ulint	page_no,/*!< in: undo log header page number */
	ulint	offset)	/*!< in: undo log header offset on page */
{
	ulint	start;

	if (page_no == page_get_page_no(undo_page)) {

		start = mach_read_from_2(offset + undo_page
					 + TRX_UNDO_LOG_START);
	} else {
		start = TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE;
	}

	return(start);
}

/******************************************************************//**
Returns the end offset of the undo log records of the specified undo
log on the page.
@return	end offset */
UNIV_INLINE
ulint
trx_undo_page_get_end(
/*==================*/
	page_t*	undo_page,/*!< in: undo log page */
	ulint	page_no,/*!< in: undo log header page number */
	ulint	offset)	/*!< in: undo log header offset on page */
{
	trx_ulogf_t*	log_hdr;
	ulint		end;

	if (page_no == page_get_page_no(undo_page)) {

		log_hdr = undo_page + offset;

		end = mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG);

		if (end == 0) {
			end = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
					       + TRX_UNDO_PAGE_FREE);
		}
	} else {
		end = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
				       + TRX_UNDO_PAGE_FREE);
	}

	return(end);
}

/******************************************************************//**
Returns the previous undo record on the page in the specified log, or
NULL if none exists.
@return	pointer to record, NULL if none */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_prev_rec(
/*=======================*/
	trx_undo_rec_t*	rec,	/*!< in: undo log record */
	ulint		page_no,/*!< in: undo log header page number */
	ulint		offset)	/*!< in: undo log header offset on page */
{
	page_t*	undo_page;
	ulint	start;

	undo_page = (page_t*) ut_align_down(rec, UNIV_PAGE_SIZE);

	start = trx_undo_page_get_start(undo_page, page_no, offset);

	if (start + undo_page == rec) {

		return(NULL);
	}

	return(undo_page + mach_read_from_2(rec - 2));
}

/******************************************************************//**
Returns the next undo log record on the page in the specified log, or
NULL if none exists.
@return	pointer to record, NULL if none */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_next_rec(
/*=======================*/
	trx_undo_rec_t*	rec,	/*!< in: undo log record */
	ulint		page_no,/*!< in: undo log header page number */
	ulint		offset)	/*!< in: undo log header offset on page */
{
	page_t*	undo_page;
	ulint	end;
	ulint	next;

	undo_page = (page_t*) ut_align_down(rec, UNIV_PAGE_SIZE);

	end = trx_undo_page_get_end(undo_page, page_no, offset);

	next = mach_read_from_2(rec);

	if (next == end) {

		return(NULL);
	}

	return(undo_page + next);
}

/******************************************************************//**
Returns the last undo record on the page in the specified undo log, or
NULL if none exists.
@return	pointer to record, NULL if none */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_last_rec(
/*=======================*/
	page_t*	undo_page,/*!< in: undo log page */
	ulint	page_no,/*!< in: undo log header page number */
	ulint	offset)	/*!< in: undo log header offset on page */
{
	ulint	start;
	ulint	end;

	start = trx_undo_page_get_start(undo_page, page_no, offset);
	end = trx_undo_page_get_end(undo_page, page_no, offset);

	if (start == end) {

		return(NULL);
	}

	return(undo_page + mach_read_from_2(undo_page + end - 2));
}

/******************************************************************//**
Returns the first undo record on the page in the specified undo log, or
NULL if none exists.
@return	pointer to record, NULL if none */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_first_rec(
/*========================*/
	page_t*	undo_page,/*!< in: undo log page */
	ulint	page_no,/*!< in: undo log header page number */
	ulint	offset)	/*!< in: undo log header offset on page */
{
	ulint	start;
	ulint	end;

	start = trx_undo_page_get_start(undo_page, page_no, offset);
	end = trx_undo_page_get_end(undo_page, page_no, offset);

	if (start == end) {

		return(NULL);
	}

	return(undo_page + start);
}
#endif /* !UNIV_HOTBACKUP */