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

Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, MariaDB Corporation.

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, Fifth Floor, Boston, MA 02110-1335 USA

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

/**************************************************//**
@file include/btr0cur.ic
The index tree cursor

Created 10/16/1994 Heikki Tuuri
*******************************************************/

#include "btr0btr.h"

#ifdef UNIV_DEBUG
# define LIMIT_OPTIMISTIC_INSERT_DEBUG(NREC, CODE)\
if (btr_cur_limit_optimistic_insert_debug > 1\
    && (NREC) >= btr_cur_limit_optimistic_insert_debug) {\
        CODE;\
}
#else
# define LIMIT_OPTIMISTIC_INSERT_DEBUG(NREC, CODE)
#endif /* UNIV_DEBUG */

#ifdef UNIV_DEBUG
/*********************************************************//**
Returns the page cursor component of a tree cursor.
@return pointer to page cursor component */
UNIV_INLINE
page_cur_t*
btr_cur_get_page_cur(
/*=================*/
	const btr_cur_t*	cursor)	/*!< in: tree cursor */
{
	return(&((btr_cur_t*) cursor)->page_cur);
}

/*********************************************************//**
Returns the buffer block on which the tree cursor is positioned.
@return pointer to buffer block */
UNIV_INLINE
buf_block_t*
btr_cur_get_block(
/*==============*/
	const btr_cur_t*	cursor)	/*!< in: tree cursor */
{
	return(page_cur_get_block(btr_cur_get_page_cur(cursor)));
}

/*********************************************************//**
Returns the record pointer of a tree cursor.
@return pointer to record */
UNIV_INLINE
rec_t*
btr_cur_get_rec(
/*============*/
	const btr_cur_t*	cursor)	/*!< in: tree cursor */
{
	return(page_cur_get_rec(btr_cur_get_page_cur(cursor)));
}
#endif /* UNIV_DEBUG */

/*********************************************************//**
Returns the compressed page on which the tree cursor is positioned.
@return pointer to compressed page, or NULL if the page is not compressed */
UNIV_INLINE
page_zip_des_t*
btr_cur_get_page_zip(
/*=================*/
	btr_cur_t*	cursor)	/*!< in: tree cursor */
{
	return(buf_block_get_page_zip(btr_cur_get_block(cursor)));
}

/*********************************************************//**
Returns the page of a tree cursor.
@return pointer to page */
UNIV_INLINE
page_t*
btr_cur_get_page(
/*=============*/
	btr_cur_t*	cursor)	/*!< in: tree cursor */
{
	return(page_align(page_cur_get_rec(&(cursor->page_cur))));
}

/*********************************************************//**
Positions a tree cursor at a given record. */
UNIV_INLINE
void
btr_cur_position(
/*=============*/
	dict_index_t*	index,	/*!< in: index */
	rec_t*		rec,	/*!< in: record in tree */
	buf_block_t*	block,	/*!< in: buffer block of rec */
	btr_cur_t*	cursor)	/*!< out: cursor */
{
	ut_ad(page_align(rec) == block->frame);

	page_cur_position(rec, block, btr_cur_get_page_cur(cursor));

	cursor->index = index;
}

/*********************************************************************//**
Checks if compressing an index page where a btr cursor is placed makes
sense.
@return TRUE if compression is recommended */
UNIV_INLINE
ibool
btr_cur_compress_recommendation(
/*============================*/
	btr_cur_t*	cursor,	/*!< in: btr cursor */
	mtr_t*		mtr)	/*!< in: mtr */
{
	const page_t*	page;

	ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor),
			       MTR_MEMO_PAGE_X_FIX));

	page = btr_cur_get_page(cursor);

	LIMIT_OPTIMISTIC_INSERT_DEBUG(page_get_n_recs(page) * 2U,
				      return(FALSE));

	if (page_get_data_size(page)
	    < BTR_CUR_PAGE_COMPRESS_LIMIT(cursor->index)
	    || !page_has_siblings(page)) {

		/* The page fillfactor has dropped below a predefined
		minimum value OR the level in the B-tree contains just
		one page: we recommend compression if this is not the
		root page. */

		return(dict_index_get_page(cursor->index)
		       != page_get_page_no(page));
	}

	return(FALSE);
}

/*********************************************************************//**
Checks if the record on which the cursor is placed can be deleted without
making tree compression necessary (or, recommended).
@return TRUE if can be deleted without recommended compression */
UNIV_INLINE
ibool
btr_cur_can_delete_without_compress(
/*================================*/
	btr_cur_t*	cursor,	/*!< in: btr cursor */
	ulint		rec_size,/*!< in: rec_get_size(btr_cur_get_rec(cursor))*/
	mtr_t*		mtr)	/*!< in: mtr */
{
	page_t*		page;

	ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor),
				MTR_MEMO_PAGE_X_FIX));

	page = btr_cur_get_page(cursor);

	if (page_get_data_size(page) - rec_size
	    < BTR_CUR_PAGE_COMPRESS_LIMIT(cursor->index)
	    || !page_has_siblings(page) || page_get_n_recs(page) < 2) {

		/* The page fillfactor will drop below a predefined
		minimum value, OR the level in the B-tree contains just
		one page, OR the page will become empty: we recommend
		compression if this is not the root page. */

		return(dict_index_get_page(cursor->index)
		       == page_get_page_no(page));
	}

	return(TRUE);
}

/*******************************************************************//**
Determine if an operation on off-page columns is an update.
@return TRUE if op != BTR_STORE_INSERT */
UNIV_INLINE
ibool
btr_blob_op_is_update(
/*==================*/
	enum blob_op	op)	/*!< in: operation */
{
	switch (op) {
	case BTR_STORE_INSERT:
	case BTR_STORE_INSERT_BULK:
		return(FALSE);
	case BTR_STORE_INSERT_UPDATE:
	case BTR_STORE_UPDATE:
		return(TRUE);
	}

	ut_ad(0);
	return(FALSE);
}

/******************************************************//**
The following function is used to set the deleted bit of a record. */
UNIV_INLINE
void
btr_rec_set_deleted_flag(
/*=====================*/
	rec_t*		rec,	/*!< in/out: physical record */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page (or NULL) */
	ulint		flag)	/*!< in: nonzero if delete marked */
{
	if (page_rec_is_comp(rec)) {
		rec_set_deleted_flag_new(rec, page_zip, flag);
	} else {
		ut_ad(!page_zip);
		rec_set_deleted_flag_old(rec, flag);
	}
}
