2014-02-26 19:11:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved.
|
MDEV-11831 Make InnoDB mini-transaction memo checks stricter
InnoDB keeps track of buffer-fixed buf_block_t or acquired rw_lock_t
within a mini-transaction. There are some memo_contains assertions
in the code that document when certain blocks or rw_locks must be held.
But, these assertions only check the mini-transaction memo, not the fact
whether the rw_lock_t are actually being held by the caller.
btr_pcur_store_position(): Remove #ifdef, and assert that the block
is always buffer-fixed.
rtr_pcur_getnext_from_path(), rtr_pcur_open_low(),
ibuf_rec_get_page_no_func(), ibuf_rec_get_space_func(),
ibuf_rec_get_info_func(), ibuf_rec_get_op_type_func(),
ibuf_build_entry_from_ibuf_rec_func(), ibuf_rec_get_volume_func(),
ibuf_get_merge_page_nos_func(), ibuf_get_volume_buffered_count_func()
ibuf_get_entry_counter_low_func(), page_set_ssn_id(),
row_vers_old_has_index_entry(), row_vers_build_for_consistent_read(),
row_vers_build_for_semi_consistent_read(),
trx_undo_prev_version_build():
Make use of mtr_memo_contains_page_flagged().
mtr_t::memo_contains(): Take a const memo. Assert rw_lock_own().
FindPage, FlaggedCheck: Assert rw_lock_own_flagged().
2017-01-18 12:53:35 +02:00
|
|
|
Copyright (c) 2016, 2017, MariaDB Corporation.
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
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/page0page.ic
|
|
|
|
Index page routines
|
|
|
|
|
|
|
|
Created 2/2/1994 Heikki Tuuri
|
|
|
|
*******************************************************/
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
#ifndef page0page_ic
|
|
|
|
#define page0page_ic
|
2015-01-19 12:39:17 +02:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
#include "mach0data.h"
|
2016-08-12 11:17:45 +03:00
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
2014-02-26 19:11:54 +01:00
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
# include "log0recv.h"
|
|
|
|
#endif /* !UNIV_DEBUG */
|
2016-12-30 15:04:10 +02:00
|
|
|
#include "rem0cmp.h"
|
2014-02-26 19:11:54 +01:00
|
|
|
#include "mtr0log.h"
|
|
|
|
#include "page0zip.h"
|
|
|
|
|
|
|
|
#ifdef UNIV_MATERIALIZE
|
|
|
|
#undef UNIV_INLINE
|
|
|
|
#define UNIV_INLINE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the start of a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return start of the page */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
page_t*
|
|
|
|
page_align(
|
|
|
|
/*=======*/
|
|
|
|
const void* ptr) /*!< in: pointer to page frame */
|
|
|
|
{
|
|
|
|
return((page_t*) ut_align_down(ptr, UNIV_PAGE_SIZE));
|
|
|
|
}
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/************************************************************//**
|
|
|
|
Gets the offset within a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return offset from the start of the page */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_offset(
|
|
|
|
/*========*/
|
|
|
|
const void* ptr) /*!< in: pointer to page frame */
|
|
|
|
{
|
|
|
|
return(ut_align_offset(ptr, UNIV_PAGE_SIZE));
|
|
|
|
}
|
|
|
|
/*************************************************************//**
|
|
|
|
Returns the max trx id field value. */
|
|
|
|
UNIV_INLINE
|
|
|
|
trx_id_t
|
|
|
|
page_get_max_trx_id(
|
|
|
|
/*================*/
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
ut_ad(page);
|
|
|
|
|
|
|
|
return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
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(mtr, block, MTR_MEMO_PAGE_X_FIX));
|
|
|
|
/* During crash recovery, this function may be called on
|
|
|
|
something else than a leaf page of a secondary index or the
|
|
|
|
insert buffer index tree (dict_index_is_sec_or_ibuf() returns
|
|
|
|
TRUE for the dummy indexes constructed during redo log
|
|
|
|
application). In that case, PAGE_MAX_TRX_ID is unused,
|
|
|
|
and trx_id is usually zero. */
|
|
|
|
ut_ad(trx_id || recv_recovery_is_on());
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
MDEV-6076 Persistent AUTO_INCREMENT for InnoDB
This should be functionally equivalent to WL#6204 in MySQL 8.0.0, with
the notable difference that the file format changes are limited to
repurposing a previously unused data field in B-tree pages.
For persistent InnoDB tables, write the last used AUTO_INCREMENT
value to the root page of the clustered index, in the previously
unused (0) PAGE_MAX_TRX_ID field, now aliased as PAGE_ROOT_AUTO_INC.
Unlike some other previously unused InnoDB data fields, this one was
actually always zero-initialized, at least since MySQL 3.23.49.
The writes to PAGE_ROOT_AUTO_INC are protected by SX or X latch on the
root page. The SX latch will allow concurrent read access to the root
page. (The field PAGE_ROOT_AUTO_INC will only be read on the
first-time call to ha_innobase::open() from the SQL layer. The
PAGE_ROOT_AUTO_INC can only be updated when executing SQL, so
read/write races are not possible.)
During INSERT, the PAGE_ROOT_AUTO_INC is updated by the low-level
function btr_cur_search_to_nth_level(), adding no extra page
access. [Adaptive hash index lookup will be disabled during INSERT.]
If some rare UPDATE modifies an AUTO_INCREMENT column, the
PAGE_ROOT_AUTO_INC will be adjusted in a separate mini-transaction in
ha_innobase::update_row().
When a page is reorganized, we have to preserve the PAGE_ROOT_AUTO_INC
field.
During ALTER TABLE, the initial AUTO_INCREMENT value will be copied
from the table. ALGORITHM=COPY and online log apply in LOCK=NONE will
update PAGE_ROOT_AUTO_INC in real time.
innodb_col_no(): Determine the dict_table_t::cols[] element index
corresponding to a Field of a non-virtual column.
(The MySQL 5.7 implementation of virtual columns breaks the 1:1
relationship between Field::field_index and dict_table_t::cols[].
Virtual columns are omitted from dict_table_t::cols[]. Therefore,
we must translate the field_index of AUTO_INCREMENT columns into
an index of dict_table_t::cols[].)
Upgrade from old data files:
By default, the AUTO_INCREMENT sequence in old data files would appear
to be reset, because PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC would contain
the value 0 in each clustered index page. In new data files,
PAGE_ROOT_AUTO_INC can only be 0 if the table is empty or does not contain
any AUTO_INCREMENT column.
For backward compatibility, we use the old method of
SELECT MAX(auto_increment_column) for initializing the sequence.
btr_read_autoinc(): Read the AUTO_INCREMENT sequence from a new-format
data file.
btr_read_autoinc_with_fallback(): A variant of btr_read_autoinc()
that will resort to reading MAX(auto_increment_column) for data files
that did not use AUTO_INCREMENT yet. It was manually tested that during
the execution of innodb.autoinc_persist the compatibility logic is
not activated (for new files, PAGE_ROOT_AUTO_INC is never 0 in nonempty
clustered index root pages).
initialize_auto_increment(): Replaces
ha_innobase::innobase_initialize_autoinc(). This initializes
the AUTO_INCREMENT metadata. Only called from ha_innobase::open().
ha_innobase::info_low(): Do not try to lazily initialize
dict_table_t::autoinc. It must already have been initialized by
ha_innobase::open() or ha_innobase::create().
Note: The adjustments to class ha_innopart were not tested, because
the source code (native InnoDB partitioning) is not being compiled.
2016-12-14 19:56:39 +02:00
|
|
|
/** Read the AUTO_INCREMENT value from a clustered index root page.
|
|
|
|
@param[in] page clustered index root page
|
|
|
|
@return the persisted AUTO_INCREMENT value */
|
|
|
|
UNIV_INLINE
|
|
|
|
ib_uint64_t
|
|
|
|
page_get_autoinc(const page_t* page)
|
|
|
|
{
|
|
|
|
ut_ad(page_is_root(page));
|
|
|
|
return(mach_read_from_8(PAGE_HEADER + PAGE_ROOT_AUTO_INC + page));
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/*************************************************************//**
|
|
|
|
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 */
|
|
|
|
{
|
|
|
|
page_t* page = buf_block_get_frame(block);
|
2016-12-30 15:04:10 +02:00
|
|
|
|
MDEV-11831 Make InnoDB mini-transaction memo checks stricter
InnoDB keeps track of buffer-fixed buf_block_t or acquired rw_lock_t
within a mini-transaction. There are some memo_contains assertions
in the code that document when certain blocks or rw_locks must be held.
But, these assertions only check the mini-transaction memo, not the fact
whether the rw_lock_t are actually being held by the caller.
btr_pcur_store_position(): Remove #ifdef, and assert that the block
is always buffer-fixed.
rtr_pcur_getnext_from_path(), rtr_pcur_open_low(),
ibuf_rec_get_page_no_func(), ibuf_rec_get_space_func(),
ibuf_rec_get_info_func(), ibuf_rec_get_op_type_func(),
ibuf_build_entry_from_ibuf_rec_func(), ibuf_rec_get_volume_func(),
ibuf_get_merge_page_nos_func(), ibuf_get_volume_buffered_count_func()
ibuf_get_entry_counter_low_func(), page_set_ssn_id(),
row_vers_old_has_index_entry(), row_vers_build_for_consistent_read(),
row_vers_build_for_semi_consistent_read(),
trx_undo_prev_version_build():
Make use of mtr_memo_contains_page_flagged().
mtr_t::memo_contains(): Take a const memo. Assert rw_lock_own().
FindPage, FlaggedCheck: Assert rw_lock_own_flagged().
2017-01-18 12:53:35 +02:00
|
|
|
ut_ad(!mtr || mtr_memo_contains_flagged(mtr, block,
|
|
|
|
MTR_MEMO_PAGE_SX_FIX
|
|
|
|
| MTR_MEMO_PAGE_X_FIX));
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
if (page_zip) {
|
|
|
|
mach_write_to_8(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id);
|
|
|
|
page_zip_write_header(page_zip,
|
|
|
|
page + FIL_RTREE_SPLIT_SEQ_NUM,
|
|
|
|
8, mtr);
|
|
|
|
} else if (mtr) {
|
|
|
|
mlog_write_ull(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id, mtr);
|
|
|
|
} else {
|
|
|
|
mach_write_to_8(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-19 12:39:17 +02:00
|
|
|
#endif /* !UNIV_INNOCHECKSUM */
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*************************************************************//**
|
|
|
|
Reads the given header field. */
|
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_header_get_field(
|
|
|
|
/*==================*/
|
|
|
|
const page_t* page, /*!< in: page */
|
|
|
|
ulint field) /*!< in: PAGE_LEVEL, ... */
|
|
|
|
{
|
|
|
|
ut_ad(page);
|
|
|
|
ut_ad(field <= PAGE_INDEX_ID);
|
|
|
|
|
|
|
|
return(mach_read_from_2(page + PAGE_HEADER + field));
|
|
|
|
}
|
|
|
|
|
2015-01-19 12:39:17 +02:00
|
|
|
#ifndef UNIV_INNOCHECKSUM
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*************************************************************//**
|
|
|
|
Sets the given header field. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_header_set_field(
|
|
|
|
/*==================*/
|
|
|
|
page_t* page, /*!< in/out: page */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
|
|
uncompressed part will be updated, or NULL */
|
|
|
|
ulint field, /*!< in: PAGE_N_DIR_SLOTS, ... */
|
|
|
|
ulint val) /*!< in: value */
|
|
|
|
{
|
|
|
|
ut_ad(page);
|
|
|
|
ut_ad(field <= PAGE_N_RECS);
|
|
|
|
ut_ad(field == PAGE_N_HEAP || val < UNIV_PAGE_SIZE);
|
|
|
|
ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < UNIV_PAGE_SIZE);
|
|
|
|
|
|
|
|
mach_write_to_2(page + PAGE_HEADER + field, val);
|
|
|
|
if (page_zip) {
|
|
|
|
page_zip_write_header(page_zip,
|
|
|
|
page + PAGE_HEADER + field, 2, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Returns the offset stored in the given header field.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return offset from the start of the page, or 0 */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_header_get_offs(
|
|
|
|
/*=================*/
|
|
|
|
const page_t* page, /*!< in: page */
|
|
|
|
ulint field) /*!< in: PAGE_FREE, ... */
|
|
|
|
{
|
|
|
|
ulint offs;
|
|
|
|
|
|
|
|
ut_ad(page);
|
|
|
|
ut_ad((field == PAGE_FREE)
|
|
|
|
|| (field == PAGE_LAST_INSERT)
|
|
|
|
|| (field == PAGE_HEAP_TOP));
|
|
|
|
|
|
|
|
offs = page_header_get_field(page, field);
|
|
|
|
|
|
|
|
ut_ad((field != PAGE_HEAP_TOP) || offs);
|
|
|
|
|
|
|
|
return(offs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Sets the pointer stored in the given header field. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_header_set_ptr(
|
|
|
|
/*================*/
|
|
|
|
page_t* page, /*!< in: page */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
|
|
uncompressed part will be updated, or NULL */
|
|
|
|
ulint field, /*!< in: PAGE_FREE, ... */
|
|
|
|
const byte* ptr) /*!< in: pointer or NULL*/
|
|
|
|
{
|
|
|
|
ulint offs;
|
|
|
|
|
|
|
|
ut_ad(page);
|
|
|
|
ut_ad((field == PAGE_FREE)
|
|
|
|
|| (field == PAGE_LAST_INSERT)
|
|
|
|
|| (field == PAGE_HEAP_TOP));
|
|
|
|
|
|
|
|
if (ptr == NULL) {
|
|
|
|
offs = 0;
|
|
|
|
} else {
|
|
|
|
offs = ptr - page;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad((field != PAGE_HEAP_TOP) || offs);
|
|
|
|
|
|
|
|
page_header_set_field(page, page_zip, field, offs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Resets the last insert info field in the page header. Writes to mlog
|
|
|
|
about this operation. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_header_reset_last_insert(
|
|
|
|
/*==========================*/
|
|
|
|
page_t* page, /*!< in/out: page */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
|
|
uncompressed part will be updated, or NULL */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(page != NULL);
|
|
|
|
ut_ad(mtr != NULL);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_zip) {
|
|
|
|
mach_write_to_2(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0);
|
|
|
|
page_zip_write_header(page_zip,
|
|
|
|
page + (PAGE_HEADER + PAGE_LAST_INSERT),
|
|
|
|
2, mtr);
|
|
|
|
} else {
|
|
|
|
mlog_write_ulint(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0,
|
|
|
|
MLOG_2BYTES, mtr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Determine whether the page is in new-style compact format.
|
|
|
|
@return nonzero if the page is in compact format, zero if it is in
|
|
|
|
old-style format */
|
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_is_comp(
|
|
|
|
/*=========*/
|
|
|
|
const page_t* page) /*!< in: index page */
|
|
|
|
{
|
|
|
|
return(page_header_get_field(page, PAGE_N_HEAP) & 0x8000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is on a page in compact format.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return nonzero if in compact format */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_rec_is_comp(
|
|
|
|
/*=============*/
|
|
|
|
const rec_t* rec) /*!< in: record */
|
|
|
|
{
|
|
|
|
return(page_is_comp(page_align(rec)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************//**
|
|
|
|
Returns the heap number of a record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return heap number */
|
2014-02-26 19:11:54 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Determine whether the page is a B-tree leaf.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return true if the page is a B-tree leaf (PAGE_LEVEL = 0) */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
bool
|
|
|
|
page_is_leaf(
|
|
|
|
/*=========*/
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
return(!*(const uint16*) (page + (PAGE_HEADER + PAGE_LEVEL)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Determine whether the page is empty.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return true if the page is empty (PAGE_N_RECS = 0) */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
bool
|
|
|
|
page_is_empty(
|
|
|
|
/*==========*/
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
return(!*(const uint16*) (page + (PAGE_HEADER + PAGE_N_RECS)));
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Determine whether a page is an index root page.
|
|
|
|
@param[in] page page frame
|
|
|
|
@return true if the page is a root page of an index */
|
|
|
|
UNIV_INLINE
|
|
|
|
bool
|
|
|
|
page_is_root(
|
|
|
|
const page_t* page)
|
|
|
|
{
|
|
|
|
#if FIL_PAGE_PREV % 8
|
|
|
|
# error FIL_PAGE_PREV must be 64-bit aligned
|
|
|
|
#endif
|
|
|
|
#if FIL_PAGE_NEXT != FIL_PAGE_PREV + 4
|
|
|
|
# error FIL_PAGE_NEXT must be adjacent to FIL_PAGE_PREV
|
|
|
|
#endif
|
|
|
|
#if FIL_NULL != 0xffffffff
|
|
|
|
# error FIL_NULL != 0xffffffff
|
|
|
|
#endif
|
|
|
|
/* Check that this is an index page and both the PREV and NEXT
|
|
|
|
pointers are FIL_NULL, because the root page does not have any
|
|
|
|
siblings. */
|
|
|
|
return(fil_page_index_page_check(page)
|
|
|
|
&& *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_PREV)
|
|
|
|
== IB_UINT64_MAX);
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/************************************************************//**
|
|
|
|
Determine whether the page contains garbage.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return true if the page contains garbage (PAGE_GARBAGE is not 0) */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
bool
|
|
|
|
page_has_garbage(
|
|
|
|
/*=============*/
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
return(!!*(const uint16*) (page + (PAGE_HEADER + PAGE_GARBAGE)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the offset of the first record on the page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return offset of the first record in record list, relative from page */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_infimum_offset(
|
|
|
|
/*====================*/
|
|
|
|
const page_t* page) /*!< in: page which must have record(s) */
|
|
|
|
{
|
|
|
|
ut_ad(page);
|
|
|
|
ut_ad(!page_offset(page));
|
|
|
|
|
|
|
|
if (page_is_comp(page)) {
|
|
|
|
return(PAGE_NEW_INFIMUM);
|
|
|
|
} else {
|
|
|
|
return(PAGE_OLD_INFIMUM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the offset of the last record on the page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return offset of the last record in record list, relative from page */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_supremum_offset(
|
|
|
|
/*=====================*/
|
|
|
|
const page_t* page) /*!< in: page which must have record(s) */
|
|
|
|
{
|
|
|
|
ut_ad(page);
|
|
|
|
ut_ad(!page_offset(page));
|
|
|
|
|
|
|
|
if (page_is_comp(page)) {
|
|
|
|
return(PAGE_NEW_SUPREMUM);
|
|
|
|
} else {
|
|
|
|
return(PAGE_OLD_SUPREMUM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is a user record on the page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if a user record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_rec_is_user_rec_low(
|
|
|
|
/*=====================*/
|
|
|
|
ulint offset) /*!< in: record offset on page */
|
|
|
|
{
|
|
|
|
ut_ad(offset >= PAGE_NEW_INFIMUM);
|
|
|
|
#if PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM
|
|
|
|
# error "PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM"
|
|
|
|
#endif
|
|
|
|
#if PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM
|
|
|
|
# error "PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM"
|
|
|
|
#endif
|
|
|
|
#if PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM
|
|
|
|
# error "PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM"
|
|
|
|
#endif
|
|
|
|
#if PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM
|
|
|
|
# error "PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM"
|
|
|
|
#endif
|
|
|
|
#if PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END
|
|
|
|
# error "PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END"
|
|
|
|
#endif
|
|
|
|
#if PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END
|
|
|
|
# error "PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END"
|
|
|
|
#endif
|
|
|
|
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
|
|
|
|
|
|
|
|
return(offset != PAGE_NEW_SUPREMUM
|
|
|
|
&& offset != PAGE_NEW_INFIMUM
|
|
|
|
&& offset != PAGE_OLD_INFIMUM
|
|
|
|
&& offset != PAGE_OLD_SUPREMUM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is the supremum record on a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if the supremum record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_rec_is_supremum_low(
|
|
|
|
/*=====================*/
|
|
|
|
ulint offset) /*!< in: record offset on page */
|
|
|
|
{
|
|
|
|
ut_ad(offset >= PAGE_NEW_INFIMUM);
|
|
|
|
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
|
|
|
|
|
|
|
|
return(offset == PAGE_NEW_SUPREMUM
|
|
|
|
|| offset == PAGE_OLD_SUPREMUM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is the infimum record on a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if the infimum record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_rec_is_infimum_low(
|
|
|
|
/*====================*/
|
|
|
|
ulint offset) /*!< in: record offset on page */
|
|
|
|
{
|
|
|
|
ut_ad(offset >= PAGE_NEW_INFIMUM);
|
|
|
|
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
|
|
|
|
|
|
|
|
return(offset == PAGE_NEW_INFIMUM || offset == PAGE_OLD_INFIMUM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is a user record on the page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if a user record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_rec_is_user_rec(
|
|
|
|
/*=================*/
|
|
|
|
const rec_t* rec) /*!< in: record */
|
|
|
|
{
|
2014-05-05 18:20:28 +02:00
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
return(page_rec_is_user_rec_low(page_offset(rec)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is the supremum record on a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if the supremum record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_rec_is_supremum(
|
|
|
|
/*=================*/
|
|
|
|
const rec_t* rec) /*!< in: record */
|
|
|
|
{
|
2014-05-05 18:20:28 +02:00
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
return(page_rec_is_supremum_low(page_offset(rec)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
TRUE if the record is the infimum record on a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if the infimum record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_rec_is_infimum(
|
|
|
|
/*================*/
|
|
|
|
const rec_t* rec) /*!< in: record */
|
|
|
|
{
|
2014-05-05 18:20:28 +02:00
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
return(page_rec_is_infimum_low(page_offset(rec)));
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/************************************************************//**
|
|
|
|
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 second user record on a page.
|
|
|
|
@return true if the second user record */
|
|
|
|
UNIV_INLINE
|
|
|
|
bool
|
|
|
|
page_rec_is_second(
|
|
|
|
/*===============*/
|
|
|
|
const rec_t* rec, /*!< in: record */
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
ut_ad(page_get_n_recs(page) > 1);
|
|
|
|
|
|
|
|
return(page_rec_get_next_const(
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
true if the record is the second last user record on a page.
|
|
|
|
@return true if the second last user record */
|
|
|
|
UNIV_INLINE
|
|
|
|
bool
|
|
|
|
page_rec_is_second_last(
|
|
|
|
/*====================*/
|
|
|
|
const rec_t* rec, /*!< in: record */
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
ut_ad(page_get_n_recs(page) > 1);
|
|
|
|
ut_ad(!page_rec_is_last(rec, page));
|
|
|
|
|
|
|
|
return(page_rec_get_next_const(
|
|
|
|
page_rec_get_next_const(rec)) == page_get_supremum_rec(page));
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/************************************************************//**
|
|
|
|
Returns the nth record of the record list.
|
|
|
|
This is the inverse function of page_rec_get_n_recs_before().
|
2016-08-12 11:17:45 +03:00
|
|
|
@return nth record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
rec_t*
|
|
|
|
page_rec_get_nth(
|
|
|
|
/*=============*/
|
|
|
|
page_t* page, /*!< in: page */
|
|
|
|
ulint nth) /*!< in: nth record */
|
|
|
|
{
|
|
|
|
return((rec_t*) page_rec_get_nth_const(page, nth));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
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.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return middle record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
rec_t*
|
|
|
|
page_get_middle_rec(
|
|
|
|
/*================*/
|
|
|
|
page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
ulint middle = (page_get_n_recs(page) + PAGE_HEAP_NO_USER_LOW) / 2;
|
|
|
|
|
|
|
|
return(page_rec_get_nth(page, middle));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Gets the page number.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return page number */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_page_no(
|
|
|
|
/*=============*/
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
ut_ad(page == page_align((page_t*) page));
|
|
|
|
return(mach_read_from_4(page + FIL_PAGE_OFFSET));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Gets the tablespace identifier.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return space id */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_space_id(
|
|
|
|
/*==============*/
|
|
|
|
const page_t* page) /*!< in: page */
|
|
|
|
{
|
|
|
|
ut_ad(page == page_align((page_t*) page));
|
|
|
|
return(mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Gets the number of user records on page (infimum and supremum records
|
|
|
|
are not user records).
|
2016-08-12 11:17:45 +03:00
|
|
|
@return number of user records */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_n_recs(
|
|
|
|
/*============*/
|
|
|
|
const page_t* page) /*!< in: index page */
|
|
|
|
{
|
|
|
|
return(page_header_get_field(page, PAGE_N_RECS));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Gets the number of dir slots in directory.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return number of slots */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_dir_get_n_slots(
|
|
|
|
/*=================*/
|
|
|
|
const page_t* page) /*!< in: index page */
|
|
|
|
{
|
|
|
|
return(page_header_get_field(page, PAGE_N_DIR_SLOTS));
|
|
|
|
}
|
|
|
|
/*************************************************************//**
|
|
|
|
Sets the number of dir slots in directory. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_dir_set_n_slots(
|
|
|
|
/*=================*/
|
|
|
|
page_t* page, /*!< in/out: page */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
|
|
uncompressed part will be updated, or NULL */
|
|
|
|
ulint n_slots)/*!< in: number of slots */
|
|
|
|
{
|
|
|
|
page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Gets the number of records in the heap.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return number of user records */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_dir_get_n_heap(
|
|
|
|
/*================*/
|
|
|
|
const page_t* page) /*!< in: index page */
|
|
|
|
{
|
|
|
|
return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Sets the number of records in the heap. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_dir_set_n_heap(
|
|
|
|
/*================*/
|
|
|
|
page_t* page, /*!< in/out: index page */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
|
|
|
|
uncompressed part will be updated, or NULL.
|
|
|
|
Note that the size of the dense page directory
|
|
|
|
in the compressed page trailer is
|
|
|
|
n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */
|
|
|
|
ulint n_heap) /*!< in: number of records */
|
|
|
|
{
|
|
|
|
ut_ad(n_heap < 0x8000);
|
|
|
|
ut_ad(!page_zip || n_heap
|
|
|
|
== (page_header_get_field(page, PAGE_N_HEAP) & 0x7fff) + 1);
|
|
|
|
|
|
|
|
page_header_set_field(page, page_zip, PAGE_N_HEAP, n_heap
|
|
|
|
| (0x8000
|
|
|
|
& page_header_get_field(page, PAGE_N_HEAP)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
/*************************************************************//**
|
|
|
|
Gets pointer to nth directory slot.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to dir slot */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
page_dir_slot_t*
|
|
|
|
page_dir_get_nth_slot(
|
|
|
|
/*==================*/
|
|
|
|
const page_t* page, /*!< in: index page */
|
|
|
|
ulint n) /*!< in: position */
|
|
|
|
{
|
|
|
|
ut_ad(page_dir_get_n_slots(page) > n);
|
|
|
|
|
|
|
|
return((page_dir_slot_t*)
|
|
|
|
page + UNIV_PAGE_SIZE - PAGE_DIR
|
|
|
|
- (n + 1) * PAGE_DIR_SLOT_SIZE);
|
|
|
|
}
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Used to check the consistency of a record on a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if succeed */
|
2014-02-26 19:11:54 +01:00
|
|
|
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 record pointed to by a directory slot.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
const rec_t*
|
|
|
|
page_dir_slot_get_rec(
|
|
|
|
/*==================*/
|
|
|
|
const page_dir_slot_t* slot) /*!< in: directory slot */
|
|
|
|
{
|
|
|
|
return(page_align(slot) + mach_read_from_2(slot));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************//**
|
|
|
|
This is used to set the record offset in a directory slot. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_dir_slot_set_rec(
|
|
|
|
/*==================*/
|
|
|
|
page_dir_slot_t* slot, /*!< in: directory slot */
|
|
|
|
rec_t* rec) /*!< in: record on the page */
|
|
|
|
{
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
|
|
|
mach_write_to_2(slot, page_offset(rec));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************//**
|
|
|
|
Gets the number of records owned by a directory slot.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return number of records */
|
2014-02-26 19:11:54 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************//**
|
|
|
|
This is used to set the owned records field of a directory slot. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_dir_slot_set_n_owned(
|
|
|
|
/*======================*/
|
|
|
|
page_dir_slot_t*slot, /*!< in/out: directory slot */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
|
|
|
|
ulint n) /*!< in: number of records owned by the slot */
|
|
|
|
{
|
|
|
|
rec_t* rec = (rec_t*) page_dir_slot_get_rec(slot);
|
|
|
|
if (page_rec_is_comp(slot)) {
|
|
|
|
rec_set_n_owned_new(rec, page_zip, n);
|
|
|
|
} else {
|
|
|
|
ut_ad(!page_zip);
|
|
|
|
rec_set_n_owned_old(rec, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
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.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to next record */
|
2014-02-26 19:11:54 +01:00
|
|
|
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 */
|
|
|
|
{
|
|
|
|
ulint offs;
|
|
|
|
const page_t* page;
|
|
|
|
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
|
|
|
page = page_align(rec);
|
|
|
|
|
|
|
|
offs = rec_get_next_offs(rec, comp);
|
|
|
|
|
|
|
|
if (offs >= UNIV_PAGE_SIZE) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"InnoDB: Next record offset is nonsensical %lu"
|
|
|
|
" in record at offset %lu\n"
|
|
|
|
"InnoDB: rec address %p, space id %lu, page %lu\n",
|
|
|
|
(ulong) offs, (ulong) page_offset(rec),
|
|
|
|
(void*) rec,
|
|
|
|
(ulong) page_get_space_id(page),
|
|
|
|
(ulong) page_get_page_no(page));
|
|
|
|
ut_error;
|
|
|
|
} else if (offs == 0) {
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(page + offs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the pointer to the next record on the page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to next record */
|
2014-02-26 19:11:54 +01:00
|
|
|
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.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to next record */
|
2014-02-26 19:11:54 +01:00
|
|
|
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)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the pointer to the next non delete-marked record on the page.
|
|
|
|
If all subsequent records are delete-marked, then this function
|
|
|
|
will return the supremum record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to next non delete-marked record or pointer to supremum */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
const rec_t*
|
|
|
|
page_rec_get_next_non_del_marked(
|
|
|
|
/*=============================*/
|
|
|
|
const rec_t* rec) /*!< in: pointer to record */
|
|
|
|
{
|
|
|
|
const rec_t* r;
|
|
|
|
ulint page_is_compact = page_rec_is_comp(rec);
|
|
|
|
|
|
|
|
for (r = page_rec_get_next_const(rec);
|
|
|
|
!page_rec_is_supremum(r)
|
|
|
|
&& rec_get_deleted_flag(r, page_is_compact);
|
|
|
|
r = page_rec_get_next_const(r)) {
|
|
|
|
/* noop */
|
|
|
|
}
|
|
|
|
|
|
|
|
return(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Sets the pointer to the next record on the page. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_rec_set_next(
|
|
|
|
/*==============*/
|
|
|
|
rec_t* rec, /*!< in: pointer to record,
|
|
|
|
must not be page supremum */
|
|
|
|
const rec_t* next) /*!< in: pointer to next record,
|
|
|
|
must not be page infimum */
|
|
|
|
{
|
|
|
|
ulint offs;
|
|
|
|
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
ut_ad(!page_rec_is_supremum(rec));
|
|
|
|
ut_ad(rec != next);
|
|
|
|
|
|
|
|
ut_ad(!next || !page_rec_is_infimum(next));
|
|
|
|
ut_ad(!next || page_align(rec) == page_align(next));
|
|
|
|
|
|
|
|
offs = next != NULL ? page_offset(next) : 0;
|
|
|
|
|
|
|
|
if (page_rec_is_comp(rec)) {
|
|
|
|
rec_set_next_offs_new(rec, offs);
|
|
|
|
} else {
|
|
|
|
rec_set_next_offs_old(rec, offs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the pointer to the previous record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to previous record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
const rec_t*
|
|
|
|
page_rec_get_prev_const(
|
|
|
|
/*====================*/
|
|
|
|
const rec_t* rec) /*!< in: pointer to record, must not be page
|
|
|
|
infimum */
|
|
|
|
{
|
|
|
|
const page_dir_slot_t* slot;
|
|
|
|
ulint slot_no;
|
|
|
|
const rec_t* rec2;
|
|
|
|
const rec_t* prev_rec = NULL;
|
|
|
|
const page_t* page;
|
|
|
|
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
|
|
|
page = page_align(rec);
|
|
|
|
|
|
|
|
ut_ad(!page_rec_is_infimum(rec));
|
|
|
|
|
|
|
|
slot_no = page_dir_find_owner_slot(rec);
|
|
|
|
|
|
|
|
ut_a(slot_no != 0);
|
|
|
|
|
|
|
|
slot = page_dir_get_nth_slot(page, slot_no - 1);
|
|
|
|
|
|
|
|
rec2 = page_dir_slot_get_rec(slot);
|
|
|
|
|
|
|
|
if (page_is_comp(page)) {
|
|
|
|
while (rec != rec2) {
|
|
|
|
prev_rec = rec2;
|
|
|
|
rec2 = page_rec_get_next_low(rec2, TRUE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (rec != rec2) {
|
|
|
|
prev_rec = rec2;
|
|
|
|
rec2 = page_rec_get_next_low(rec2, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(prev_rec);
|
|
|
|
|
|
|
|
return(prev_rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Gets the pointer to the previous record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return pointer to previous record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
rec_t*
|
|
|
|
page_rec_get_prev(
|
|
|
|
/*==============*/
|
|
|
|
rec_t* rec) /*!< in: pointer to record, must not be page
|
|
|
|
infimum */
|
|
|
|
{
|
|
|
|
return((rec_t*) page_rec_get_prev_const(rec));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************//**
|
|
|
|
Looks for the record which owns the given record.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return the owner record */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
rec_t*
|
|
|
|
page_rec_find_owner_rec(
|
|
|
|
/*====================*/
|
|
|
|
rec_t* rec) /*!< in: the physical record */
|
|
|
|
{
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
|
|
|
|
if (page_rec_is_comp(rec)) {
|
|
|
|
while (rec_get_n_owned_new(rec) == 0) {
|
|
|
|
rec = page_rec_get_next(rec);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (rec_get_n_owned_old(rec) == 0) {
|
|
|
|
rec = page_rec_get_next(rec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************//**
|
|
|
|
Returns the base extra size of a physical record. This is the
|
|
|
|
size of the fixed header, independent of the record size.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_rec_get_base_extra_size(
|
|
|
|
/*=========================*/
|
|
|
|
const rec_t* rec) /*!< in: physical record */
|
|
|
|
{
|
|
|
|
#if REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES
|
|
|
|
# error "REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES"
|
|
|
|
#endif
|
|
|
|
return(REC_N_NEW_EXTRA_BYTES + (ulint) !page_rec_is_comp(rec));
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Returns the sum of the sizes of the records in the record list, excluding
|
|
|
|
the infimum and supremum records.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return data in bytes */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_data_size(
|
|
|
|
/*===============*/
|
|
|
|
const page_t* page) /*!< in: index page */
|
|
|
|
{
|
|
|
|
ulint ret;
|
|
|
|
|
|
|
|
ret = (ulint)(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 < UNIV_PAGE_SIZE);
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Allocates a block of memory from the free list of an index page. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_mem_alloc_free(
|
|
|
|
/*================*/
|
|
|
|
page_t* page, /*!< in/out: index page */
|
|
|
|
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
|
|
|
|
space available for inserting the record,
|
|
|
|
or NULL */
|
|
|
|
rec_t* next_rec,/*!< in: pointer to the new head of the
|
|
|
|
free record list */
|
|
|
|
ulint need) /*!< in: number of bytes allocated */
|
|
|
|
{
|
|
|
|
ulint garbage;
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
const rec_t* old_rec = page_header_get_ptr(page, PAGE_FREE);
|
|
|
|
ulint next_offs;
|
|
|
|
|
|
|
|
ut_ad(old_rec);
|
|
|
|
next_offs = rec_get_next_offs(old_rec, page_is_comp(page));
|
|
|
|
ut_ad(next_rec == (next_offs ? page + next_offs : NULL));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
page_header_set_ptr(page, page_zip, PAGE_FREE, next_rec);
|
|
|
|
|
|
|
|
garbage = page_header_get_field(page, PAGE_GARBAGE);
|
|
|
|
ut_ad(garbage >= need);
|
|
|
|
|
|
|
|
page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage - need);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
Calculates free space if a page is emptied.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return free space */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_get_free_space_of_empty(
|
|
|
|
/*=========================*/
|
|
|
|
ulint comp) /*!< in: nonzero=compact page layout */
|
|
|
|
{
|
|
|
|
if (comp) {
|
|
|
|
return((ulint)(UNIV_PAGE_SIZE
|
|
|
|
- PAGE_NEW_SUPREMUM_END
|
|
|
|
- PAGE_DIR
|
|
|
|
- 2 * PAGE_DIR_SLOT_SIZE));
|
|
|
|
}
|
|
|
|
|
|
|
|
return((ulint)(UNIV_PAGE_SIZE
|
|
|
|
- PAGE_OLD_SUPREMUM_END
|
|
|
|
- PAGE_DIR
|
|
|
|
- 2 * PAGE_DIR_SLOT_SIZE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
Write a 32-bit field in a data dictionary record. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_rec_write_field(
|
|
|
|
/*=================*/
|
|
|
|
rec_t* rec, /*!< in/out: record to update */
|
|
|
|
ulint i, /*!< in: index of the field to update */
|
|
|
|
ulint val, /*!< in: value to write */
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
|
|
|
{
|
|
|
|
byte* data;
|
|
|
|
ulint len;
|
|
|
|
|
|
|
|
data = rec_get_nth_field_old(rec, i, &len);
|
|
|
|
|
|
|
|
ut_ad(len == 4);
|
|
|
|
|
|
|
|
mlog_write_ulint(data, val, MLOG_4BYTES, mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
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.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return maximum combined size for inserted records */
|
2014-02-26 19:11:54 +01:00
|
|
|
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.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return maximum combined size for inserted records */
|
2014-02-26 19:11:54 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************//**
|
|
|
|
Puts a record to free list. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_mem_free(
|
|
|
|
/*==========*/
|
|
|
|
page_t* page, /*!< in/out: index page */
|
|
|
|
page_zip_des_t* page_zip, /*!< in/out: compressed page,
|
|
|
|
or NULL */
|
|
|
|
rec_t* rec, /*!< in: pointer to the
|
|
|
|
(origin of) record */
|
|
|
|
const dict_index_t* index, /*!< in: index of rec */
|
|
|
|
const ulint* offsets) /*!< in: array returned by
|
|
|
|
rec_get_offsets() */
|
|
|
|
{
|
|
|
|
rec_t* free;
|
|
|
|
ulint garbage;
|
|
|
|
|
|
|
|
ut_ad(rec_offs_validate(rec, index, offsets));
|
|
|
|
free = page_header_get_ptr(page, PAGE_FREE);
|
|
|
|
|
2014-12-22 16:53:17 +02:00
|
|
|
bool scrub = srv_immediate_scrub_data_uncompressed;
|
|
|
|
if (scrub) {
|
|
|
|
/* scrub record */
|
|
|
|
uint size = rec_offs_data_size(offsets);
|
|
|
|
memset(rec, 0, size);
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
page_rec_set_next(rec, free);
|
|
|
|
page_header_set_ptr(page, page_zip, PAGE_FREE, rec);
|
|
|
|
|
|
|
|
garbage = page_header_get_field(page, PAGE_GARBAGE);
|
|
|
|
|
|
|
|
page_header_set_field(page, page_zip, PAGE_GARBAGE,
|
|
|
|
garbage + rec_offs_size(offsets));
|
|
|
|
|
|
|
|
if (page_zip) {
|
|
|
|
page_zip_dir_delete(page_zip, rec, index, offsets, free);
|
|
|
|
} else {
|
|
|
|
page_header_set_field(page, page_zip, PAGE_N_RECS,
|
|
|
|
page_get_n_recs(page) - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-19 12:39:17 +02:00
|
|
|
#endif /* !UNIV_INNOCHECKSUM */
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
#ifdef UNIV_MATERIALIZE
|
|
|
|
#undef UNIV_INLINE
|
|
|
|
#define UNIV_INLINE UNIV_INLINE_ORIGINAL
|
|
|
|
#endif
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
#endif
|