mirror of
https://github.com/MariaDB/server.git
synced 2025-02-05 13:22:17 +01:00
895cd553a3
When srv_page_size and innodb_page_size were introduced, the functions page_align() and page_offset() got more expensive. Let us try to replace such calls with simpler pointer arithmetics with respect to the buffer page frame. page_rec_get_next_non_del_marked(): Add a page frame as a parameter, and template<bool comp>. page_rec_next_get(): A more efficient variant of page_rec_get_next(), with template<bool comp> and const page_t* parameters. lock_get_heap_no(): Replaces page_rec_get_heap_no() outside debug checks. fseg_free_step(), fseg_free_step_not_header(): Take the header block as a parameter. Reviewed by: Vladislav Lesin
552 lines
15 KiB
C++
552 lines
15 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 1994, 2019, 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/page0page.ic
|
|
Index page routines
|
|
|
|
Created 2/2/1994 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
|
#include "rem0cmp.h"
|
|
#include "mtr0log.h"
|
|
#include "page0zip.h"
|
|
|
|
/*************************************************************//**
|
|
Sets the max trx id field value if trx_id is bigger than the previous
|
|
value. */
|
|
UNIV_INLINE
|
|
void
|
|
page_update_max_trx_id(
|
|
/*===================*/
|
|
buf_block_t* block, /*!< in/out: page */
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
uncompressed part will be updated, or NULL */
|
|
trx_id_t trx_id, /*!< in: transaction id */
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
{
|
|
ut_ad(block);
|
|
ut_ad(mtr->memo_contains_flagged(block, MTR_MEMO_PAGE_X_FIX));
|
|
ut_ad(trx_id);
|
|
ut_ad(page_is_leaf(buf_block_get_frame(block)));
|
|
|
|
if (page_get_max_trx_id(buf_block_get_frame(block)) < trx_id) {
|
|
|
|
page_set_max_trx_id(block, page_zip, trx_id, mtr);
|
|
}
|
|
}
|
|
|
|
/*************************************************************//**
|
|
Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM).
|
|
@return SPLIT SEQUENCE NUMBER */
|
|
UNIV_INLINE
|
|
node_seq_t
|
|
page_get_ssn_id(
|
|
/*============*/
|
|
const page_t* page) /*!< in: page */
|
|
{
|
|
ut_ad(page);
|
|
|
|
return(static_cast<node_seq_t>(
|
|
mach_read_from_8(page + FIL_RTREE_SPLIT_SEQ_NUM)));
|
|
}
|
|
|
|
/*************************************************************//**
|
|
Sets the RTREE SPLIT SEQUENCE NUMBER field value */
|
|
UNIV_INLINE
|
|
void
|
|
page_set_ssn_id(
|
|
/*============*/
|
|
buf_block_t* block, /*!< in/out: page */
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
uncompressed part will be updated, or NULL */
|
|
node_seq_t ssn_id, /*!< in: transaction id */
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
{
|
|
ut_ad(mtr->memo_contains_flagged(block, MTR_MEMO_PAGE_SX_FIX |
|
|
MTR_MEMO_PAGE_X_FIX));
|
|
ut_ad(!page_zip || page_zip == &block->page.zip);
|
|
constexpr uint16_t field= FIL_RTREE_SPLIT_SEQ_NUM;
|
|
byte *b= my_assume_aligned<2>(&block->page.frame[field]);
|
|
if (mtr->write<8,mtr_t::MAYBE_NOP>(*block, b, ssn_id) &&
|
|
UNIV_LIKELY_NULL(page_zip))
|
|
memcpy_aligned<2>(&page_zip->data[field], b, 8);
|
|
}
|
|
|
|
#endif /* !UNIV_INNOCHECKSUM */
|
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
|
/*************************************************************//**
|
|
Returns the offset stored in the given header field.
|
|
@return offset from the start of the page, or 0 */
|
|
UNIV_INLINE
|
|
uint16_t
|
|
page_header_get_offs(
|
|
/*=================*/
|
|
const page_t* page, /*!< in: page */
|
|
ulint field) /*!< in: PAGE_FREE, ... */
|
|
{
|
|
ut_ad((field == PAGE_FREE)
|
|
|| (field == PAGE_LAST_INSERT)
|
|
|| (field == PAGE_HEAP_TOP));
|
|
|
|
uint16_t offs = page_header_get_field(page, field);
|
|
|
|
ut_ad((field != PAGE_HEAP_TOP) || offs);
|
|
|
|
return(offs);
|
|
}
|
|
|
|
|
|
/**
|
|
Reset PAGE_LAST_INSERT.
|
|
@param[in,out] block file page
|
|
@param[in,out] mtr mini-transaction */
|
|
inline void page_header_reset_last_insert(buf_block_t *block, mtr_t *mtr)
|
|
{
|
|
constexpr uint16_t field= PAGE_HEADER + PAGE_LAST_INSERT;
|
|
byte *b= my_assume_aligned<2>(&block->page.frame[field]);
|
|
if (mtr->write<2,mtr_t::MAYBE_NOP>(*block, b, 0U) &&
|
|
UNIV_LIKELY_NULL(block->page.zip.data))
|
|
memset_aligned<2>(&block->page.zip.data[field], 0, 2);
|
|
}
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/***************************************************************//**
|
|
Returns the heap number of a record.
|
|
@return heap number */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_rec_get_heap_no(
|
|
/*=================*/
|
|
const rec_t* rec) /*!< in: the physical record */
|
|
{
|
|
if (page_rec_is_comp(rec)) {
|
|
return(rec_get_heap_no_new(rec));
|
|
} else {
|
|
return(rec_get_heap_no_old(rec));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/** Determine whether an index page record is a user record.
|
|
@param[in] rec record in an index page
|
|
@return true if a user record */
|
|
inline
|
|
bool
|
|
page_rec_is_user_rec(const rec_t* rec)
|
|
{
|
|
ut_ad(page_rec_check(rec));
|
|
return(page_rec_is_user_rec_low(page_offset(rec)));
|
|
}
|
|
|
|
/** Determine whether an index page record is the supremum record.
|
|
@param[in] rec record in an index page
|
|
@return true if the supremum record */
|
|
inline
|
|
bool
|
|
page_rec_is_supremum(const rec_t* rec)
|
|
{
|
|
ut_ad(page_rec_check(rec));
|
|
return(page_rec_is_supremum_low(page_offset(rec)));
|
|
}
|
|
|
|
/** Determine whether an index page record is the infimum record.
|
|
@param[in] rec record in an index page
|
|
@return true if the infimum record */
|
|
inline
|
|
bool
|
|
page_rec_is_infimum(const rec_t* rec)
|
|
{
|
|
ut_ad(page_rec_check(rec));
|
|
return(page_rec_is_infimum_low(page_offset(rec)));
|
|
}
|
|
|
|
/************************************************************//**
|
|
true if the record is the first user record on a page.
|
|
@return true if the first user record */
|
|
UNIV_INLINE
|
|
bool
|
|
page_rec_is_first(
|
|
/*==============*/
|
|
const rec_t* rec, /*!< in: record */
|
|
const page_t* page) /*!< in: page */
|
|
{
|
|
ut_ad(page_get_n_recs(page) > 0);
|
|
|
|
return(page_rec_get_next_const(page_get_infimum_rec(page)) == rec);
|
|
}
|
|
|
|
/************************************************************//**
|
|
true if the record is the last user record on a page.
|
|
@return true if the last user record */
|
|
UNIV_INLINE
|
|
bool
|
|
page_rec_is_last(
|
|
/*=============*/
|
|
const rec_t* rec, /*!< in: record */
|
|
const page_t* page) /*!< in: page */
|
|
{
|
|
ut_ad(page_get_n_recs(page) > 0);
|
|
|
|
return(page_rec_get_next_const(rec) == page_get_supremum_rec(page));
|
|
}
|
|
|
|
/************************************************************//**
|
|
Returns the middle record of the records on the page. If there is an
|
|
even number of records in the list, returns the first record of the
|
|
upper half-list.
|
|
@return middle record */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_get_middle_rec(
|
|
/*================*/
|
|
page_t* page) /*!< in: page */
|
|
{
|
|
ulint middle = (ulint(page_get_n_recs(page))
|
|
+ PAGE_HEAP_NO_USER_LOW) / 2;
|
|
|
|
return(page_rec_get_nth(page, middle));
|
|
}
|
|
|
|
#endif /* !UNIV_INNOCHECKSUM */
|
|
|
|
/*************************************************************//**
|
|
Gets the page number.
|
|
@return page number */
|
|
UNIV_INLINE
|
|
uint32_t
|
|
page_get_page_no(
|
|
/*=============*/
|
|
const page_t* page) /*!< in: page */
|
|
{
|
|
#ifndef UNIV_INNOCHECKSUM
|
|
ut_ad(page == page_align(page));
|
|
#endif /* !UNIV_INNOCHECKSUM */
|
|
return mach_read_from_4(my_assume_aligned<4>(page + FIL_PAGE_OFFSET));
|
|
}
|
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
|
/*************************************************************//**
|
|
Gets the tablespace identifier.
|
|
@return space id */
|
|
UNIV_INLINE
|
|
uint32_t
|
|
page_get_space_id(
|
|
/*==============*/
|
|
const page_t* page) /*!< in: page */
|
|
{
|
|
ut_ad(page == page_align(page));
|
|
return mach_read_from_4(my_assume_aligned<2>
|
|
(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID));
|
|
}
|
|
|
|
#endif /* !UNIV_INNOCHECKSUM */
|
|
|
|
/*************************************************************//**
|
|
Gets the number of user records on page (infimum and supremum records
|
|
are not user records).
|
|
@return number of user records */
|
|
UNIV_INLINE
|
|
uint16_t
|
|
page_get_n_recs(
|
|
/*============*/
|
|
const page_t* page) /*!< in: index page */
|
|
{
|
|
return(page_header_get_field(page, PAGE_N_RECS));
|
|
}
|
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
|
/*************************************************************//**
|
|
Gets the number of dir slots in directory.
|
|
@return number of slots */
|
|
UNIV_INLINE
|
|
uint16_t
|
|
page_dir_get_n_slots(
|
|
/*=================*/
|
|
const page_t* page) /*!< in: index page */
|
|
{
|
|
return(page_header_get_field(page, PAGE_N_DIR_SLOTS));
|
|
}
|
|
|
|
/*************************************************************//**
|
|
Gets the number of records in the heap.
|
|
@return number of user records */
|
|
UNIV_INLINE
|
|
uint16_t
|
|
page_dir_get_n_heap(
|
|
/*================*/
|
|
const page_t* page) /*!< in: index page */
|
|
{
|
|
return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
|
|
}
|
|
|
|
/**************************************************************//**
|
|
Used to check the consistency of a record on a page.
|
|
@return TRUE if succeed */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_check(
|
|
/*===========*/
|
|
const rec_t* rec) /*!< in: record */
|
|
{
|
|
const page_t* page = page_align(rec);
|
|
|
|
ut_a(rec);
|
|
|
|
ut_a(page_offset(rec) <= page_header_get_field(page, PAGE_HEAP_TOP));
|
|
ut_a(page_offset(rec) >= PAGE_DATA);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/***************************************************************//**
|
|
Gets the number of records owned by a directory slot.
|
|
@return number of records */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_dir_slot_get_n_owned(
|
|
/*======================*/
|
|
const page_dir_slot_t* slot) /*!< in: page directory slot */
|
|
{
|
|
const rec_t* rec = page_dir_slot_get_rec(slot);
|
|
if (page_rec_is_comp(slot)) {
|
|
return(rec_get_n_owned_new(rec));
|
|
} else {
|
|
return(rec_get_n_owned_old(rec));
|
|
}
|
|
}
|
|
|
|
/************************************************************//**
|
|
Calculates the space reserved for directory slots of a given number of
|
|
records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE /
|
|
PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_dir_calc_reserved_space(
|
|
/*=========================*/
|
|
ulint n_recs) /*!< in: number of records */
|
|
{
|
|
return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1)
|
|
/ PAGE_DIR_SLOT_MIN_N_OWNED);
|
|
}
|
|
|
|
/************************************************************//**
|
|
Gets the pointer to the next record on the page.
|
|
@return pointer to next record */
|
|
UNIV_INLINE
|
|
const rec_t*
|
|
page_rec_get_next_low(
|
|
/*==================*/
|
|
const rec_t* rec, /*!< in: pointer to record */
|
|
ulint comp) /*!< in: nonzero=compact page layout */
|
|
{
|
|
const page_t *page= page_align(rec);
|
|
ut_ad(page_rec_check(rec));
|
|
ulint offs= rec_get_next_offs(rec, comp);
|
|
if (UNIV_UNLIKELY(offs < (comp ? PAGE_NEW_SUPREMUM : PAGE_OLD_SUPREMUM)))
|
|
return nullptr;
|
|
if (UNIV_UNLIKELY(offs > page_header_get_field(page, PAGE_HEAP_TOP)))
|
|
return nullptr;
|
|
ut_ad(page_rec_is_infimum(rec) ||
|
|
(!page_is_leaf(page) && !page_has_prev(page)) ||
|
|
!(rec_get_info_bits(page + offs, comp) & REC_INFO_MIN_REC_FLAG));
|
|
return page + offs;
|
|
}
|
|
|
|
/************************************************************//**
|
|
Gets the pointer to the next record on the page.
|
|
@return pointer to next record */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_rec_get_next(
|
|
/*==============*/
|
|
rec_t* rec) /*!< in: pointer to record */
|
|
{
|
|
return((rec_t*) page_rec_get_next_low(rec, page_rec_is_comp(rec)));
|
|
}
|
|
|
|
/************************************************************//**
|
|
Gets the pointer to the next record on the page.
|
|
@return pointer to next record */
|
|
UNIV_INLINE
|
|
const rec_t*
|
|
page_rec_get_next_const(
|
|
/*====================*/
|
|
const rec_t* rec) /*!< in: pointer to record */
|
|
{
|
|
return(page_rec_get_next_low(rec, page_rec_is_comp(rec)));
|
|
}
|
|
#endif /* UNIV_INNOCHECKSUM */
|
|
|
|
/************************************************************//**
|
|
Returns the sum of the sizes of the records in the record list, excluding
|
|
the infimum and supremum records.
|
|
@return data in bytes */
|
|
UNIV_INLINE
|
|
uint16_t
|
|
page_get_data_size(
|
|
/*===============*/
|
|
const page_t* page) /*!< in: index page */
|
|
{
|
|
unsigned ret = page_header_get_field(page, PAGE_HEAP_TOP)
|
|
- (page_is_comp(page)
|
|
? PAGE_NEW_SUPREMUM_END
|
|
: PAGE_OLD_SUPREMUM_END)
|
|
- page_header_get_field(page, PAGE_GARBAGE);
|
|
ut_ad(ret < srv_page_size);
|
|
return static_cast<uint16_t>(ret);
|
|
}
|
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
|
/*************************************************************//**
|
|
Calculates free space if a page is emptied.
|
|
@return free space */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_free_space_of_empty(
|
|
/*=========================*/
|
|
ulint comp) /*!< in: nonzero=compact page layout */
|
|
{
|
|
if (comp) {
|
|
return((ulint)(srv_page_size
|
|
- PAGE_NEW_SUPREMUM_END
|
|
- PAGE_DIR
|
|
- 2 * PAGE_DIR_SLOT_SIZE));
|
|
}
|
|
|
|
return((ulint)(srv_page_size
|
|
- PAGE_OLD_SUPREMUM_END
|
|
- PAGE_DIR
|
|
- 2 * PAGE_DIR_SLOT_SIZE));
|
|
}
|
|
|
|
/************************************************************//**
|
|
Each user record on a page, and also the deleted user records in the heap
|
|
takes its size plus the fraction of the dir cell size /
|
|
PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the
|
|
value of page_get_free_space_of_empty, the insert is impossible, otherwise
|
|
it is allowed. This function returns the maximum combined size of records
|
|
which can be inserted on top of the record heap.
|
|
@return maximum combined size for inserted records */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_max_insert_size(
|
|
/*=====================*/
|
|
const page_t* page, /*!< in: index page */
|
|
ulint n_recs) /*!< in: number of records */
|
|
{
|
|
ulint occupied;
|
|
ulint free_space;
|
|
|
|
if (page_is_comp(page)) {
|
|
occupied = page_header_get_field(page, PAGE_HEAP_TOP)
|
|
- PAGE_NEW_SUPREMUM_END
|
|
+ page_dir_calc_reserved_space(
|
|
n_recs + page_dir_get_n_heap(page) - 2);
|
|
|
|
free_space = page_get_free_space_of_empty(TRUE);
|
|
} else {
|
|
occupied = page_header_get_field(page, PAGE_HEAP_TOP)
|
|
- PAGE_OLD_SUPREMUM_END
|
|
+ page_dir_calc_reserved_space(
|
|
n_recs + page_dir_get_n_heap(page) - 2);
|
|
|
|
free_space = page_get_free_space_of_empty(FALSE);
|
|
}
|
|
|
|
/* Above the 'n_recs +' part reserves directory space for the new
|
|
inserted records; the '- 2' excludes page infimum and supremum
|
|
records */
|
|
|
|
if (occupied > free_space) {
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(free_space - occupied);
|
|
}
|
|
|
|
/************************************************************//**
|
|
Returns the maximum combined size of records which can be inserted on top
|
|
of the record heap if a page is first reorganized.
|
|
@return maximum combined size for inserted records */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_max_insert_size_after_reorganize(
|
|
/*======================================*/
|
|
const page_t* page, /*!< in: index page */
|
|
ulint n_recs) /*!< in: number of records */
|
|
{
|
|
ulint occupied;
|
|
ulint free_space;
|
|
|
|
occupied = page_get_data_size(page)
|
|
+ page_dir_calc_reserved_space(n_recs + page_get_n_recs(page));
|
|
|
|
free_space = page_get_free_space_of_empty(page_is_comp(page));
|
|
|
|
if (occupied > free_space) {
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(free_space - occupied);
|
|
}
|
|
|
|
/** Read the PAGE_DIRECTION field from a byte.
|
|
@param[in] ptr pointer to PAGE_DIRECTION_B
|
|
@return the value of the PAGE_DIRECTION field */
|
|
inline
|
|
byte
|
|
page_ptr_get_direction(const byte* ptr)
|
|
{
|
|
ut_ad(page_offset(ptr) == PAGE_HEADER + PAGE_DIRECTION_B);
|
|
return *ptr & ((1U << 3) - 1);
|
|
}
|
|
|
|
/** Read the PAGE_INSTANT field.
|
|
@param[in] page index page
|
|
@return the value of the PAGE_INSTANT field */
|
|
inline
|
|
uint16_t
|
|
page_get_instant(const page_t* page)
|
|
{
|
|
uint16_t i = page_header_get_field(page, PAGE_INSTANT);
|
|
#ifdef UNIV_DEBUG
|
|
switch (fil_page_get_type(page)) {
|
|
case FIL_PAGE_TYPE_INSTANT:
|
|
ut_ad(page_get_direction(page) <= PAGE_NO_DIRECTION);
|
|
ut_ad(i >> 3);
|
|
break;
|
|
case FIL_PAGE_INDEX:
|
|
ut_ad(i <= PAGE_NO_DIRECTION || !page_is_comp(page));
|
|
break;
|
|
case FIL_PAGE_RTREE:
|
|
ut_ad(i <= PAGE_NO_DIRECTION);
|
|
break;
|
|
default:
|
|
ut_ad("invalid page type" == 0);
|
|
break;
|
|
}
|
|
#endif /* UNIV_DEBUG */
|
|
return static_cast<uint16_t>(i >> 3); /* i / 8 */
|
|
}
|
|
#endif /* !UNIV_INNOCHECKSUM */
|