1 /*****************************************************************************
2 
3 Copyright (c) 2017, 2018, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8 
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation.  The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License, version 2.0, for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file include/lob0undo.h
28  Undo logging small changes to BLOBs. */
29 
30 #ifndef lob0undo_h
31 #define lob0undo_h
32 
33 #include <list>
34 #include "dict0mem.h"
35 #include "mem0mem.h"
36 #include "univ.i"
37 
38 namespace lob {
39 
40 /** Undo information about LOB data alone without including LOB index. */
41 struct undo_data_t {
42   /** Apply the undo information to the given LOB.
43   @param[in]	index		clustered index containing the LOB.
44   @param[in]	lob_mem		LOB on which the given undo will be
45                                   applied.
46   @param[in]    len             length of LOB.
47   @param[in]    lob_version     lob version number
48   @param[in]    first_page_no   the first page number of lob */
49   void apply(dict_index_t *index, byte *lob_mem, size_t len, size_t lob_version,
50              page_no_t first_page_no);
51 
52   /** The LOB first page number. */
53   page_no_t m_page_no = FIL_NULL;
54 
55   /** The LOB version number on which this undo should be applied. */
56   ulint m_version = 0;
57 
58   /** The offset within LOB where partial update happened. */
59   ulint m_offset = 0;
60 
61   /** The length of the modification. */
62   ulint m_length = 0;
63 
64   /** Changes to the LOB data. */
65   byte *m_old_data = nullptr;
66 
67   /** Copy the old data from the undo page into this object.
68   @param[in]  undo_ptr  the pointer into the undo log record.
69   @param[in]  len       length of the old data.
70   @return pointer past the old data. */
71   const byte *copy_old_data(const byte *undo_ptr, ulint len);
72 
73   /** Free allocated memory for old data. */
74   void destroy();
75 
76   std::ostream &print(std::ostream &out) const;
77 };
78 
79 inline std::ostream &operator<<(std::ostream &out, const undo_data_t &obj) {
80   return (obj.print(out));
81 }
82 
83 /** Container to hold a sequence of undo log records containing modification
84 of BLOBs. */
85 struct undo_seq_t {
86   /** Constructor.
87   @param[in]	field_no	the field number of LOB.*/
undo_seq_tundo_seq_t88   undo_seq_t(ulint field_no) : m_field_no(field_no), m_undo_list(nullptr) {}
89 
90   /** Apply the undo log records on the given LOB in memory.
91   @param[in]	index	the clustered index to which LOB belongs.
92   @param[in]	lob	the BLOB in memory.
93   @param[in]	len	the length of BLOB in memory.
94   @param[in]	lob_version	the LOB version number.
95   @param[in]	first_page_no	the first page number of BLOB.*/
applyundo_seq_t96   void apply(dict_index_t *index, byte *lob, size_t len, size_t lob_version,
97              page_no_t first_page_no) {
98     if (m_undo_list != nullptr) {
99       for (auto iter = m_undo_list->begin(); iter != m_undo_list->end();
100            ++iter) {
101         iter->apply(index, lob, len, lob_version, first_page_no);
102       }
103     }
104   }
105 
106   /** Get the field number of BLOB.
107   @return the field number of BLOB. */
get_field_noundo_seq_t108   ulint get_field_no() const { return (m_field_no); }
109 
110   /** Append the given undo log record to the end of container.
111   @param[in]	u1	the undo log record information. */
push_backundo_seq_t112   void push_back(undo_data_t &u1) {
113     if (m_undo_list == nullptr) {
114       m_undo_list = UT_NEW_NOKEY(std::list<undo_data_t>());
115     }
116     m_undo_list->push_back(u1);
117   }
118 
119   /** Destroy the contents of this undo sequence list. */
destroyundo_seq_t120   void destroy() {
121     if (m_undo_list != nullptr) {
122       std::for_each(m_undo_list->begin(), m_undo_list->end(),
123                     [](undo_data_t &obj) { obj.destroy(); });
124       m_undo_list->clear();
125       UT_DELETE(m_undo_list);
126       m_undo_list = nullptr;
127     }
128   }
129 
130   /** Check if any undo log exists to apply. */
existsundo_seq_t131   bool exists() const {
132     return (m_undo_list == nullptr ? false : !m_undo_list->empty());
133   }
134 
135   ulint m_field_no;
136 
137  private:
138   std::list<undo_data_t> *m_undo_list = nullptr;
139 };
140 
141 /** The list of modifications to be applied on LOBs to get older versions.
142 Given a field number, it should be able to obtain the list of undo
143 information. */
144 struct undo_vers_t {
145  public:
146   /** Get the undo log sequence object for the given field number, which
147   represents one blob.
148   @param[in]    field_no   the field number of the blob.
149   @return the undo sequence object or nullptr. */
get_undo_sequence_if_existsundo_vers_t150   undo_seq_t *get_undo_sequence_if_exists(ulint field_no) {
151     if (m_versions == nullptr) {
152       return (nullptr);
153     } else {
154       for (auto iter = m_versions->begin(); iter != m_versions->end(); ++iter) {
155         if ((*iter)->get_field_no() == field_no) {
156           return (*iter);
157         }
158       }
159     }
160     return (nullptr);
161   }
162 
163   /** Get the undo log sequence object for the given field number, which
164   represents one blob.  The undo sequence object is allocated if it does
165   not exist.
166   @param[in]    field_no   the field number of the blob.
167   @return the undo sequence object. */
get_undo_sequenceundo_vers_t168   undo_seq_t *get_undo_sequence(ulint field_no) {
169     if (m_versions == nullptr) {
170       m_versions = UT_NEW_NOKEY(std::list<undo_seq_t *>());
171     } else {
172       for (auto iter = m_versions->begin(); iter != m_versions->end(); ++iter) {
173         if ((*iter)->get_field_no() == field_no) {
174           return (*iter);
175         }
176       }
177     }
178 
179     undo_seq_t *seq = UT_NEW_NOKEY(undo_seq_t(field_no));
180     m_versions->push_back(seq);
181 
182     return (seq);
183   }
184 
185   /** Empty the collected LOB undo information from cache. */
resetundo_vers_t186   void reset() {
187     if (m_versions != nullptr) {
188       for (auto iter = m_versions->begin(); iter != m_versions->end(); ++iter) {
189         (*iter)->destroy();
190         UT_DELETE(*iter);
191       }
192       m_versions->clear();
193     }
194   }
195 
196   /** Apply the undo log record on the given LOB in memory.
197   @param[in]	clust_index	the clust index to which LOB belongs.
198   @param[in]	field_no	the field number of the LOB.
199   @param[in]	lob		the LOB data.
200   @param[in]	len		the length of LOB.
201   @param[in]	lob_version	LOB version number.
202   @param[in]	first_page	the first page number of LOB.*/
applyundo_vers_t203   void apply(dict_index_t *clust_index, ulint field_no, byte *lob, size_t len,
204              size_t lob_version, page_no_t first_page) {
205     undo_seq_t *seq = get_undo_sequence_if_exists(field_no);
206 
207     if (seq != nullptr) {
208       seq->apply(clust_index, lob, len, lob_version, first_page);
209     }
210   }
211 
212   /** Destroy the accumulated undo_seq_t objects. */
destroyundo_vers_t213   void destroy() {
214     if (m_versions != nullptr) {
215       reset();
216       UT_DELETE(m_versions);
217       m_versions = nullptr;
218     }
219   }
220 
221   /** Check if the undo contained older versions.
222   @return true if there are no older versions, false otherwise. */
is_emptyundo_vers_t223   bool is_empty() const { return (m_versions->empty()); }
224 
225   /** Destructor to free the resources. */
~undo_vers_tundo_vers_t226   ~undo_vers_t() { destroy(); }
227 
228  private:
229   /** Maintain a list of undo_seq_t objects. */
230   std::list<undo_seq_t *> *m_versions = nullptr;
231 };
232 
233 } /* namespace lob */
234 
235 #endif /* lob0undo_h */
236