/******************************************************
Transaction undo log

(c) 1996 Innobase Oy

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

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

/***************************************************************************
Builds a roll pointer dulint. */
UNIV_INLINE
dulint
trx_undo_build_roll_ptr(
/*====================*/
				/* out: roll pointer */
	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 */
{
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
	ut_ad(rseg_id < 128);

	return(ut_dulint_create(is_insert * 128 * 256 * 256
				+ rseg_id * 256 * 256
				+ (page_no / 256) / 256,
				(page_no % (256 * 256)) * 256 * 256
				+ offset));
}

/***************************************************************************
Decodes a roll pointer dulint. */
UNIV_INLINE
void
trx_undo_decode_roll_ptr(
/*=====================*/
	dulint	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 */
{
	ulint	low;
	ulint	high;
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
#if TRUE != 1
# error "TRUE != 1"
#endif
	high = ut_dulint_get_high(roll_ptr);
	low = ut_dulint_get_low(roll_ptr);

	*offset = low % (256 * 256);

	*is_insert = high / (256 * 256 * 128);	/* TRUE == 1 */
	*rseg_id = (high / (256 * 256)) % 128;

	*page_no = (high % (256 * 256)) * 256 * 256
		+ (low / 256) / 256;
}

/***************************************************************************
Returns TRUE if the roll pointer is of the insert type. */
UNIV_INLINE
ibool
trx_undo_roll_ptr_is_insert(
/*========================*/
				/* out: TRUE if insert undo log */
	dulint	roll_ptr)	/* in: roll pointer */
{
	ulint	high;
#if DATA_ROLL_PTR_LEN != 7
# error "DATA_ROLL_PTR_LEN != 7"
#endif
#if TRUE != 1
# error "TRUE != 1"
#endif
	high = ut_dulint_get_high(roll_ptr);

	return(high / (256 * 256 * 128));
}

/*********************************************************************
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 */
	dulint	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_... */
UNIV_INLINE
dulint
trx_read_roll_ptr(
/*==============*/
				/* out: 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));
}

/**********************************************************************
Gets an undo log page and x-latches it. */
UNIV_INLINE
page_t*
trx_undo_page_get(
/*==============*/
				/* out: pointer to page x-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_X_LATCH, mtr);
#ifdef UNIV_SYNC_DEBUG
	buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
#endif /* UNIV_SYNC_DEBUG */

	return(buf_block_get_frame(block));
}

/**********************************************************************
Gets an undo log page and s-latches it. */
UNIV_INLINE
page_t*
trx_undo_page_get_s_latched(
/*========================*/
				/* out: pointer to page 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);
#ifdef UNIV_SYNC_DEBUG
	buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
#endif /* UNIV_SYNC_DEBUG */

	return(buf_block_get_frame(block));
}

/**********************************************************************
Returns the start offset of the undo log records of the specified undo
log on the page. */
UNIV_INLINE
ulint
trx_undo_page_get_start(
/*====================*/
			/* out: start offset */
	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. */
UNIV_INLINE
ulint
trx_undo_page_get_end(
/*==================*/
			/* out: end offset */
	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. */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_prev_rec(
/*=======================*/
				/* out: pointer to record, NULL if none */
	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. */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_next_rec(
/*=======================*/
				/* out: pointer to record, NULL if none */
	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. */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_last_rec(
/*=======================*/
			/* out: pointer to record, NULL if none */
	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. */
UNIV_INLINE
trx_undo_rec_t*
trx_undo_page_get_first_rec(
/*========================*/
			/* out: pointer to record, NULL if none */
	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);
}