diff options
Diffstat (limited to 'storage/xtradb/page/page0page.cc')
-rw-r--r-- | storage/xtradb/page/page0page.cc | 2873 |
1 files changed, 0 insertions, 2873 deletions
diff --git a/storage/xtradb/page/page0page.cc b/storage/xtradb/page/page0page.cc deleted file mode 100644 index 800f76e1532..00000000000 --- a/storage/xtradb/page/page0page.cc +++ /dev/null @@ -1,2873 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2012, Facebook Inc. - -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 page/page0page.cc -Index page routines - -Created 2/2/1994 Heikki Tuuri -*******************************************************/ - -#define THIS_MODULE -#include "page0page.h" -#ifdef UNIV_NONINL -#include "page0page.ic" -#endif -#undef THIS_MODULE - -#include "ha_prototypes.h" -#include "buf0checksum.h" - -#ifndef UNIV_INNOCHECKSUM - -#include "page0cur.h" -#include "page0zip.h" -#include "buf0buf.h" -#include "btr0btr.h" -#ifndef UNIV_HOTBACKUP -# include "srv0srv.h" -# include "lock0lock.h" -# include "fut0lst.h" -# include "btr0sea.h" -#endif /* !UNIV_HOTBACKUP */ - -/* THE INDEX PAGE - ============== - -The index page consists of a page header which contains the page's -id and other information. On top of it are the index records -in a heap linked into a one way linear list according to alphabetic order. - -Just below page end is an array of pointers which we call page directory, -to about every sixth record in the list. The pointers are placed in -the directory in the alphabetical order of the records pointed to, -enabling us to make binary search using the array. Each slot n:o I -in the directory points to a record, where a 4-bit field contains a count -of those records which are in the linear list between pointer I and -the pointer I - 1 in the directory, including the record -pointed to by pointer I and not including the record pointed to by I - 1. -We say that the record pointed to by slot I, or that slot I, owns -these records. The count is always kept in the range 4 to 8, with -the exception that it is 1 for the first slot, and 1--8 for the second slot. - -An essentially binary search can be performed in the list of index -records, like we could do if we had pointer to every record in the -page directory. The data structure is, however, more efficient when -we are doing inserts, because most inserts are just pushed on a heap. -Only every 8th insert requires block move in the directory pointer -table, which itself is quite small. A record is deleted from the page -by just taking it off the linear list and updating the number of owned -records-field of the record which owns it, and updating the page directory, -if necessary. A special case is the one when the record owns itself. -Because the overhead of inserts is so small, we may also increase the -page size from the projected default of 8 kB to 64 kB without too -much loss of efficiency in inserts. Bigger page becomes actual -when the disk transfer rate compared to seek and latency time rises. -On the present system, the page size is set so that the page transfer -time (3 ms) is 20 % of the disk random access time (15 ms). - -When the page is split, merged, or becomes full but contains deleted -records, we have to reorganize the page. - -Assuming a page size of 8 kB, a typical index page of a secondary -index contains 300 index entries, and the size of the page directory -is 50 x 4 bytes = 200 bytes. */ - -/***************************************************************//** -Looks for the directory slot which owns the given record. -@return the directory slot number */ -UNIV_INTERN -ulint -page_dir_find_owner_slot( -/*=====================*/ - const rec_t* rec) /*!< in: the physical record */ -{ - const page_t* page; - register uint16 rec_offs_bytes; - register const page_dir_slot_t* slot; - register const page_dir_slot_t* first_slot; - register const rec_t* r = rec; - - ut_ad(page_rec_check(rec)); - - page = page_align(rec); - first_slot = page_dir_get_nth_slot(page, 0); - slot = page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1); - - if (page_is_comp(page)) { - while (rec_get_n_owned_new(r) == 0) { - r = rec_get_next_ptr_const(r, TRUE); - ut_ad(r >= page + PAGE_NEW_SUPREMUM); - ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR)); - } - } else { - while (rec_get_n_owned_old(r) == 0) { - r = rec_get_next_ptr_const(r, FALSE); - ut_ad(r >= page + PAGE_OLD_SUPREMUM); - ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR)); - } - } - - rec_offs_bytes = mach_encode_2(r - page); - - while (UNIV_LIKELY(*(uint16*) slot != rec_offs_bytes)) { - - if (UNIV_UNLIKELY(slot == first_slot)) { - fprintf(stderr, - "InnoDB: Probable data corruption on" - " page %lu\n" - "InnoDB: Original record ", - (ulong) page_get_page_no(page)); - - if (page_is_comp(page)) { - fputs("(compact record)", stderr); - } else { - rec_print_old(stderr, rec); - } - - fputs("\n" - "InnoDB: on that page.\n" - "InnoDB: Cannot find the dir slot for record ", - stderr); - if (page_is_comp(page)) { - fputs("(compact record)", stderr); - } else { - rec_print_old(stderr, page - + mach_decode_2(rec_offs_bytes)); - } - fputs("\n" - "InnoDB: on that page!\n", stderr); - - buf_page_print(page, 0); - - ut_error; - } - - slot += PAGE_DIR_SLOT_SIZE; - } - - return(((ulint) (first_slot - slot)) / PAGE_DIR_SLOT_SIZE); -} - -/**************************************************************//** -Used to check the consistency of a directory slot. -@return TRUE if succeed */ -static -ibool -page_dir_slot_check( -/*================*/ - const page_dir_slot_t* slot) /*!< in: slot */ -{ - const page_t* page; - ulint n_slots; - ulint n_owned; - - ut_a(slot); - - page = page_align(slot); - - n_slots = page_dir_get_n_slots(page); - - ut_a(slot <= page_dir_get_nth_slot(page, 0)); - ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1)); - - ut_a(page_rec_check(page_dir_slot_get_rec(slot))); - - if (page_is_comp(page)) { - n_owned = rec_get_n_owned_new(page_dir_slot_get_rec(slot)); - } else { - n_owned = rec_get_n_owned_old(page_dir_slot_get_rec(slot)); - } - - if (slot == page_dir_get_nth_slot(page, 0)) { - ut_a(n_owned == 1); - } else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) { - ut_a(n_owned >= 1); - ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED); - } else { - ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED); - ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED); - } - - return(TRUE); -} - -/*************************************************************//** -Sets the max trx id field value. */ -UNIV_INTERN -void -page_set_max_trx_id( -/*================*/ - buf_block_t* block, /*!< in/out: page */ - page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ - trx_id_t trx_id, /*!< in: transaction id */ - mtr_t* mtr) /*!< in/out: mini-transaction, or NULL */ -{ - page_t* page = buf_block_get_frame(block); -#ifndef UNIV_HOTBACKUP - ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); -#endif /* !UNIV_HOTBACKUP */ - - /* It is not necessary to write this change to the redo log, as - during a database recovery we assume that the max trx id of every - page is the maximum trx id assigned before the crash. */ - - if (page_zip) { - mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id); - page_zip_write_header(page_zip, - page + (PAGE_HEADER + PAGE_MAX_TRX_ID), - 8, mtr); -#ifndef UNIV_HOTBACKUP - } else if (mtr) { - mlog_write_ull(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), - trx_id, mtr); -#endif /* !UNIV_HOTBACKUP */ - } else { - mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id); - } -} - -/************************************************************//** -Allocates a block of memory from the heap of an index page. -@return pointer to start of allocated buffer, or NULL if allocation fails */ -UNIV_INTERN -byte* -page_mem_alloc_heap( -/*================*/ - 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 */ - ulint need, /*!< in: total number of bytes needed */ - ulint* heap_no)/*!< out: this contains the heap number - of the allocated record - if allocation succeeds */ -{ - byte* block; - ulint avl_space; - - ut_ad(page && heap_no); - - avl_space = page_get_max_insert_size(page, 1); - - if (avl_space >= need) { - block = page_header_get_ptr(page, PAGE_HEAP_TOP); - - page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP, - block + need); - *heap_no = page_dir_get_n_heap(page); - - page_dir_set_n_heap(page, page_zip, 1 + *heap_no); - - return(block); - } - - return(NULL); -} - -#ifndef UNIV_HOTBACKUP -/**********************************************************//** -Writes a log record of page creation. */ -UNIV_INLINE -void -page_create_write_log( -/*==================*/ - buf_frame_t* frame, /*!< in: a buffer frame where the page is - created */ - mtr_t* mtr, /*!< in: mini-transaction handle */ - ibool comp) /*!< in: TRUE=compact page format */ -{ - mlog_write_initial_log_record(frame, comp - ? MLOG_COMP_PAGE_CREATE - : MLOG_PAGE_CREATE, mtr); -} -#else /* !UNIV_HOTBACKUP */ -# define page_create_write_log(frame,mtr,comp) ((void) 0) -#endif /* !UNIV_HOTBACKUP */ - -/***********************************************************//** -Parses a redo log record of creating a page. -@return end of log record or NULL */ -UNIV_INTERN -byte* -page_parse_create( -/*==============*/ - byte* ptr, /*!< in: buffer */ - byte* end_ptr MY_ATTRIBUTE((unused)), /*!< in: buffer end */ - ulint comp, /*!< in: nonzero=compact page format */ - buf_block_t* block, /*!< in: block or NULL */ - mtr_t* mtr) /*!< in: mtr or NULL */ -{ - ut_ad(ptr && end_ptr); - - /* The record is empty, except for the record initial part */ - - if (block) { - page_create(block, mtr, comp); - } - - return(ptr); -} - -/**********************************************************//** -The index page creation function. -@return pointer to the page */ -static -page_t* -page_create_low( -/*============*/ - buf_block_t* block, /*!< in: a buffer block where the - page is created */ - ulint comp) /*!< in: nonzero=compact page format */ -{ - page_dir_slot_t* slot; - mem_heap_t* heap; - dtuple_t* tuple; - dfield_t* field; - byte* heap_top; - rec_t* infimum_rec; - rec_t* supremum_rec; - page_t* page; - dict_index_t* index; - ulint* offsets; - - ut_ad(block); -#if PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA -# error "PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA" -#endif -#if PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA -# error "PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA" -#endif - - /* The infimum and supremum records use a dummy index. */ - if (UNIV_LIKELY(comp)) { - index = dict_ind_compact; - } else { - index = dict_ind_redundant; - } - - /* 1. INCREMENT MODIFY CLOCK */ - buf_block_modify_clock_inc(block); - - page = buf_block_get_frame(block); - - fil_page_set_type(page, FIL_PAGE_INDEX); - - heap = mem_heap_create(200); - - /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */ - - /* Create first a data tuple for infimum record */ - tuple = dtuple_create(heap, 1); - dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM); - field = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(field, "infimum", 8); - dtype_set(dfield_get_type(field), - DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8); - /* Set the corresponding physical record to its place in the page - record heap */ - - heap_top = page + PAGE_DATA; - - infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0); - - if (UNIV_LIKELY(comp)) { - ut_a(infimum_rec == page + PAGE_NEW_INFIMUM); - - rec_set_n_owned_new(infimum_rec, NULL, 1); - rec_set_heap_no_new(infimum_rec, 0); - } else { - ut_a(infimum_rec == page + PAGE_OLD_INFIMUM); - - rec_set_n_owned_old(infimum_rec, 1); - rec_set_heap_no_old(infimum_rec, 0); - } - - offsets = rec_get_offsets(infimum_rec, index, NULL, - ULINT_UNDEFINED, &heap); - - heap_top = rec_get_end(infimum_rec, offsets); - - /* Create then a tuple for supremum */ - - tuple = dtuple_create(heap, 1); - dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM); - field = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(field, "supremum", comp ? 8 : 9); - dtype_set(dfield_get_type(field), - DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9); - - supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0); - - if (UNIV_LIKELY(comp)) { - ut_a(supremum_rec == page + PAGE_NEW_SUPREMUM); - - rec_set_n_owned_new(supremum_rec, NULL, 1); - rec_set_heap_no_new(supremum_rec, 1); - } else { - ut_a(supremum_rec == page + PAGE_OLD_SUPREMUM); - - rec_set_n_owned_old(supremum_rec, 1); - rec_set_heap_no_old(supremum_rec, 1); - } - - offsets = rec_get_offsets(supremum_rec, index, offsets, - ULINT_UNDEFINED, &heap); - heap_top = rec_get_end(supremum_rec, offsets); - - ut_ad(heap_top == page - + (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END)); - - mem_heap_free(heap); - - /* 4. INITIALIZE THE PAGE */ - - page_header_set_field(page, NULL, PAGE_N_DIR_SLOTS, 2); - page_header_set_ptr(page, NULL, PAGE_HEAP_TOP, heap_top); - page_header_set_field(page, NULL, PAGE_N_HEAP, comp - ? 0x8000 | PAGE_HEAP_NO_USER_LOW - : PAGE_HEAP_NO_USER_LOW); - page_header_set_ptr(page, NULL, PAGE_FREE, NULL); - page_header_set_field(page, NULL, PAGE_GARBAGE, 0); - page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, NULL); - page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION); - page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0); - page_header_set_field(page, NULL, PAGE_N_RECS, 0); - page_set_max_trx_id(block, NULL, 0, NULL); - memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START - - page_offset(heap_top)); - - /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */ - - /* Set the slots to point to infimum and supremum. */ - - slot = page_dir_get_nth_slot(page, 0); - page_dir_slot_set_rec(slot, infimum_rec); - - slot = page_dir_get_nth_slot(page, 1); - page_dir_slot_set_rec(slot, supremum_rec); - - /* Set the next pointers in infimum and supremum */ - - if (UNIV_LIKELY(comp)) { - rec_set_next_offs_new(infimum_rec, PAGE_NEW_SUPREMUM); - rec_set_next_offs_new(supremum_rec, 0); - } else { - rec_set_next_offs_old(infimum_rec, PAGE_OLD_SUPREMUM); - rec_set_next_offs_old(supremum_rec, 0); - } - - return(page); -} - -/**********************************************************//** -Create an uncompressed B-tree index page. -@return pointer to the page */ -UNIV_INTERN -page_t* -page_create( -/*========*/ - buf_block_t* block, /*!< in: a buffer block where the - page is created */ - mtr_t* mtr, /*!< in: mini-transaction handle */ - ulint comp) /*!< in: nonzero=compact page format */ -{ - page_create_write_log(buf_block_get_frame(block), mtr, comp); - return(page_create_low(block, comp)); -} - -/**********************************************************//** -Create a compressed B-tree index page. -@return pointer to the page */ -UNIV_INTERN -page_t* -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 */ -{ - page_t* page; - page_zip_des_t* page_zip = buf_block_get_page_zip(block); - - ut_ad(block); - ut_ad(page_zip); - ut_ad(index); - ut_ad(dict_table_is_comp(index->table)); - - page = page_create_low(block, TRUE); - mach_write_to_2(PAGE_HEADER + PAGE_LEVEL + page, level); - mach_write_to_8(PAGE_HEADER + PAGE_MAX_TRX_ID + page, max_trx_id); - - if (!page_zip_compress(page_zip, page, index, - page_zip_level, mtr)) { - /* The compression of a newly created page - should always succeed. */ - ut_error; - } - - return(page); -} - -/**********************************************************//** -Empty a previously created B-tree index page. */ -UNIV_INTERN -void -page_create_empty( -/*==============*/ - buf_block_t* block, /*!< in/out: B-tree block */ - dict_index_t* index, /*!< in: the index of the page */ - mtr_t* mtr) /*!< in/out: mini-transaction */ -{ - trx_id_t max_trx_id = 0; - const page_t* page = buf_block_get_frame(block); - page_zip_des_t* page_zip= buf_block_get_page_zip(block); - - ut_ad(fil_page_get_type(page) == FIL_PAGE_INDEX); - - if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) { - max_trx_id = page_get_max_trx_id(page); - ut_ad(max_trx_id); - } - - if (page_zip) { - page_create_zip(block, index, - page_header_get_field(page, PAGE_LEVEL), - max_trx_id, mtr); - } else { - page_create(block, mtr, page_is_comp(page)); - - if (max_trx_id) { - page_update_max_trx_id( - block, page_zip, max_trx_id, mtr); - } - } -} - -/*************************************************************//** -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. - -IMPORTANT: The caller will have to update IBUF_BITMAP_FREE -if new_block is a compressed leaf page in a secondary index. -This has to be done either within the same mini-transaction, -or by invoking ibuf_reset_free_bits() before mtr_commit(). */ -UNIV_INTERN -void -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; - rec_t* cur2; - mem_heap_t* heap = NULL; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets = offsets_; - rec_offs_init(offsets_); - - page_cur_position(rec, block, &cur1); - - if (page_cur_is_before_first(&cur1)) { - - page_cur_move_to_next(&cur1); - } - - btr_assert_not_corrupted(new_block, index); - ut_a(page_is_comp(new_page) == page_rec_is_comp(rec)); - ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == (ulint) - (page_is_comp(new_page) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM)); - - cur2 = page_get_infimum_rec(buf_block_get_frame(new_block)); - - /* Copy records from the original page to the new page */ - - while (!page_cur_is_after_last(&cur1)) { - rec_t* cur1_rec = page_cur_get_rec(&cur1); - rec_t* ins_rec; - offsets = rec_get_offsets(cur1_rec, index, offsets, - ULINT_UNDEFINED, &heap); - ins_rec = page_cur_insert_rec_low(cur2, index, - cur1_rec, offsets, mtr); - if (UNIV_UNLIKELY(!ins_rec)) { - /* Track an assertion failure reported on the mailing - list on June 18th, 2003 */ - - buf_page_print(new_page, 0); - buf_page_print(page_align(rec), 0); - ut_print_timestamp(stderr); - - fprintf(stderr, - "InnoDB: rec offset %lu, cur1 offset %lu," - " cur2 offset %lu\n", - (ulong) page_offset(rec), - (ulong) page_offset(page_cur_get_rec(&cur1)), - (ulong) page_offset(cur2)); - ut_error; - } - - page_cur_move_to_next(&cur1); - cur2 = ins_rec; - } - - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } -} - -#ifndef UNIV_HOTBACKUP -/*************************************************************//** -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. - -IMPORTANT: The caller will have to update IBUF_BITMAP_FREE -if new_block is a compressed leaf page in a secondary index. -This has to be done either within the same mini-transaction, -or by invoking ibuf_reset_free_bits() before mtr_commit(). - -@return pointer to the original successor of the infimum record on -new_page, or NULL on zip overflow (new_block will be decompressed) */ -UNIV_INTERN -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: mtr */ -{ - page_t* new_page = buf_block_get_frame(new_block); - page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block); - page_t* page = page_align(rec); - rec_t* ret = page_rec_get_next( - page_get_infimum_rec(new_page)); - ulint log_mode = 0; /* remove warning */ - -#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() may set FIL_PAGE_PREV to - FIL_NULL on new_page while leaving it intact on - 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 (new_page_zip) { - log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); - } - - if (page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW) { - page_copy_rec_list_end_to_created_page(new_page, rec, - index, mtr); - } else { - page_copy_rec_list_end_no_locks(new_block, block, rec, - index, mtr); - } - - /* Update PAGE_MAX_TRX_ID on the uncompressed page. - Modifications will be redo logged and copied to the compressed - page in page_zip_compress() or page_zip_reorganize() below. */ - if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) { - 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_page_zip, new_page, - 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 - the predefined infimum record. It must still - have at least one predecessor (the predefined - infimum record, or a freshly copied record - that is smaller than "ret"). */ - ut_a(ret_pos > 0); - - if (!page_zip_reorganize(new_block, index, mtr)) { - - btr_blob_dbg_remove(new_page, index, - "copy_end_reorg_fail"); - if (!page_zip_decompress(new_page_zip, - new_page, FALSE)) { - ut_error; - } - ut_ad(page_validate(new_page, index)); - btr_blob_dbg_add(new_page, index, - "copy_end_reorg_fail"); - return(NULL); - } else { - /* The page was reorganized: - Seek to ret_pos. */ - ret = new_page + PAGE_NEW_INFIMUM; - - do { - ret = rec_get_next_ptr(ret, TRUE); - } while (--ret_pos); - } - } - } - - /* Update the lock table and possible hash index */ - - lock_move_rec_list_end(new_block, block, rec); - - btr_search_move_or_delete_hash_entries(new_block, block, index); - - return(ret); -} - -/*************************************************************//** -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. - -IMPORTANT: The caller will have to update IBUF_BITMAP_FREE -if new_block is a compressed leaf page in a secondary index. -This has to be done either within the same mini-transaction, -or by invoking ibuf_reset_free_bits() before mtr_commit(). - -@return pointer to the original predecessor of the supremum record on -new_page, or NULL on zip overflow (new_block will be decompressed) */ -UNIV_INTERN -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: mtr */ -{ - page_t* new_page = buf_block_get_frame(new_block); - page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block); - page_cur_t cur1; - rec_t* cur2; - ulint log_mode = 0 /* remove warning */; - mem_heap_t* heap = NULL; - rec_t* ret - = page_rec_get_prev(page_get_supremum_rec(new_page)); - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets = offsets_; - rec_offs_init(offsets_); - - /* Here, "ret" may be pointing to a user record or the - predefined infimum record. */ - - if (page_rec_is_infimum(rec)) { - - return(ret); - } - - if (new_page_zip) { - log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); - } - - page_cur_set_before_first(block, &cur1); - page_cur_move_to_next(&cur1); - - cur2 = ret; - - /* Copy records from the original page to the new page */ - - while (page_cur_get_rec(&cur1) != rec) { - rec_t* cur1_rec = page_cur_get_rec(&cur1); - offsets = rec_get_offsets(cur1_rec, index, offsets, - ULINT_UNDEFINED, &heap); - cur2 = page_cur_insert_rec_low(cur2, index, - cur1_rec, offsets, mtr); - ut_a(cur2); - - page_cur_move_to_next(&cur1); - } - - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } - - /* Update PAGE_MAX_TRX_ID on the uncompressed page. - Modifications will be redo logged and copied to the compressed - page in page_zip_compress() or page_zip_reorganize() below. */ - if (dict_index_is_sec_or_ibuf(index) - && page_is_leaf(page_align(rec))) { - page_update_max_trx_id(new_block, NULL, - page_get_max_trx_id(page_align(rec)), - mtr); - } - - if (new_page_zip) { - mtr_set_log_mode(mtr, log_mode); - - DBUG_EXECUTE_IF("page_copy_rec_list_start_compress_fail", - goto zip_reorganize;); - - if (!page_zip_compress(new_page_zip, new_page, index, - page_zip_level, mtr)) { - - ulint ret_pos; -#ifndef DBUG_OFF -zip_reorganize: -#endif /* DBUG_OFF */ - /* Before trying to reorganize the page, - store the number of preceding records on the page. */ - ret_pos = page_rec_get_n_recs_before(ret); - /* Before copying, "ret" was the predecessor - of the predefined supremum record. If it was - the predefined infimum record, then it would - still be the infimum, and we would have - ret_pos == 0. */ - - if (UNIV_UNLIKELY - (!page_zip_reorganize(new_block, index, mtr))) { - - btr_blob_dbg_remove(new_page, index, - "copy_start_reorg_fail"); - if (UNIV_UNLIKELY - (!page_zip_decompress(new_page_zip, - new_page, FALSE))) { - ut_error; - } - ut_ad(page_validate(new_page, index)); - btr_blob_dbg_add(new_page, index, - "copy_start_reorg_fail"); - return(NULL); - } - - /* The page was reorganized: Seek to ret_pos. */ - ret = page_rec_get_nth(new_page, ret_pos); - } - } - - /* Update the lock table and possible hash index */ - - lock_move_rec_list_start(new_block, block, rec, ret); - - btr_search_move_or_delete_hash_entries(new_block, block, index); - - return(ret); -} - -/**********************************************************//** -Writes a log record of a record list end or start deletion. */ -UNIV_INLINE -void -page_delete_rec_list_write_log( -/*===========================*/ - rec_t* rec, /*!< in: record on page */ - dict_index_t* index, /*!< in: record descriptor */ - byte type, /*!< in: operation type: - MLOG_LIST_END_DELETE, ... */ - mtr_t* mtr) /*!< in: mtr */ -{ - byte* log_ptr; - ut_ad(type == MLOG_LIST_END_DELETE - || type == MLOG_LIST_START_DELETE - || type == MLOG_COMP_LIST_END_DELETE - || type == MLOG_COMP_LIST_START_DELETE); - - log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2); - if (log_ptr) { - /* Write the parameter as a 2-byte ulint */ - mach_write_to_2(log_ptr, page_offset(rec)); - mlog_close(mtr, log_ptr + 2); - } -} -#else /* !UNIV_HOTBACKUP */ -# define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0) -#endif /* !UNIV_HOTBACKUP */ - -/**********************************************************//** -Parses a log record of a record list end or start deletion. -@return end of log record or NULL */ -UNIV_INTERN -byte* -page_parse_delete_rec_list( -/*=======================*/ - byte type, /*!< in: MLOG_LIST_END_DELETE, - MLOG_LIST_START_DELETE, - MLOG_COMP_LIST_END_DELETE or - MLOG_COMP_LIST_START_DELETE */ - byte* ptr, /*!< in: buffer */ - byte* end_ptr,/*!< in: buffer end */ - buf_block_t* block, /*!< in/out: buffer block or NULL */ - dict_index_t* index, /*!< in: record descriptor */ - mtr_t* mtr) /*!< in: mtr or NULL */ -{ - page_t* page; - ulint offset; - - ut_ad(type == MLOG_LIST_END_DELETE - || type == MLOG_LIST_START_DELETE - || type == MLOG_COMP_LIST_END_DELETE - || type == MLOG_COMP_LIST_START_DELETE); - - /* Read the record offset as a 2-byte ulint */ - - if (end_ptr < ptr + 2) { - - return(NULL); - } - - offset = mach_read_from_2(ptr); - ptr += 2; - - if (!block) { - - return(ptr); - } - - page = buf_block_get_frame(block); - - ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table)); - - if (type == MLOG_LIST_END_DELETE - || type == MLOG_COMP_LIST_END_DELETE) { - page_delete_rec_list_end(page + offset, block, index, - ULINT_UNDEFINED, ULINT_UNDEFINED, - mtr); - } else { - page_delete_rec_list_start(page + offset, block, index, mtr); - } - - return(ptr); -} - -/*************************************************************//** -Deletes records from a page from a given record onward, including that record. -The infimum and supremum records are not deleted. */ -UNIV_INTERN -void -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 - records in the end of the chain to - delete, or ULINT_UNDEFINED if not known */ - mtr_t* mtr) /*!< in: mtr */ -{ - page_dir_slot_t*slot; - ulint slot_index; - rec_t* last_rec; - rec_t* prev_rec; - ulint n_owned; - page_zip_des_t* page_zip = buf_block_get_page_zip(block); - page_t* page = page_align(rec); - mem_heap_t* heap = NULL; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets = offsets_; - rec_offs_init(offsets_); - - ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE); - ut_ad(!page_zip || page_rec_is_comp(rec)); -#ifdef UNIV_ZIP_DEBUG - ut_a(!page_zip || page_zip_validate(page_zip, page, index)); -#endif /* UNIV_ZIP_DEBUG */ - - 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; - } - - if (recv_recovery_is_on()) { - /* If we are replaying a redo log record, we must - replay it exactly. Since MySQL 5.6.11, we should be - generating a redo log record for page creation if - the page would become empty. Thus, this branch should - only be executed when applying redo log that was - generated by an older version of MySQL. */ - } else if (page_rec_is_infimum(rec) - || n_recs == page_get_n_recs(page)) { -delete_all: - /* We are deleting all records. */ - page_create_empty(block, index, mtr); - return; - } else if (page_is_comp(page)) { - if (page_rec_get_next_low(page + PAGE_NEW_INFIMUM, 1) == rec) { - /* We are deleting everything from the first - user record onwards. */ - goto delete_all; - } - } else { - if (page_rec_get_next_low(page + PAGE_OLD_INFIMUM, 0) == rec) { - /* We are deleting everything from the first - user record onwards. */ - goto delete_all; - } - } - - /* Reset the last insert info in the page header and increment - the modify clock for the frame */ - - page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL); - - /* The page gets invalid for optimistic searches: increment the - frame modify clock */ - - buf_block_modify_clock_inc(block); - - page_delete_rec_list_write_log(rec, index, page_is_comp(page) - ? MLOG_COMP_LIST_END_DELETE - : MLOG_LIST_END_DELETE, mtr); - - if (page_zip) { - ulint log_mode; - - ut_a(page_is_comp(page)); - /* Individual deletes are not logged */ - - log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); - - do { - page_cur_t cur; - page_cur_position(rec, block, &cur); - - offsets = rec_get_offsets(rec, index, offsets, - ULINT_UNDEFINED, &heap); - rec = rec_get_next_ptr(rec, TRUE); -#ifdef UNIV_ZIP_DEBUG - ut_a(page_zip_validate(page_zip, page, index)); -#endif /* UNIV_ZIP_DEBUG */ - page_cur_delete_rec(&cur, index, offsets, mtr); - } while (page_offset(rec) != PAGE_NEW_SUPREMUM); - - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } - - /* Restore log mode */ - - mtr_set_log_mode(mtr, log_mode); - return; - } - - prev_rec = page_rec_get_prev(rec); - - last_rec = page_rec_get_prev(page_get_supremum_rec(page)); - - bool scrub = srv_immediate_scrub_data_uncompressed; - if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED) || - scrub) { - rec_t* rec2 = rec; - /* Calculate the sum of sizes and the number of records */ - size = 0; - n_recs = 0; - - do { - ulint s; - offsets = rec_get_offsets(rec2, index, offsets, - ULINT_UNDEFINED, &heap); - s = rec_offs_size(offsets); - ut_ad(rec2 - page + s - rec_offs_extra_size(offsets) - < UNIV_PAGE_SIZE); - ut_ad(size + s < UNIV_PAGE_SIZE); - size += s; - n_recs++; - - if (scrub) { - /* scrub record */ - uint recsize = rec_offs_data_size(offsets); - memset(rec2, 0, recsize); - } - - rec2 = page_rec_get_next(rec2); - } while (!page_rec_is_supremum(rec2)); - - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } - } - - ut_ad(size < UNIV_PAGE_SIZE); - - /* Update the page directory; there is no need to balance the number - of the records owned by the supremum record, as it is allowed to be - less than PAGE_DIR_SLOT_MIN_N_OWNED */ - - if (page_is_comp(page)) { - rec_t* rec2 = rec; - ulint count = 0; - - while (rec_get_n_owned_new(rec2) == 0) { - count++; - - rec2 = rec_get_next_ptr(rec2, TRUE); - } - - ut_ad(rec_get_n_owned_new(rec2) > count); - - n_owned = rec_get_n_owned_new(rec2) - count; - slot_index = page_dir_find_owner_slot(rec2); - ut_ad(slot_index > 0); - slot = page_dir_get_nth_slot(page, slot_index); - } else { - rec_t* rec2 = rec; - ulint count = 0; - - while (rec_get_n_owned_old(rec2) == 0) { - count++; - - rec2 = rec_get_next_ptr(rec2, FALSE); - } - - ut_ad(rec_get_n_owned_old(rec2) > count); - - n_owned = rec_get_n_owned_old(rec2) - count; - slot_index = page_dir_find_owner_slot(rec2); - ut_ad(slot_index > 0); - slot = page_dir_get_nth_slot(page, slot_index); - } - - page_dir_slot_set_rec(slot, page_get_supremum_rec(page)); - page_dir_slot_set_n_owned(slot, NULL, n_owned); - - page_dir_set_n_slots(page, NULL, slot_index + 1); - - /* Remove the record chain segment from the record chain */ - page_rec_set_next(prev_rec, page_get_supremum_rec(page)); - - btr_blob_dbg_op(page, rec, index, "delete_end", - btr_blob_dbg_remove_rec); - - /* Catenate the deleted chain segment to the page free list */ - - page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE)); - page_header_set_ptr(page, NULL, PAGE_FREE, rec); - - page_header_set_field(page, NULL, PAGE_GARBAGE, size - + page_header_get_field(page, PAGE_GARBAGE)); - - page_header_set_field(page, NULL, PAGE_N_RECS, - (ulint)(page_get_n_recs(page) - n_recs)); -} - -/*************************************************************//** -Deletes records from page, up to the given record, NOT including -that record. Infimum and supremum records are not deleted. */ -UNIV_INTERN -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; - ulint log_mode; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets = offsets_; - mem_heap_t* heap = NULL; - byte type; - - rec_offs_init(offsets_); - - ut_ad((ibool) !!page_rec_is_comp(rec) - == dict_table_is_comp(index->table)); -#ifdef UNIV_ZIP_DEBUG - { - page_zip_des_t* page_zip= buf_block_get_page_zip(block); - page_t* page = buf_block_get_frame(block); - - /* page_zip_validate() would detect a min_rec_mark mismatch - in btr_page_split_and_insert() - between btr_attach_half_pages() and insert_page = ... - when btr_page_get_split_rec_to_left() holds - (direction == FSP_DOWN). */ - ut_a(!page_zip - || page_zip_validate_low(page_zip, page, index, TRUE)); - } -#endif /* UNIV_ZIP_DEBUG */ - - if (page_rec_is_infimum(rec)) { - return; - } - - if (page_rec_is_supremum(rec)) { - /* We are deleting all records. */ - page_create_empty(block, index, mtr); - return; - } - - if (page_rec_is_comp(rec)) { - type = MLOG_COMP_LIST_START_DELETE; - } else { - type = MLOG_LIST_START_DELETE; - } - - page_delete_rec_list_write_log(rec, index, type, mtr); - - page_cur_set_before_first(block, &cur1); - page_cur_move_to_next(&cur1); - - /* Individual deletes are not logged */ - - log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); - - while (page_cur_get_rec(&cur1) != rec) { - offsets = rec_get_offsets(page_cur_get_rec(&cur1), index, - offsets, ULINT_UNDEFINED, &heap); - page_cur_delete_rec(&cur1, index, offsets, mtr); - } - - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } - - /* Restore log mode */ - - mtr_set_log_mode(mtr, log_mode); -} - -#ifndef UNIV_HOTBACKUP -/*************************************************************//** -Moves record list end to another page. Moved records include -split_rec. - -IMPORTANT: The caller will have to update IBUF_BITMAP_FREE -if new_block is a compressed leaf page in a secondary index. -This has to be done either within the same mini-transaction, -or by invoking ibuf_reset_free_bits() before mtr_commit(). - -@return TRUE on success; FALSE on compression failure (new_block will -be decompressed) */ -UNIV_INTERN -ibool -page_move_rec_list_end( -/*===================*/ - buf_block_t* new_block, /*!< in/out: index page where to move */ - buf_block_t* block, /*!< in: index page from where to move */ - rec_t* split_rec, /*!< in: first record to move */ - dict_index_t* index, /*!< in: record descriptor */ - mtr_t* mtr) /*!< in: mtr */ -{ - page_t* new_page = buf_block_get_frame(new_block); - ulint old_data_size; - ulint new_data_size; - ulint old_n_recs; - ulint new_n_recs; - - old_data_size = page_get_data_size(new_page); - old_n_recs = page_get_n_recs(new_page); -#ifdef UNIV_ZIP_DEBUG - { - page_zip_des_t* new_page_zip - = buf_block_get_page_zip(new_block); - page_zip_des_t* page_zip - = buf_block_get_page_zip(block); - ut_a(!new_page_zip == !page_zip); - ut_a(!new_page_zip - || page_zip_validate(new_page_zip, new_page, index)); - ut_a(!page_zip - || page_zip_validate(page_zip, page_align(split_rec), - index)); - } -#endif /* UNIV_ZIP_DEBUG */ - - if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block, - split_rec, index, mtr))) { - return(FALSE); - } - - new_data_size = page_get_data_size(new_page); - new_n_recs = page_get_n_recs(new_page); - - ut_ad(new_data_size >= old_data_size); - - page_delete_rec_list_end(split_rec, block, index, - new_n_recs - old_n_recs, - new_data_size - old_data_size, mtr); - - return(TRUE); -} - -/*************************************************************//** -Moves record list start to another page. Moved records do not include -split_rec. - -IMPORTANT: The caller will have to update IBUF_BITMAP_FREE -if new_block is a compressed leaf page in a secondary index. -This has to be done either within the same mini-transaction, -or by invoking ibuf_reset_free_bits() before mtr_commit(). - -@return TRUE on success; FALSE on compression failure */ -UNIV_INTERN -ibool -page_move_rec_list_start( -/*=====================*/ - buf_block_t* new_block, /*!< in/out: index page where to move */ - buf_block_t* block, /*!< in/out: page containing split_rec */ - rec_t* split_rec, /*!< in: first record not to move */ - dict_index_t* index, /*!< in: record descriptor */ - mtr_t* mtr) /*!< in: mtr */ -{ - if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block, - split_rec, index, mtr))) { - return(FALSE); - } - - page_delete_rec_list_start(split_rec, block, index, mtr); - - return(TRUE); -} -#endif /* !UNIV_HOTBACKUP */ - -/**************************************************************//** -Used to delete n slots from the directory. This function updates -also n_owned fields in the records, so that the first slot after -the deleted ones inherits the records of the deleted slots. */ -UNIV_INLINE -void -page_dir_delete_slot( -/*=================*/ - page_t* page, /*!< in/out: the index page */ - page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ - ulint slot_no)/*!< in: slot to be deleted */ -{ - page_dir_slot_t* slot; - ulint n_owned; - ulint i; - ulint n_slots; - - ut_ad(!page_zip || page_is_comp(page)); - ut_ad(slot_no > 0); - ut_ad(slot_no + 1 < page_dir_get_n_slots(page)); - - n_slots = page_dir_get_n_slots(page); - - /* 1. Reset the n_owned fields of the slots to be - deleted */ - slot = page_dir_get_nth_slot(page, slot_no); - n_owned = page_dir_slot_get_n_owned(slot); - page_dir_slot_set_n_owned(slot, page_zip, 0); - - /* 2. Update the n_owned value of the first non-deleted slot */ - - slot = page_dir_get_nth_slot(page, slot_no + 1); - page_dir_slot_set_n_owned(slot, page_zip, - n_owned + page_dir_slot_get_n_owned(slot)); - - /* 3. Destroy the slot by copying slots */ - for (i = slot_no + 1; i < n_slots; i++) { - rec_t* rec = (rec_t*) - page_dir_slot_get_rec(page_dir_get_nth_slot(page, i)); - page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec); - } - - /* 4. Zero out the last slot, which will be removed */ - mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0); - - /* 5. Update the page header */ - page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1); -} - -/**************************************************************//** -Used to add n slots to the directory. Does not set the record pointers -in the added slots or update n_owned values: this is the responsibility -of the caller. */ -UNIV_INLINE -void -page_dir_add_slot( -/*==============*/ - page_t* page, /*!< in/out: the index page */ - page_zip_des_t* page_zip,/*!< in/out: comprssed page, or NULL */ - ulint start) /*!< in: the slot above which the new slots - are added */ -{ - page_dir_slot_t* slot; - ulint n_slots; - - n_slots = page_dir_get_n_slots(page); - - ut_ad(start < n_slots - 1); - - /* Update the page header */ - page_dir_set_n_slots(page, page_zip, n_slots + 1); - - /* Move slots up */ - slot = page_dir_get_nth_slot(page, n_slots); - memmove(slot, slot + PAGE_DIR_SLOT_SIZE, - (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE); -} - -/****************************************************************//** -Splits a directory slot which owns too many records. */ -UNIV_INTERN -void -page_dir_split_slot( -/*================*/ - page_t* page, /*!< in/out: index page */ - page_zip_des_t* page_zip,/*!< in/out: compressed page whose - uncompressed part will be written, or NULL */ - ulint slot_no)/*!< in: the directory slot */ -{ - rec_t* rec; - page_dir_slot_t* new_slot; - page_dir_slot_t* prev_slot; - page_dir_slot_t* slot; - ulint i; - ulint n_owned; - - ut_ad(!page_zip || page_is_comp(page)); - ut_ad(slot_no > 0); - - slot = page_dir_get_nth_slot(page, slot_no); - - n_owned = page_dir_slot_get_n_owned(slot); - ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1); - - /* 1. We loop to find a record approximately in the middle of the - records owned by the slot. */ - - prev_slot = page_dir_get_nth_slot(page, slot_no - 1); - rec = (rec_t*) page_dir_slot_get_rec(prev_slot); - - for (i = 0; i < n_owned / 2; i++) { - rec = page_rec_get_next(rec); - } - - ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED); - - /* 2. We add one directory slot immediately below the slot to be - split. */ - - page_dir_add_slot(page, page_zip, slot_no - 1); - - /* The added slot is now number slot_no, and the old slot is - now number slot_no + 1 */ - - new_slot = page_dir_get_nth_slot(page, slot_no); - slot = page_dir_get_nth_slot(page, slot_no + 1); - - /* 3. We store the appropriate values to the new slot. */ - - page_dir_slot_set_rec(new_slot, rec); - page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2); - - /* 4. Finally, we update the number of records field of the - original slot */ - - page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2)); -} - -/*************************************************************//** -Tries to balance the given directory slot with too few records with the upper -neighbor, so that there are at least the minimum number of records owned by -the slot; this may result in the merging of two slots. */ -UNIV_INTERN -void -page_dir_balance_slot( -/*==================*/ - page_t* page, /*!< in/out: index page */ - page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ - ulint slot_no)/*!< in: the directory slot */ -{ - page_dir_slot_t* slot; - page_dir_slot_t* up_slot; - ulint n_owned; - ulint up_n_owned; - rec_t* old_rec; - rec_t* new_rec; - - ut_ad(!page_zip || page_is_comp(page)); - ut_ad(slot_no > 0); - - slot = page_dir_get_nth_slot(page, slot_no); - - /* The last directory slot cannot be balanced with the upper - neighbor, as there is none. */ - - if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) { - - return; - } - - up_slot = page_dir_get_nth_slot(page, slot_no + 1); - - n_owned = page_dir_slot_get_n_owned(slot); - up_n_owned = page_dir_slot_get_n_owned(up_slot); - - ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1); - - /* If the upper slot has the minimum value of n_owned, we will merge - the two slots, therefore we assert: */ - ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED); - - if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) { - - /* In this case we can just transfer one record owned - by the upper slot to the property of the lower slot */ - old_rec = (rec_t*) page_dir_slot_get_rec(slot); - - if (page_is_comp(page)) { - new_rec = rec_get_next_ptr(old_rec, TRUE); - - rec_set_n_owned_new(old_rec, page_zip, 0); - rec_set_n_owned_new(new_rec, page_zip, n_owned + 1); - } else { - new_rec = rec_get_next_ptr(old_rec, FALSE); - - rec_set_n_owned_old(old_rec, 0); - rec_set_n_owned_old(new_rec, n_owned + 1); - } - - page_dir_slot_set_rec(slot, new_rec); - - page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1); - } else { - /* In this case we may merge the two slots */ - page_dir_delete_slot(page, page_zip, slot_no); - } -} - -/************************************************************//** -Returns the nth record of the record list. -This is the inverse function of page_rec_get_n_recs_before(). -@return nth record */ -UNIV_INTERN -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)); - } - - ut_ad(nth < UNIV_PAGE_SIZE / (REC_N_NEW_EXTRA_BYTES + 1)); - - for (i = 0;; i++) { - - slot = page_dir_get_nth_slot(page, i); - n_owned = page_dir_slot_get_n_owned(slot); - - if (n_owned > nth) { - break; - } else { - nth -= n_owned; - } - } - - ut_ad(i > 0); - slot = page_dir_get_nth_slot(page, i - 1); - rec = page_dir_slot_get_rec(slot); - - if (page_is_comp(page)) { - do { - rec = page_rec_get_next_low(rec, TRUE); - ut_ad(rec); - } while (nth--); - } else { - do { - rec = page_rec_get_next_low(rec, FALSE); - ut_ad(rec); - } while (nth--); - } - - return(rec); -} - -/***************************************************************//** -Returns the number of records before the given record in chain. -The number includes infimum and supremum records. -@return number of records */ -UNIV_INTERN -ulint -page_rec_get_n_recs_before( -/*=======================*/ - const rec_t* rec) /*!< in: the physical record */ -{ - const page_dir_slot_t* slot; - const rec_t* slot_rec; - const page_t* page; - ulint i; - lint n = 0; - - ut_ad(page_rec_check(rec)); - - page = page_align(rec); - if (page_is_comp(page)) { - while (rec_get_n_owned_new(rec) == 0) { - - rec = rec_get_next_ptr_const(rec, TRUE); - n--; - } - - for (i = 0; ; i++) { - slot = page_dir_get_nth_slot(page, i); - slot_rec = page_dir_slot_get_rec(slot); - - n += rec_get_n_owned_new(slot_rec); - - if (rec == slot_rec) { - - break; - } - } - } else { - while (rec_get_n_owned_old(rec) == 0) { - - rec = rec_get_next_ptr_const(rec, FALSE); - n--; - } - - for (i = 0; ; i++) { - slot = page_dir_get_nth_slot(page, i); - slot_rec = page_dir_slot_get_rec(slot); - - n += rec_get_n_owned_old(slot_rec); - - if (rec == slot_rec) { - - break; - } - } - } - - n--; - - ut_ad(n >= 0); - ut_ad((ulong) n < UNIV_PAGE_SIZE / (REC_N_NEW_EXTRA_BYTES + 1)); - - return((ulint) n); -} - -#ifndef UNIV_HOTBACKUP -/************************************************************//** -Prints record contents including the data relevant only in -the index page context. */ -UNIV_INTERN -void -page_rec_print( -/*===========*/ - const rec_t* rec, /*!< in: physical record */ - const ulint* offsets)/*!< in: record descriptor */ -{ - ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets)); - rec_print_new(stderr, rec, offsets); - if (page_rec_is_comp(rec)) { - fprintf(stderr, - " n_owned: %lu; heap_no: %lu; next rec: %lu\n", - (ulong) rec_get_n_owned_new(rec), - (ulong) rec_get_heap_no_new(rec), - (ulong) rec_get_next_offs(rec, TRUE)); - } else { - fprintf(stderr, - " n_owned: %lu; heap_no: %lu; next rec: %lu\n", - (ulong) rec_get_n_owned_old(rec), - (ulong) rec_get_heap_no_old(rec), - (ulong) rec_get_next_offs(rec, FALSE)); - } - - page_rec_check(rec); - rec_validate(rec, offsets); -} - -# ifdef UNIV_BTR_PRINT -/***************************************************************//** -This is used to print the contents of the directory for -debugging purposes. */ -UNIV_INTERN -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: %lu; number of slots: %lu\n", - page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)), - (ulong) 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: %lu: n_owned: %lu," - " rec offs: %lu\n", - (ulong) i, - (ulong) page_dir_slot_get_n_owned(slot), - (ulong) - page_offset(page_dir_slot_get_rec(slot))); - } - } - fprintf(stderr, "Total of %lu records\n" - "--------------------------------\n", - (ulong) (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. */ -UNIV_INTERN -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->frame; - page_cur_t cur; - ulint count; - ulint n_recs; - mem_heap_t* heap = NULL; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets = offsets_; - rec_offs_init(offsets_); - - ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); - - fprintf(stderr, - "--------------------------------\n" - "PAGE RECORD LIST\n" - "Page address %p\n", page); - - n_recs = page_get_n_recs(page); - - page_cur_set_before_first(block, &cur); - count = 0; - for (;;) { - offsets = rec_get_offsets(cur.rec, index, offsets, - ULINT_UNDEFINED, &heap); - page_rec_print(cur.rec, offsets); - - if (count == pr_n) { - break; - } - if (page_cur_is_after_last(&cur)) { - break; - } - page_cur_move_to_next(&cur); - count++; - } - - if (n_recs > 2 * pr_n) { - fputs(" ... \n", stderr); - } - - while (!page_cur_is_after_last(&cur)) { - page_cur_move_to_next(&cur); - - if (count + pr_n >= n_recs) { - offsets = rec_get_offsets(cur.rec, index, offsets, - ULINT_UNDEFINED, &heap); - page_rec_print(cur.rec, offsets); - } - count++; - } - - 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. */ -UNIV_INTERN -void -page_header_print( -/*==============*/ - const page_t* page) -{ - fprintf(stderr, - "--------------------------------\n" - "PAGE HEADER INFO\n" - "Page address %p, n records %lu (%s)\n" - "n dir slots %lu, heap top %lu\n" - "Page n heap %lu, free %lu, garbage %lu\n" - "Page last insert %lu, direction %lu, n direction %lu\n", - page, (ulong) page_header_get_field(page, PAGE_N_RECS), - page_is_comp(page) ? "compact format" : "original format", - (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS), - (ulong) page_header_get_field(page, PAGE_HEAP_TOP), - (ulong) page_dir_get_n_heap(page), - (ulong) page_header_get_field(page, PAGE_FREE), - (ulong) page_header_get_field(page, PAGE_GARBAGE), - (ulong) page_header_get_field(page, PAGE_LAST_INSERT), - (ulong) page_header_get_field(page, PAGE_DIRECTION), - (ulong) page_header_get_field(page, PAGE_N_DIRECTION)); -} - -/***************************************************************//** -This is used to print the contents of the page for -debugging purposes. */ -UNIV_INTERN -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->frame; - - page_header_print(page); - page_dir_print(page, dn); - page_print_list(block, index, rn); -} -# endif /* UNIV_BTR_PRINT */ -#endif /* !UNIV_HOTBACKUP */ - -/***************************************************************//** -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. -@return TRUE if ok */ -UNIV_INTERN -ibool -page_rec_validate( -/*==============*/ - const rec_t* rec, /*!< in: physical record */ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ -{ - ulint n_owned; - ulint heap_no; - const page_t* page; - - page = page_align(rec); - ut_a(!page_is_comp(page) == !rec_offs_comp(offsets)); - - page_rec_check(rec); - rec_validate(rec, offsets); - - if (page_rec_is_comp(rec)) { - n_owned = rec_get_n_owned_new(rec); - heap_no = rec_get_heap_no_new(rec); - } else { - n_owned = rec_get_n_owned_old(rec); - heap_no = rec_get_heap_no_old(rec); - } - - if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) { - fprintf(stderr, - "InnoDB: Dir slot of rec %lu, n owned too big %lu\n", - (ulong) page_offset(rec), (ulong) n_owned); - return(FALSE); - } - - if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) { - fprintf(stderr, - "InnoDB: Heap no of rec %lu too big %lu %lu\n", - (ulong) page_offset(rec), (ulong) heap_no, - (ulong) page_dir_get_n_heap(page)); - return(FALSE); - } - - return(TRUE); -} - -#ifndef UNIV_HOTBACKUP -/***************************************************************//** -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. */ -UNIV_INTERN -void -page_check_dir( -/*===========*/ - const page_t* page) /*!< in: index page */ -{ - ulint n_slots; - ulint infimum_offs; - ulint supremum_offs; - - n_slots = page_dir_get_n_slots(page); - infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0)); - supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page, - n_slots - 1)); - - if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) { - - fprintf(stderr, - "InnoDB: Page directory corruption:" - " infimum not pointed to\n"); - buf_page_print(page, 0); - ut_ad(0); - } - - if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) { - - fprintf(stderr, - "InnoDB: Page directory corruption:" - " supremum not pointed to\n"); - buf_page_print(page, 0); - ut_ad(0); - } -} -#endif /* !UNIV_HOTBACKUP */ - -/***************************************************************//** -This function checks the consistency of an index page when we do not -know the index. This is also resilient so that this should never crash -even if the page is total garbage. -@return TRUE if ok */ -UNIV_INTERN -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 > UNIV_PAGE_SIZE / 4)) { - fprintf(stderr, - "InnoDB: Nonsensical number %lu of page dir slots\n", - (ulong) n_slots); - - goto func_exit; - } - - rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP); - - if (UNIV_UNLIKELY(rec_heap_top - > page_dir_get_nth_slot(page, n_slots - 1))) { - - fprintf(stderr, - "InnoDB: Record heap and dir overlap on a page," - " heap top %lu, dir %lu\n", - (ulong) page_header_get_field(page, PAGE_HEAP_TOP), - (ulong) - page_offset(page_dir_get_nth_slot(page, n_slots - 1))); - - goto func_exit; - } - - /* Validate the record list in a loop checking also that it is - consistent with the page record directory. */ - - count = 0; - own_count = 1; - slot_no = 0; - slot = page_dir_get_nth_slot(page, slot_no); - - rec = page_get_infimum_rec(page); - - for (;;) { - if (UNIV_UNLIKELY(rec > rec_heap_top)) { - fprintf(stderr, - "InnoDB: Record %lu is above" - " rec heap top %lu\n", - (ulong)(rec - page), - (ulong)(rec_heap_top - page)); - - goto func_exit; - } - - if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) { - /* This is a record pointed to by a dir slot */ - if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) - != own_count)) { - - fprintf(stderr, - "InnoDB: Wrong owned count %lu, %lu," - " rec %lu\n", - (ulong) rec_get_n_owned_old(rec), - (ulong) own_count, - (ulong)(rec - page)); - - goto func_exit; - } - - if (UNIV_UNLIKELY - (page_dir_slot_get_rec(slot) != rec)) { - fprintf(stderr, - "InnoDB: Dir slot does not point" - " to right rec %lu\n", - (ulong)(rec - page)); - - goto func_exit; - } - - own_count = 0; - - if (!page_rec_is_supremum(rec)) { - slot_no++; - slot = page_dir_get_nth_slot(page, slot_no); - } - } - - if (page_rec_is_supremum(rec)) { - - break; - } - - if (UNIV_UNLIKELY - (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA - || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Next record offset" - " nonsensical %lu for rec %lu\n", - (ulong) rec_get_next_offs(rec, FALSE), - (ulong) (rec - page)); - - goto func_exit; - } - - count++; - - if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Page record list appears" - " to be circular %lu\n", - (ulong) count); - goto func_exit; - } - - rec = page_rec_get_next_const(rec); - own_count++; - } - - if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) { - fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n"); - - goto func_exit; - } - - if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { - fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n", - (ulong) slot_no, (ulong) (n_slots - 1)); - goto func_exit; - } - - if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS) - + PAGE_HEAP_NO_USER_LOW - != count + 1)) { - fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n", - (ulong) page_header_get_field(page, PAGE_N_RECS) - + PAGE_HEAP_NO_USER_LOW, - (ulong) (count + 1)); - - goto func_exit; - } - - /* Check then the free list */ - rec = page_header_get_ptr(page, PAGE_FREE); - - while (rec != NULL) { - if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA - || rec >= page + UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Free list record has" - " a nonsensical offset %lu\n", - (ulong) (rec - page)); - - goto func_exit; - } - - if (UNIV_UNLIKELY(rec > rec_heap_top)) { - fprintf(stderr, - "InnoDB: Free list record %lu" - " is above rec heap top %lu\n", - (ulong) (rec - page), - (ulong) (rec_heap_top - page)); - - goto func_exit; - } - - count++; - - if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Page free list appears" - " to be circular %lu\n", - (ulong) count); - goto func_exit; - } - - rec = page_rec_get_next_const(rec); - } - - if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { - - fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n", - (ulong) page_dir_get_n_heap(page), - (ulong) (count + 1)); - - goto func_exit; - } - - ret = TRUE; - -func_exit: - return(ret); -} - -/***************************************************************//** -This function checks the consistency of an index page when we do not -know the index. This is also resilient so that this should never crash -even if the page is total garbage. -@return TRUE if ok */ -UNIV_INTERN -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 > UNIV_PAGE_SIZE / 4)) { - fprintf(stderr, - "InnoDB: Nonsensical number %lu" - " of page dir slots\n", (ulong) n_slots); - - goto func_exit; - } - - rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP); - - if (UNIV_UNLIKELY(rec_heap_top - > page_dir_get_nth_slot(page, n_slots - 1))) { - - fprintf(stderr, - "InnoDB: Record heap and dir overlap on a page," - " heap top %lu, dir %lu\n", - (ulong) page_header_get_field(page, PAGE_HEAP_TOP), - (ulong) - page_offset(page_dir_get_nth_slot(page, n_slots - 1))); - - goto func_exit; - } - - /* Validate the record list in a loop checking also that it is - consistent with the page record directory. */ - - count = 0; - own_count = 1; - slot_no = 0; - slot = page_dir_get_nth_slot(page, slot_no); - - rec = page_get_infimum_rec(page); - - for (;;) { - if (UNIV_UNLIKELY(rec > rec_heap_top)) { - fprintf(stderr, - "InnoDB: Record %lu is above rec" - " heap top %lu\n", - (ulong) page_offset(rec), - (ulong) page_offset(rec_heap_top)); - - goto func_exit; - } - - if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) { - /* This is a record pointed to by a dir slot */ - if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) - != own_count)) { - - fprintf(stderr, - "InnoDB: Wrong owned count %lu, %lu," - " rec %lu\n", - (ulong) rec_get_n_owned_new(rec), - (ulong) own_count, - (ulong) page_offset(rec)); - - goto func_exit; - } - - if (UNIV_UNLIKELY - (page_dir_slot_get_rec(slot) != rec)) { - fprintf(stderr, - "InnoDB: Dir slot does not point" - " to right rec %lu\n", - (ulong) page_offset(rec)); - - goto func_exit; - } - - own_count = 0; - - if (!page_rec_is_supremum(rec)) { - slot_no++; - slot = page_dir_get_nth_slot(page, slot_no); - } - } - - if (page_rec_is_supremum(rec)) { - - break; - } - - if (UNIV_UNLIKELY - (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA - || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Next record offset nonsensical %lu" - " for rec %lu\n", - (ulong) rec_get_next_offs(rec, TRUE), - (ulong) page_offset(rec)); - - goto func_exit; - } - - count++; - - if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Page record list appears" - " to be circular %lu\n", - (ulong) count); - goto func_exit; - } - - rec = page_rec_get_next_const(rec); - own_count++; - } - - if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) { - fprintf(stderr, "InnoDB: n owned is zero" - " in a supremum rec\n"); - - goto func_exit; - } - - if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { - fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n", - (ulong) slot_no, (ulong) (n_slots - 1)); - goto func_exit; - } - - if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS) - + PAGE_HEAP_NO_USER_LOW - != count + 1)) { - fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n", - (ulong) page_header_get_field(page, PAGE_N_RECS) - + PAGE_HEAP_NO_USER_LOW, - (ulong) (count + 1)); - - goto func_exit; - } - - /* Check then the free list */ - rec = page_header_get_ptr(page, PAGE_FREE); - - while (rec != NULL) { - if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA - || rec >= page + UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Free list record has" - " a nonsensical offset %lu\n", - (ulong) page_offset(rec)); - - goto func_exit; - } - - if (UNIV_UNLIKELY(rec > rec_heap_top)) { - fprintf(stderr, - "InnoDB: Free list record %lu" - " is above rec heap top %lu\n", - (ulong) page_offset(rec), - (ulong) page_offset(rec_heap_top)); - - goto func_exit; - } - - count++; - - if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { - fprintf(stderr, - "InnoDB: Page free list appears" - " to be circular %lu\n", - (ulong) count); - goto func_exit; - } - - rec = page_rec_get_next_const(rec); - } - - if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { - - fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n", - (ulong) page_dir_get_n_heap(page), - (ulong) (count + 1)); - - goto func_exit; - } - - ret = TRUE; - -func_exit: - return(ret); -} - -/***************************************************************//** -This function checks the consistency of an index page. -@return TRUE if ok */ -UNIV_INTERN -ibool -page_validate( -/*==========*/ - const page_t* page, /*!< in: index page */ - dict_index_t* index) /*!< in: data dictionary index containing - the page record type definition */ -{ - const page_dir_slot_t* slot; - mem_heap_t* heap; - byte* buf; - ulint count; - ulint own_count; - ulint rec_own_count; - ulint slot_no; - ulint data_size; - const rec_t* rec; - const rec_t* old_rec = NULL; - ulint offs; - ulint n_slots; - ibool ret = FALSE; - ulint i; - ulint* offsets = NULL; - ulint* old_offsets = NULL; - - if (UNIV_UNLIKELY((ibool) !!page_is_comp(page) - != dict_table_is_comp(index->table))) { - fputs("InnoDB: 'compact format' flag mismatch\n", stderr); - goto func_exit2; - } - 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; - } - } - - if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page) - && !page_is_empty(page)) { - trx_id_t max_trx_id = page_get_max_trx_id(page); - trx_id_t sys_max_trx_id = trx_sys_get_max_trx_id(); - - if (max_trx_id == 0 || max_trx_id > sys_max_trx_id) { - ib_logf(IB_LOG_LEVEL_ERROR, - "PAGE_MAX_TRX_ID out of bounds: " - TRX_ID_FMT ", " TRX_ID_FMT, - max_trx_id, sys_max_trx_id); - goto func_exit2; - } - } - - heap = mem_heap_create(UNIV_PAGE_SIZE + 200); - - /* The following buffer is used to check that the - records in the page record heap do not overlap */ - - buf = static_cast<byte*>(mem_heap_zalloc(heap, UNIV_PAGE_SIZE)); - - /* 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)))) { - - fprintf(stderr, - "InnoDB: Record heap and dir overlap" - " on space %lu page %lu index %s, %p, %p\n", - (ulong) page_get_space_id(page), - (ulong) page_get_page_no(page), index->name, - page_header_get_ptr(page, PAGE_HEAP_TOP), - page_dir_get_nth_slot(page, n_slots - 1)); - - goto func_exit; - } - - /* Validate the record list in a loop checking also that - it is consistent with the directory. */ - count = 0; - data_size = 0; - own_count = 1; - slot_no = 0; - slot = page_dir_get_nth_slot(page, slot_no); - - rec = page_get_infimum_rec(page); - - for (;;) { - offsets = rec_get_offsets(rec, index, offsets, - ULINT_UNDEFINED, &heap); - - if (page_is_comp(page) && page_rec_is_user_rec(rec) - && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec) - == page_is_leaf(page))) { - fputs("InnoDB: node_ptr flag mismatch\n", stderr); - goto func_exit; - } - - if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) { - goto func_exit; - } - -#ifndef UNIV_HOTBACKUP - /* Check that the records are in the ascending order */ - if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW) - && !page_rec_is_supremum(rec)) { - if (UNIV_UNLIKELY - (1 != cmp_rec_rec(rec, old_rec, - offsets, old_offsets, index))) { - fprintf(stderr, - "InnoDB: Records in wrong order" - " on space %lu page %lu index %s\n", - (ulong) page_get_space_id(page), - (ulong) page_get_page_no(page), - index->name); - fputs("\nInnoDB: previous record ", stderr); - rec_print_new(stderr, old_rec, old_offsets); - fputs("\nInnoDB: record ", stderr); - rec_print_new(stderr, rec, offsets); - putc('\n', stderr); - - goto func_exit; - } - } -#endif /* !UNIV_HOTBACKUP */ - - if (page_rec_is_user_rec(rec)) { - - data_size += rec_offs_size(offsets); - } - - offs = page_offset(rec_get_start(rec, offsets)); - i = rec_offs_size(offsets); - if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) { - fputs("InnoDB: record offset out of bounds\n", stderr); - goto func_exit; - } - - while (i--) { - if (UNIV_UNLIKELY(buf[offs + i])) { - /* No other record may overlap this */ - - fputs("InnoDB: Record overlaps another\n", - stderr); - goto func_exit; - } - - buf[offs + i] = 1; - } - - if (page_is_comp(page)) { - rec_own_count = rec_get_n_owned_new(rec); - } else { - rec_own_count = rec_get_n_owned_old(rec); - } - - if (UNIV_UNLIKELY(rec_own_count)) { - /* This is a record pointed to by a dir slot */ - if (UNIV_UNLIKELY(rec_own_count != own_count)) { - fprintf(stderr, - "InnoDB: Wrong owned count %lu, %lu\n", - (ulong) rec_own_count, - (ulong) own_count); - goto func_exit; - } - - if (page_dir_slot_get_rec(slot) != rec) { - fputs("InnoDB: Dir slot does not" - " point to right rec\n", - stderr); - goto func_exit; - } - - page_dir_slot_check(slot); - - own_count = 0; - if (!page_rec_is_supremum(rec)) { - slot_no++; - slot = page_dir_get_nth_slot(page, slot_no); - } - } - - if (page_rec_is_supremum(rec)) { - break; - } - - count++; - own_count++; - old_rec = rec; - rec = page_rec_get_next_const(rec); - - /* set old_offsets to offsets; recycle offsets */ - { - ulint* offs = old_offsets; - old_offsets = offsets; - offsets = offs; - } - } - - if (page_is_comp(page)) { - if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) { - - goto n_owned_zero; - } - } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) { -n_owned_zero: - fputs("InnoDB: n owned is zero\n", stderr); - goto func_exit; - } - - if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { - fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n", - (ulong) slot_no, (ulong) (n_slots - 1)); - goto func_exit; - } - - if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS) - + PAGE_HEAP_NO_USER_LOW - != count + 1)) { - fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n", - (ulong) page_header_get_field(page, PAGE_N_RECS) - + PAGE_HEAP_NO_USER_LOW, - (ulong) (count + 1)); - goto func_exit; - } - - if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) { - fprintf(stderr, - "InnoDB: Summed data size %lu, returned by func %lu\n", - (ulong) data_size, (ulong) page_get_data_size(page)); - goto func_exit; - } - - /* Check then the free list */ - rec = page_header_get_ptr(page, PAGE_FREE); - - while (rec != NULL) { - offsets = rec_get_offsets(rec, index, offsets, - ULINT_UNDEFINED, &heap); - if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) { - - goto func_exit; - } - - count++; - offs = page_offset(rec_get_start(rec, offsets)); - i = rec_offs_size(offsets); - if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) { - fputs("InnoDB: record offset out of bounds\n", stderr); - goto func_exit; - } - - while (i--) { - - if (UNIV_UNLIKELY(buf[offs + i])) { - fputs("InnoDB: Record overlaps another" - " in free list\n", stderr); - goto func_exit; - } - - buf[offs + i] = 1; - } - - rec = page_rec_get_next_const(rec); - } - - if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { - fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n", - (ulong) page_dir_get_n_heap(page), - (ulong) count + 1); - goto func_exit; - } - - ret = TRUE; - -func_exit: - mem_heap_free(heap); - - if (UNIV_UNLIKELY(ret == FALSE)) { -func_exit2: - fprintf(stderr, - "InnoDB: Apparent corruption" - " in space %lu page %lu index %s\n", - (ulong) page_get_space_id(page), - (ulong) page_get_page_no(page), - index->name); - buf_page_print(page, 0); - ut_ad(0); - } - - return(ret); -} - -#ifndef UNIV_HOTBACKUP -/***************************************************************//** -Looks in the page record list for a record with the given heap number. -@return record, NULL if not found */ -UNIV_INTERN -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); - - if (rec_heap_no == heap_no) { - - return(rec); - } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) { - - return(NULL); - } - - rec = page + rec_get_next_offs(rec, TRUE); - } - } else { - rec = page + PAGE_OLD_INFIMUM; - - for (;;) { - ulint rec_heap_no = rec_get_heap_no_old(rec); - - if (rec_heap_no == heap_no) { - - return(rec); - } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) { - - return(NULL); - } - - rec = page + rec_get_next_offs(rec, FALSE); - } - } -} -#endif /* !UNIV_HOTBACKUP */ - -/*******************************************************//** -Removes the record from a leaf page. This function does not log -any changes. It is used by the IMPORT tablespace functions. -The cursor is moved to the next record after the deleted one. -@return true if success, i.e., the page did not become too empty */ -UNIV_INTERN -bool -page_delete_rec( -/*============*/ - const dict_index_t* index, /*!< in: The index that the record - belongs to */ - page_cur_t* pcur, /*!< in/out: page cursor on record - to delete */ - page_zip_des_t* page_zip,/*!< in: compressed page descriptor */ - const ulint* offsets)/*!< in: offsets for record */ -{ - bool no_compress_needed; - buf_block_t* block = pcur->block; - page_t* page = buf_block_get_frame(block); - - ut_ad(page_is_leaf(page)); - - if (!rec_offs_any_extern(offsets) - && ((page_get_data_size(page) - rec_offs_size(offsets) - < BTR_CUR_PAGE_COMPRESS_LIMIT) - || (mach_read_from_4(page + FIL_PAGE_NEXT) == FIL_NULL - && mach_read_from_4(page + FIL_PAGE_PREV) == FIL_NULL) - || (page_get_n_recs(page) < 2))) { - - ulint root_page_no = dict_index_get_page(index); - - /* The page fillfactor will drop below a predefined - minimum value, OR the level in the B-tree contains just - one page, OR the page will become empty: we recommend - compression if this is not the root page. */ - - no_compress_needed = page_get_page_no(page) == root_page_no; - } else { - no_compress_needed = true; - } - - if (no_compress_needed) { -#ifdef UNIV_ZIP_DEBUG - ut_a(!page_zip || page_zip_validate(page_zip, page, index)); -#endif /* UNIV_ZIP_DEBUG */ - - page_cur_delete_rec(pcur, index, offsets, 0); - -#ifdef UNIV_ZIP_DEBUG - ut_a(!page_zip || page_zip_validate(page_zip, page, index)); -#endif /* UNIV_ZIP_DEBUG */ - } - - return(no_compress_needed); -} - -/** Get the last non-delete-marked record on a page. -@param[in] page index tree leaf page -@return the last record, not delete-marked -@retval infimum record if all records are delete-marked */ - -const rec_t* -page_find_rec_max_not_deleted( - const page_t* page) -{ - const rec_t* rec = page_get_infimum_rec(page); - const rec_t* prev_rec = NULL; // remove warning - - /* Because the page infimum is never delete-marked, - prev_rec will always be assigned to it first. */ - ut_ad(!rec_get_deleted_flag(rec, page_rec_is_comp(rec))); - if (page_is_comp(page)) { - do { - if (!rec_get_deleted_flag(rec, true)) { - prev_rec = rec; - } - rec = page_rec_get_next_low(rec, true); - } while (rec != page + PAGE_NEW_SUPREMUM); - } else { - do { - if (!rec_get_deleted_flag(rec, false)) { - prev_rec = rec; - } - rec = page_rec_get_next_low(rec, false); - } while (rec != page + PAGE_OLD_SUPREMUM); - } - return(prev_rec); -} - -#endif /* #ifndef UNIV_INNOCHECKSUM */ - -/** Issue a warning when the checksum that is stored in the page is valid, -but different than the global setting innodb_checksum_algorithm. -@param[in] current_algo current checksum algorithm -@param[in] page_checksum page valid checksum -@param[in] space_id tablespace id -@param[in] page_no page number */ -void -page_warn_strict_checksum( - srv_checksum_algorithm_t curr_algo, - srv_checksum_algorithm_t page_checksum, - ulint space_id, - ulint page_no) -{ - srv_checksum_algorithm_t curr_algo_nonstrict; - switch (curr_algo) { - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_CRC32; - break; - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_INNODB; - break; - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_NONE; - break; - default: - ut_error; - } - -#ifdef UNIV_INNOCHECKSUM - fprintf(stderr, -#else - ib_logf(IB_LOG_LEVEL_WARN, -#endif - "innodb_checksum_algorithm is set to \"%s\"" - " but the page [page id: space=" ULINTPF "," - " page number=" ULINTPF "] contains a valid checksum \"%s\"." - " Accepting the page as valid. Change innodb_checksum_algorithm" - " to \"%s\" to silently accept such pages or rewrite all pages" - " so that they contain \"%s\" checksum.", - buf_checksum_algorithm_name(curr_algo), - space_id, page_no, - buf_checksum_algorithm_name(page_checksum), - buf_checksum_algorithm_name(curr_algo_nonstrict), - buf_checksum_algorithm_name(curr_algo_nonstrict)); -} |