447 lines
16 KiB
C++
447 lines
16 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 2016, 2019, Oracle and/or its affiliates. All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License, version 2.0, as published by the
|
|
Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including but not
|
|
limited to OpenSSL) that is licensed under separate terms, as designated in a
|
|
particular file or component or in included license documentation. The authors
|
|
of MySQL hereby grant you an additional permission to link the program and
|
|
your derivative works with the separately licensed software that they have
|
|
included with MySQL.
|
|
|
|
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, version 2.0,
|
|
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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
#ifndef lob0first_h
|
|
#define lob0first_h
|
|
|
|
#include "btr0btr.h"
|
|
#include "buf0buf.h"
|
|
#include "dict0dict.h"
|
|
#include "fut0lst.h"
|
|
#include "lob0index.h"
|
|
#include "lob0lob.h"
|
|
#include "lob0util.h"
|
|
#include "mtr0log.h"
|
|
|
|
namespace lob {
|
|
|
|
/** The first page of an uncompressed LOB. */
|
|
struct first_page_t : public basic_page_t {
|
|
/** Version information. One byte. */
|
|
static const ulint OFFSET_VERSION = FIL_PAGE_DATA;
|
|
|
|
/** One byte of flag bits. Currently only one bit (the least
|
|
significant bit) is used, other 7 bits are available for future use.*/
|
|
static const ulint OFFSET_FLAGS = FIL_PAGE_DATA + 1;
|
|
|
|
/** LOB version. 4 bytes.*/
|
|
static const uint32_t OFFSET_LOB_VERSION = OFFSET_FLAGS + 1;
|
|
|
|
/** The latest transaction that modified this LOB. */
|
|
static const ulint OFFSET_LAST_TRX_ID = OFFSET_LOB_VERSION + 4;
|
|
|
|
/** The latest transaction undo_no that modified this LOB. */
|
|
static const ulint OFFSET_LAST_UNDO_NO = OFFSET_LAST_TRX_ID + 6;
|
|
|
|
/** Length of data stored in this page. 4 bytes. */
|
|
static const ulint OFFSET_DATA_LEN = OFFSET_LAST_UNDO_NO + 4;
|
|
|
|
/** The trx that created the data stored in this page. */
|
|
static const ulint OFFSET_TRX_ID = OFFSET_DATA_LEN + 4;
|
|
|
|
/** The offset where the list base node is located. This is the list
|
|
of LOB pages. */
|
|
static const ulint OFFSET_INDEX_LIST = OFFSET_TRX_ID + 6;
|
|
|
|
/** The offset where the list base node is located. This is the list
|
|
of free nodes. */
|
|
static const ulint OFFSET_INDEX_FREE_NODES =
|
|
OFFSET_INDEX_LIST + FLST_BASE_NODE_SIZE;
|
|
|
|
/** The offset where the contents of the first page begins. */
|
|
static const ulint LOB_PAGE_DATA =
|
|
OFFSET_INDEX_FREE_NODES + FLST_BASE_NODE_SIZE;
|
|
|
|
static const ulint LOB_PAGE_TRAILER_LEN = FIL_PAGE_DATA_END;
|
|
|
|
/** The default constructor. */
|
|
first_page_t() {}
|
|
|
|
/** Constructor.
|
|
@param[in] block the buffer block of the first page.
|
|
@param[in] mtr the mini-transaction context. */
|
|
first_page_t(buf_block_t *block, mtr_t *mtr) : basic_page_t(block, mtr) {}
|
|
|
|
/** Constructor.
|
|
@param[in] block the buffer block of the first page.*/
|
|
first_page_t(buf_block_t *block) : basic_page_t(block, nullptr) {}
|
|
|
|
/** Constructor.
|
|
@param[in] block the buffer block of the first page.
|
|
@param[in] mtr the mini-transaction context.
|
|
@param[in] index the clustered index containing the LOB. */
|
|
first_page_t(buf_block_t *block, mtr_t *mtr, dict_index_t *index)
|
|
: basic_page_t(block, mtr, index) {}
|
|
|
|
/** Constructor.
|
|
@param[in] mtr the mini-transaction context.
|
|
@param[in] index the clustered index containing the LOB. */
|
|
first_page_t(mtr_t *mtr, dict_index_t *index)
|
|
: basic_page_t(nullptr, mtr, index) {}
|
|
|
|
/** Set the LOB format version number to 0. */
|
|
void set_version_0() {
|
|
mlog_write_ulint(frame() + OFFSET_VERSION, 0, MLOG_1BYTE, m_mtr);
|
|
}
|
|
|
|
/** Obtain the flags value. This has 8 bits of which only the first
|
|
bit is used.
|
|
@return one byte flag */
|
|
uint8_t get_flags() { return (mach_read_from_1(frame() + OFFSET_FLAGS)); }
|
|
|
|
/** When the bit is set, the LOB is not partially updatable anymore.
|
|
@return true, if partially updatable.
|
|
@return false, if partially NOT updatable. */
|
|
bool can_be_partially_updated() {
|
|
uint8_t flags = get_flags();
|
|
return (!(flags & 0x01));
|
|
}
|
|
|
|
/** Do tablespace import. */
|
|
void import(trx_id_t trx_id);
|
|
|
|
/** When the bit is set, the LOB is not partially updatable anymore.
|
|
Enable the bit.
|
|
@param[in] trx the current transaction. */
|
|
void mark_cannot_be_partially_updated(trx_t *trx);
|
|
|
|
/** Allocate the first page for uncompressed LOB.
|
|
@param[in,out] alloc_mtr the allocation mtr.
|
|
@param[in] is_bulk true if it is bulk operation
|
|
(OPCODE_INSERT_BULK)
|
|
@return the allocated buffer block.*/
|
|
buf_block_t *alloc(mtr_t *alloc_mtr, bool is_bulk);
|
|
|
|
/** Free all the index pages. The list of index pages can be accessed
|
|
by traversing via the FIL_PAGE_NEXT field.*/
|
|
void free_all_index_pages();
|
|
|
|
/** Free all the data pages. The data pages can be accessed through
|
|
index entry. */
|
|
void free_all_data_pages();
|
|
|
|
/** Load the first page of LOB with s-latch.
|
|
@param[in] page_id the page identifier of the first page.
|
|
@param[in] page_size the page size information.
|
|
@return the buffer block of the first page. */
|
|
buf_block_t *load_s(page_id_t page_id, page_size_t page_size) {
|
|
m_block = buf_page_get(page_id, page_size, RW_S_LATCH, m_mtr);
|
|
return (m_block);
|
|
}
|
|
|
|
/** Load the first page of LOB with x-latch.
|
|
@param[in] page_id the page identifier of the first page.
|
|
@param[in] page_size the page size information.
|
|
@return the buffer block of the first page. */
|
|
buf_block_t *load_x(const page_id_t &page_id, const page_size_t &page_size);
|
|
|
|
/** Get the buffer block of the LOB first page.
|
|
@return the buffer block. */
|
|
buf_block_t *get_block() { return (m_block); }
|
|
|
|
/** Load the file list node from the given location. An x-latch is taken
|
|
on the page containing the file list node.
|
|
@param[in] addr the location of file list node.
|
|
@return the file list node.*/
|
|
flst_node_t *addr2ptr_x(fil_addr_t &addr) const {
|
|
space_id_t space = dict_index_get_space(m_index);
|
|
const page_size_t page_size = dict_table_page_size(m_index->table);
|
|
return (fut_get_ptr(space, page_size, addr, RW_X_LATCH, m_mtr));
|
|
}
|
|
|
|
/** Load the file list node from the given location, assuming that it
|
|
exists in the first page itself.
|
|
@param[in] addr the location of file list node.
|
|
@return the file list node.*/
|
|
flst_node_t *addr2ptr(const fil_addr_t &addr) {
|
|
ut_ad(m_block->page.id.page_no() == addr.page);
|
|
return (buf_block_get_frame(m_block) + addr.boffset);
|
|
}
|
|
|
|
/** Load the file list node from the given location. An s-latch is taken
|
|
on the page containing the file list node.
|
|
@param[in] addr the location of file list node.
|
|
@return the file list node.*/
|
|
flst_node_t *addr2ptr_s(fil_addr_t &addr) {
|
|
space_id_t space = dict_index_get_space(m_index);
|
|
const page_size_t page_size = dict_table_page_size(m_index->table);
|
|
return (fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr));
|
|
}
|
|
|
|
/** Load the file list node from the given location. An s-latch is taken
|
|
on the page containing the file list node. The given cache is checked to
|
|
see if the page is already loaded.
|
|
@param[in] cache cache of loaded buffer blocks.
|
|
@param[in] addr the location of file list node.
|
|
@return the file list node.*/
|
|
flst_node_t *addr2ptr_s_cache(std::map<page_no_t, buf_block_t *> &cache,
|
|
fil_addr_t &addr) const {
|
|
byte *result;
|
|
space_id_t space = dict_index_get_space(m_index);
|
|
const page_size_t page_size = dict_table_page_size(m_index->table);
|
|
|
|
auto iter = cache.find(addr.page);
|
|
|
|
if (iter == cache.end()) {
|
|
/* Not there in cached blocks. Add the loaded block to cache. */
|
|
buf_block_t *block = nullptr;
|
|
result = fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr, &block);
|
|
cache.insert(std::make_pair(addr.page, block));
|
|
} else {
|
|
buf_block_t *block = iter->second;
|
|
ut_ad(block->page.id.page_no() == addr.page);
|
|
result = buf_block_get_frame(block) + addr.boffset;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/** Free the first page. This is done when all other LOB pages have
|
|
been freed. */
|
|
void dealloc();
|
|
|
|
/** Check if the index list is empty or not.
|
|
@return true if empty, false otherwise. */
|
|
bool is_empty() const {
|
|
flst_base_node_t *base = index_list();
|
|
ut_ad(base != nullptr);
|
|
return (flst_get_len(base) == 0);
|
|
}
|
|
|
|
/** Allocate one index entry. If required an index page (of type
|
|
FIL_PAGE_TYPE_LOB_INDEX) will be allocated.
|
|
@param[in] bulk true if it is a bulk operation
|
|
(OPCODE_INSERT_BULK), false otherwise.
|
|
@return the file list node of the index entry. */
|
|
flst_node_t *alloc_index_entry(bool bulk);
|
|
|
|
/** Get a pointer to the beginning of the index entry nodes in the
|
|
first part of the page.
|
|
@return the first index entry node. */
|
|
byte *nodes_begin() const { return (frame() + LOB_PAGE_DATA); }
|
|
|
|
/** Calculate and return the payload.
|
|
@return the payload possible in this page. */
|
|
static ulint payload() {
|
|
return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN);
|
|
}
|
|
|
|
/** Set the transaction identifier in the first page header without
|
|
generating redo logs.
|
|
@param[in] id the transaction identifier. */
|
|
void set_trx_id_no_redo(trx_id_t id) {
|
|
byte *ptr = frame() + OFFSET_TRX_ID;
|
|
mach_write_to_6(ptr, id);
|
|
}
|
|
|
|
/** Set the transaction identifier in the first page header.
|
|
@param[in] id the transaction identifier. */
|
|
void set_trx_id(trx_id_t id) {
|
|
byte *ptr = frame() + OFFSET_TRX_ID;
|
|
mach_write_to_6(ptr, id);
|
|
mlog_log_string(ptr, 6, m_mtr);
|
|
}
|
|
|
|
/** Initialize the LOB version to 1. */
|
|
void init_lob_version() {
|
|
ut_ad(m_mtr != nullptr);
|
|
|
|
mlog_write_ulint(frame() + OFFSET_LOB_VERSION, 1, MLOG_4BYTES, m_mtr);
|
|
}
|
|
|
|
/** Get the lob version number.
|
|
@return the lob version. */
|
|
uint32_t get_lob_version() {
|
|
return (mach_read_from_4(frame() + OFFSET_LOB_VERSION));
|
|
}
|
|
|
|
/** Increment the lob version by 1. */
|
|
uint32_t incr_lob_version();
|
|
|
|
/** Set the last transaction identifier, without generating redo log
|
|
records.
|
|
@param[in] id the trx identifier. */
|
|
void set_last_trx_id_no_redo(trx_id_t id) {
|
|
byte *ptr = frame() + OFFSET_LAST_TRX_ID;
|
|
mach_write_to_6(ptr, id);
|
|
}
|
|
|
|
/** Set the last transaction identifier.
|
|
@param[in] id the trx identifier. */
|
|
void set_last_trx_id(trx_id_t id) {
|
|
byte *ptr = frame() + OFFSET_LAST_TRX_ID;
|
|
mach_write_to_6(ptr, id);
|
|
mlog_log_string(ptr, 6, m_mtr);
|
|
}
|
|
|
|
/** Set the last transaction undo number.
|
|
@param[in] undo_no the trx undo number. */
|
|
void set_last_trx_undo_no(undo_no_t undo_no) {
|
|
ut_ad(m_mtr != nullptr);
|
|
|
|
byte *ptr = frame() + OFFSET_LAST_UNDO_NO;
|
|
mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr);
|
|
}
|
|
|
|
/** Get the last transaction identifier.
|
|
@return the transaction identifier. */
|
|
trx_id_t get_last_trx_id() const {
|
|
byte *ptr = frame() + OFFSET_LAST_TRX_ID;
|
|
return (mach_read_from_6(ptr));
|
|
}
|
|
|
|
/** Get the last transaction undo number.
|
|
@return the transaction undo number. */
|
|
undo_no_t get_last_trx_undo_no() const {
|
|
byte *ptr = frame() + OFFSET_LAST_UNDO_NO;
|
|
return (mach_read_from_4(ptr));
|
|
}
|
|
|
|
/** Set the length of data stored in bytes.
|
|
@param[in] len amount of data stored in bytes. */
|
|
void set_data_len(ulint len) {
|
|
ut_ad(m_mtr != nullptr);
|
|
|
|
mlog_write_ulint(frame() + OFFSET_DATA_LEN, len, MLOG_4BYTES, m_mtr);
|
|
}
|
|
|
|
/** Write as much as possible of the given data into the page.
|
|
@param[in] trxid the current transaction.
|
|
@param[in] data the data to be written.
|
|
@param[in] len the length of the given data.
|
|
@return number of bytes actually written. */
|
|
ulint write(trx_id_t trxid, const byte *&data, ulint &len);
|
|
|
|
/** Replace data in the page by making a copy-on-write.
|
|
@param[in] trx the current transaction.
|
|
@param[in] offset the location where replace operation starts.
|
|
@param[in,out] ptr the buffer containing new data. after the
|
|
call it will point to remaining data.
|
|
@param[in,out] want requested amount of data to be replaced.
|
|
after the call it will contain amount of
|
|
data yet to be replaced.
|
|
@param[in] mtr the mini-transaction context.
|
|
@return the newly allocated buffer block. */
|
|
buf_block_t *replace(trx_t *trx, ulint offset, const byte *&ptr, ulint &want,
|
|
mtr_t *mtr);
|
|
|
|
/** Replace data in the page inline.
|
|
@param[in] trx the current transaction.
|
|
@param[in] offset the location where replace operation starts.
|
|
@param[in,out] ptr the buffer containing new data. after the
|
|
call it will point to remaining data.
|
|
@param[in,out] want requested amount of data to be replaced.
|
|
after the call it will contain amount of
|
|
data yet to be replaced.
|
|
@param[in] mtr the mini-transaction context.*/
|
|
void replace_inline(trx_t *trx, ulint offset, const byte *&ptr, ulint &want,
|
|
mtr_t *mtr);
|
|
|
|
ulint get_data_len() const {
|
|
return (mach_read_from_4(frame() + OFFSET_DATA_LEN));
|
|
}
|
|
|
|
/** Read data from the first page.
|
|
@param[in] offset the offset from where read starts.
|
|
@param[out] ptr the output buffer
|
|
@param[in] want number of bytes to read.
|
|
@return number of bytes read. */
|
|
ulint read(ulint offset, byte *ptr, ulint want);
|
|
|
|
void set_page_type() {
|
|
ut_ad(m_mtr != nullptr);
|
|
|
|
mlog_write_ulint(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_FIRST,
|
|
MLOG_2BYTES, m_mtr);
|
|
}
|
|
|
|
flst_base_node_t *index_list() const { return (frame() + OFFSET_INDEX_LIST); }
|
|
|
|
flst_base_node_t *free_list() const {
|
|
return (frame() + OFFSET_INDEX_FREE_NODES);
|
|
}
|
|
|
|
/** Get the number of bytes used to store LOB data in the first page
|
|
of uncompressed LOB.
|
|
@return Number of bytes available for LOB data. */
|
|
static ulint max_space_available() {
|
|
const ulint index_array_size = node_count() * index_entry_t::SIZE;
|
|
|
|
return (payload() - index_array_size);
|
|
}
|
|
|
|
/** Get the number of index entries this page can hold.
|
|
@return Number of index entries this page can hold. */
|
|
constexpr static ulint node_count() {
|
|
/* Each index entry is of size 60 bytes. We store only 10
|
|
index entries in the first page of the LOB. This means that
|
|
only 600 bytes are used for index data in the first page of
|
|
LOB. This will help to reserve more space in the first page
|
|
for the LOB data.*/
|
|
return (10);
|
|
}
|
|
|
|
std::ostream &print_index_entries(std::ostream &out) const;
|
|
|
|
std::ostream &print_index_entries_cache_s(std::ostream &out,
|
|
BlockCache &cache) const;
|
|
|
|
/** Obtain the location where the data begins.
|
|
@return pointer to location within page where data begins. */
|
|
byte *data_begin() const {
|
|
ut_ad(buf_block_get_page_zip(m_block) == NULL);
|
|
|
|
constexpr ulint index_array_size = node_count() * index_entry_t::SIZE;
|
|
|
|
return (frame() + LOB_PAGE_DATA + index_array_size);
|
|
}
|
|
|
|
/** Append data into a LOB first page. */
|
|
ulint append(trx_id_t trxid, byte *&data, ulint &len);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Validate the first page. */
|
|
bool validate() const;
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
page_type_t get_page_type() { return (basic_page_t::get_page_type()); }
|
|
|
|
static page_type_t get_page_type(dict_index_t *index,
|
|
const page_id_t &page_id,
|
|
const page_size_t &page_size) {
|
|
mtr_t local_mtr;
|
|
mtr_start(&local_mtr);
|
|
first_page_t first(&local_mtr, index);
|
|
first.load_x(page_id, page_size);
|
|
page_type_t page_type = first.get_page_type();
|
|
mtr_commit(&local_mtr);
|
|
return (page_type);
|
|
}
|
|
};
|
|
|
|
} /* namespace lob */
|
|
|
|
#endif /* lob0first_h */
|