mariadb/storage/innobase/include/row0row.h
Marko Mäkelä 24fe53477c MDEV-29603 btr_cur_open_at_index_side() is missing some consistency checks
btr_cur_t: Zero-initialize all fields in the default constructor.

btr_cur_t::index: Remove; it duplicated page_cur.index.

Many functions: Remove arguments that were duplicating
page_cur_t::index and page_cur_t::block.

page_cur_open_level(), btr_pcur_open_level(): Replaces
btr_cur_open_at_index_side() for dict_stats_analyze_index().
At the end, release all latches except the dict_index_t::lock
and the buf_page_t::lock on the requested page.

dict_stats_analyze_index(): Rely on mtr_t::rollback_to_savepoint()
to release all uninteresting page latches.

btr_search_guess_on_hash(): Simplify the logic, and invoke
mtr_t::rollback_to_savepoint().

We will use plain C++ std::vector<mtr_memo_slot_t> for mtr_t::m_memo.
In this way, we can avoid setting mtr_memo_slot_t::object to nullptr
and instead just remove garbage from m_memo.

mtr_t::rollback_to_savepoint(): Shrink the vector. We will be needing this
in dict_stats_analyze_index(), where we will release page latches and
only retain the index->lock in mtr_t::m_memo.

mtr_t::release_last_page(): Release the last acquired page latch.
Replaces btr_leaf_page_release().

mtr_t::release(const buf_block_t&): Release a single page latch.
Used in btr_pcur_move_backward_from_page().

mtr_t::memo_release(): Replaced with mtr_t::release().

mtr_t::upgrade_buffer_fix(): Acquire a latch for a buffer-fixed page.
This replaces the double bookkeeping in btr_cur_t::open_leaf().

Reviewed by: Vladislav Lesin
2022-11-17 08:19:01 +02:00

431 lines
16 KiB
C

/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 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/row0row.h
General row routines
Created 4/20/1996 Heikki Tuuri
*******************************************************/
#ifndef row0row_h
#define row0row_h
#include "que0types.h"
#include "ibuf0ibuf.h"
#include "trx0types.h"
#include "mtr0mtr.h"
#include "rem0types.h"
#include "row0types.h"
#include "btr0types.h"
/*********************************************************************//**
Gets the offset of the DB_TRX_ID field, in bytes relative to the origin of
a clustered index record.
@return offset of DATA_TRX_ID */
UNIV_INLINE
ulint
row_get_trx_id_offset(
/*==================*/
const dict_index_t* index, /*!< in: clustered index */
const rec_offs* offsets)/*!< in: record offsets */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*********************************************************************//**
Reads the trx id field from a clustered index record.
@return value of the field */
UNIV_INLINE
trx_id_t
row_get_rec_trx_id(
/*===============*/
const rec_t* rec, /*!< in: record */
const dict_index_t* index, /*!< in: clustered index */
const rec_offs* offsets)/*!< in: rec_get_offsets(rec, index) */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*********************************************************************//**
Reads the roll pointer field from a clustered index record.
@return value of the field */
UNIV_INLINE
roll_ptr_t
row_get_rec_roll_ptr(
/*=================*/
const rec_t* rec, /*!< in: record */
const dict_index_t* index, /*!< in: clustered index */
const rec_offs* offsets)/*!< in: rec_get_offsets(rec, index) */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/* Flags for row build type. */
#define ROW_BUILD_NORMAL 0 /*!< build index row */
#define ROW_BUILD_FOR_PURGE 1 /*!< build row for purge. */
#define ROW_BUILD_FOR_UNDO 2 /*!< build row for undo. */
#define ROW_BUILD_FOR_INSERT 3 /*!< build row for insert. */
/*****************************************************************//**
When an insert or purge to a table is performed, this function builds
the entry to be inserted into or purged from an index on the table.
@return index entry which should be inserted or purged
@retval NULL if the externally stored columns in the clustered index record
are unavailable and ext != NULL, or row is missing some needed columns. */
dtuple_t*
row_build_index_entry_low(
/*======================*/
const dtuple_t* row, /*!< in: row which should be
inserted or purged */
const row_ext_t* ext, /*!< in: externally stored column
prefixes, or NULL */
const dict_index_t* index, /*!< in: index on the table */
mem_heap_t* heap, /*!< in,out: memory heap from which
the memory for the index entry
is allocated */
ulint flag) /*!< in: ROW_BUILD_NORMAL,
ROW_BUILD_FOR_PURGE
or ROW_BUILD_FOR_UNDO */
MY_ATTRIBUTE((warn_unused_result, nonnull(1,3,4)));
/*****************************************************************//**
When an insert or purge to a table is performed, this function builds
the entry to be inserted into or purged from an index on the table.
@return index entry which should be inserted or purged, or NULL if the
externally stored columns in the clustered index record are
unavailable and ext != NULL */
UNIV_INLINE
dtuple_t*
row_build_index_entry(
/*==================*/
const dtuple_t* row, /*!< in: row which should be
inserted or purged */
const row_ext_t* ext, /*!< in: externally stored column
prefixes, or NULL */
const dict_index_t* index, /*!< in: index on the table */
mem_heap_t* heap) /*!< in,out: memory heap from which
the memory for the index entry
is allocated */
MY_ATTRIBUTE((warn_unused_result, nonnull(1,3,4)));
/*******************************************************************//**
An inverse function to row_build_index_entry. Builds a row from a
record in a clustered index.
@return own: row built; see the NOTE below! */
dtuple_t*
row_build(
/*======*/
ulint type, /*!< in: ROW_COPY_POINTERS or
ROW_COPY_DATA; the latter
copies also the data fields to
heap while the first only
places pointers to data fields
on the index page, and thus is
more efficient */
const dict_index_t* index, /*!< in: clustered index */
const rec_t* rec, /*!< in: record in the clustered
index; NOTE: in the case
ROW_COPY_POINTERS the data
fields in the row will point
directly into this record,
therefore, the buffer page of
this record must be at least
s-latched and the latch held
as long as the row dtuple is used! */
const rec_offs* offsets,/*!< in: rec_get_offsets(rec,index)
or NULL, in which case this function
will invoke rec_get_offsets() */
const dict_table_t* col_table,
/*!< in: table, to check which
externally stored columns
occur in the ordering columns
of an index, or NULL if
index->table should be
consulted instead; the user
columns in this table should be
the same columns as in index->table */
const dtuple_t* defaults,
/*!< in: default values of
added, changed columns, or NULL */
const ulint* col_map,/*!< in: mapping of old column
numbers to new ones, or NULL */
row_ext_t** ext, /*!< out, own: cache of
externally stored column
prefixes, or NULL */
mem_heap_t* heap); /*!< in: memory heap from which
the memory needed is allocated */
/** An inverse function to row_build_index_entry. Builds a row from a
record in a clustered index, with possible indexing on ongoing
addition of new virtual columns.
@param[in] type ROW_COPY_POINTERS or ROW_COPY_DATA;
@param[in] index clustered index
@param[in] rec record in the clustered index
@param[in] offsets rec_get_offsets(rec,index) or NULL
@param[in] col_table table, to check which
externally stored columns
occur in the ordering columns
of an index, or NULL if
index->table should be
consulted instead
@param[in] defaults default values of added, changed columns, or NULL
@param[in] add_v new virtual columns added
along with new indexes
@param[in] col_map mapping of old column
numbers to new ones, or NULL
@param[in] ext cache of externally stored column
prefixes, or NULL
@param[in] heap memory heap from which
the memory needed is allocated
@return own: row built */
dtuple_t*
row_build_w_add_vcol(
ulint type,
const dict_index_t* index,
const rec_t* rec,
const rec_offs* offsets,
const dict_table_t* col_table,
const dtuple_t* defaults,
const dict_add_v_col_t* add_v,
const ulint* col_map,
row_ext_t** ext,
mem_heap_t* heap);
/*******************************************************************//**
Converts an index record to a typed data tuple.
@return index entry built; does not set info_bits, and the data fields
in the entry will point directly to rec */
dtuple_t*
row_rec_to_index_entry_low(
/*=======================*/
const rec_t* rec, /*!< in: record in the index */
const dict_index_t* index, /*!< in: index */
const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
mem_heap_t* heap) /*!< in: memory heap from which
the memory needed is allocated */
MY_ATTRIBUTE((warn_unused_result));
/*******************************************************************//**
Converts an index record to a typed data tuple. NOTE that externally
stored (often big) fields are NOT copied to heap.
@return own: index entry built */
dtuple_t*
row_rec_to_index_entry(
/*===================*/
const rec_t* rec, /*!< in: record in the index */
const dict_index_t* index, /*!< in: index */
const rec_offs* offsets,/*!< in/out: rec_get_offsets(rec) */
mem_heap_t* heap) /*!< in: memory heap from which
the memory needed is allocated */
MY_ATTRIBUTE((warn_unused_result));
/** Convert a metadata record to a data tuple.
@param[in] rec metadata record
@param[in] index clustered index after instant ALTER TABLE
@param[in] offsets rec_get_offsets(rec)
@param[in,out] heap memory heap for allocations
@param[in] info_bits the info_bits after an update
@param[in] pad whether to pad to index->n_fields */
dtuple_t*
row_metadata_to_tuple(
const rec_t* rec,
const dict_index_t* index,
const rec_offs* offsets,
mem_heap_t* heap,
ulint info_bits,
bool pad)
MY_ATTRIBUTE((nonnull,warn_unused_result));
/*******************************************************************//**
Builds from a secondary index record a row reference with which we can
search the clustered index record.
@return own: row reference built; see the NOTE below! */
dtuple_t*
row_build_row_ref(
/*==============*/
ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS:
the former copies also the data fields to
heap, whereas the latter only places pointers
to data fields on the index page */
dict_index_t* index, /*!< in: secondary index */
const rec_t* rec, /*!< in: record in the index;
NOTE: in the case ROW_COPY_POINTERS
the data fields in the row will point
directly into this record, therefore,
the buffer page of this record must be
at least s-latched and the latch held
as long as the row reference is used! */
mem_heap_t* heap) /*!< in: memory heap from which the memory
needed is allocated */
MY_ATTRIBUTE((warn_unused_result));
/*******************************************************************//**
Builds from a secondary index record a row reference with which we can
search the clustered index record. */
void
row_build_row_ref_in_tuple(
/*=======================*/
dtuple_t* ref, /*!< in/out: row reference built;
see the NOTE below! */
const rec_t* rec, /*!< in: record in the index;
NOTE: the data fields in ref
will point directly into this
record, therefore, the buffer
page of this record must be at
least s-latched and the latch
held as long as the row
reference is used! */
const dict_index_t* index, /*!< in: secondary index */
rec_offs* offsets)/*!< in: rec_get_offsets(rec, index)
or NULL */
MY_ATTRIBUTE((nonnull(1,2,3)));
/*******************************************************************//**
Builds from a secondary index record a row reference with which we can
search the clustered index record. */
UNIV_INLINE
void
row_build_row_ref_fast(
/*===================*/
dtuple_t* ref, /*!< in/out: typed data tuple where the
reference is built */
const ulint* map, /*!< in: array of field numbers in rec
telling how ref should be built from
the fields of rec */
const rec_t* rec, /*!< in: secondary index record;
must be preserved while ref is used, as we do
not copy field values to heap */
const rec_offs* offsets);/*!< in: array returned by rec_get_offsets() */
/***************************************************************//**
Searches the clustered index record for a row, if we have the row
reference.
@return true if found */
bool
row_search_on_row_ref(
/*==================*/
btr_pcur_t* pcur, /*!< out: persistent cursor, which must
be closed by the caller */
btr_latch_mode mode, /*!< in: BTR_MODIFY_LEAF, ... */
const dict_table_t* table, /*!< in: table */
const dtuple_t* ref, /*!< in: row reference */
mtr_t* mtr) /*!< in/out: mtr */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*********************************************************************//**
Fetches the clustered index record for a secondary index record. The latches
on the secondary index record are preserved.
@return record or NULL, if no record found */
rec_t*
row_get_clust_rec(
/*==============*/
btr_latch_mode mode, /*!< in: BTR_MODIFY_LEAF, ... */
const rec_t* rec, /*!< in: record in a secondary index */
dict_index_t* index, /*!< in: secondary index */
dict_index_t** clust_index,/*!< out: clustered index */
mtr_t* mtr) /*!< in: mtr */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Parse the integer data from specified data, which could be
DATA_INT, DATA_FLOAT or DATA_DOUBLE. If the value is less than 0
and the type is not unsigned then we reset the value to 0
@param[in] data data to read
@param[in] len length of data
@param[in] mtype mtype of data
@param[in] unsigned_type if the data is unsigned
@return the integer value from the data */
inline
ib_uint64_t
row_parse_int(
const byte* data,
ulint len,
ulint mtype,
bool unsigned_type);
/** Result of row_search_index_entry */
enum row_search_result {
ROW_FOUND = 0, /*!< the record was found */
ROW_NOT_FOUND, /*!< record not found */
ROW_BUFFERED, /*!< one of BTR_INSERT, BTR_DELETE, or
BTR_DELETE_MARK was specified, the
secondary index leaf page was not in
the buffer pool, and the operation was
enqueued in the insert/delete buffer */
ROW_NOT_DELETED_REF /*!< BTR_DELETE was specified, and
row_purge_poss_sec() failed */
};
/***************************************************************//**
Searches an index record.
@return whether the record was found or buffered */
enum row_search_result
row_search_index_entry(
/*===================*/
const dtuple_t* entry, /*!< in: index entry */
btr_latch_mode mode, /*!< in: BTR_MODIFY_LEAF, ... */
btr_pcur_t* pcur, /*!< in/out: persistent cursor, which must
be closed by the caller */
mtr_t* mtr) /*!< in: mtr */
MY_ATTRIBUTE((nonnull, warn_unused_result));
#define ROW_COPY_DATA 1
#define ROW_COPY_POINTERS 2
/* The allowed latching order of index records is the following:
(1) a secondary index record ->
(2) the clustered index record ->
(3) rollback segment data for the clustered index record. */
/*******************************************************************//**
Formats the raw data in "data" (in InnoDB on-disk format) using
"dict_field" and writes the result to "buf".
Not more than "buf_size" bytes are written to "buf".
The result is always NUL-terminated (provided buf_size is positive) and the
number of bytes that were written to "buf" is returned (including the
terminating NUL).
@return number of bytes that were written */
ulint
row_raw_format(
/*===========*/
const char* data, /*!< in: raw data */
ulint data_len, /*!< in: raw data length
in bytes */
const dict_field_t* dict_field, /*!< in: index field */
char* buf, /*!< out: output buffer */
ulint buf_size) /*!< in: output buffer size
in bytes */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Prepare to start a mini-transaction to modify an index.
@param[in,out] mtr mini-transaction
@param[in,out] index possibly secondary index
@param[in] pessimistic whether this is a pessimistic operation */
inline
void
row_mtr_start(mtr_t* mtr, dict_index_t* index, bool pessimistic)
{
mtr->start();
switch (index->table->space_id) {
case IBUF_SPACE_ID:
if (pessimistic
&& !(index->type & (DICT_UNIQUE | DICT_SPATIAL))) {
ibuf_free_excess_pages();
}
break;
case SRV_TMP_SPACE_ID:
mtr->set_log_mode(MTR_LOG_NO_REDO);
break;
default:
index->set_modified(*mtr);
break;
}
log_free_check();
}
#include "row0row.inl"
#endif