mirror of
https://github.com/MariaDB/server.git
synced 2025-01-22 14:54:20 +01:00
f9cc391863
This only merges MDEV-12253, adapting it to MDEV-12602 which is already present in 10.2 but not yet in the 10.1 revision that is being merged. TODO: Error handling in crash recovery needs to be improved. If a page cannot be decrypted (or read), we should cleanly abort the startup. If innodb_force_recovery is specified, we should ignore the problematic page and apply redo log to other pages. Currently, the test encryption.innodb-redo-badkey randomly fails like this (the last messages are from cmake -DWITH_ASAN): 2017-05-05 10:19:40 140037071685504 [Note] InnoDB: Starting crash recovery from checkpoint LSN=1635994 2017-05-05 10:19:40 140037071685504 [ERROR] InnoDB: Missing MLOG_FILE_NAME or MLOG_FILE_DELETE before MLOG_CHECKPOINT for tablespace 1 2017-05-05 10:19:40 140037071685504 [ERROR] InnoDB: Plugin initialization aborted at srv0start.cc[2201] with error Data structure corruption 2017-05-05 10:19:41 140037071685504 [Note] InnoDB: Starting shutdown... i================================================================= ==5226==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x612000018588 in thread T0 #0 0x736750 in operator delete(void*) (/mariadb/server/build/sql/mysqld+0x736750) #1 0x1e4833f in LatchCounter::~LatchCounter() /mariadb/server/storage/innobase/include/sync0types.h:599:4 #2 0x1e480b8 in LatchMeta<LatchCounter>::~LatchMeta() /mariadb/server/storage/innobase/include/sync0types.h:786:17 #3 0x1e35509 in sync_latch_meta_destroy() /mariadb/server/storage/innobase/sync/sync0debug.cc:1622:3 #4 0x1e35314 in sync_check_close() /mariadb/server/storage/innobase/sync/sync0debug.cc:1839:2 #5 0x1dfdc18 in innodb_shutdown() /mariadb/server/storage/innobase/srv/srv0start.cc:2888:2 #6 0x197e5e6 in innobase_init(void*) /mariadb/server/storage/innobase/handler/ha_innodb.cc:4475:3
337 lines
8.8 KiB
Text
337 lines
8.8 KiB
Text
/*****************************************************************************
|
|
|
|
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
|
Copyright (c) 2015, 2017, MariaDB Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/**************************************************//**
|
|
@file include/btr0btr.ic
|
|
The B-tree
|
|
|
|
Created 6/2/1994 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#include "mach0data.h"
|
|
#include "mtr0mtr.h"
|
|
#include "mtr0log.h"
|
|
#include "page0zip.h"
|
|
|
|
#define BTR_MAX_NODE_LEVEL 50 /*!< Maximum B-tree page level
|
|
(not really a hard limit).
|
|
Used in debug assertions
|
|
in btr_page_set_level and
|
|
btr_page_get_level_low */
|
|
|
|
/** Gets a buffer page and declares its latching order level.
|
|
@param[in] page_id page id
|
|
@param[in] mode latch mode
|
|
@param[in] file file name
|
|
@param[in] line line where called
|
|
@param[in] index index tree, may be NULL if it is not an insert buffer
|
|
tree
|
|
@param[in,out] mtr mini-transaction
|
|
@return block */
|
|
UNIV_INLINE
|
|
buf_block_t*
|
|
btr_block_get_func(
|
|
const page_id_t& page_id,
|
|
const page_size_t& page_size,
|
|
ulint mode,
|
|
const char* file,
|
|
unsigned line,
|
|
dict_index_t* index,
|
|
mtr_t* mtr)
|
|
{
|
|
buf_block_t* block;
|
|
dberr_t err=DB_SUCCESS;
|
|
|
|
block = buf_page_get_gen(
|
|
page_id, page_size, mode, NULL, BUF_GET, file, line, mtr, &err);
|
|
|
|
if (err == DB_DECRYPTION_FAILED) {
|
|
if (index && index->table) {
|
|
index->table->file_unreadable = true;
|
|
}
|
|
}
|
|
|
|
if (block) {
|
|
if (mode != RW_NO_LATCH) {
|
|
|
|
buf_block_dbg_add_level(
|
|
block, index != NULL && dict_index_is_ibuf(index)
|
|
? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE);
|
|
}
|
|
}
|
|
|
|
return(block);
|
|
}
|
|
|
|
/**************************************************************//**
|
|
Sets the index id field of a page. */
|
|
UNIV_INLINE
|
|
void
|
|
btr_page_set_index_id(
|
|
/*==================*/
|
|
page_t* page, /*!< in: page to be created */
|
|
page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed
|
|
part will be updated, or NULL */
|
|
index_id_t id, /*!< in: index id */
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
{
|
|
if (page_zip) {
|
|
mach_write_to_8(page + (PAGE_HEADER + PAGE_INDEX_ID), id);
|
|
page_zip_write_header(page_zip,
|
|
page + (PAGE_HEADER + PAGE_INDEX_ID),
|
|
8, mtr);
|
|
} else {
|
|
mlog_write_ull(page + (PAGE_HEADER + PAGE_INDEX_ID), id, mtr);
|
|
}
|
|
}
|
|
|
|
/** Gets a buffer page and declares its latching order level.
|
|
@param space tablespace identifier
|
|
@param zip_size compressed page size in bytes or 0 for uncompressed pages
|
|
@param page_no page number
|
|
@param mode latch mode
|
|
@param idx index tree, may be NULL if not the insert buffer tree
|
|
@param mtr mini-transaction handle
|
|
@return the uncompressed page frame */
|
|
UNIV_INLINE
|
|
page_t*
|
|
btr_page_get(
|
|
/*=========*/
|
|
const page_id_t& page_id,
|
|
const page_size_t& page_size,
|
|
ulint mode,
|
|
dict_index_t* index,
|
|
mtr_t* mtr)
|
|
{
|
|
buf_block_t* block=NULL;
|
|
buf_frame_t* frame=NULL;
|
|
|
|
block = btr_block_get(page_id, page_size, mode, index, mtr);
|
|
|
|
if (block) {
|
|
frame = buf_block_get_frame(block);
|
|
}
|
|
|
|
return ((page_t*)frame);
|
|
}
|
|
|
|
/**************************************************************//**
|
|
Gets the index id field of a page.
|
|
@return index id */
|
|
UNIV_INLINE
|
|
index_id_t
|
|
btr_page_get_index_id(
|
|
/*==================*/
|
|
const page_t* page) /*!< in: index page */
|
|
{
|
|
return(mach_read_from_8(page + PAGE_HEADER + PAGE_INDEX_ID));
|
|
}
|
|
|
|
/********************************************************//**
|
|
Gets the node level field in an index page.
|
|
@return level, leaf level == 0 */
|
|
UNIV_INLINE
|
|
ulint
|
|
btr_page_get_level_low(
|
|
/*===================*/
|
|
const page_t* page) /*!< in: index page */
|
|
{
|
|
ulint level;
|
|
|
|
ut_ad(page);
|
|
|
|
level = mach_read_from_2(page + PAGE_HEADER + PAGE_LEVEL);
|
|
|
|
ut_ad(level <= BTR_MAX_NODE_LEVEL);
|
|
|
|
return(level);
|
|
}
|
|
|
|
/********************************************************//**
|
|
Sets the node level field in an index page. */
|
|
UNIV_INLINE
|
|
void
|
|
btr_page_set_level(
|
|
/*===============*/
|
|
page_t* page, /*!< in: index page */
|
|
page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed
|
|
part will be updated, or NULL */
|
|
ulint level, /*!< in: level, leaf level == 0 */
|
|
mtr_t* mtr) /*!< in: mini-transaction handle */
|
|
{
|
|
ut_ad(page != NULL);
|
|
ut_ad(mtr != NULL);
|
|
ut_ad(level <= BTR_MAX_NODE_LEVEL);
|
|
|
|
if (page_zip) {
|
|
mach_write_to_2(page + (PAGE_HEADER + PAGE_LEVEL), level);
|
|
page_zip_write_header(page_zip,
|
|
page + (PAGE_HEADER + PAGE_LEVEL),
|
|
2, mtr);
|
|
} else {
|
|
mlog_write_ulint(page + (PAGE_HEADER + PAGE_LEVEL), level,
|
|
MLOG_2BYTES, mtr);
|
|
}
|
|
}
|
|
|
|
/********************************************************//**
|
|
Gets the next index page number.
|
|
@return next page number */
|
|
UNIV_INLINE
|
|
ulint
|
|
btr_page_get_next(
|
|
/*==============*/
|
|
const page_t* page, /*!< in: index page */
|
|
mtr_t* mtr MY_ATTRIBUTE((unused)))
|
|
/*!< in: mini-transaction handle */
|
|
{
|
|
ut_ad(page != NULL);
|
|
ut_ad(mtr != NULL);
|
|
|
|
return(mach_read_from_4(page + FIL_PAGE_NEXT));
|
|
}
|
|
|
|
/********************************************************//**
|
|
Sets the next index page field. */
|
|
UNIV_INLINE
|
|
void
|
|
btr_page_set_next(
|
|
/*==============*/
|
|
page_t* page, /*!< in: index page */
|
|
page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed
|
|
part will be updated, or NULL */
|
|
ulint next, /*!< in: next page number */
|
|
mtr_t* mtr) /*!< in: mini-transaction handle */
|
|
{
|
|
ut_ad(page != NULL);
|
|
ut_ad(mtr != NULL);
|
|
|
|
if (page_zip) {
|
|
mach_write_to_4(page + FIL_PAGE_NEXT, next);
|
|
page_zip_write_header(page_zip, page + FIL_PAGE_NEXT, 4, mtr);
|
|
} else {
|
|
mlog_write_ulint(page + FIL_PAGE_NEXT, next, MLOG_4BYTES, mtr);
|
|
}
|
|
}
|
|
|
|
/********************************************************//**
|
|
Gets the previous index page number.
|
|
@return prev page number */
|
|
UNIV_INLINE
|
|
ulint
|
|
btr_page_get_prev(
|
|
/*==============*/
|
|
const page_t* page, /*!< in: index page */
|
|
mtr_t* mtr MY_ATTRIBUTE((unused))) /*!< in: mini-transaction handle */
|
|
{
|
|
ut_ad(page != NULL);
|
|
ut_ad(mtr != NULL);
|
|
|
|
return(mach_read_from_4(page + FIL_PAGE_PREV));
|
|
}
|
|
|
|
/********************************************************//**
|
|
Sets the previous index page field. */
|
|
UNIV_INLINE
|
|
void
|
|
btr_page_set_prev(
|
|
/*==============*/
|
|
page_t* page, /*!< in: index page */
|
|
page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed
|
|
part will be updated, or NULL */
|
|
ulint prev, /*!< in: previous page number */
|
|
mtr_t* mtr) /*!< in: mini-transaction handle */
|
|
{
|
|
ut_ad(page != NULL);
|
|
ut_ad(mtr != NULL);
|
|
|
|
if (page_zip) {
|
|
mach_write_to_4(page + FIL_PAGE_PREV, prev);
|
|
page_zip_write_header(page_zip, page + FIL_PAGE_PREV, 4, mtr);
|
|
} else {
|
|
mlog_write_ulint(page + FIL_PAGE_PREV, prev, MLOG_4BYTES, mtr);
|
|
}
|
|
}
|
|
|
|
/**************************************************************//**
|
|
Gets the child node file address in a node pointer.
|
|
NOTE: the offsets array must contain all offsets for the record since
|
|
we read the last field according to offsets and assume that it contains
|
|
the child page number. In other words offsets must have been retrieved
|
|
with rec_get_offsets(n_fields=ULINT_UNDEFINED).
|
|
@return child node address */
|
|
UNIV_INLINE
|
|
ulint
|
|
btr_node_ptr_get_child_page_no(
|
|
/*===========================*/
|
|
const rec_t* rec, /*!< in: node pointer record */
|
|
const ulint* offsets)/*!< in: array returned by rec_get_offsets() */
|
|
{
|
|
const byte* field;
|
|
ulint len;
|
|
ulint page_no;
|
|
|
|
ut_ad(!rec_offs_comp(offsets) || rec_get_node_ptr_flag(rec));
|
|
|
|
/* The child address is in the last field */
|
|
field = rec_get_nth_field(rec, offsets,
|
|
rec_offs_n_fields(offsets) - 1, &len);
|
|
|
|
ut_ad(len == 4);
|
|
|
|
page_no = mach_read_from_4(field);
|
|
ut_ad(page_no > 1);
|
|
|
|
return(page_no);
|
|
}
|
|
|
|
/**************************************************************//**
|
|
Releases the latches on a leaf page and bufferunfixes it. */
|
|
UNIV_INLINE
|
|
void
|
|
btr_leaf_page_release(
|
|
/*==================*/
|
|
buf_block_t* block, /*!< in: buffer block */
|
|
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or
|
|
BTR_MODIFY_LEAF */
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
{
|
|
ut_ad(latch_mode == BTR_SEARCH_LEAF
|
|
|| latch_mode == BTR_MODIFY_LEAF
|
|
|| latch_mode == BTR_NO_LATCHES);
|
|
|
|
ut_ad(!mtr_memo_contains(mtr, block, MTR_MEMO_MODIFY));
|
|
|
|
ulint mode;
|
|
switch (latch_mode) {
|
|
case BTR_SEARCH_LEAF:
|
|
mode = MTR_MEMO_PAGE_S_FIX;
|
|
break;
|
|
case BTR_MODIFY_LEAF:
|
|
mode = MTR_MEMO_PAGE_X_FIX;
|
|
break;
|
|
case BTR_NO_LATCHES:
|
|
mode = MTR_MEMO_BUF_FIX;
|
|
break;
|
|
default:
|
|
ut_a(0);
|
|
}
|
|
|
|
mtr->memo_release(block, mode);
|
|
}
|