mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			745 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			745 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*****************************************************************************
 | 
						|
 | 
						|
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
 | 
						|
Copyright (c) 2013, 2022, MariaDB Corporation.
 | 
						|
 | 
						|
This program is free software; you can redistribute it and/or modify it under
 | 
						|
the terms of the GNU General Public License as published by the Free Software
 | 
						|
Foundation; version 2 of the License.
 | 
						|
 | 
						|
This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
						|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License along with
 | 
						|
this program; if not, write to the Free Software Foundation, Inc.,
 | 
						|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
 | 
						|
 | 
						|
*****************************************************************************/
 | 
						|
 | 
						|
/**************************************************//**
 | 
						|
@file include/fsp0fsp.h
 | 
						|
File space management
 | 
						|
 | 
						|
Created 12/18/1995 Heikki Tuuri
 | 
						|
*******************************************************/
 | 
						|
 | 
						|
#ifndef fsp0fsp_h
 | 
						|
#define fsp0fsp_h
 | 
						|
 | 
						|
#include "assume_aligned.h"
 | 
						|
#include "fsp0types.h"
 | 
						|
#include "fut0lst.h"
 | 
						|
#include "ut0byte.h"
 | 
						|
 | 
						|
#ifndef UNIV_INNOCHECKSUM
 | 
						|
#include "mtr0mtr.h"
 | 
						|
#include "page0types.h"
 | 
						|
#include "rem0types.h"
 | 
						|
#else
 | 
						|
# include "mach0data.h"
 | 
						|
#endif /* !UNIV_INNOCHECKSUM */
 | 
						|
 | 
						|
/** @return the PAGE_SSIZE flags for the current innodb_page_size */
 | 
						|
#define FSP_FLAGS_PAGE_SSIZE()						\
 | 
						|
	((srv_page_size == UNIV_PAGE_SIZE_ORIG) ?			\
 | 
						|
	 0U : (srv_page_size_shift - UNIV_ZIP_SIZE_SHIFT_MIN + 1)	\
 | 
						|
	 << FSP_FLAGS_POS_PAGE_SSIZE)
 | 
						|
 | 
						|
/** @return the PAGE_SSIZE flags for the current innodb_page_size in
 | 
						|
full checksum format */
 | 
						|
#define FSP_FLAGS_FCRC32_PAGE_SSIZE()					\
 | 
						|
	((srv_page_size_shift - UNIV_ZIP_SIZE_SHIFT_MIN + 1)		\
 | 
						|
	<< FSP_FLAGS_FCRC32_POS_PAGE_SSIZE)
 | 
						|
 | 
						|
/* @defgroup Compatibility macros for MariaDB 10.1.0 through 10.1.20;
 | 
						|
see the table in fsp0types.h @{ */
 | 
						|
/** Zero relative shift position of the PAGE_COMPRESSION field */
 | 
						|
#define FSP_FLAGS_POS_PAGE_COMPRESSION_MARIADB101	\
 | 
						|
	(FSP_FLAGS_POS_ATOMIC_BLOBS			\
 | 
						|
	 + FSP_FLAGS_WIDTH_ATOMIC_BLOBS)
 | 
						|
/** Zero relative shift position of the PAGE_COMPRESSION_LEVEL field */
 | 
						|
#define FSP_FLAGS_POS_PAGE_COMPRESSION_LEVEL_MARIADB101	\
 | 
						|
	(FSP_FLAGS_POS_PAGE_COMPRESSION_MARIADB101 + 1)
 | 
						|
/** Zero relative shift position of the ATOMIC_WRITES field */
 | 
						|
#define FSP_FLAGS_POS_ATOMIC_WRITES_MARIADB101		\
 | 
						|
	(FSP_FLAGS_POS_PAGE_COMPRESSION_LEVEL_MARIADB101 + 4)
 | 
						|
/** Zero relative shift position of the PAGE_SSIZE field */
 | 
						|
#define FSP_FLAGS_POS_PAGE_SSIZE_MARIADB101		\
 | 
						|
	(FSP_FLAGS_POS_ATOMIC_WRITES_MARIADB101 + 2)
 | 
						|
 | 
						|
/** Bit mask of the PAGE_COMPRESSION field */
 | 
						|
#define FSP_FLAGS_MASK_PAGE_COMPRESSION_MARIADB101		\
 | 
						|
	(1U << FSP_FLAGS_POS_PAGE_COMPRESSION_MARIADB101)
 | 
						|
/** Bit mask of the PAGE_COMPRESSION_LEVEL field */
 | 
						|
#define FSP_FLAGS_MASK_PAGE_COMPRESSION_LEVEL_MARIADB101	\
 | 
						|
	(15U << FSP_FLAGS_POS_PAGE_COMPRESSION_LEVEL_MARIADB101)
 | 
						|
/** Bit mask of the ATOMIC_WRITES field */
 | 
						|
#define FSP_FLAGS_MASK_ATOMIC_WRITES_MARIADB101			\
 | 
						|
	(3U << FSP_FLAGS_POS_ATOMIC_WRITES_MARIADB101)
 | 
						|
/** Bit mask of the PAGE_SSIZE field */
 | 
						|
#define FSP_FLAGS_MASK_PAGE_SSIZE_MARIADB101			\
 | 
						|
	(15U << FSP_FLAGS_POS_PAGE_SSIZE_MARIADB101)
 | 
						|
 | 
						|
/** Return the value of the PAGE_COMPRESSION field */
 | 
						|
#define FSP_FLAGS_GET_PAGE_COMPRESSION_MARIADB101(flags)	\
 | 
						|
		((flags & FSP_FLAGS_MASK_PAGE_COMPRESSION_MARIADB101)	\
 | 
						|
		>> FSP_FLAGS_POS_PAGE_COMPRESSION_MARIADB101)
 | 
						|
/** Return the value of the PAGE_COMPRESSION_LEVEL field */
 | 
						|
#define FSP_FLAGS_GET_PAGE_COMPRESSION_LEVEL_MARIADB101(flags)	\
 | 
						|
		((flags & FSP_FLAGS_MASK_PAGE_COMPRESSION_LEVEL_MARIADB101) \
 | 
						|
		>> FSP_FLAGS_POS_PAGE_COMPRESSION_LEVEL_MARIADB101)
 | 
						|
/** Return the value of the PAGE_SSIZE field */
 | 
						|
#define FSP_FLAGS_GET_PAGE_SSIZE_MARIADB101(flags)		\
 | 
						|
		((flags & FSP_FLAGS_MASK_PAGE_SSIZE_MARIADB101)	\
 | 
						|
		>> FSP_FLAGS_POS_PAGE_SSIZE_MARIADB101)
 | 
						|
 | 
						|
/* @} */
 | 
						|
 | 
						|
/* @defgroup Tablespace Header Constants (moved from fsp0fsp.c) @{ */
 | 
						|
 | 
						|
/** Offset of the space header within a file page */
 | 
						|
#define FSP_HEADER_OFFSET	FIL_PAGE_DATA
 | 
						|
 | 
						|
/* The data structures in files are defined just as byte strings in C */
 | 
						|
typedef	byte	xdes_t;
 | 
						|
 | 
						|
/*			SPACE HEADER
 | 
						|
			============
 | 
						|
 | 
						|
File space header data structure: this data structure is contained in the
 | 
						|
first page of a space. The space for this header is reserved in every extent
 | 
						|
descriptor page, but used only in the first. */
 | 
						|
 | 
						|
/*-------------------------------------*/
 | 
						|
#define FSP_SPACE_ID		0	/* space id */
 | 
						|
#define FSP_NOT_USED		4	/* this field contained a value up to
 | 
						|
					which we know that the modifications
 | 
						|
					in the database have been flushed to
 | 
						|
					the file space; not used now */
 | 
						|
#define	FSP_SIZE		8	/* Current size of the space in
 | 
						|
					pages */
 | 
						|
#define	FSP_FREE_LIMIT		12	/* Minimum page number for which the
 | 
						|
					free list has not been initialized:
 | 
						|
					the pages >= this limit are, by
 | 
						|
					definition, free; note that in a
 | 
						|
					single-table tablespace where size
 | 
						|
					< 64 pages, this number is 64, i.e.,
 | 
						|
					we have initialized the space
 | 
						|
					about the first extent, but have not
 | 
						|
					physically allocated those pages to the
 | 
						|
					file */
 | 
						|
#define	FSP_SPACE_FLAGS		16	/* fsp_space_t.flags, similar to
 | 
						|
					dict_table_t::flags */
 | 
						|
#define	FSP_FRAG_N_USED		20	/* number of used pages in the
 | 
						|
					FSP_FREE_FRAG list */
 | 
						|
#define	FSP_FREE		24	/* list of free extents */
 | 
						|
#define	FSP_FREE_FRAG		(24 + FLST_BASE_NODE_SIZE)
 | 
						|
					/* list of partially free extents not
 | 
						|
					belonging to any segment */
 | 
						|
#define	FSP_FULL_FRAG		(24 + 2 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* list of full extents not belonging
 | 
						|
					to any segment */
 | 
						|
#define FSP_SEG_ID		(24 + 3 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* 8 bytes which give the first unused
 | 
						|
					segment id */
 | 
						|
#define FSP_SEG_INODES_FULL	(32 + 3 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* list of pages containing segment
 | 
						|
					headers, where all the segment inode
 | 
						|
					slots are reserved */
 | 
						|
#define FSP_SEG_INODES_FREE	(32 + 4 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* list of pages containing segment
 | 
						|
					headers, where not all the segment
 | 
						|
					header slots are reserved */
 | 
						|
/*-------------------------------------*/
 | 
						|
/* File space header size */
 | 
						|
#define	FSP_HEADER_SIZE		(32 + 5 * FLST_BASE_NODE_SIZE)
 | 
						|
 | 
						|
#define	FSP_FREE_ADD		4	/* this many free extents are added
 | 
						|
					to the free list from above
 | 
						|
					FSP_FREE_LIMIT at a time */
 | 
						|
/* @} */
 | 
						|
 | 
						|
/* @defgroup File Segment Inode Constants (moved from fsp0fsp.c) @{ */
 | 
						|
 | 
						|
/*			FILE SEGMENT INODE
 | 
						|
			==================
 | 
						|
 | 
						|
Segment inode which is created for each segment in a tablespace. NOTE: in
 | 
						|
purge we assume that a segment having only one currently used page can be
 | 
						|
freed in a few steps, so that the freeing cannot fill the file buffer with
 | 
						|
bufferfixed file pages. */
 | 
						|
 | 
						|
typedef	byte	fseg_inode_t;
 | 
						|
 | 
						|
#define FSEG_INODE_PAGE_NODE	FSEG_PAGE_DATA
 | 
						|
					/* the list node for linking
 | 
						|
					segment inode pages */
 | 
						|
 | 
						|
#define FSEG_ARR_OFFSET		(FSEG_PAGE_DATA + FLST_NODE_SIZE)
 | 
						|
/*-------------------------------------*/
 | 
						|
#define	FSEG_ID			0	/* 8 bytes of segment id: if this is 0,
 | 
						|
					it means that the header is unused */
 | 
						|
#define FSEG_NOT_FULL_N_USED	8
 | 
						|
					/* number of used segment pages in
 | 
						|
					the FSEG_NOT_FULL list */
 | 
						|
#define	FSEG_FREE		12
 | 
						|
					/* list of free extents of this
 | 
						|
					segment */
 | 
						|
#define	FSEG_NOT_FULL		(12 + FLST_BASE_NODE_SIZE)
 | 
						|
					/* list of partially free extents */
 | 
						|
#define	FSEG_FULL		(12 + 2 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* list of full extents */
 | 
						|
#define	FSEG_MAGIC_N		(12 + 3 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* magic number used in debugging */
 | 
						|
#define	FSEG_FRAG_ARR		(16 + 3 * FLST_BASE_NODE_SIZE)
 | 
						|
					/* array of individual pages
 | 
						|
					belonging to this segment in fsp
 | 
						|
					fragment extent lists */
 | 
						|
#define FSEG_FRAG_ARR_N_SLOTS	(FSP_EXTENT_SIZE / 2)
 | 
						|
					/* number of slots in the array for
 | 
						|
					the fragment pages */
 | 
						|
#define	FSEG_FRAG_SLOT_SIZE	4	/* a fragment page slot contains its
 | 
						|
					page number within space, FIL_NULL
 | 
						|
					means that the slot is not in use */
 | 
						|
/*-------------------------------------*/
 | 
						|
#define FSEG_INODE_SIZE					\
 | 
						|
	(16 + 3 * FLST_BASE_NODE_SIZE			\
 | 
						|
	 + FSEG_FRAG_ARR_N_SLOTS * FSEG_FRAG_SLOT_SIZE)
 | 
						|
 | 
						|
static constexpr byte FSEG_MAGIC_N_BYTES[4]={0x05,0xd6,0x69,0xd2};
 | 
						|
 | 
						|
#define	FSEG_FREE_LIST_LIMIT	40	/* If the reserved size of a segment
 | 
						|
					is at least this many extents, we
 | 
						|
					allow extents to be put to the free
 | 
						|
					list of the extent: at most
 | 
						|
					FSEG_FREE_LIST_MAX_LEN many */
 | 
						|
#define	FSEG_FREE_LIST_MAX_LEN	4
 | 
						|
/* @} */
 | 
						|
 | 
						|
/* @defgroup Extent Descriptor Constants (moved from fsp0fsp.c) @{ */
 | 
						|
 | 
						|
/*			EXTENT DESCRIPTOR
 | 
						|
			=================
 | 
						|
 | 
						|
File extent descriptor data structure: contains bits to tell which pages in
 | 
						|
the extent are free and which contain old tuple version to clean. */
 | 
						|
 | 
						|
/*-------------------------------------*/
 | 
						|
#define	XDES_ID			0	/* The identifier of the segment
 | 
						|
					to which this extent belongs */
 | 
						|
#define XDES_FLST_NODE		8	/* The list node data structure
 | 
						|
					for the descriptors */
 | 
						|
#define	XDES_STATE		(FLST_NODE_SIZE + 8)
 | 
						|
					/* contains state information
 | 
						|
					of the extent */
 | 
						|
#define	XDES_BITMAP		(FLST_NODE_SIZE + 12)
 | 
						|
					/* Descriptor bitmap of the pages
 | 
						|
					in the extent */
 | 
						|
/*-------------------------------------*/
 | 
						|
 | 
						|
#define	XDES_BITS_PER_PAGE	2	/* How many bits are there per page */
 | 
						|
#define	XDES_FREE_BIT		0	/* Index of the bit which tells if
 | 
						|
					the page is free */
 | 
						|
#define	XDES_CLEAN_BIT		1	/* NOTE: currently not used!
 | 
						|
					Index of the bit which tells if
 | 
						|
					there are old versions of tuples
 | 
						|
					on the page */
 | 
						|
/* States of a descriptor */
 | 
						|
#define	XDES_FREE		1	/* extent is in free list of space */
 | 
						|
#define	XDES_FREE_FRAG		2	/* extent is in free fragment list of
 | 
						|
					space */
 | 
						|
#define	XDES_FULL_FRAG		3	/* extent is in full fragment list of
 | 
						|
					space */
 | 
						|
#define	XDES_FSEG		4	/* extent belongs to a segment */
 | 
						|
 | 
						|
/** File extent data structure size in bytes. */
 | 
						|
#define	XDES_SIZE							\
 | 
						|
	(XDES_BITMAP							\
 | 
						|
	+ UT_BITS_IN_BYTES(FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE))
 | 
						|
 | 
						|
/** File extent data structure size in bytes for MAX page size. */
 | 
						|
#define	XDES_SIZE_MAX							\
 | 
						|
	(XDES_BITMAP							\
 | 
						|
	+ UT_BITS_IN_BYTES(FSP_EXTENT_SIZE_MAX * XDES_BITS_PER_PAGE))
 | 
						|
 | 
						|
/** File extent data structure size in bytes for MIN page size. */
 | 
						|
#define	XDES_SIZE_MIN							\
 | 
						|
	(XDES_BITMAP							\
 | 
						|
	+ UT_BITS_IN_BYTES(FSP_EXTENT_SIZE_MIN * XDES_BITS_PER_PAGE))
 | 
						|
 | 
						|
/** Offset of the descriptor array on a descriptor page */
 | 
						|
#define	XDES_ARR_OFFSET		(FSP_HEADER_OFFSET + FSP_HEADER_SIZE)
 | 
						|
 | 
						|
/**
 | 
						|
Determine if a page is marked free.
 | 
						|
@param[in]	descr	extent descriptor
 | 
						|
@param[in]	offset	page offset within extent
 | 
						|
@return whether the page is free */
 | 
						|
inline bool xdes_is_free(const xdes_t *descr, uint32_t offset)
 | 
						|
{
 | 
						|
  ut_ad(offset < FSP_EXTENT_SIZE);
 | 
						|
  ulint index= XDES_FREE_BIT + XDES_BITS_PER_PAGE * offset;
 | 
						|
  return ut_bit_get_nth(descr[XDES_BITMAP + (index >> 3)], index & 7);
 | 
						|
}
 | 
						|
 | 
						|
#ifndef UNIV_INNOCHECKSUM
 | 
						|
/* @} */
 | 
						|
 | 
						|
/** Read a tablespace header field.
 | 
						|
@param[in]	page	first page of a tablespace
 | 
						|
@param[in]	field	the header field
 | 
						|
@return the contents of the header field */
 | 
						|
inline uint32_t fsp_header_get_field(const page_t* page, ulint field)
 | 
						|
{
 | 
						|
  return mach_read_from_4(FSP_HEADER_OFFSET + field +
 | 
						|
			  my_assume_aligned<UNIV_ZIP_SIZE_MIN>(page));
 | 
						|
}
 | 
						|
 | 
						|
/** Read the flags from the tablespace header page.
 | 
						|
@param[in]	page	first page of a tablespace
 | 
						|
@return the contents of FSP_SPACE_FLAGS */
 | 
						|
inline uint32_t fsp_header_get_flags(const page_t *page)
 | 
						|
{
 | 
						|
  return fsp_header_get_field(page, FSP_SPACE_FLAGS);
 | 
						|
}
 | 
						|
 | 
						|
/** Get the byte offset of encryption information in page 0.
 | 
						|
@param[in]	zip_size	ROW_FORMAT=COMPRESSED page size, or 0
 | 
						|
@return	byte offset relative to FSP_HEADER_OFFSET */
 | 
						|
inline MY_ATTRIBUTE((pure, warn_unused_result))
 | 
						|
ulint fsp_header_get_encryption_offset(ulint zip_size)
 | 
						|
{
 | 
						|
	return zip_size
 | 
						|
		? XDES_ARR_OFFSET + XDES_SIZE * zip_size / FSP_EXTENT_SIZE
 | 
						|
		: XDES_ARR_OFFSET + (XDES_SIZE << srv_page_size_shift)
 | 
						|
		/ FSP_EXTENT_SIZE;
 | 
						|
}
 | 
						|
 | 
						|
/** Check the encryption key from the first page of a tablespace.
 | 
						|
@param[in]	fsp_flags	tablespace flags
 | 
						|
@param[in]	page		first page of a tablespace
 | 
						|
@return true if success */
 | 
						|
bool
 | 
						|
fsp_header_check_encryption_key(
 | 
						|
	ulint			fsp_flags,
 | 
						|
	page_t*			page);
 | 
						|
 | 
						|
/** Initialize a tablespace header.
 | 
						|
@param[in,out]	space	tablespace
 | 
						|
@param[in]	size	current size in blocks
 | 
						|
@param[in,out]	mtr	mini-transaction
 | 
						|
@return error code */
 | 
						|
dberr_t fsp_header_init(fil_space_t *space, uint32_t size, mtr_t *mtr)
 | 
						|
  MY_ATTRIBUTE((nonnull, warn_unused_result));
 | 
						|
 | 
						|
/** Create a new segment.
 | 
						|
@param space                tablespace
 | 
						|
@param byte_offset          byte offset of the created segment header
 | 
						|
@param mtr                  mini-transaction
 | 
						|
@param err                  error code
 | 
						|
@param has_done_reservation whether fsp_reserve_free_extents() was invoked
 | 
						|
@param block                block where segment header is placed,
 | 
						|
                            or NULL to allocate an additional page for that
 | 
						|
@return the block where the segment header is placed, x-latched
 | 
						|
@retval nullptr if could not create segment */
 | 
						|
buf_block_t*
 | 
						|
fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err,
 | 
						|
            bool has_done_reservation= false, buf_block_t *block= nullptr)
 | 
						|
  MY_ATTRIBUTE((nonnull(1,3,4), warn_unused_result));
 | 
						|
 | 
						|
/** Calculate the number of pages reserved by a segment,
 | 
						|
and how many pages are currently used.
 | 
						|
@param[in]      block   buffer block containing the file segment header
 | 
						|
@param[in]      header  file segment header
 | 
						|
@param[out]     used    number of pages that are used (not more than reserved)
 | 
						|
@param[in,out]  mtr     mini-transaction
 | 
						|
@return number of reserved pages */
 | 
						|
uint32_t fseg_n_reserved_pages(const buf_block_t &block,
 | 
						|
                               const fseg_header_t *header, uint32_t *used,
 | 
						|
                               mtr_t *mtr) noexcept
 | 
						|
  MY_ATTRIBUTE((nonnull));
 | 
						|
/**********************************************************************//**
 | 
						|
Allocates a single free page from a segment. This function implements
 | 
						|
the intelligent allocation strategy which tries to minimize file space
 | 
						|
fragmentation.
 | 
						|
@retval NULL if no page could be allocated */
 | 
						|
buf_block_t*
 | 
						|
fseg_alloc_free_page_general(
 | 
						|
/*=========================*/
 | 
						|
	fseg_header_t*	seg_header,/*!< in/out: segment header */
 | 
						|
	uint32_t	hint,	/*!< in: hint of which page would be
 | 
						|
				desirable */
 | 
						|
	byte		direction,/*!< in: if the new page is needed because
 | 
						|
				of an index page split, and records are
 | 
						|
				inserted there in order, into which
 | 
						|
				direction they go alphabetically: FSP_DOWN,
 | 
						|
				FSP_UP, FSP_NO_DIR */
 | 
						|
	bool		has_done_reservation, /*!< in: true if the caller has
 | 
						|
				already done the reservation for the page
 | 
						|
				with fsp_reserve_free_extents, then there
 | 
						|
				is no need to do the check for this individual
 | 
						|
				page */
 | 
						|
	mtr_t*		mtr,	/*!< in/out: mini-transaction */
 | 
						|
	mtr_t*		init_mtr,/*!< in/out: mtr or another mini-transaction
 | 
						|
				in which the page should be initialized. */
 | 
						|
	dberr_t*	err)	/*!< out: error code */
 | 
						|
	MY_ATTRIBUTE((warn_unused_result, nonnull));
 | 
						|
 | 
						|
/** Reserves free pages from a tablespace. All mini-transactions which may
 | 
						|
use several pages from the tablespace should call this function beforehand
 | 
						|
and reserve enough free extents so that they certainly will be able
 | 
						|
to do their operation, like a B-tree page split, fully. Reservations
 | 
						|
must be released with function fil_space_t::release_free_extents()!
 | 
						|
 | 
						|
The alloc_type below has the following meaning: FSP_NORMAL means an
 | 
						|
operation which will probably result in more space usage, like an
 | 
						|
insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are
 | 
						|
deleting rows, then this allocation will in the long run result in
 | 
						|
less space usage (after a purge); FSP_CLEANING means allocation done
 | 
						|
in a physical record delete (like in a purge) or other cleaning operation
 | 
						|
which will result in less space usage in the long run. We prefer the latter
 | 
						|
two types of allocation: when space is scarce, FSP_NORMAL allocations
 | 
						|
will not succeed, but the latter two allocations will succeed, if possible.
 | 
						|
The purpose is to avoid dead end where the database is full but the
 | 
						|
user cannot free any space because these freeing operations temporarily
 | 
						|
reserve some space.
 | 
						|
 | 
						|
Single-table tablespaces whose size is < FSP_EXTENT_SIZE pages are a special
 | 
						|
case. In this function we would liberally reserve several extents for
 | 
						|
every page split or merge in a B-tree. But we do not want to waste disk space
 | 
						|
if the table only occupies < FSP_EXTENT_SIZE pages. That is why we apply
 | 
						|
different rules in that special case, just ensuring that there are n_pages
 | 
						|
free pages available.
 | 
						|
 | 
						|
@param[out]     n_reserved      number of extents actually reserved; if we
 | 
						|
                                return true and the tablespace size is <
 | 
						|
                                FSP_EXTENT_SIZE pages, then this can be 0,
 | 
						|
                                otherwise it is n_ext
 | 
						|
@param[in,out]  space           tablespace
 | 
						|
@param[in]      n_ext           number of extents to reserve
 | 
						|
@param[in]      alloc_type      page reservation type (FSP_BLOB, etc)
 | 
						|
@param[in,out]  mtr             the mini transaction
 | 
						|
@param[out]     err             error code
 | 
						|
@param[in]      n_pages         for small tablespaces (tablespace size is
 | 
						|
                                less than FSP_EXTENT_SIZE), number of free
 | 
						|
                                pages to reserve.
 | 
						|
@return error code
 | 
						|
@retval DB_SUCCESS if we were able to make the reservation */
 | 
						|
dberr_t
 | 
						|
fsp_reserve_free_extents(
 | 
						|
	uint32_t*	n_reserved,
 | 
						|
	fil_space_t*	space,
 | 
						|
	uint32_t	n_ext,
 | 
						|
	fsp_reserve_t	alloc_type,
 | 
						|
	mtr_t*		mtr,
 | 
						|
	uint32_t	n_pages = 2);
 | 
						|
 | 
						|
/** Free a page in a file segment.
 | 
						|
@param[in,out]	seg_header	file segment header
 | 
						|
@param[in,out]	space		tablespace
 | 
						|
@param[in]	offset		page number
 | 
						|
@param[in,out]	mtr		mini-transaction
 | 
						|
@param[in]	have_latch	whether space->x_lock() was already called
 | 
						|
@return error code */
 | 
						|
dberr_t
 | 
						|
fseg_free_page(
 | 
						|
	fseg_header_t*	seg_header,
 | 
						|
	fil_space_t*	space,
 | 
						|
	uint32_t	offset,
 | 
						|
	mtr_t*		mtr,
 | 
						|
	bool		have_latch = false)
 | 
						|
	MY_ATTRIBUTE((nonnull, warn_unused_result));
 | 
						|
 | 
						|
/** Determine whether a page is allocated.
 | 
						|
@param space   tablespace
 | 
						|
@param page    page number
 | 
						|
@return error code
 | 
						|
@retval DB_SUCCESS             if the page is marked as free
 | 
						|
@retval DB_SUCCESS_LOCKED_REC  if the page is marked as allocated */
 | 
						|
dberr_t fseg_page_is_allocated(fil_space_t *space, unsigned page)
 | 
						|
  MY_ATTRIBUTE((nonnull, warn_unused_result));
 | 
						|
 | 
						|
MY_ATTRIBUTE((nonnull, warn_unused_result))
 | 
						|
/** Frees part of a segment. This function can be used to free
 | 
						|
a segment by repeatedly calling this function in different
 | 
						|
mini-transactions. Doing the freeing in a single mini-transaction
 | 
						|
might result in too big a mini-transaction.
 | 
						|
@param block   segment header block
 | 
						|
@param header  segment header offset in the block;
 | 
						|
NOTE: if the header resides on first page of the frag list of the segment,
 | 
						|
this pointer becomes obsolete after the last freeing step
 | 
						|
@param mtr     mini-transaction
 | 
						|
@return whether the freeing was completed */
 | 
						|
bool fseg_free_step(buf_block_t *block, size_t header, mtr_t *mtr
 | 
						|
#ifdef BTR_CUR_HASH_ADAPT
 | 
						|
                    , bool ahi=false /*!< whether to drop the AHI */
 | 
						|
#endif /* BTR_CUR_HASH_ADAPT */
 | 
						|
                    ) noexcept;
 | 
						|
 | 
						|
MY_ATTRIBUTE((nonnull, warn_unused_result))
 | 
						|
/** Frees part of a segment. Differs from fseg_free_step because
 | 
						|
this function leaves the header page unfreed.
 | 
						|
@param block   segment header block; must reside on the first
 | 
						|
fragment page of the segment
 | 
						|
@param header  segment header offset in the block
 | 
						|
@param mtr     mini-transaction
 | 
						|
@return whether the freeing was completed, except for the header page */
 | 
						|
bool fseg_free_step_not_header(buf_block_t *block, size_t header, mtr_t *mtr
 | 
						|
#ifdef BTR_CUR_HASH_ADAPT
 | 
						|
                               , bool ahi=false /*!< whether to drop the AHI */
 | 
						|
#endif /* BTR_CUR_HASH_ADAPT */
 | 
						|
                               ) noexcept;
 | 
						|
 | 
						|
/** Reset the page type.
 | 
						|
Data files created before MySQL 5.1.48 may contain garbage in FIL_PAGE_TYPE.
 | 
						|
In MySQL 3.23.53, only undo log pages and index pages were tagged.
 | 
						|
Any other pages were written with uninitialized bytes in FIL_PAGE_TYPE.
 | 
						|
@param[in]	block	block with invalid FIL_PAGE_TYPE
 | 
						|
@param[in]	type	expected page type
 | 
						|
@param[in,out]	mtr	mini-transaction */
 | 
						|
ATTRIBUTE_COLD
 | 
						|
void fil_block_reset_type(const buf_block_t& block, ulint type, mtr_t* mtr);
 | 
						|
 | 
						|
/** Check (and if needed, reset) the page type.
 | 
						|
Data files created before MySQL 5.1.48 may contain
 | 
						|
garbage in the FIL_PAGE_TYPE field.
 | 
						|
In MySQL 3.23.53, only undo log pages and index pages were tagged.
 | 
						|
Any other pages were written with uninitialized bytes in FIL_PAGE_TYPE.
 | 
						|
@param[in]	page_id	page number
 | 
						|
@param[in,out]	page	page with possibly invalid FIL_PAGE_TYPE
 | 
						|
@param[in]	type	expected page type
 | 
						|
@param[in,out]	mtr	mini-transaction */
 | 
						|
inline void
 | 
						|
fil_block_check_type(
 | 
						|
	const buf_block_t&	block,
 | 
						|
	ulint			type,
 | 
						|
	mtr_t*			mtr)
 | 
						|
{
 | 
						|
  if (UNIV_UNLIKELY(type != fil_page_get_type(block.page.frame)))
 | 
						|
    fil_block_reset_type(block, type, mtr);
 | 
						|
}
 | 
						|
 | 
						|
/** Checks if a page address is an extent descriptor page address.
 | 
						|
@param[in]	page_id		page id
 | 
						|
@param[in]	physical_size	page size
 | 
						|
@return whether a descriptor page */
 | 
						|
inline bool fsp_descr_page(const page_id_t page_id, ulint physical_size)
 | 
						|
{
 | 
						|
	return (page_id.page_no() & (physical_size - 1)) == FSP_XDES_OFFSET;
 | 
						|
}
 | 
						|
 | 
						|
/** Initialize a file page whose prior contents should be ignored.
 | 
						|
@param[in,out]	block	buffer pool block */
 | 
						|
void fsp_apply_init_file_page(buf_block_t *block);
 | 
						|
 | 
						|
/** Initialize a file page.
 | 
						|
@param[in]	space	tablespace
 | 
						|
@param[in,out]	block	file page
 | 
						|
@param[in,out]	mtr	mini-transaction */
 | 
						|
inline void fsp_init_file_page(
 | 
						|
#ifdef UNIV_DEBUG
 | 
						|
	const fil_space_t* space,
 | 
						|
#endif
 | 
						|
	buf_block_t* block, mtr_t* mtr)
 | 
						|
{
 | 
						|
	ut_d(space->modify_check(*mtr));
 | 
						|
	ut_ad(space->id == block->page.id().space());
 | 
						|
	fsp_apply_init_file_page(block);
 | 
						|
	mtr->init(block);
 | 
						|
}
 | 
						|
 | 
						|
/** Truncate the system tablespace
 | 
						|
@param shutdown Called during shutdown */
 | 
						|
void fsp_system_tablespace_truncate(bool shutdown);
 | 
						|
 | 
						|
/** Truncate the temporary tablespace */
 | 
						|
void fsp_shrink_temp_space();
 | 
						|
 | 
						|
#ifndef UNIV_DEBUG
 | 
						|
# define fsp_init_file_page(space, block, mtr) fsp_init_file_page(block, mtr)
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef UNIV_BTR_PRINT
 | 
						|
/*******************************************************************//**
 | 
						|
Writes info of a segment. */
 | 
						|
void
 | 
						|
fseg_print(
 | 
						|
/*=======*/
 | 
						|
	fseg_header_t*	header, /*!< in: segment header */
 | 
						|
	mtr_t*		mtr);	/*!< in/out: mini-transaction */
 | 
						|
#endif /* UNIV_BTR_PRINT */
 | 
						|
 | 
						|
/** Convert FSP_SPACE_FLAGS from the buggy MariaDB 10.1.0..10.1.20 format.
 | 
						|
@param[in]	flags	the contents of FSP_SPACE_FLAGS
 | 
						|
@return	the flags corrected from the buggy MariaDB 10.1 format
 | 
						|
@retval	UINT32_MAX  if the flags are not in the buggy 10.1 format */
 | 
						|
MY_ATTRIBUTE((warn_unused_result, const))
 | 
						|
inline uint32_t fsp_flags_convert_from_101(uint32_t flags)
 | 
						|
{
 | 
						|
	DBUG_EXECUTE_IF("fsp_flags_is_valid_failure", return UINT32_MAX;);
 | 
						|
	if (flags == 0 || fil_space_t::full_crc32(flags)) {
 | 
						|
		return(flags);
 | 
						|
	}
 | 
						|
 | 
						|
	if (flags >> 18) {
 | 
						|
		/* The most significant FSP_SPACE_FLAGS bit that was ever set
 | 
						|
		by MariaDB 10.1.0 to 10.1.20 was bit 17 (misplaced DATA_DIR flag).
 | 
						|
		The flags must be less than 1<<18 in order to be valid. */
 | 
						|
		return UINT32_MAX;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((flags & (FSP_FLAGS_MASK_POST_ANTELOPE | FSP_FLAGS_MASK_ATOMIC_BLOBS))
 | 
						|
	    == FSP_FLAGS_MASK_ATOMIC_BLOBS) {
 | 
						|
		/* If the "atomic blobs" flag (indicating
 | 
						|
		ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED) flag
 | 
						|
		is set, then the "post Antelope" (ROW_FORMAT!=REDUNDANT) flag
 | 
						|
		must also be set. */
 | 
						|
		return UINT32_MAX;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Bits 6..10 denote compression in MariaDB 10.1.0 to 10.1.20.
 | 
						|
	They must be either 0b00000 or 0b00011 through 0b10011.
 | 
						|
	In correct versions, these bits would be
 | 
						|
	0bd0sss where d is the DATA_DIR flag (garbage bit) and
 | 
						|
	sss is the PAGE_SSIZE (3, 4, 6, or 7).
 | 
						|
 | 
						|
	NOTE: MariaDB 10.1.0 to 10.1.20 can misinterpret
 | 
						|
	uncompressed data files with innodb_page_size=4k or 64k as
 | 
						|
	compressed innodb_page_size=16k files. Below is an exhaustive
 | 
						|
	state space analysis.
 | 
						|
 | 
						|
	-0by1zzz: impossible (the bit 4 must be clean; see above)
 | 
						|
	-0b101xx: DATA_DIR, innodb_page_size>4k: invalid (COMPRESSION_LEVEL>9)
 | 
						|
	+0bx0011: innodb_page_size=4k:
 | 
						|
	!!!	Misinterpreted as COMPRESSION_LEVEL=9 or 1, COMPRESSION=1.
 | 
						|
	-0bx0010: impossible, because sss must be 0b011 or 0b1xx
 | 
						|
	-0bx0001: impossible, because sss must be 0b011 or 0b1xx
 | 
						|
	-0b10000: DATA_DIR, innodb_page_size=16:
 | 
						|
	invalid (COMPRESSION_LEVEL=8 but COMPRESSION=0)
 | 
						|
	+0b00111: no DATA_DIR, innodb_page_size=64k:
 | 
						|
	!!!	Misinterpreted as COMPRESSION_LEVEL=3, COMPRESSION=1.
 | 
						|
	-0b00101: impossible, because sss must be 0 for 16k, not 0b101
 | 
						|
	-0b001x0: no DATA_DIR, innodb_page_size=32k or 8k:
 | 
						|
	invalid (COMPRESSION_LEVEL=3 but COMPRESSION=0)
 | 
						|
	+0b00000: innodb_page_size=16k (looks like COMPRESSION=0)
 | 
						|
	???	Could actually be compressed; see PAGE_SSIZE below */
 | 
						|
	const uint32_t level = FSP_FLAGS_GET_PAGE_COMPRESSION_LEVEL_MARIADB101(
 | 
						|
		flags);
 | 
						|
	if (FSP_FLAGS_GET_PAGE_COMPRESSION_MARIADB101(flags) != (level != 0)
 | 
						|
	    || level > 9) {
 | 
						|
		/* The compression flags are not in the buggy MariaDB
 | 
						|
		10.1 format. */
 | 
						|
		return UINT32_MAX;
 | 
						|
	}
 | 
						|
	if (!(~flags & FSP_FLAGS_MASK_ATOMIC_WRITES_MARIADB101)) {
 | 
						|
		/* The ATOMIC_WRITES flags cannot be 0b11.
 | 
						|
		(The bits 11..12 should actually never be 0b11,
 | 
						|
		because in MySQL they would be SHARED|TEMPORARY.) */
 | 
						|
		return UINT32_MAX;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Bits 13..16 are the wrong position for PAGE_SSIZE, and they
 | 
						|
	should contain one of the values 3,4,6,7, that is, be of the form
 | 
						|
	0b0011 or 0b01xx (except 0b0101).
 | 
						|
	In correct versions, these bits should be 0bc0se
 | 
						|
	where c is the MariaDB COMPRESSED flag
 | 
						|
	and e is the MySQL 5.7 ENCRYPTION flag
 | 
						|
	and s is the MySQL 8.0 SDI flag. MariaDB can only support s=0, e=0.
 | 
						|
 | 
						|
	Compressed innodb_page_size=16k tables with correct FSP_SPACE_FLAGS
 | 
						|
	will be properly rejected by older MariaDB 10.1.x because they
 | 
						|
	would read as PAGE_SSIZE>=8 which is not valid. */
 | 
						|
 | 
						|
	const uint32_t ssize = FSP_FLAGS_GET_PAGE_SSIZE_MARIADB101(flags);
 | 
						|
	if (ssize == 1 || ssize == 2 || ssize == 5 || ssize & 8) {
 | 
						|
		/* the page_size is not between 4k and 64k;
 | 
						|
		16k should be encoded as 0, not 5 */
 | 
						|
		return UINT32_MAX;
 | 
						|
	}
 | 
						|
	const uint32_t zssize = FSP_FLAGS_GET_ZIP_SSIZE(flags);
 | 
						|
	if (zssize == 0) {
 | 
						|
		/* not ROW_FORMAT=COMPRESSED */
 | 
						|
	} else if (zssize > (ssize ? ssize : 5)) {
 | 
						|
		/* invalid KEY_BLOCK_SIZE */
 | 
						|
		return UINT32_MAX;
 | 
						|
	} else if (~flags & (FSP_FLAGS_MASK_POST_ANTELOPE
 | 
						|
			     | FSP_FLAGS_MASK_ATOMIC_BLOBS)) {
 | 
						|
		/* both these flags should be set for
 | 
						|
		ROW_FORMAT=COMPRESSED */
 | 
						|
		return UINT32_MAX;
 | 
						|
	}
 | 
						|
 | 
						|
	flags = ((flags & 0x3f) | ssize << FSP_FLAGS_POS_PAGE_SSIZE
 | 
						|
		 | FSP_FLAGS_GET_PAGE_COMPRESSION_MARIADB101(flags)
 | 
						|
		 << FSP_FLAGS_POS_PAGE_COMPRESSION);
 | 
						|
	ut_ad(fil_space_t::is_valid_flags(flags, false));
 | 
						|
	return(flags);
 | 
						|
}
 | 
						|
 | 
						|
/** Compare tablespace flags.
 | 
						|
@param[in]	expected	expected flags from dict_tf_to_fsp_flags()
 | 
						|
@param[in]	actual		flags read from FSP_SPACE_FLAGS
 | 
						|
@return whether the flags match */
 | 
						|
MY_ATTRIBUTE((warn_unused_result))
 | 
						|
inline bool fsp_flags_match(uint32_t expected, uint32_t actual)
 | 
						|
{
 | 
						|
  expected&= ~FSP_FLAGS_MEM_MASK;
 | 
						|
  ut_ad(fil_space_t::is_valid_flags(expected, false));
 | 
						|
  return actual == expected || fsp_flags_convert_from_101(actual) == expected;
 | 
						|
}
 | 
						|
 | 
						|
/** Determine if FSP_SPACE_FLAGS are from an incompatible MySQL format.
 | 
						|
@param	flags	the contents of FSP_SPACE_FLAGS
 | 
						|
@return	MySQL flags shifted.
 | 
						|
@retval	0, if not a MySQL incompatible format. */
 | 
						|
MY_ATTRIBUTE((warn_unused_result, const))
 | 
						|
inline uint32_t fsp_flags_is_incompatible_mysql(uint32_t flags)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    MySQL-8.0 SDI flag (bit 14),
 | 
						|
    or MySQL 5.7 Encyption flag (bit 13)
 | 
						|
  */
 | 
						|
  return flags >> 13 & 3;
 | 
						|
}
 | 
						|
 | 
						|
/** Determine the descriptor index within a descriptor page.
 | 
						|
@param[in]	zip_size	ROW_FORMAT=COMPRESSED page size, or 0
 | 
						|
@param[in]	offset		page offset
 | 
						|
@return descriptor index */
 | 
						|
inline ulint xdes_calc_descriptor_index(ulint zip_size, ulint offset)
 | 
						|
{
 | 
						|
	return ut_2pow_remainder<ulint>(offset,
 | 
						|
					zip_size ? zip_size : srv_page_size)
 | 
						|
		/ FSP_EXTENT_SIZE;
 | 
						|
}
 | 
						|
 | 
						|
/** Determine the descriptor page number for a page.
 | 
						|
@param[in]	zip_size	ROW_FORMAT=COMPRESSED page size, or 0
 | 
						|
@param[in]	offset		page offset
 | 
						|
@return descriptor page offset */
 | 
						|
inline uint32_t xdes_calc_descriptor_page(ulint zip_size, uint32_t offset)
 | 
						|
{
 | 
						|
	compile_time_assert(UNIV_PAGE_SIZE_MAX > XDES_ARR_OFFSET
 | 
						|
			    + (UNIV_PAGE_SIZE_MAX / FSP_EXTENT_SIZE_MAX)
 | 
						|
			    * XDES_SIZE_MAX);
 | 
						|
	compile_time_assert(UNIV_PAGE_SIZE_MIN > XDES_ARR_OFFSET
 | 
						|
			    + (UNIV_PAGE_SIZE_MIN / FSP_EXTENT_SIZE_MIN)
 | 
						|
			    * XDES_SIZE_MIN);
 | 
						|
 | 
						|
	ut_ad(srv_page_size > XDES_ARR_OFFSET
 | 
						|
	      + (srv_page_size / FSP_EXTENT_SIZE)
 | 
						|
	      * XDES_SIZE);
 | 
						|
	ut_ad(UNIV_ZIP_SIZE_MIN > XDES_ARR_OFFSET
 | 
						|
	      + (UNIV_ZIP_SIZE_MIN / FSP_EXTENT_SIZE)
 | 
						|
	      * XDES_SIZE);
 | 
						|
	ut_ad(!zip_size
 | 
						|
	      || zip_size > XDES_ARR_OFFSET
 | 
						|
	      + (zip_size / FSP_EXTENT_SIZE) * XDES_SIZE);
 | 
						|
	return ut_2pow_round(offset,
 | 
						|
			     uint32_t(zip_size ? zip_size : srv_page_size));
 | 
						|
}
 | 
						|
 | 
						|
#endif /* UNIV_INNOCHECKSUM */
 | 
						|
 | 
						|
#endif
 |