/***************************************************************************** Copyright (c) 2016, 2018, 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 zlob0index_h #define zlob0index_h #include "fil0fil.h" #include "fut0lst.h" #include "lob0impl.h" namespace lob { /** In-memory copy of the information from z_index_entry_t. */ struct z_index_entry_mem_t { z_index_entry_mem_t() : m_trx_id(0), m_z_page_no(FIL_NULL), m_z_frag_id(FRAG_ID_NULL) {} /** The location of this index entry node. */ fil_addr_t m_self; fil_addr_t m_prev; fil_addr_t m_next; flst_bnode_t m_versions; trx_id_t m_trx_id; trx_id_t m_trx_id_modifier; undo_no_t m_trx_undo_no; undo_no_t m_trx_undo_no_modifier; page_no_t m_z_page_no; frag_id_t m_z_frag_id; /** Uncompressed data length. */ ulint m_data_len; /** Compressed data length. */ ulint m_z_data_len; std::ostream &print(std::ostream &out) const; /** Initialize all the members. */ void reset() { m_self = fil_addr_null; m_prev = fil_addr_null; m_next = fil_addr_null; m_versions.reset(); m_trx_id = 0; m_trx_id_modifier = 0; m_trx_undo_no = 0; m_trx_undo_no_modifier = 0; m_z_page_no = FIL_NULL; m_z_frag_id = 0; m_data_len = 0; m_z_data_len = 0; } bool is_null() { return (m_self.is_equal(fil_addr_null)); } }; inline std::ostream &operator<<(std::ostream &out, const z_index_entry_mem_t &obj) { return (obj.print(out)); } /** An index entry pointing to one zlib stream. */ struct z_index_entry_t { /** Offset with index entry pointing to the prev index entry. */ static const ulint OFFSET_PREV = 0; /** Offset with index entry pointing to the next index entry. */ static const ulint OFFSET_NEXT = OFFSET_PREV + FIL_ADDR_SIZE; /** Offset within index entry pointing to base node of list of versions.*/ static const ulint OFFSET_VERSIONS = OFFSET_NEXT + FIL_ADDR_SIZE; /** Offset within index entry pointing to creator trxid.*/ static const ulint OFFSET_TRXID = OFFSET_VERSIONS + FLST_BASE_NODE_SIZE; /** The modifier trx id. */ static const ulint OFFSET_TRXID_MODIFIER = OFFSET_TRXID + 6; /** Offset within index entry pointing to trx undo no.*/ static const ulint OFFSET_TRX_UNDO_NO = OFFSET_TRXID_MODIFIER + 6; /** Offset within index entry pointing to modifier trx undo no.*/ static const ulint OFFSET_TRX_UNDO_NO_MODIFIER = OFFSET_TRX_UNDO_NO + 4; /** Offset within index entry pointing to page number where zlib stream starts. This could be a data page or a fragment page. */ static const ulint OFFSET_Z_PAGE_NO = OFFSET_TRX_UNDO_NO_MODIFIER + 4; /** Offset within index entry pointing to location of zlib stream.*/ static const ulint OFFSET_Z_FRAG_ID = OFFSET_Z_PAGE_NO + 4; /** Offset within index entry pointing to uncompressed data len (bytes).*/ static const ulint OFFSET_DATA_LEN = OFFSET_Z_FRAG_ID + 2; /** Offset within index entry pointing to compressed data len (bytes).*/ static const ulint OFFSET_ZDATA_LEN = OFFSET_DATA_LEN + 4; /** LOB version */ static const ulint OFFSET_LOB_VERSION = OFFSET_ZDATA_LEN + 4; /** Total size of one index entry. */ static const ulint SIZE = OFFSET_LOB_VERSION + 4; /** Constructor. */ z_index_entry_t(flst_node_t *node, mtr_t *mtr) : m_node(node), m_mtr(mtr) {} /** Constructor. */ z_index_entry_t(flst_node_t *node, mtr_t *mtr, dict_index_t *index) : m_node(node), m_mtr(mtr), m_index(index) {} /** Constructor @param[in] mtr the mini transaction @param[in] index the clustered index to which LOB belongs. */ z_index_entry_t(mtr_t *mtr, dict_index_t *index) : m_node(nullptr), m_mtr(mtr), m_index(index), m_block(nullptr), m_page_no(FIL_NULL) {} /** Constructor @param[in] node the location where index entry starts. */ z_index_entry_t(flst_node_t *node) : m_node(node), m_mtr(nullptr), m_index(nullptr), m_block(nullptr), m_page_no(FIL_NULL) {} /** Default constructor */ z_index_entry_t() : m_node(nullptr), m_mtr(nullptr), m_index(nullptr), m_block(nullptr), m_page_no(FIL_NULL) {} void set_index(dict_index_t *index) { m_index = index; } /** Point to another index entry. @param[in] node point to this file list node. */ void reset(flst_node_t *node) { m_node = node; } /** Point to another index entry. @param[in] entry another index entry.*/ void reset(const z_index_entry_t &entry) { m_node = entry.m_node; } /** Initialize an index entry to some sane value. */ void init() { ut_ad(m_mtr != nullptr); set_prev_null(); set_next_null(); set_versions_null(); set_trx_id(0); set_trx_undo_no(0); set_z_page_no(FIL_NULL); set_z_frag_id(FRAG_ID_NULL); set_data_len(0); set_zdata_len(0); } /** Determine if the current index entry be rolled back. @param[in] trxid the transaction that is being rolled back. @param[in] undo_no the savepoint undo number of trx, upto which rollback happens. @return true if this entry can be rolled back, false otherwise. */ bool can_rollback(trx_id_t trxid, undo_no_t undo_no) { /* For rollback, make use of creator trx id. */ return ((trxid == get_trx_id()) && (get_trx_undo_no() >= undo_no)); } /** Determine if the current index entry be purged. @param[in] trxid the transaction that is being purged. @param[in] undo_no the undo number of trx. @return true if this entry can be purged, false otherwise. */ bool can_be_purged(trx_id_t trxid, undo_no_t undo_no) { return ((trxid == get_trx_id_modifier()) && (get_trx_undo_no_modifier() == undo_no)); } /** Purge one index entry. @param[in] index index to which LOB belongs. @param[in] trxid purging data belonging to trxid. @param[in] first first page of LOB. @param[in,out] lst list from which this entry will be removed. @param[in,out] free_list list to which this entry will be added.*/ fil_addr_t purge_version(dict_index_t *index, trx_id_t trxid, z_first_page_t &first, flst_base_node_t *lst, flst_base_node_t *free_list); /** Purge the current index entry. An index entry points to either a FIRST page or DATA page. That LOB page will be freed if it is DATA page. A FIRST page should not be freed. */ void purge(dict_index_t *index, z_first_page_t &first); /** Remove this node from the given list. @param[in] bnode the base node of the list from which to remove current node. */ void remove(flst_base_node_t *bnode) { ut_ad(m_mtr != nullptr); flst_remove(bnode, m_node, m_mtr); } /** Insert the given index entry after the current index entry. @param[in] base the base node of the file based list. @param[in] entry the new node to be inserted.*/ void insert_after(flst_base_node_t *base, z_index_entry_t &entry) { ut_ad(m_mtr != nullptr); flst_insert_after(base, m_node, entry.get_node(), m_mtr); } void insert_before(flst_base_node_t *base, z_index_entry_t &entry) { ut_ad(m_mtr != nullptr); flst_insert_before(base, entry.get_node(), m_node, m_mtr); } /** Add this node as the last node in the given list. @param[in] bnode the base node of the file list. */ void push_back(flst_base_node_t *bnode) { ut_ad(m_mtr != nullptr); flst_add_last(bnode, m_node, m_mtr); } /** Add this node as the last node in the given list. @param[in] bnode the base node of the file list. */ void push_front(flst_base_node_t *bnode) { ut_ad(m_mtr != nullptr); flst_add_first(bnode, m_node, m_mtr); } /** Set the previous index entry as null. */ void set_prev_null() { flst_write_addr(m_node + OFFSET_PREV, fil_addr_null, m_mtr); } /** Get the location of previous index entry. */ fil_addr_t get_prev() const { return (flst_read_addr(m_node + OFFSET_PREV, m_mtr)); } /** Get the location of next index entry. */ fil_addr_t get_next() const { return (flst_read_addr(m_node + OFFSET_NEXT, m_mtr)); } /** Set the next index entry as null. */ void set_next_null() { ut_ad(m_mtr != nullptr); flst_write_addr(m_node + OFFSET_NEXT, fil_addr_null, m_mtr); } /** Set the versions list as null. */ void set_versions_null() { flst_base_node_t *bnode = get_versions_list(); flst_init(bnode, m_mtr); } /** Get the base node of the list of versions. */ flst_base_node_t *get_versions_list() const { return (m_node + OFFSET_VERSIONS); } /** Get the base node of the list of versions. */ flst_bnode_t get_versions_mem() const { ut_ad(m_mtr != nullptr); flst_base_node_t *node = get_versions_list(); return (flst_bnode_t(node, m_mtr)); } trx_id_t get_trx_id() const { return (mach_read_from_6(m_node + OFFSET_TRXID)); } trx_id_t get_trx_id_modifier() const { return (mach_read_from_6(m_node + OFFSET_TRXID_MODIFIER)); } /** Get the undo number of the creator transaction. This is used for rollback purposes. @return the undo number of creator trx. */ undo_no_t get_trx_undo_no() const { byte *ptr = m_node + OFFSET_TRX_UNDO_NO; return (mach_read_from_4(ptr)); } /** Get the undo number of the modifier transaction. This is used for purging purposes. @return the undo number of modifier trx. */ undo_no_t get_trx_undo_no_modifier() const { byte *ptr = m_node + OFFSET_TRX_UNDO_NO_MODIFIER; return (mach_read_from_4(ptr)); } /** Set the trx identifier to given value, without generating redo log records. @param[in] id the given trx identifier.*/ void set_trx_id_no_redo(trx_id_t id) { byte *ptr = m_node + OFFSET_TRXID; mach_write_to_6(ptr, id); } /** Set the trx identifier to given value. @param[in] id the given trx identifier.*/ void set_trx_id(trx_id_t id) { ut_ad(m_mtr != nullptr); byte *ptr = m_node + OFFSET_TRXID; mach_write_to_6(ptr, id); mlog_log_string(ptr, 6, m_mtr); } /** Set the modifier trxid to the given value. @param[in] id the modifier trxid.*/ void set_trx_id_modifier(trx_id_t id) { ut_ad(m_mtr != nullptr); byte *ptr = m_node + OFFSET_TRXID_MODIFIER; mach_write_to_6(ptr, id); mlog_log_string(ptr, 6, m_mtr); } /** Set the modifier trxid to the given value, without generating redo log records. @param[in] id the modifier trxid.*/ void set_trx_id_modifier_no_redo(trx_id_t id) { byte *ptr = m_node + OFFSET_TRXID_MODIFIER; mach_write_to_6(ptr, id); } /** Set the undo number of the creator trx. @param[in] undo_no the undo number value.*/ void set_trx_undo_no(undo_no_t undo_no) { ut_ad(m_mtr != nullptr); byte *ptr = m_node + OFFSET_TRX_UNDO_NO; mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr); } /** Set the undo number of the modifier trx. @param[in] undo_no the undo number value.*/ void set_trx_undo_no_modifier(undo_no_t undo_no) { ut_ad(m_mtr != nullptr); byte *ptr = m_node + OFFSET_TRX_UNDO_NO_MODIFIER; mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr); } page_no_t get_z_page_no() const { return (mach_read_from_4(m_node + OFFSET_Z_PAGE_NO)); } void set_z_page_no(page_no_t page_no) { ut_ad(m_mtr != nullptr); mlog_write_ulint(m_node + OFFSET_Z_PAGE_NO, page_no, MLOG_4BYTES, m_mtr); } page_no_t get_z_frag_id() const { return (mach_read_from_2(m_node + OFFSET_Z_FRAG_ID)); } void set_z_frag_id(frag_id_t id) { ut_ad(m_mtr != nullptr); mlog_write_ulint(m_node + OFFSET_Z_FRAG_ID, id, MLOG_2BYTES, m_mtr); } /** Get the uncompressed data length in bytes. */ ulint get_data_len() const { return (mach_read_from_4(m_node + OFFSET_DATA_LEN)); } /** Set the uncompressed data length in bytes. @param[in] len the uncompressed data length in bytes */ void set_data_len(ulint len) { ut_ad(m_mtr != nullptr); mlog_write_ulint(m_node + OFFSET_DATA_LEN, len, MLOG_4BYTES, m_mtr); } /** Get the compressed data length in bytes. */ ulint get_zdata_len() const { return (mach_read_from_4(m_node + OFFSET_ZDATA_LEN)); } /** Set the compressed data length in bytes. @param[in] len the compressed data length in bytes */ void set_zdata_len(ulint len) { ut_ad(m_mtr != nullptr); mlog_write_ulint(m_node + OFFSET_ZDATA_LEN, len, MLOG_4BYTES, m_mtr); } /** Get the LOB version. */ uint32_t get_lob_version() const { return (mach_read_from_4(m_node + OFFSET_LOB_VERSION)); } /** Set the LOB version . @param[in] version the lob version. */ void set_lob_version(ulint version) { ut_ad(m_mtr != nullptr); mlog_write_ulint(m_node + OFFSET_LOB_VERSION, version, MLOG_4BYTES, m_mtr); } /* The given entry becomes the old version of the current entry. Move the version base node from old entry to current entry. @param[in] entry the old entry */ void set_old_version(z_index_entry_t &entry); /** The current index entry points to a latest LOB page. It may or may not have older versions. If older version is there, bring it back to the index list from the versions list. Then remove the current entry from the index list. Move the versions list from current entry to older entry. @param[in] index the index in which LOB exists. @param[in] trxid The transaction identifier. @param[in] first The first lob page containing index list and free list. */ fil_addr_t make_old_version_current(dict_index_t *index, trx_id_t trxid, z_first_page_t &first); flst_node_t *get_node() { return (m_node); } bool is_null() const { return (m_node == nullptr); } std::ostream &print(std::ostream &out) const; std::ostream &print_pages(std::ostream &out) const; /** Load the page (in shared mode) whose number was cached. @return the buffer block of the page loaded. */ buf_block_t *load_s() { ut_ad(m_page_no != FIL_NULL); page_id_t page_id(dict_index_get_space(m_index), m_page_no); page_size_t page_size(dict_table_page_size(m_index->table)); m_block = buf_page_get(page_id, page_size, RW_S_LATCH, m_mtr); return (m_block); } /** Load the given file address in s mode. @param[in] addr the file address of the required node. */ void load_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); m_node = fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr, &m_block); m_page_no = m_block->get_page_no(); } /** Load the given file address in x mode. @param[in] addr the file address of the required node. */ void load_x(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); m_node = fut_get_ptr(space, page_size, addr, RW_X_LATCH, m_mtr, &m_block); m_page_no = m_block->get_page_no(); } /** Read the given LOB index entry. @param[in] entry_mem the LOB index entry. */ void read(z_index_entry_mem_t &entry_mem) const; /** Read the given LOB index entry and then commit the mtr. @param[in] entry_mem the LOB index entry. */ void read_and_commit(z_index_entry_mem_t &entry_mem) { read(entry_mem); mtr_commit(m_mtr); m_node = nullptr; } /** Get the location of the current index entry. */ fil_addr_t get_self() const; private: /** Move the version base node from current entry to the given entry. @param[in] entry The index entry to which the version base node is moved to. */ void move_version_base_node(z_index_entry_t &entry); /** The file list node in a db page. This node is persisted. */ flst_node_t *m_node; /** A mini transaction. */ mtr_t *m_mtr; /** The index containing the LOB. */ dict_index_t *m_index; /** The buffer block in which this entry exists. While reading data from m_node, appropriate latches must be held on this block. */ buf_block_t *m_block; /** The page number in which this entry is available. This information will be cached and can be used to reload the page conveniently. */ page_no_t m_page_no; }; inline std::ostream &operator<<(std::ostream &out, const z_index_entry_t &obj) { return (obj.print(out)); } } /* namespace lob */ #endif /* zlob0index_h */