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

Copyright (c) 1997, 2015, 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/read0types.h
Cursor read

Created 2/16/1997 Heikki Tuuri
*******************************************************/

#ifndef read0types_h
#define read0types_h

#include <algorithm>
#include "dict0mem.h"

#include "mem0mem.h"
#include "trx0types.h"

// Friend declaration
class MVCC;

/** Read view lists the trx ids of those transactions for which a consistent
read should not see the modifications to the database. */

class ReadView {
	/** This is similar to a std::vector but it is not a drop
	in replacement. It is specific to ReadView. */
	class ids_t {
		typedef trx_ids_t::value_type value_type;

		/**
		Constructor */
		ids_t() : m_ptr(), m_size(), m_reserved() { }

		/**
		Destructor */
		~ids_t() { UT_DELETE_ARRAY(m_ptr); }

		/**
		Try and increase the size of the array. Old elements are
		copied across. It is a no-op if n is < current size.

		@param n 		Make space for n elements */
		void reserve(ulint n);

		/**
		Resize the array, sets the current element count.
		@param n		new size of the array, in elements */
		void resize(ulint n)
		{
			ut_ad(n <= capacity());

			m_size = n;
		}

		/**
		Reset the size to 0 */
		void clear() { resize(0); }

		/**
		@return the capacity of the array in elements */
		ulint capacity() const { return(m_reserved); }

		/**
		Copy and overwrite the current array contents

		@param start		Source array
		@param end		Pointer to end of array */
		void assign(const value_type* start, const value_type* end);

		/**
		Insert the value in the correct slot, preserving the order.
		Doesn't check for duplicates. */
		void insert(value_type value);

		/**
		@return the value of the first element in the array */
		value_type front() const
		{
			ut_ad(!empty());

			return(m_ptr[0]);
		}

		/**
		@return the value of the last element in the array */
		value_type back() const
		{
			ut_ad(!empty());

			return(m_ptr[m_size - 1]);
		}

		/**
		Append a value to the array.
		@param value		the value to append */
		void push_back(value_type value);

		/**
		@return a pointer to the start of the array */
		trx_id_t* data() { return(m_ptr); };

		/**
		@return a const pointer to the start of the array */
		const trx_id_t* data() const { return(m_ptr); };

		/**
		@return the number of elements in the array */
		ulint size() const { return(m_size); }

		/**
		@return true if size() == 0 */
		bool empty() const { return(size() == 0); }

	private:
		// Prevent copying
		ids_t(const ids_t&);
		ids_t& operator=(const ids_t&);

	private:
		/** Memory for the array */
		value_type*	m_ptr;

		/** Number of active elements in the array */
		ulint		m_size;

		/** Size of m_ptr in elements */
		ulint		m_reserved;

		friend class ReadView;
	};
public:
	ReadView();
	~ReadView();
	/** Check whether transaction id is valid.
	@param[in]	id		transaction id to check
	@param[in]	name		table name */
	static void check_trx_id_sanity(
		trx_id_t		id,
		const table_name_t&	name);

	/** Check whether the changes by id are visible.
	@param[in]	id	transaction id to check against the view
	@param[in]	name	table name
	@return whether the view sees the modifications of id. */
	bool changes_visible(
		trx_id_t		id,
		const table_name_t&	name) const
		__attribute__((warn_unused_result))
	{
		ut_ad(id > 0);

		if (id < m_up_limit_id || id == m_creator_trx_id) {

			return(true);
		}

		check_trx_id_sanity(id, name);

		if (id >= m_low_limit_id) {

			return(false);

		} else if (m_ids.empty()) {

			return(true);
		}

		const ids_t::value_type*	p = m_ids.data();

		return(!std::binary_search(p, p + m_ids.size(), id));
	}

	/**
	@param id		transaction to check
	@return true if view sees transaction id */
	bool sees(trx_id_t id) const
	{
		return(id < m_up_limit_id);
	}

	/**
	Mark the view as closed */
	void close()
	{
		ut_ad(m_creator_trx_id != TRX_ID_MAX);
		m_creator_trx_id = TRX_ID_MAX;
		m_cloned = false;
	}

	/**
	@return true if the view is closed */
	bool is_closed() const
	{
		return(m_closed);
	}

	/**
	Write the limits to the file.
	@param file		file to write to */
	void print_limits(FILE* file) const
	{
		fprintf(file,
			"Trx read view will not see trx with"
			" id >= " TRX_ID_FMT ", sees < " TRX_ID_FMT "\n",
			m_low_limit_id, m_up_limit_id);
	}

	/**
	@return the low limit no */
	trx_id_t low_limit_no() const
	{
		return(m_low_limit_no);
	}

	/**
	@return the low limit id */
	trx_id_t low_limit_id() const
	{
		return(m_low_limit_id);
	}

	/**
	@return true if there are no transaction ids in the snapshot */
	bool empty() const
	{
		return(m_ids.empty());
	}

	/**
	Clones a read view object. The resulting read view has identical change
	visibility as the donor read view
	@param	result	pointer to resulting read view. If NULL, a view will be
	allocated. If non-NULL, a view will overwrite a previously-existing
	in-use or released view.
	@param	from_trx	transation owning the donor read view. */

	void clone(ReadView*& result, trx_t* from_trx) const;

#ifdef UNIV_DEBUG
	/**
	@param rhs		view to compare with
	@return truen if this view is less than or equal rhs */
	bool le(const ReadView* rhs) const
	{
		return(m_low_limit_no <= rhs->m_low_limit_no);
	}
#endif /* UNIV_DEBUG */

	trx_id_t up_limit_id() const
	{
		return(m_up_limit_id);
	}

	void print(FILE* file) const
	{
		fprintf(file, "Read view low limit trx n:o " TRX_ID_FMT "\n",
			low_limit_no());
		print_limits(file);
		fprintf(file, "Read view individually stored trx ids:\n");
		for (ulint i = 0; i < m_ids.size(); i++) {
			fprintf(file, "Read view trx id " TRX_ID_FMT "\n",
				m_ids.data()[i]);
		}
	}

	bool is_cloned() const
	{
		return(m_cloned);
	}

private:
	/**
	Copy the transaction ids from the source vector */
	inline void copy_trx_ids(const trx_ids_t& trx_ids);

	/**
	Opens a read view where exactly the transactions serialized before this
	point in time are seen in the view.
	@param id		Creator transaction id */
	inline void prepare(trx_id_t id);

	/**
	Complete the read view creation */
	inline void complete();

	/**
	Copy state from another view. Must call copy_complete() to finish.
	@param other		view to copy from */
	inline void copy_prepare(const ReadView& other);

	/**
	Complete the copy, insert the creator transaction id into the
	m_trx_ids too and adjust the m_up_limit_id *, if required */
	inline void copy_complete();

	/**
	Set the creator transaction id, existing id must be 0 */
	void creator_trx_id(trx_id_t id)
	{
		ut_ad(m_creator_trx_id == 0);
		m_creator_trx_id = id;
	}

	friend class MVCC;

private:
	// Disable copying
	ReadView(const ReadView&);
	ReadView& operator=(const ReadView&);

private:
	/** The read should not see any transaction with trx id >= this
	value. In other words, this is the "high water mark". */
	trx_id_t	m_low_limit_id;

	/** The read should see all trx ids which are strictly
	smaller (<) than this value.  In other words, this is the
	low water mark". */
	trx_id_t	m_up_limit_id;

	/** trx id of creating transaction, set to TRX_ID_MAX for free
	views. */
	trx_id_t	m_creator_trx_id;

	/** Set of RW transactions that was active when this snapshot
	was taken */
	ids_t		m_ids;

	/** The view does not need to see the undo logs for transactions
	whose transaction number is strictly smaller (<) than this value:
	they can be removed in purge if not needed by other views */
	trx_id_t	m_low_limit_no;

	/** AC-NL-RO transaction view that has been "closed". */
	bool		m_closed;

	/** This is a view cloned by clone but not by
	MVCC::clone_oldest_view. Used to make sure the cloned transaction does
	not see its own changes. */
	bool		m_cloned;

	typedef UT_LIST_NODE_T(ReadView) node_t;

	/** List of read views in trx_sys */
	byte		pad1[64 - sizeof(node_t)];
	node_t		m_view_list;
};

#endif
