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

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2022, 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/row0mysql.h
Interface between Innobase row operations and MySQL.
Contains also create table and other data dictionary operations.

Created 9/17/2000 Heikki Tuuri
*******************************************************/

#ifndef row0mysql_h
#define row0mysql_h

#include "que0types.h"
#include "trx0types.h"
#include "row0types.h"
#include "btr0types.h"
#include "lock0types.h"
#include "fil0fil.h"
#include "fts0fts.h"
#include "gis0type.h"

struct row_prebuilt_t;
class ha_innobase;
class ha_handler_stats;

/*******************************************************************//**
Frees the blob heap in prebuilt when no longer needed. */
void
row_mysql_prebuilt_free_blob_heap(
/*==============================*/
	row_prebuilt_t*	prebuilt);	/*!< in: prebuilt struct of a
					ha_innobase:: table handle */
/*******************************************************************//**
Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL row
format.
@return pointer to the data, we skip the 1 or 2 bytes at the start
that are used to store the len */
byte*
row_mysql_store_true_var_len(
/*=========================*/
	byte*	dest,	/*!< in: where to store */
	ulint	len,	/*!< in: length, must fit in two bytes */
	ulint	lenlen);/*!< in: storage length of len: either 1 or 2 bytes */
/*******************************************************************//**
Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and
returns a pointer to the data.
@return pointer to the data, we skip the 1 or 2 bytes at the start
that are used to store the len */
const byte*
row_mysql_read_true_varchar(
/*========================*/
	ulint*		len,	/*!< out: variable-length field length */
	const byte*	field,	/*!< in: field in the MySQL format */
	ulint		lenlen);/*!< in: storage length of len: either 1
				or 2 bytes */
/*******************************************************************//**
Stores a reference to a BLOB in the MySQL format. */
void
row_mysql_store_blob_ref(
/*=====================*/
	byte*		dest,	/*!< in: where to store */
	ulint		col_len,/*!< in: dest buffer size: determines into
				how many bytes the BLOB length is stored,
				the space for the length may vary from 1
				to 4 bytes */
	const void*	data,	/*!< in: BLOB data; if the value to store
				is SQL NULL this should be NULL pointer */
	ulint		len);	/*!< in: BLOB length; if the value to store
				is SQL NULL this should be 0; remember
				also to set the NULL bit in the MySQL record
				header! */
/*******************************************************************//**
Reads a reference to a BLOB in the MySQL format.
@return pointer to BLOB data */
const byte*
row_mysql_read_blob_ref(
/*====================*/
	ulint*		len,		/*!< out: BLOB length */
	const byte*	ref,		/*!< in: BLOB reference in the
					MySQL format */
	ulint		col_len);	/*!< in: BLOB reference length
					(not BLOB length) */
/*******************************************************************//**
Converts InnoDB geometry data format to MySQL data format. */
void
row_mysql_store_geometry(
/*=====================*/
	byte*		dest,		/*!< in/out: where to store */
	ulint		dest_len,	/*!< in: dest buffer size: determines into
					how many bytes the geometry length is stored,
					the space for the length may vary from 1
					to 4 bytes */
	const byte*	src,		/*!< in: geometry data; if the value to store
					is SQL NULL this should be NULL pointer */
	ulint		src_len);	/*!< in: geometry length; if the value to store
					is SQL NULL this should be 0; remember
					also to set the NULL bit in the MySQL record
					header! */
/**************************************************************//**
Pad a column with spaces. */
void
row_mysql_pad_col(
/*==============*/
	ulint	mbminlen,	/*!< in: minimum size of a character,
				in bytes */
	byte*	pad,		/*!< out: padded buffer */
	ulint	len);		/*!< in: number of bytes to pad */

/**************************************************************//**
Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format.
The counterpart of this function is row_sel_field_store_in_mysql_format() in
row0sel.cc.
@return up to which byte we used buf in the conversion */
byte*
row_mysql_store_col_in_innobase_format(
/*===================================*/
	dfield_t*	dfield,		/*!< in/out: dfield where dtype
					information must be already set when
					this function is called! */
	byte*		buf,		/*!< in/out: buffer for a converted
					integer value; this must be at least
					col_len long then! NOTE that dfield
					may also get a pointer to 'buf',
					therefore do not discard this as long
					as dfield is used! */
	ibool		row_format_col,	/*!< TRUE if the mysql_data is from
					a MySQL row, FALSE if from a MySQL
					key value;
					in MySQL, a true VARCHAR storage
					format differs in a row and in a
					key value: in a key value the length
					is always stored in 2 bytes! */
	const byte*	mysql_data,	/*!< in: MySQL column value, not
					SQL NULL; NOTE that dfield may also
					get a pointer to mysql_data,
					therefore do not discard this as long
					as dfield is used! */
	ulint		col_len,	/*!< in: MySQL column length; NOTE that
					this is the storage length of the
					column in the MySQL format row, not
					necessarily the length of the actual
					payload data; if the column is a true
					VARCHAR then this is irrelevant */
	ulint		comp);		/*!< in: nonzero=compact format */
/****************************************************************//**
Handles user errors and lock waits detected by the database engine.
@return true if it was a lock wait and we should continue running the
query thread */
bool
row_mysql_handle_errors(
/*====================*/
	dberr_t*	new_err,/*!< out: possible new error encountered in
				rollback, or the old error which was
				during the function entry */
	trx_t*		trx,	/*!< in: transaction */
	que_thr_t*	thr,	/*!< in: query thread, or NULL */
	const undo_no_t*savept)	/*!< in: pointer to savepoint, or nullptr */
	MY_ATTRIBUTE((nonnull(1,2)));
/********************************************************************//**
Create a prebuilt struct for a MySQL table handle.
@return own: a prebuilt struct */
row_prebuilt_t*
row_create_prebuilt(
/*================*/
	dict_table_t*	table,		/*!< in: Innobase table handle */
	ulint		mysql_row_len);	/*!< in: length in bytes of a row in
					the MySQL format */
/** Free a prebuilt struct for a TABLE handle. */
void row_prebuilt_free(row_prebuilt_t *prebuilt);
/*********************************************************************//**
Updates the transaction pointers in query graphs stored in the prebuilt
struct. */
void
row_update_prebuilt_trx(
/*====================*/
	row_prebuilt_t*	prebuilt,	/*!< in/out: prebuilt struct
					in MySQL handle */
	trx_t*		trx);		/*!< in: transaction handle */

/*********************************************************************//**
Sets an AUTO_INC type lock on the table mentioned in prebuilt. The
AUTO_INC lock gives exclusive access to the auto-inc counter of the
table. The lock is reserved only for the duration of an SQL statement.
It is not compatible with another AUTO_INC or exclusive lock on the
table.
@return error code or DB_SUCCESS */
dberr_t
row_lock_table_autoinc_for_mysql(
/*=============================*/
	row_prebuilt_t*	prebuilt)	/*!< in: prebuilt struct in the MySQL
					table handle */
	MY_ATTRIBUTE((nonnull, warn_unused_result));

/** Lock a table.
@param[in,out]	prebuilt	table handle
@return error code or DB_SUCCESS */
dberr_t
row_lock_table(row_prebuilt_t* prebuilt);

/** System Versioning: row_insert_for_mysql() modes */
enum ins_mode_t {
	/* plain row (without versioning) */
	ROW_INS_NORMAL = 0,
	/* row_start = TRX_ID, row_end = MAX */
	ROW_INS_VERSIONED,
	/* row_end = TRX_ID */
	ROW_INS_HISTORICAL
};

/** Does an insert for MySQL.
@param[in]	mysql_rec	row in the MySQL format
@param[in,out]	prebuilt	prebuilt struct in MySQL handle
@param[in]	ins_mode	what row type we're inserting
@return error code or DB_SUCCESS*/
dberr_t
row_insert_for_mysql(
	const byte*		mysql_rec,
	row_prebuilt_t*		prebuilt,
	ins_mode_t		ins_mode)
	MY_ATTRIBUTE((warn_unused_result));

/*********************************************************************//**
Builds a dummy query graph used in selects. */
void
row_prebuild_sel_graph(
/*===================*/
	row_prebuilt_t*	prebuilt);	/*!< in: prebuilt struct in MySQL
					handle */
/*********************************************************************//**
Gets pointer to a prebuilt update vector used in updates. If the update
graph has not yet been built in the prebuilt struct, then this function
first builds it.
@return prebuilt update vector */
upd_t*
row_get_prebuilt_update_vector(
/*===========================*/
	row_prebuilt_t*	prebuilt);	/*!< in: prebuilt struct in MySQL
					handle */
/** Does an update or delete of a row for MySQL.
@param[in,out]	prebuilt	prebuilt struct in MySQL handle
@return error code or DB_SUCCESS */
dberr_t
row_update_for_mysql(
	row_prebuilt_t*		prebuilt)
	MY_ATTRIBUTE((warn_unused_result));

/** This can only be used when the current transaction is at
READ COMMITTED or READ UNCOMMITTED isolation level.
Before calling this function row_search_mvcc() must have
initialized prebuilt->new_rec_locks to store the information which new
record locks really were set. This function removes a newly set
clustered index record lock under prebuilt->pcur or
prebuilt->clust_pcur.  Thus, this implements a 'mini-rollback' that
releases the latest clustered index record lock we set.
@param[in,out]	prebuilt		prebuilt struct in MySQL handle
@param[in]	has_latches_on_recs	TRUE if called so that we have the
					latches on the records under pcur
					and clust_pcur, and we do not need
					to reposition the cursors. */
void
row_unlock_for_mysql(
	row_prebuilt_t*	prebuilt,
	ibool		has_latches_on_recs);

/*********************************************************************//**
Creates an query graph node of 'update' type to be used in the MySQL
interface.
@return own: update node */
upd_node_t*
row_create_update_node_for_mysql(
/*=============================*/
	dict_table_t*	table,	/*!< in: table to update */
	mem_heap_t*	heap);	/*!< in: mem heap from which allocated */

/**********************************************************************//**
Does a cascaded delete or set null in a foreign key operation.
@return error code or DB_SUCCESS */
dberr_t
row_update_cascade_for_mysql(
/*=========================*/
        que_thr_t*      thr,    /*!< in: query thread */
        upd_node_t*     node,   /*!< in: update node used in the cascade
                                or set null operation */
        dict_table_t*   table)  /*!< in: table where we do the operation */
        MY_ATTRIBUTE((nonnull, warn_unused_result));

/** Lock the data dictionary cache exclusively. */
#define row_mysql_lock_data_dictionary(trx)			\
	do {							\
		ut_ad(!trx->dict_operation_lock_mode);		\
		dict_sys.lock(SRW_LOCK_CALL);			\
		trx->dict_operation_lock_mode = true;		\
	} while (0)

/** Unlock the data dictionary. */
#define row_mysql_unlock_data_dictionary(trx)			\
	do {							\
		ut_ad(!lock_trx_has_sys_table_locks(trx));	\
		ut_ad(trx->dict_operation_lock_mode);		\
		trx->dict_operation_lock_mode = false;		\
		dict_sys.unlock();				\
	} while (0)

/*********************************************************************//**
Creates a table for MySQL. On failure the transaction will be rolled back
and the 'table' object will be freed.
@return error code or DB_SUCCESS */
dberr_t
row_create_table_for_mysql(
/*=======================*/
	dict_table_t*	table,	/*!< in, own: table definition
				(will be freed, or on DB_SUCCESS
				added to the data dictionary cache) */
	trx_t*		trx)	/*!< in/out: transaction */
	MY_ATTRIBUTE((warn_unused_result));

/*********************************************************************//**
Create an index when creating a table.
On failure, the caller must drop the table!
@return error number or DB_SUCCESS */
dberr_t
row_create_index_for_mysql(
/*=======================*/
	dict_index_t*	index,		/*!< in, own: index definition
					(will be freed) */
	trx_t*		trx,		/*!< in: transaction handle */
	const ulint*	field_lengths,	/*!< in: if not NULL, must contain
					dict_index_get_n_fields(index)
					actual field lengths for the
					index columns, which are
					then checked for not being too
					large. */
	fil_encryption_t mode,	/*!< in: encryption mode */
	uint32_t	key_id)	/*!< in: encryption key_id */
	MY_ATTRIBUTE((warn_unused_result));

/*********************************************************************//**
Discards the tablespace of a table which stored in an .ibd file. Discarding
means that this function deletes the .ibd file and assigns a new table id for
the table. Also the file_unreadable flag is set.
@return error code or DB_SUCCESS */
dberr_t row_discard_tablespace_for_mysql(dict_table_t *table, trx_t *trx)
	MY_ATTRIBUTE((nonnull, warn_unused_result));
/*****************************************************************//**
Imports a tablespace. The space id in the .ibd file must match the space id
of the table in the data dictionary.
@return error code or DB_SUCCESS */
dberr_t
row_import_tablespace_for_mysql(
/*============================*/
	dict_table_t*	table,		/*!< in/out: table */
	row_prebuilt_t*	prebuilt)	/*!< in: prebuilt struct in MySQL */
        MY_ATTRIBUTE((nonnull, warn_unused_result));

enum rename_fk {
  /** ignore FOREIGN KEY constraints */
  RENAME_IGNORE_FK= 0,
  /** Rename a table as part of a native table-rebuilding DDL operation */
  RENAME_REBUILD,
  /** Rename as part of ALTER TABLE...ALGORITHM=COPY */
  RENAME_ALTER_COPY
};

/*********************************************************************//**
Renames a table for MySQL.
@return error code or DB_SUCCESS */
dberr_t
row_rename_table_for_mysql(
/*=======================*/
	const char*	old_name,	/*!< in: old table name */
	const char*	new_name,	/*!< in: new table name */
	trx_t*		trx,		/*!< in/out: transaction */
	rename_fk	fk)		/*!< in: how to handle
					FOREIGN KEY constraints */
	MY_ATTRIBUTE((nonnull, warn_unused_result));

/* A struct describing a place for an individual column in the MySQL
row format which is presented to the table handler in ha_innobase.
This template struct is used to speed up row transformations between
Innobase and MySQL. */

struct mysql_row_templ_t {
	ulint	col_no;			/*!< column number of the column */
	ulint	rec_field_no;		/*!< field number of the column in an
					Innobase record in the current index;
					not defined if template_type is
					ROW_MYSQL_WHOLE_ROW */
	ibool	rec_field_is_prefix;	/* is this field in a prefix index? */
	ulint	rec_prefix_field_no;	/* record field, even if just a
					prefix; same as rec_field_no when not a
					prefix, otherwise rec_field_no is
					ULINT_UNDEFINED but this is the true
					field number*/
	ulint	clust_rec_field_no;	/*!< field number of the column in an
					Innobase record in the clustered index;
					not defined if template_type is
					ROW_MYSQL_WHOLE_ROW */
	ulint	icp_rec_field_no;	/*!< field number of the column in an
					Innobase record in the current index;
					not defined unless
					index condition pushdown is used */
	ulint	mysql_col_offset;	/*!< offset of the column in the MySQL
					row format */
	ulint	mysql_col_len;		/*!< length of the column in the MySQL
					row format */
	ulint	mysql_null_byte_offset;	/*!< MySQL NULL bit byte offset in a
					MySQL record */
	ulint	mysql_null_bit_mask;	/*!< bit mask to get the NULL bit,
					zero if column cannot be NULL */
	ulint	type;			/*!< column type in Innobase mtype
					numbers DATA_CHAR... */
	ulint	mysql_type;		/*!< MySQL type code; this is always
					< 256 */
	ulint	mysql_length_bytes;	/*!< if mysql_type
					== DATA_MYSQL_TRUE_VARCHAR, this tells
					whether we should use 1 or 2 bytes to
					store the MySQL true VARCHAR data
					length at the start of row in the MySQL
					format (NOTE that the MySQL key value
					format always uses 2 bytes for the data
					len) */
	ulint	charset;		/*!< MySQL charset-collation code
					of the column, or zero */
	ulint	mbminlen;		/*!< minimum length of a char, in bytes,
					or zero if not a char type */
	ulint	mbmaxlen;		/*!< maximum length of a char, in bytes,
					or zero if not a char type */
	ulint	is_unsigned;		/*!< if a column type is an integer
					type and this field is != 0, then
					it is an unsigned integer type */
	ulint	is_virtual;		/*!< if a column is a virtual column */
};

#define MYSQL_FETCH_CACHE_SIZE		8
/* After fetching this many rows, we start caching them in fetch_cache */
#define MYSQL_FETCH_CACHE_THRESHOLD	4

#define ROW_PREBUILT_ALLOCATED	78540783
#define ROW_PREBUILT_FREED	26423527

/** A struct for (sometimes lazily) prebuilt structures in an Innobase table
handle used within MySQL; these are used to save CPU time. */

struct row_prebuilt_t {
	ulint		magic_n;	/*!< this magic number is set to
					ROW_PREBUILT_ALLOCATED when created,
					or ROW_PREBUILT_FREED when the
					struct has been freed */
	dict_table_t*	table;		/*!< Innobase table handle */
	dict_index_t*	index;		/*!< current index for a search, if
					any */
	trx_t*		trx;		/*!< current transaction handle */
	unsigned	sql_stat_start:1;/*!< TRUE when we start processing of
					an SQL statement: we may have to set
					an intention lock on the table,
					create a consistent read view etc. */
	unsigned	clust_index_was_generated:1;
					/*!< if the user did not define a
					primary key in MySQL, then Innobase
					automatically generated a clustered
					index where the ordering column is
					the row id: in this case this flag
					is set to TRUE */
	unsigned	index_usable:1;	/*!< caches the value of
					row_merge_is_index_usable(trx,index) */
	unsigned	read_just_key:1;/*!< set to 1 when MySQL calls
					ha_innobase::extra with the
					argument HA_EXTRA_KEYREAD; it is enough
					to read just columns defined in
					the index (i.e., no read of the
					clustered index record necessary) */
	unsigned	used_in_HANDLER:1;/*!< TRUE if we have been using this
					handle in a MySQL HANDLER low level
					index cursor command: then we must
					store the pcur position even in a
					unique search from a clustered index,
					because HANDLER allows NEXT and PREV
					in such a situation */
	unsigned	template_type:2;/*!< ROW_MYSQL_WHOLE_ROW,
					ROW_MYSQL_REC_FIELDS,
					ROW_MYSQL_DUMMY_TEMPLATE, or
					ROW_MYSQL_NO_TEMPLATE */
	unsigned	n_template:10;	/*!< number of elements in the
					template */
	unsigned	null_bitmap_len:10;/*!< number of bytes in the SQL NULL
					bitmap at the start of a row in the
					MySQL format */
	unsigned	need_to_access_clustered:1; /*!< if we are fetching
					columns through a secondary index
					and at least one column is not in
					the secondary index, then this is
					set to TRUE; note that sometimes this
					is set but we later optimize out the
					clustered index lookup */
	unsigned	templ_contains_blob:1;/*!< TRUE if the template contains
					a column with DATA_LARGE_MTYPE(
					get_innobase_type_from_mysql_type())
					is TRUE;
					not to be confused with InnoDB
					externally stored columns
					(VARCHAR can be off-page too) */
	unsigned	versioned_write:1;/*!< whether this is
					a versioned write */
	mysql_row_templ_t* mysql_template;/*!< template used to transform
					rows fast between MySQL and Innobase
					formats; memory for this template
					is not allocated from 'heap' */
	mem_heap_t*	heap;		/*!< memory heap from which
					these auxiliary structures are
					allocated when needed */
	ins_node_t*	ins_node;	/*!< Innobase SQL insert node
					used to perform inserts
					to the table */
	byte*		ins_upd_rec_buff;/*!< buffer for storing data converted
					to the Innobase format from the MySQL
					format */
	const byte*	default_rec;	/*!< the default values of all columns
					(a "default row") in MySQL format */
	ulint		hint_need_to_fetch_extra_cols;
					/*!< normally this is set to 0; if this
					is set to ROW_RETRIEVE_PRIMARY_KEY,
					then we should at least retrieve all
					columns in the primary key; if this
					is set to ROW_RETRIEVE_ALL_COLS, then
					we must retrieve all columns in the
					key (if read_just_key == 1), or all
					columns in the table */
	upd_node_t*	upd_node;	/*!< Innobase SQL update node used
					to perform updates and deletes */
	trx_id_t	trx_id;		/*!< The table->def_trx_id when
					ins_graph was built */
	que_fork_t*	ins_graph;	/*!< Innobase SQL query graph used
					in inserts. Will be rebuilt on
					trx_id or n_indexes mismatch. */
	que_fork_t*	upd_graph;	/*!< Innobase SQL query graph used
					in updates or deletes */
	btr_pcur_t*	pcur;		/*!< persistent cursor used in selects
					and updates */
	btr_pcur_t*	clust_pcur;	/*!< persistent cursor used in
					some selects and updates */
	que_fork_t*	sel_graph;	/*!< dummy query graph used in
					selects */
	dtuple_t*	search_tuple;	/*!< prebuilt dtuple used in selects */
	byte		row_id[DATA_ROW_ID_LEN];
					/*!< if the clustered index was
					generated, the row id of the
					last row fetched is stored
					here */
	doc_id_t	fts_doc_id;	/* if the table has an FTS index on
					it then we fetch the doc_id.
					FTS-FIXME: Currently we fetch it always
					but in the future we must only fetch
					it when FTS columns are being
					updated */
	dtuple_t*	clust_ref;	/*!< prebuilt dtuple used in
					sel/upd/del */
	lock_mode	select_lock_type;/*!< LOCK_NONE, LOCK_S, or LOCK_X */
	bool		skip_locked;	/*!< TL_{READ,WRITE}_SKIP_LOCKED */
	lock_mode	stored_select_lock_type;/*!< this field is used to
					remember the original select_lock_type
					that was decided in ha_innodb.cc,
					::store_lock(), ::external_lock(),
					etc. */
	ulint		row_read_type;	/*!< ROW_READ_WITH_LOCKS if row locks
					should be the obtained for records
					under an UPDATE or DELETE cursor.
					At READ UNCOMMITTED or
					READ COMMITTED isolation level,
					this can be set to
					ROW_READ_TRY_SEMI_CONSISTENT, so that
					if the row under an UPDATE or DELETE
					cursor was locked by another
					transaction, InnoDB will resort
					to reading the last committed value
					('semi-consistent read').  Then,
					this field will be set to
					ROW_READ_DID_SEMI_CONSISTENT to
					indicate that.	If the row does not
					match the WHERE condition, MySQL will
					invoke handler::unlock_row() to
					clear the flag back to
					ROW_READ_TRY_SEMI_CONSISTENT and
					to simply skip the row.	 If
					the row matches, the next call to
					row_search_mvcc() will lock
					the row.
					This eliminates lock waits in some
					cases; note that this breaks
					serializability. */
	ulint		new_rec_locks;	/*!< normally 0; if
					the session is using READ
					COMMITTED or READ UNCOMMITTED
					isolation level, set in
					row_search_mvcc() if we set a new
					record lock on the secondary
					or clustered index; this is
					used in row_unlock_for_mysql()
					when releasing the lock under
					the cursor if we determine
					after retrieving the row that
					it does not need to be locked
					('mini-rollback') */
	ulint		mysql_prefix_len;/*!< byte offset of the end of
					the last requested column */
	ulint		mysql_row_len;	/*!< length in bytes of a row in the
					MySQL format */
	ulint		n_rows_fetched;	/*!< number of rows fetched after
					positioning the current cursor */
	ulint		fetch_direction;/*!< ROW_SEL_NEXT or ROW_SEL_PREV */
	byte*		fetch_cache[MYSQL_FETCH_CACHE_SIZE];
					/*!< a cache for fetched rows if we
					fetch many rows from the same cursor:
					it saves CPU time to fetch them in a
					batch; we reserve mysql_row_len
					bytes for each such row; these
					pointers point 4 bytes past the
					allocated mem buf start, because
					there is a 4 byte magic number at the
					start and at the end */
	bool		keep_other_fields_on_keyread; /*!< when using fetch
					cache with HA_EXTRA_KEYREAD, don't
					overwrite other fields in mysql row
					row buffer.*/
	ulint		fetch_cache_first;/*!< position of the first not yet
					fetched row in fetch_cache */
	ulint		n_fetch_cached;	/*!< number of not yet fetched rows
					in fetch_cache */
	mem_heap_t*	blob_heap;	/*!< in SELECTS BLOB fields are copied
					to this heap */
	mem_heap_t*	old_vers_heap;	/*!< memory heap where a previous
					version is built in consistent read */
	bool		in_fts_query;	/*!< Whether we are in a FTS query */
	bool		fts_doc_id_in_read_set; /*!< true if table has externally
					defined FTS_DOC_ID column. */
	/*----------------------*/
	ulonglong	autoinc_last_value;
					/*!< last value of AUTO-INC interval */
	ulonglong	autoinc_increment;/*!< The increment step of the auto
					increment column. Value must be
					greater than or equal to 1. Required to
					calculate the next value */
	ulonglong	autoinc_offset; /*!< The offset passed to
					get_auto_increment() by MySQL. Required
					to calculate the next value */
	dberr_t		autoinc_error;	/*!< The actual error code encountered
					while trying to init or read the
					autoinc value from the table. We
					store it here so that we can return
					it to MySQL */
	/*----------------------*/

	/** Argument of handler_rowid_filter_check(),
	or NULL if no PRIMARY KEY filter is pushed */
	ha_innobase*	pk_filter;

	/** Argument to handler_index_cond_check(),
	or NULL if no index condition pushdown (ICP) is used. */
	ha_innobase*	idx_cond;
	ulint		idx_cond_n_cols;/*!< Number of fields in idx_cond_cols.
					0 if and only if idx_cond == NULL. */
	/*----------------------*/

	/*----------------------*/
	rtr_info_t*	rtr_info;	/*!< R-tree Search Info */
	/*----------------------*/

	ulint		magic_n2;	/*!< this should be the same as
					magic_n */

	byte*		srch_key_val1;  /*!< buffer used in converting
					search key values from MySQL format
					to InnoDB format.*/
	byte*		srch_key_val2;  /*!< buffer used in converting
					search key values from MySQL format
					to InnoDB format.*/
	uint		srch_key_val_len; /*!< Size of search key */
	/** The MySQL table object */
	TABLE*		m_mysql_table;

	/** Get template by dict_table_t::cols[] number */
	const mysql_row_templ_t* get_template_by_col(ulint col) const
	{
		ut_ad(col < n_template);
		ut_ad(mysql_template);
		for (ulint i = col; i < n_template; ++i) {
			const mysql_row_templ_t* templ = &mysql_template[i];
			if (!templ->is_virtual && templ->col_no == col) {
				return templ;
			}
		}
		return NULL;
	}
};

/** Callback for row_mysql_sys_index_iterate() */
struct SysIndexCallback {
	virtual ~SysIndexCallback() = default;

	/** Callback method
	@param mtr current mini transaction
	@param pcur persistent cursor. */
	virtual void operator()(mtr_t* mtr, btr_pcur_t* pcur) throw() = 0;
};


/** Storage for calculating virtual columns */

class String;
struct VCOL_STORAGE
{
	TABLE *maria_table;
	byte *innobase_record;
	byte *maria_record;
	String *blob_value_storage;
	VCOL_STORAGE(): maria_table(NULL), innobase_record(NULL),
		maria_record(NULL),  blob_value_storage(NULL) {}
};

/**
   Allocate a heap and record for calculating virtual fields
   Used mainly for virtual fields in indexes

@param[in]	thd		MariaDB THD
@param[in]	index		Index in use
@param[out]	heap		Heap that holds temporary row
@param[in,out]	mysql_table	MariaDB table
@param[out]	rec		Pointer to allocated MariaDB record
@param[out]	storage		Internal storage for blobs etc

@return		FALSE ok
@return		TRUE  malloc failure
*/

bool innobase_allocate_row_for_vcol(THD *thd,
				    const dict_index_t* index,
				    mem_heap_t**  heap,
				    TABLE**	  table,
				    VCOL_STORAGE* storage);

/** Free memory allocated by innobase_allocate_row_for_vcol() */
void innobase_free_row_for_vcol(VCOL_STORAGE *storage);

class ib_vcol_row
{
  VCOL_STORAGE storage;
public:
  mem_heap_t *heap;

  ib_vcol_row(mem_heap_t *heap) : heap(heap) {}

  byte *record(THD *thd, const dict_index_t *index, TABLE **table)
  {
    if (!storage.innobase_record &&
        !innobase_allocate_row_for_vcol(thd, index, &heap, table, &storage))
      return nullptr;
    return storage.innobase_record;
  }

  ~ib_vcol_row()
  {
    if (heap)
    {
      if (storage.innobase_record)
        innobase_free_row_for_vcol(&storage);
      mem_heap_free(heap);
    }
  }
};

/** Report virtual value computation failure in ib::error
@param[in]    row    the data row
*/
ATTRIBUTE_COLD
void innobase_report_computed_value_failed(dtuple_t *row);

/** Get the computed value by supplying the base column values.
@param[in,out]	row		the data row
@param[in]	col		virtual column
@param[in]	index		index on the virtual column
@param[in,out]	local_heap	heap memory for processing large data etc.
@param[in,out]	heap		memory heap that copies the actual index row
@param[in]	ifield		index field
@param[in]	thd		connection handle
@param[in,out]	mysql_table	MariaDB table handle
@param[in,out]	mysql_rec	MariaDB record buffer
@param[in]	old_table	during ALTER TABLE, this is the old table
				or NULL.
@param[in]	update	update vector for the parent row
@param[in]	ignore_warnings	ignore warnings during calculation. Usually
				means that a calculation is internal and
				should have no side effects.
@return the field filled with computed value */
dfield_t*
innobase_get_computed_value(
	dtuple_t*		row,
	const dict_v_col_t*	col,
	const dict_index_t*	index,
	mem_heap_t**		local_heap,
	mem_heap_t*		heap,
	const dict_field_t*	ifield,
	THD*			thd,
	TABLE*			mysql_table,
	byte*			mysql_rec,
	const dict_table_t*	old_table=NULL,
	const upd_t*		update=NULL,
	bool			ignore_warnings=false);

/** Change dbname and table name in table->vc_templ.
@param[in,out]	table	the table whose virtual column template
dbname and tbname to be renamed. */
void
innobase_rename_vc_templ(
	dict_table_t*	table);

#define ROW_PREBUILT_FETCH_MAGIC_N	465765687

#define ROW_MYSQL_WHOLE_ROW	0
#define ROW_MYSQL_REC_FIELDS	1
#define ROW_MYSQL_NO_TEMPLATE	2
#define ROW_MYSQL_DUMMY_TEMPLATE 3	/* dummy template used in
					row_check_index() */

/* Values for hint_need_to_fetch_extra_cols */
#define ROW_RETRIEVE_PRIMARY_KEY	1
#define ROW_RETRIEVE_ALL_COLS		2

/* Values for row_read_type */
#define ROW_READ_WITH_LOCKS		0
#define ROW_READ_TRY_SEMI_CONSISTENT	1
#define ROW_READ_DID_SEMI_CONSISTENT	2

#endif /* row0mysql.h */
