/***************************************************************************** Copyright (c) 2017, 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 *****************************************************************************/ /** @file include/lob0undo.h Undo logging small changes to BLOBs. */ #ifndef lob0undo_h #define lob0undo_h #include #include "dict0mem.h" #include "mem0mem.h" #include "univ.i" namespace lob { /** Undo information about LOB data alone without including LOB index. */ struct undo_data_t { /** Apply the undo information to the given LOB. @param[in] index clustered index containing the LOB. @param[in] lob_mem LOB on which the given undo will be applied. @param[in] len length of LOB. @param[in] lob_version lob version number @param[in] first_page_no the first page number of lob */ void apply(dict_index_t *index, byte *lob_mem, size_t len, size_t lob_version, page_no_t first_page_no); /** The LOB first page number. */ page_no_t m_page_no = FIL_NULL; /** The LOB version number on which this undo should be applied. */ ulint m_version = 0; /** The offset within LOB where partial update happened. */ ulint m_offset = 0; /** The length of the modification. */ ulint m_length = 0; /** Changes to the LOB data. */ byte *m_old_data = nullptr; /** Copy the old data from the undo page into this object. @param[in] undo_ptr the pointer into the undo log record. @param[in] len length of the old data. @return pointer past the old data. */ const byte *copy_old_data(const byte *undo_ptr, ulint len); /** Free allocated memory for old data. */ void destroy(); std::ostream &print(std::ostream &out) const; }; inline std::ostream &operator<<(std::ostream &out, const undo_data_t &obj) { return (obj.print(out)); } /** Container to hold a sequence of undo log records containing modification of BLOBs. */ struct undo_seq_t { /** Constructor. @param[in] field_no the field number of LOB.*/ undo_seq_t(ulint field_no) : m_field_no(field_no), m_undo_list(nullptr) {} /** Apply the undo log records on the given LOB in memory. @param[in] index the clustered index to which LOB belongs. @param[in] lob the BLOB in memory. @param[in] len the length of BLOB in memory. @param[in] lob_version the LOB version number. @param[in] first_page_no the first page number of BLOB.*/ void apply(dict_index_t *index, byte *lob, size_t len, size_t lob_version, page_no_t first_page_no) { if (m_undo_list != nullptr) { for (auto iter = m_undo_list->begin(); iter != m_undo_list->end(); ++iter) { iter->apply(index, lob, len, lob_version, first_page_no); } } } /** Get the field number of BLOB. @return the field number of BLOB. */ ulint get_field_no() const { return (m_field_no); } /** Append the given undo log record to the end of container. @param[in] u1 the undo log record information. */ void push_back(undo_data_t &u1) { if (m_undo_list == nullptr) { m_undo_list = UT_NEW_NOKEY(std::list()); } m_undo_list->push_back(u1); } /** Destroy the contents of this undo sequence list. */ void destroy() { if (m_undo_list != nullptr) { std::for_each(m_undo_list->begin(), m_undo_list->end(), [](undo_data_t &obj) { obj.destroy(); }); m_undo_list->clear(); UT_DELETE(m_undo_list); m_undo_list = nullptr; } } /** Check if any undo log exists to apply. */ bool exists() const { return (m_undo_list == nullptr ? false : !m_undo_list->empty()); } ulint m_field_no; private: std::list *m_undo_list = nullptr; }; /** The list of modifications to be applied on LOBs to get older versions. Given a field number, it should be able to obtain the list of undo information. */ struct undo_vers_t { public: /** Get the undo log sequence object for the given field number, which represents one blob. @param[in] field_no the field number of the blob. @return the undo sequence object or nullptr. */ undo_seq_t *get_undo_sequence_if_exists(ulint field_no) { if (m_versions == nullptr) { return (nullptr); } else { for (auto iter = m_versions->begin(); iter != m_versions->end(); ++iter) { if ((*iter)->get_field_no() == field_no) { return (*iter); } } } return (nullptr); } /** Get the undo log sequence object for the given field number, which represents one blob. The undo sequence object is allocated if it does not exist. @param[in] field_no the field number of the blob. @return the undo sequence object. */ undo_seq_t *get_undo_sequence(ulint field_no) { if (m_versions == nullptr) { m_versions = UT_NEW_NOKEY(std::list()); } else { for (auto iter = m_versions->begin(); iter != m_versions->end(); ++iter) { if ((*iter)->get_field_no() == field_no) { return (*iter); } } } undo_seq_t *seq = UT_NEW_NOKEY(undo_seq_t(field_no)); m_versions->push_back(seq); return (seq); } /** Empty the collected LOB undo information from cache. */ void reset() { if (m_versions != nullptr) { for (auto iter = m_versions->begin(); iter != m_versions->end(); ++iter) { (*iter)->destroy(); UT_DELETE(*iter); } m_versions->clear(); } } /** Apply the undo log record on the given LOB in memory. @param[in] clust_index the clust index to which LOB belongs. @param[in] field_no the field number of the LOB. @param[in] lob the LOB data. @param[in] len the length of LOB. @param[in] lob_version LOB version number. @param[in] first_page the first page number of LOB.*/ void apply(dict_index_t *clust_index, ulint field_no, byte *lob, size_t len, size_t lob_version, page_no_t first_page) { undo_seq_t *seq = get_undo_sequence_if_exists(field_no); if (seq != nullptr) { seq->apply(clust_index, lob, len, lob_version, first_page); } } /** Destroy the accumulated undo_seq_t objects. */ void destroy() { if (m_versions != nullptr) { reset(); UT_DELETE(m_versions); m_versions = nullptr; } } /** Check if the undo contained older versions. @return true if there are no older versions, false otherwise. */ bool is_empty() const { return (m_versions->empty()); } /** Destructor to free the resources. */ ~undo_vers_t() { destroy(); } private: /** Maintain a list of undo_seq_t objects. */ std::list *m_versions = nullptr; }; } /* namespace lob */ #endif /* lob0undo_h */