Assumingapagesizeof8kB,atypicalindexpageofasecondary indexcontains300indexentries,andthesizeofthepagedirectory
is 50 x 4 bytes = 200 bytes. */
/***************************************************************//**
Looks for the directory slot which owns the given record.
@return the directory slot number
@retval ULINT_UNDEFINED on corruption */
ulint
page_dir_find_owner_slot( /*=====================*/ const rec_t* rec) /*!< in: the physical record */
{
ut_ad(page_rec_check(rec));
/** The page infimum and supremum of an empty page in ROW_FORMAT=REDUNDANT */ staticconst byte infimum_supremum_redundant[] = { /* the infimum record */ 0x08/*end offset*/, 0x01/*n_owned*/, 0x00, 0x00/*heap_no=0*/, 0x03/*n_fields=1, 1-byte offsets*/, 0x00, 0x74/* pointer to supremum */, 'i', 'n', 'f', 'i', 'm', 'u', 'm', 0, /* the supremum record */ 0x09/*end offset*/, 0x01/*n_owned*/, 0x00, 0x08/*heap_no=1*/, 0x03/*n_fields=1, 1-byte offsets*/, 0x00, 0x00/* end of record list */, 's', 'u', 'p', 'r', 'e', 'm', 'u', 'm', 0
};
/** The page infimum and supremum of an empty page in ROW_FORMAT=COMPACT */ staticconst byte infimum_supremum_compact[] = { /* the infimum record */ 0x01/*n_owned=1*/, 0x00, 0x02/* heap_no=0, REC_STATUS_INFIMUM */, 0x00, 0x0d/* pointer to supremum */, 'i', 'n', 'f', 'i', 'm', 'u', 'm', 0, /* the supremum record */ 0x01/*n_owned=1*/, 0x00, 0x0b/* heap_no=1, REC_STATUS_SUPREMUM */, 0x00, 0x00/* end of record list */, 's', 'u', 'p', 'r', 'e', 'm', 'u', 'm'
};
/** Create an index page. @param[in,out]blockbufferblock
@param[in] comp nonzero=compact page format */ void page_create_low(const buf_block_t* block, bool comp)
{
page_t* page;
/** Create an uncompressed index page. @param[in,out]blockbufferblock @param[in,out]mtrmini-transaction
@param[in] comp set unless ROW_FORMAT=REDUNDANT */ void page_create(buf_block_t *block, mtr_t *mtr, bool comp)
{
mtr->page_create(*block, comp);
buf_block_modify_clock_inc(block);
page_create_low(block, comp);
}
/**********************************************************//**
Create a compressed B-tree index page. */ void
page_create_zip( /*============*/
buf_block_t* block, /*!< in/out: a buffer frame
where the page is created */
dict_index_t* index, /*!< in: the index of the
page */
ulint level, /*!< in: the B-tree level
of the page */
trx_id_t max_trx_id, /*!< in: PAGE_MAX_TRX_ID */
mtr_t* mtr) /*!< in/out: mini-transaction
handle */
{
ut_ad(block);
ut_ad(buf_block_get_page_zip(block));
ut_ad(dict_table_is_comp(index->table));
/* PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC are always 0 for
temporary tables. */
ut_ad(max_trx_id == 0 || !index->table->is_temporary()); /* In secondary indexes, PAGE_MAX_TRX_ID mustbezeroonnon-leafpages.max_trx_idcanbe0whenthe indexconsistsofanemptyroot(leaf)page.
theclusteredindex,PAGE_ROOT_AUTOINCor
PAGE_MAX_TRX_ID must be 0 on other pages than the root. */
ut_ad(max_trx_id == 0 || level == 0 || index->is_primary()
|| index->table->is_temporary());
/*************************************************************//**
Differs from page_copy_rec_list_end, because this function does not
touch the lock table and max trx id on page or compress the page.
@return error code */
dberr_t
page_copy_rec_list_end_no_locks( /*============================*/
buf_block_t* new_block, /*!< in: index page to copy to */
buf_block_t* block, /*!< in: index page of rec */
rec_t* rec, /*!< in: record on page */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr) /*!< in: mtr */
{
page_t* new_page = buf_block_get_frame(new_block);
page_cur_t cur1;
page_cur_t cur2;
mem_heap_t* heap = NULL;
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
rec_offs_init(offsets_);
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
return err;
}
/*************************************************************//**
Copies records from page to new_page, from a given record onward,
including that record. Infimum and supremum records are not copied.
The records are copied to the start of the record list on new_page.
@return pointer to the original successor of the infimum record on new_block
@retval nullptr on ROW_FORMAT=COMPRESSED page overflow */
rec_t*
page_copy_rec_list_end( /*===================*/
buf_block_t* new_block, /*!< in/out: index page to copy to */
buf_block_t* block, /*!< in: index page containing rec */
rec_t* rec, /*!< in: record on page */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr, /*!< in/out: mini-transaction */
dberr_t* err) /*!< out: error code */
{
page_t* new_page = new_block->page.frame;
page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block);
page_t* page = block->page.frame;
rec_t* ret = page_rec_get_next(
page_get_infimum_rec(new_page));
ulint num_moved = 0;
ut_ad(page_align(rec) == page);
if (UNIV_UNLIKELY(!ret)) {
*err = DB_CORRUPTION; return nullptr;
}
#ifdef UNIV_ZIP_DEBUG if (new_page_zip) {
page_zip_des_t* page_zip = buf_block_get_page_zip(block);
ut_a(page_zip);
/* Strict page_zip_validate() may fail here. Furthermore,btr_compress()maysetFIL_PAGE_PREVto FIL_NULLonnew_pagewhileleavingitintacton
new_page_zip. So, we cannot validate new_page_zip. */
ut_a(page_zip_validate_low(page_zip, page, index, TRUE));
} #endif/* UNIV_ZIP_DEBUG */
ut_ad(buf_block_get_frame(block) == page);
ut_ad(page_is_leaf(page) == page_is_leaf(new_page));
ut_ad(page_is_comp(page) == page_is_comp(new_page)); /* Here, "ret" may be pointing to a user record or the
predefined supremum record. */
if (index->is_spatial()) {
ulint max_to_move = page_get_n_recs(
buf_block_get_frame(block));
heap = mem_heap_create(256);
rec_move= static_cast<rtr_rec_move_t*>(
mem_heap_alloc(heap, max_to_move * sizeof *rec_move)); /* For spatial index, we need to insert recs one by one
to keep recs ordered. */
*err = rtr_page_copy_rec_list_end_no_locks(new_block,
block, rec, index,
heap, rec_move,
max_to_move,
&num_moved,
mtr);
} else {
*err = page_copy_rec_list_end_no_locks(new_block, block, rec,
index, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) {
err_exit: if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
} return nullptr;
} if (was_empty) {
mtr->memcpy<mtr_t::MAYBE_NOP>(*new_block, PAGE_HEADER
+ PAGE_LAST_INSERT
+ new_page, h, sizeof h);
}
}
/* Update PAGE_MAX_TRX_ID on the uncompressed page. Modificationswillberedologgedandcopiedtothecompressed pageinpage_zip_compress()orpage_zip_reorganize()below. Multipletransactionscannotsimultaneouslyoperateonthe sametemp-tableinparallel. max_trx_idisignoredfortemptablesbecauseitnotrequired
for MVCC. */ if (!index->is_primary() && page_is_leaf(page)
&& !index->table->is_temporary()) {
ut_ad(!was_empty || page_dir_get_n_heap(new_page)
== PAGE_HEAP_NO_USER_LOW
+ page_header_get_field(new_page, PAGE_N_RECS));
page_update_max_trx_id(new_block, NULL,
page_get_max_trx_id(page), mtr);
}
if (new_page_zip) {
mtr_set_log_mode(mtr, log_mode);
if (!page_zip_compress(new_block, index,
page_zip_level, mtr)) { /* Before trying to reorganize the page,
store the number of preceding records on the page. */
ulint ret_pos
= page_rec_get_n_recs_before(ret); /* Before copying, "ret" was the successor of thepredefinedinfimumrecord.Itmuststill haveatleastonepredecessor(thepredefined infimumrecord,orafreshlycopiedrecord
that is smaller than "ret"). */ if (UNIV_UNLIKELY(!ret_pos
|| ret_pos == ULINT_UNDEFINED)) {
*err = DB_CORRUPTION; goto err_exit;
}
*err = page_zip_reorganize(new_block, index,
page_zip_level, mtr); switch (*err) { case DB_FAIL: if (!page_zip_decompress(new_page_zip,
new_page, FALSE)) {
ut_error;
}
ut_ad(page_validate(new_page, index)); /* fall through */ default: goto err_exit; case DB_SUCCESS: /* The page was reorganized:
Seek to ret_pos. */
ret = page_rec_get_nth(new_page, ret_pos);
ut_ad(ret);
}
}
}
/* Update the lock table and possible hash index */
/*************************************************************//**
Copies records from page to new_page, up to the given record, NOT including that record. Infimum and supremum records are not copied.
The records are copied to the end of the record list on new_page.
@return pointer to the original predecessor of the supremum record on new_block
@retval nullptr on ROW_FORMAT=COMPRESSED page overflow */
rec_t*
page_copy_rec_list_start( /*=====================*/
buf_block_t* new_block, /*!< in/out: index page to copy to */
buf_block_t* block, /*!< in: index page containing rec */
rec_t* rec, /*!< in: record on page */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr, /*!< in/out: mini-transaction */
dberr_t* err) /*!< out: error code */
{
ut_ad(page_align(rec) == block->page.frame);
/* Copy records from the original page to the new page */ if (index->is_spatial()) {
ut_ad(!index->is_instant());
ulint max_to_move = page_get_n_recs(
buf_block_get_frame(block));
heap = mem_heap_create(256);
if (!page_zip_compress(new_block, index,
page_zip_level, mtr)) { #ifndef DBUG_OFF
zip_reorganize: #endif/* DBUG_OFF */ /* Before trying to reorganize the page,
store the number of preceding records on the page. */
ulint ret_pos = page_rec_get_n_recs_before(ret); /* Before copying, "ret" was the predecessor ofthepredefinedsupremumrecord.Ifitwas thepredefinedinfimumrecord,thenitwould stillbetheinfimum,andwewouldhave
ret_pos == 0. */ if (UNIV_UNLIKELY(ret_pos == ULINT_UNDEFINED)) {
*err = DB_CORRUPTION; return nullptr;
}
*err = page_zip_reorganize(new_block, index,
page_zip_level, mtr); switch (*err) { case DB_SUCCESS:
ret = page_rec_get_nth(new_page, ret_pos);
ut_ad(ret); break; case DB_FAIL: if (UNIV_UNLIKELY
(!page_zip_decompress(new_page_zip,
new_page, FALSE))) {
ut_error;
}
ut_ad(page_validate(new_page, index)); /* fall through */ default: if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
return nullptr;
}
}
}
/* Update the lock table and possible hash index */
/*************************************************************//**
Deletes records from a page from a given record onward, including that record.
The infimum and supremum records are not deleted. */
dberr_t
page_delete_rec_list_end( /*=====================*/
rec_t* rec, /*!< in: pointer to record on page */
buf_block_t* block, /*!< in: buffer block of the page */
dict_index_t* index, /*!< in: record descriptor */
ulint n_recs, /*!< in: number of records to delete,
or ULINT_UNDEFINED if not known */
ulint size, /*!< in: the sum of the sizes of the recordsintheendofthechainto
delete, or ULINT_UNDEFINED if not known */
mtr_t* mtr) /*!< in: mtr */
{
page_t * const page= block->page.frame;
if (page_rec_is_supremum(rec))
{
ut_ad(n_recs == 0 || n_recs == ULINT_UNDEFINED); /* Nothing to do, there are no records bigger than the page supremum. */ return DB_SUCCESS;
}
if (n_recs == page_get_n_recs(page) ||
(page_is_comp(page)
? (rec == page + PAGE_NEW_INFIMUM ||
rec == page_rec_next_get<true>(page, page + PAGE_NEW_INFIMUM))
: (rec == page + PAGE_OLD_INFIMUM ||
rec == page_rec_next_get<false>(page, page + PAGE_OLD_INFIMUM))))
{ /* We are deleting all records. */
page_create_empty(block, index, mtr); return DB_SUCCESS;
}
#if0// FIXME: consider deleting the last record as a special case if (page_rec_is_last(rec))
{
page_cur_t cursor= { index, rec, offsets, block };
page_cur_delete_rec(&cursor, index, offsets, mtr); return DB_SUCCESS;
} #endif
/* The page becomes invalid for optimistic searches */
buf_block_modify_clock_inc(block);
if (UNIV_LIKELY_NULL(heap))
mem_heap_free(heap); return DB_SUCCESS;
} #endif
byte *prev_rec= page_rec_get_prev(rec); if (UNIV_UNLIKELY(!prev_rec)) return DB_CORRUPTION;
byte *last_rec= page_rec_get_prev(page_get_supremum_rec(page)); if (UNIV_UNLIKELY(!last_rec)) return DB_CORRUPTION;
// FIXME: consider a special case of shrinking PAGE_HEAP_TOP
constbool scrub= srv_immediate_scrub_data_uncompressed; if (scrub || size == ULINT_UNDEFINED || n_recs == ULINT_UNDEFINED)
{
rec_t *rec2= rec; /* Calculate the sum of sizes and the number of records */
size= 0;
n_recs= 0;
do
{
offsets = rec_get_offsets(rec2, index, offsets, n_core,
ULINT_UNDEFINED, &heap);
ulint s= rec_offs_size(offsets);
ut_ad(ulint(rec2 - page) + s - rec_offs_extra_size(offsets) <
srv_page_size);
ut_ad(size + s < srv_page_size);
size+= s;
n_recs++;
/* Update the page directory; there is no need to balance the number oftherecordsownedbythesupremumrecord,asitisallowedtobe
less than PAGE_DIR_SLOT_MIN_N_OWNED */
page_dir_slot_t *slot= page_dir_get_nth_slot(page, slot_index);
/*************************************************************//**
Deletes records from page, up to the given record, NOT including
that record. Infimum and supremum records are not deleted. */ void
page_delete_rec_list_start( /*=======================*/
rec_t* rec, /*!< in: record on page */
buf_block_t* block, /*!< in: buffer block of the page */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr) /*!< in: mtr */
{
page_cur_t cur1;
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
mem_heap_t* heap = NULL;
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
}
/************************************************************//**
Returns the nth record of the record list. This is the inverse function of page_rec_get_n_recs_before().
@return nth record
@retval nullptr on corrupted page */ const rec_t*
page_rec_get_nth_const( /*===================*/ const page_t* page, /*!< in: page */
ulint nth) /*!< in: nth record */
{ const page_dir_slot_t* slot;
ulint i;
ulint n_owned; const rec_t* rec;
if (nth == 0) { return(page_get_infimum_rec(page));
}
if (page_is_comp(page)) { do {
rec = page_rec_next_get<true>(page, rec);
} while (rec && nth--);
} else { do {
rec = page_rec_next_get<false>(page, rec);
} while (rec && nth--);
}
return(rec);
}
/************************************************************//**
Gets the pointer to the previous record.
@return pointer to previous record
@retval nullptr on error */ const rec_t*
page_rec_get_prev_const( /*====================*/ const rec_t* rec) /*!< in: pointer to record, must not be page
infimum */
{ const rec_t* rec2; const rec_t* prev_rec = NULL;
ut_ad(page_rec_check(rec));
const page_t* const page = page_align(rec);
ut_ad(!page_rec_is_infimum(rec));
ulint slot_no = page_dir_find_owner_slot(rec);
if (UNIV_UNLIKELY(!slot_no || slot_no == ULINT_UNDEFINED)) { return nullptr;
}
if (UNIV_UNLIKELY(!(rec2 = page_dir_slot_get_rec_validate(slot)))) { return nullptr;
}
if (page_is_comp(page)) { while (rec2 && rec != rec2) {
prev_rec = rec2;
ulint offs = rec_get_next_offs(rec2, TRUE); if (offs < PAGE_NEW_INFIMUM
|| offs > page_header_get_field(page,
PAGE_HEAP_TOP)) { return nullptr;
}
rec2 = page + offs;
} switch (rec_get_status(prev_rec)) { case REC_STATUS_INSTANT: case REC_STATUS_ORDINARY: if (!page_is_leaf(page)) { return nullptr;
} break; case REC_STATUS_INFIMUM: break; case REC_STATUS_NODE_PTR: if (!page_is_leaf(page)) { break;
} /* fall through */ default: return nullptr;
}
} else { while (rec2 && rec != rec2) {
prev_rec = rec2;
ulint offs = rec_get_next_offs(rec2, FALSE); if (offs < PAGE_OLD_INFIMUM
|| offs > page_header_get_field(page,
PAGE_HEAP_TOP)) { return nullptr;
}
rec2 = page + offs;
}
}
return(prev_rec);
}
/** Return the number of preceding records in an index page. @paramrecindexrecord @returnnumberofprecedingrecords,includingtheinfimumpseudo-record
@retval ULINT_UNDEFINED on corrupted page */
ulint page_rec_get_n_recs_before(const rec_t *rec)
{ const page_t *const page= page_align(rec); const page_dir_slot_t *slot = page_dir_get_nth_slot(page, 0); const page_dir_slot_t *const end_slot= slot - 2 * page_dir_get_n_slots(page);
lint n= 0;
ut_ad(page_rec_check(rec));
if (page_is_comp(page))
{ for (; rec_get_n_owned_new(rec) == 0; n--) if (UNIV_UNLIKELY(!(rec= page_rec_next_get<true>(page, rec)))) return ULINT_UNDEFINED;
do
{ const rec_t *slot_rec= page_dir_slot_get_rec_validate(slot); if (UNIV_UNLIKELY(!slot_rec)) break;
n+= lint(rec_get_n_owned_new(slot_rec));
if (rec == slot_rec) goto found;
} while ((slot-= 2) > end_slot);
} else
{ for (; rec_get_n_owned_old(rec) == 0; n--) if (UNIV_UNLIKELY(!(rec= page_rec_next_get<false>(page, rec)))) return ULINT_UNDEFINED;
do
{ const rec_t *slot_rec= page_dir_slot_get_rec_validate(slot); if (UNIV_UNLIKELY(!slot_rec)) break;
n+= lint(rec_get_n_owned_old(slot_rec));
if (rec == slot_rec) goto found;
} while ((slot-= 2) > end_slot);
}
#ifdef UNIV_BTR_PRINT /***************************************************************//** This is used to print the contents of the directory for
debugging purposes. */ void
page_dir_print( /*===========*/
page_t* page, /*!< in: index page */
ulint pr_n) /*!< in: print n first and n last entries */
{
ulint n;
ulint i;
page_dir_slot_t* slot;
n = page_dir_get_n_slots(page);
fprintf(stderr, "--------------------------------\n" "PAGE DIRECTORY\n" "Page address %p\n" "Directory stack top at offs: %zu; number of slots: %zu\n",
page, page_dir_get_nth_slot(page, n - 1) - page, n); for (i = 0; i < n; i++) {
slot = page_dir_get_nth_slot(page, i); if ((i == pr_n) && (i < n - pr_n)) {
fputs(" ... \n", stderr);
} if ((i < pr_n) || (i >= n - pr_n)) {
fprintf(stderr, "Contents of slot: %zu: n_owned: %zu," " rec offs: %zu\n",
i,
page_dir_slot_get_n_owned(slot),
page_dir_slot_get_rec(slot) - page);
}
}
fprintf(stderr, "Total of %zu records\n" "--------------------------------\n",
PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page));
}
/***************************************************************//** This is used to print the contents of the page record list for
debugging purposes. */ void
page_print_list( /*============*/
buf_block_t* block, /*!< in: index page */
dict_index_t* index, /*!< in: dictionary index of the page */
ulint pr_n) /*!< in: print n first and n last entries */
{
page_t* page = block->page.frame;
page_cur_t cur;
ulint count;
ulint n_recs;
mem_heap_t* heap = NULL;
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
rec_offs_init(offsets_);
fprintf(stderr, "Total of %lu records \n" "--------------------------------\n",
(ulong) (count + 1));
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
}
/***************************************************************//**
Prints the info in a page header. */ void
page_header_print( /*==============*/ const page_t* page)
{
fprintf(stderr, "--------------------------------\n" "PAGE HEADER INFO\n" "Page address %p, n records %u (%s)\n" "n dir slots %u, heap top %u\n" "Page n heap %u, free %u, garbage %u\n" "Page last insert %u, direction %u, n direction %u\n",
page, page_header_get_field(page, PAGE_N_RECS),
page_is_comp(page) ? "compact format" : "original format",
page_header_get_field(page, PAGE_N_DIR_SLOTS),
page_header_get_field(page, PAGE_HEAP_TOP),
page_dir_get_n_heap(page),
page_header_get_field(page, PAGE_FREE),
page_header_get_field(page, PAGE_GARBAGE),
page_header_get_field(page, PAGE_LAST_INSERT),
page_get_direction(page),
page_header_get_field(page, PAGE_N_DIRECTION));
}
/***************************************************************//** This is used to print the contents of the page for
debugging purposes. */ void
page_print( /*=======*/
buf_block_t* block, /*!< in: index page */
dict_index_t* index, /*!< in: dictionary index of the page */
ulint dn, /*!< in: print dn first and last entries
in directory */
ulint rn) /*!< in: print rn first and last records
in directory */
{
page_t* page = block->page.frame;
/***************************************************************//**
The following is used to validate a record on a page. This function
differs from rec_validate as it can also check the n_owned field and
the heap_no field.
@returnTRUEif ok */
ibool
page_rec_validate( /*==============*/ const rec_t* rec, /*!< in: physical record */ const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
{
ulint n_owned;
ulint heap_no; const page_t* page;
if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) {
ib::warn() << "Dir slot of rec " << rec - page
<< ", n owned too big " << n_owned; return(FALSE);
}
if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) {
ib::warn() << "Heap no of rec " << rec - page
<< " too big " << heap_no << " "
<< page_dir_get_n_heap(page); return(FALSE);
}
return(TRUE);
}
#ifdef UNIV_DEBUG /***************************************************************//**
Checks that the first directory slot points to the infimum record and
the last to the supremum. This function is intended to track if the
bug fixed in 4.0.14 has caused corruption to users' databases. */ void
page_check_dir( /*===========*/ const page_t* page) /*!< in: index page */
{
ulint n_slots;
ulint infimum_offs;
ulint supremum_offs;
/***************************************************************//** This function checks the consistency of an index page when we donot
know the index. This is also resilient so that this should never crash
even if the page is total garbage.
@returnTRUEif ok */
ibool
page_simple_validate_old( /*=====================*/ const page_t* page) /*!< in: index page in ROW_FORMAT=REDUNDANT */
{ const page_dir_slot_t* slot;
ulint slot_no;
ulint n_slots; const rec_t* rec; const byte* rec_heap_top;
ulint count;
ulint own_count;
ibool ret = FALSE;
ut_a(!page_is_comp(page));
/* Check first that the record heap and the directory do not
overlap. */
n_slots = page_dir_get_n_slots(page);
if (UNIV_UNLIKELY(n_slots < 2 || n_slots > srv_page_size / 4)) {
ib::error() << "Nonsensical number of page dir slots: "
<< n_slots; goto func_exit;
}
if (UNIV_UNLIKELY(rec_heap_top
> page_dir_get_nth_slot(page, n_slots - 1))) {
ib::error()
<< "Record heap and dir overlap on a page, heap top "
<< page_header_get_field(page, PAGE_HEAP_TOP)
<< ", dir "
<< page_dir_get_nth_slot(page, n_slots - 1) - page;
goto func_exit;
}
/* Validate the record list in a loop checking also that it is
consistent with the page record directory. */
for (;;) { if (UNIV_UNLIKELY(rec > rec_heap_top)) {
ib::error() << "Record " << (rec - page)
<< " is above rec heap top "
<< (rec_heap_top - page);
goto func_exit;
}
if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) != 0)) { /* This is a record pointed to by a dir slot */ if (UNIV_UNLIKELY(rec_get_n_owned_old(rec)
!= own_count)) {
/***************************************************************//** This function checks the consistency of an index page when we donot
know the index. This is also resilient so that this should never crash
even if the page is total garbage.
@returnTRUEif ok */
ibool
page_simple_validate_new( /*=====================*/ const page_t* page) /*!< in: index page in ROW_FORMAT!=REDUNDANT */
{ const page_dir_slot_t* slot;
ulint slot_no;
ulint n_slots; const rec_t* rec; const byte* rec_heap_top;
ulint count;
ulint own_count;
ibool ret = FALSE;
ut_a(page_is_comp(page));
/* Check first that the record heap and the directory do not
overlap. */
n_slots = page_dir_get_n_slots(page);
if (UNIV_UNLIKELY(n_slots < 2 || n_slots > srv_page_size / 4)) {
ib::error() << "Nonsensical number of page dir slots: "
<< n_slots; goto func_exit;
}
if (UNIV_UNLIKELY(rec_heap_top
> page_dir_get_nth_slot(page, n_slots - 1))) {
ib::error() << "Record heap and dir overlap on a page," " heap top "
<< page_header_get_field(page, PAGE_HEAP_TOP)
<< ", dir "
<< page_dir_get_nth_slot(page, n_slots - 1) - page;
goto func_exit;
}
/* Validate the record list in a loop checking also that it is
consistent with the page record directory. */
for (;;) { if (UNIV_UNLIKELY(rec < page + PAGE_NEW_INFIMUM
|| rec > rec_heap_top)) {
ib::error() << "Record " << rec - page
<< " is out of bounds: "
<< rec_heap_top - page; goto func_exit;
}
if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) != 0)) { /* This is a record pointed to by a dir slot */ if (UNIV_UNLIKELY(rec_get_n_owned_new(rec)
!= own_count)) {
if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
!= dict_table_is_comp(index->table))) {
ib::error() << "'compact format' flag mismatch";
func_exit2:
ib::error() << "Apparent corruption in space "
<< page_get_space_id(page) << " page "
<< page_get_page_no(page)
<< " of index " << index->name
<< " of table " << index->table->name; returnFALSE;
}
if (page_is_comp(page)) { if (UNIV_UNLIKELY(!page_simple_validate_new(page))) { goto func_exit2;
}
} else { if (UNIV_UNLIKELY(!page_simple_validate_old(page))) { goto func_exit2;
}
}
/* Multiple transactions cannot simultaneously operate on the sametemp-tableinparallel. max_trx_idisignoredfortemptablesbecauseitnotrequired
for MVCC. */ if (!page_is_leaf(page) || page_is_empty(page)
|| index->is_primary()
|| index->table->is_temporary()) {
} elseif (trx_id_t sys_max_trx_id = trx_sys.get_max_trx_id()) {
trx_id_t max_trx_id = page_get_max_trx_id(page);
if (max_trx_id == 0 || max_trx_id > sys_max_trx_id) {
ib::error() << "PAGE_MAX_TRX_ID out of bounds: "
<< max_trx_id << ", " << sys_max_trx_id;
ret = FALSE;
}
} else {
ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN);
}
/* Check first that the record heap and the directory do not
overlap. */
n_slots = page_dir_get_n_slots(page);
if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
<= page_dir_get_nth_slot(page, n_slots - 1)))) {
ib::warn() << "Record heap and directory overlap"; goto func_exit2;
}
switch (uint16_t type = fil_page_get_type(page)) { case FIL_PAGE_RTREE: if (!index->is_spatial()) {
wrong_page_type:
ib::warn() << "Wrong page type " << type;
ret = FALSE;
} break; case FIL_PAGE_TYPE_INSTANT: if (index->is_instant()
&& page_get_page_no(page) == index->page) { break;
} goto wrong_page_type; case FIL_PAGE_INDEX: if (index->is_spatial()) { goto wrong_page_type;
} if (index->is_instant()
&& page_get_page_no(page) == index->page) { goto wrong_page_type;
} break; default: goto wrong_page_type;
}
/* The following buffer is used to check that the
records in the page record heap do not overlap */
mem_heap_t* heap = mem_heap_create(srv_page_size + 200);;
byte* buf = static_cast<byte*>(mem_heap_zalloc(heap, srv_page_size));
/* Validate the record list in a loop checking also that
it is consistent with the directory. */
ulint count = 0, data_size = 0, own_count = 1, slot_no = 0;
ulint info_bits;
slot_no = 0;
slot = page_dir_get_nth_slot(page, slot_no);
if (page_is_comp(page) && page_rec_is_user_rec(rec)
&& UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
== page_is_leaf(page))) {
ib::error() << "'node_ptr' flag mismatch";
ret = FALSE; goto next_rec;
}
if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
ret = FALSE; goto next_rec;
}
info_bits = rec_get_info_bits(rec, page_is_comp(page)); if (info_bits
& ~(REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG)) {
ib::error() << "info_bits has an incorrect value "
<< info_bits;
ret = false;
}
if (rec == first_rec) { if (info_bits & REC_INFO_MIN_REC_FLAG) { if (page_has_prev(page)) {
ib::error() << "REC_INFO_MIN_REC_FLAG " "is set on non-left page";
ret = false;
} elseif (!page_is_leaf(page)) { /* leftmost node pointer page */
} elseif (!index->is_instant()) {
ib::error() << "REC_INFO_MIN_REC_FLAG " "is set in a leaf-page record";
ret = false;
} elseif (!(info_bits & REC_INFO_DELETED_FLAG)
!= !index->table->instant) {
ib::error() << (index->table->instant
? "Metadata record " "is not delete-marked"
: "Metadata record " "is delete-marked");
ret = false;
}
} elseif (!page_has_prev(page)
&& index->is_instant()) {
ib::error() << "Metadata record is missing";
ret = false;
}
} elseif (info_bits & REC_INFO_MIN_REC_FLAG) {
ib::error() << "REC_INFO_MIN_REC_FLAG record is not " "first in page";
ret = false;
}
if (page_is_comp(page)) { const rec_comp_status_t status = rec_get_status(rec); if (status != REC_STATUS_ORDINARY
&& status != REC_STATUS_NODE_PTR
&& status != REC_STATUS_INFIMUM
&& status != REC_STATUS_SUPREMUM
&& status != REC_STATUS_INSTANT) {
ib::error() << "impossible record status "
<< status;
ret = false;
} elseif (page_rec_is_infimum(rec)) { if (status != REC_STATUS_INFIMUM) {
ib::error()
<< "infimum record has status "
<< status;
ret = false;
}
} elseif (page_rec_is_supremum(rec)) { if (status != REC_STATUS_SUPREMUM) {
ib::error() << "supremum record has " "status "
<< status;
ret = false;
}
} elseif (!page_is_leaf(page)) { if (status != REC_STATUS_NODE_PTR) {
ib::error() << "node ptr record has " "status "
<< status;
ret = false;
}
} elseif (!index->is_instant()
&& status == REC_STATUS_INSTANT) {
ib::error() << "instantly added record in a " "non-instant index";
ret = false;
}
}
/* Check that the records are in the ascending order */ if (count >= PAGE_HEAP_NO_USER_LOW
&& !page_rec_is_supremum(rec)) {
int ret = cmp_rec_rec(
rec, old_rec, offsets, old_offsets, index);
/* For spatial index, on nonleaf level, we
allow recs to be equal. */ if (ret <= 0 && !(ret == 0 && index->is_spatial()
&& !page_is_leaf(page))) {
ib::error() << "Records in wrong order";
fputs("\nInnoDB: previous record ", stderr); /* For spatial index, print the mbr info.*/ if (index->type & DICT_SPATIAL) {
putc('\n', stderr);
rec_print_mbr_rec(stderr,
old_rec, old_offsets);
fputs("\nInnoDB: record ", stderr);
putc('\n', stderr);
rec_print_mbr_rec(stderr, rec, offsets);
putc('\n', stderr);
putc('\n', stderr);
#ifdefined(UNIV_GIS_DEBUG) /* For spatial index, print the mbr info.*/ if (index->type & DICT_SPATIAL) {
rec_print_mbr_rec(stderr, rec, offsets);
putc('\n', stderr);
} #endif/* UNIV_GIS_DEBUG */
}
offs = rec_get_start(rec, offsets) - page;
i = rec_offs_size(offsets); if (UNIV_UNLIKELY(offs + i >= srv_page_size)) {
ib::error() << "Record offset out of bounds: "
<< offs << '+' << i;
ret = FALSE; goto next_rec;
} while (i--) { if (UNIV_UNLIKELY(buf[offs + i])) {
ib::error() << "Record overlaps another: "
<< offs << '+' << i;
ret = FALSE; break;
}
buf[offs + i] = 1;
}
if (ulint rec_own_count = page_is_comp(page)
? rec_get_n_owned_new(rec)
: rec_get_n_owned_old(rec)) { /* This is a record pointed to by a dir slot */ if (UNIV_UNLIKELY(rec_own_count != own_count)) {
ib::error() << "Wrong owned count at " << offs
<< ": " << rec_own_count
<< ", " << own_count;
ret = FALSE;
}
if (page_dir_slot_get_rec(slot) != rec) {
ib::error() << "Dir slot does not" " point to right rec at " << offs;
ret = FALSE;
}
if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
ib::error() << "Summed data size " << data_size
<< ", returned by func " << page_get_data_size(page);
ret = FALSE;
}
/* Check then the free list */
rec = page_header_get_ptr(page, PAGE_FREE);
while (rec != NULL) {
offsets = rec_get_offsets(rec, index, offsets, n_core,
ULINT_UNDEFINED, &heap); if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
ret = FALSE;
next_free: const ulint offs = rec_get_next_offs(
rec, page_is_comp(page)); if (!offs) { break;
} if (UNIV_UNLIKELY(offs < PAGE_OLD_INFIMUM
|| offs >= srv_page_size)) {
ib::error() << "Page free list is corrupted";
ret = FALSE; break;
}
rec = page + offs; continue;
}
count++;
offs = rec_get_start(rec, offsets) - page;
i = rec_offs_size(offsets); if (UNIV_UNLIKELY(offs + i >= srv_page_size)) {
ib::error() << "Free record offset out of bounds: "
<< offs << '+' << i;
ret = FALSE; goto next_free;
} while (i--) { if (UNIV_UNLIKELY(buf[offs + i])) {
ib::error() << "Free record overlaps another: "
<< offs << '+' << i;
ret = FALSE; break;
}
buf[offs + i] = 1;
}
goto next_free;
}
if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
ib::error() << "N heap is wrong "
<< page_dir_get_n_heap(page) << " " << count + 1;
ret = FALSE;
}
mem_heap_free(heap);
if (UNIV_UNLIKELY(!ret)) { goto func_exit2;
}
return(ret);
}
PRAGMA_REENABLE_CHECK_STACK_FRAME
/***************************************************************//**
Looks in the page record list for a record with the given heap number.
@return record, NULL ifnot found */ const rec_t*
page_find_rec_with_heap_no( /*=======================*/ const page_t* page, /*!< in: index page */
ulint heap_no)/*!< in: heap number */
{ const rec_t* rec;
if (page_is_comp(page)) {
rec = page + PAGE_NEW_INFIMUM;
for (;;) {
ulint rec_heap_no = rec_get_heap_no_new(rec);
/** Get the last non-delete-marked record on a page. @param[in]pageindextreeleafpage @returnthelastrecord,notdelete-marked
@retval infimum record if all records are delete-marked */ const rec_t *page_find_rec_last_not_deleted(const page_t *page)
{
ut_ad(page_is_leaf(page));
if (page_is_comp(page))
{ const rec_t *rec= page + PAGE_NEW_INFIMUM; const rec_t *prev_rec= rec; do
{ if (!(rec[-REC_NEW_INFO_BITS] &
(REC_INFO_DELETED_FLAG | REC_INFO_MIN_REC_FLAG)))
prev_rec= rec; if (!(rec= page_rec_next_get<true>(page, rec))) return page + PAGE_NEW_INFIMUM;
} while (rec != page + PAGE_NEW_SUPREMUM); return prev_rec;
} else
{ const rec_t *rec= page + PAGE_OLD_INFIMUM; const rec_t *prev_rec= rec; do
{ if (!(rec[-REC_OLD_INFO_BITS] &
(REC_INFO_DELETED_FLAG | REC_INFO_MIN_REC_FLAG)))
prev_rec= rec; if (!(rec= page_rec_next_get<false>(page, rec))) return page + PAGE_OLD_INFIMUM;
} while (rec != page + PAGE_OLD_SUPREMUM); return prev_rec;
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.42 Sekunden
(vorverarbeitet am 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.