1 /*****************************************************************************
2 
3 Copyright (c) 2016, 2019, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 #ifndef lob0index_h
27 #define lob0index_h
28 
29 #include "fut0lst.h"
30 #include "lob0util.h"
31 #include "trx0trx.h"
32 #include "univ.i"
33 
34 namespace lob {
35 
36 typedef std::map<page_no_t, buf_block_t *> BlockCache;
37 struct first_page_t;
38 
39 /** An in-memory copy of an index_entry_t data */
40 struct index_entry_mem_t {
41   fil_addr_t m_self;
42   fil_addr_t m_prev;
43   fil_addr_t m_next;
44   flst_bnode_t m_versions;
45   trx_id_t m_trx_id;
46   trx_id_t m_trx_id_modifier;
47   undo_no_t m_undo_no;
48   undo_no_t m_undo_no_modifier;
49   page_no_t m_page_no;
50   ulint m_data_len;
51 
index_entry_mem_tindex_entry_mem_t52   index_entry_mem_t() { reset(); }
53 
54   void reset();
55 
is_nullindex_entry_mem_t56   bool is_null() { return (m_self.is_equal(fil_addr_null)); }
57 
get_page_noindex_entry_mem_t58   page_no_t get_page_no() const { return (m_page_no); }
59 
60   /** Print this object into the given output stream.
61   @param[in]	out	the output stream.
62   @return the output stream. */
63   std::ostream &print(std::ostream &out) const;
64 };
65 
66 /** List of index entry memory (iem) objects. */
67 using List_iem_t = std::list<index_entry_mem_t>;
68 
69 /** Overloading the global output operator to print the index_entry_mem_t
70 object.
71 @param[in,out]	out	the output stream.
72 @param[in]	obj	an object of type index_entry_mem_t
73 @return the output stream. */
74 inline std::ostream &operator<<(std::ostream &out,
75                                 const index_entry_mem_t &obj) {
76   return (obj.print(out));
77 }
78 
79 /** An index entry pointing to an LOB page. */
80 struct index_entry_t {
81   /** Index entry offsets within node. */
82   static const ulint OFFSET_PREV = 0;
83   static const ulint OFFSET_NEXT = OFFSET_PREV + FIL_ADDR_SIZE;
84 
85   /** Points to base node of the list of versions. The size of base node is
86   16 bytes. */
87   static const ulint OFFSET_VERSIONS = OFFSET_NEXT + FIL_ADDR_SIZE;
88 
89   /** The creator trx id. */
90   static const ulint OFFSET_TRXID = OFFSET_VERSIONS + FLST_BASE_NODE_SIZE;
91 
92   /** The modifier trx id. */
93   static const ulint OFFSET_TRXID_MODIFIER = OFFSET_TRXID + 6;
94   static const ulint OFFSET_TRX_UNDO_NO = OFFSET_TRXID_MODIFIER + 6;
95 
96   /** The undo number of the modifier trx. */
97   static const ulint OFFSET_TRX_UNDO_NO_MODIFIER = OFFSET_TRX_UNDO_NO + 4;
98   static const ulint OFFSET_PAGE_NO = OFFSET_TRX_UNDO_NO_MODIFIER + 4;
99   static const ulint OFFSET_DATA_LEN = OFFSET_PAGE_NO + 4;
100 
101   /** The LOB version number. */
102   static const ulint OFFSET_LOB_VERSION = OFFSET_DATA_LEN + 4;
103 
104   /** Total length of an index node. */
105   static const ulint SIZE = OFFSET_LOB_VERSION + 4;
106 
107   /** Constructor.
108   @param[in]	node	the pointer where index entry is located. */
index_entry_tindex_entry_t109   index_entry_t(flst_node_t *node)
110       : m_node(node), m_mtr(nullptr), m_index(nullptr), m_block(nullptr) {}
111 
index_entry_tindex_entry_t112   index_entry_t(flst_node_t *node, mtr_t *mtr)
113       : m_node(node), m_mtr(mtr), m_index(nullptr), m_block(nullptr) {}
114 
index_entry_tindex_entry_t115   index_entry_t(flst_node_t *node, mtr_t *mtr, dict_index_t *index)
116       : m_node(node), m_mtr(mtr), m_index(index), m_block(nullptr) {}
117 
index_entry_tindex_entry_t118   index_entry_t(mtr_t *mtr, const dict_index_t *index)
119       : m_node(nullptr), m_mtr(mtr), m_index(index), m_block(nullptr) {}
120 
121   /* Move the node pointer to a different place within the same page.
122   @param[in]	addr	new location of node pointer. */
resetindex_entry_t123   void reset(fil_addr_t &addr) {
124     ut_ad(m_block->page.id.page_no() == addr.page);
125 
126     m_node = buf_block_get_frame(m_block) + addr.boffset;
127   }
128 
129   /* Get the buffer block of the current index entry.
130   @return the buffer block of the current index entry.*/
get_blockindex_entry_t131   buf_block_t *get_block() const { return (m_block); }
132 
133   /* Reset the current object to point to a different node.
134   @param[in]	node	the new file list node. */
resetindex_entry_t135   void reset(flst_node_t *node) { m_node = node; }
136 
is_nullindex_entry_t137   bool is_null() const {
138     const byte zero[SIZE] = {0x00};
139     return (m_node == nullptr || memcmp(m_node, zero, SIZE) == 0);
140   }
141 
142   /** Initialize the object fully. */
initindex_entry_t143   void init() {
144     set_prev_null();
145     set_next_null();
146     set_versions_null();
147     set_trx_id(0);
148     set_trx_undo_no(0);
149     set_page_no(FIL_NULL);
150     set_data_len(0);
151   }
152 
153   /** Get the location of the current index entry. */
154   fil_addr_t get_self() const;
155 
156   /** The versions base node is set to NULL. */
set_versions_nullindex_entry_t157   void set_versions_null() {
158     ut_ad(m_mtr != nullptr);
159 
160     byte *base_node = get_versions_ptr();
161     flst_init(base_node, m_mtr);
162   }
163 
164   /** Determine if the current index entry be rolled back.
165   @param[in]	trxid		the transaction that is being purged.
166   @param[in]	undo_no		the undo number of trx.
167   @return true if this entry can be rolled back, false otherwise. */
can_rollbackindex_entry_t168   bool can_rollback(trx_id_t trxid, undo_no_t undo_no) {
169     /* For rollback, make use of creator trx id. */
170     return ((trxid == get_trx_id()) && (get_trx_undo_no() >= undo_no));
171   }
172 
173   /** Determine if the current index entry be purged.
174   @param[in]	trxid		the transaction that is being purged.
175   @param[in]	undo_no		the undo number of trx.
176   @return true if this entry can be purged, false otherwise. */
can_be_purgedindex_entry_t177   bool can_be_purged(trx_id_t trxid, undo_no_t undo_no) {
178     return ((trxid == get_trx_id_modifier()) &&
179             (get_trx_undo_no_modifier() == undo_no));
180   }
181 
182   /* The given entry becomes the old version of the current entry.
183   Move the version base node from old entry to current entry.
184   @param[in]  entry  the old entry */
set_old_versionindex_entry_t185   void set_old_version(index_entry_t &entry) {
186     flst_node_t *node = entry.get_node_ptr();
187     flst_base_node_t *version_list = get_versions_ptr();
188     ut_ad(flst_get_len(version_list) == 0);
189 
190     entry.move_version_base_node(*this);
191     flst_add_first(version_list, node, m_mtr);
192   }
193 
194   /** The current index entry points to a latest LOB page.  It may or
195   may not have older versions.  If older version is there, bring it
196   back to the index list from the versions list.  Then remove the
197   current entry from the index list.  Move the versions list from
198   current entry to older entry.
199   @param[in]	index		the clustered index containing the LOB.
200   @param[in]	trxid		The transaction identifier.
201   @param[in]	first_page	The first lob page containing index
202                                   list and free list.
203   @return the location of next entry. */
204   fil_addr_t make_old_version_current(dict_index_t *index, trx_id_t trxid,
205                                       first_page_t &first_page);
206 
207   /** Purge the current entry.
208   @param[in]  index  the clustered index containing the LOB.
209   @param[in]  trxid  The transaction identifier.
210   @param[in]  lst    the base node of index list.
211   @param[in]  free_list    the base node of free list.
212   @return the location of the next entry. */
213   fil_addr_t purge_version(dict_index_t *index, trx_id_t trxid,
214                            flst_base_node_t *lst, flst_base_node_t *free_list);
215 
add_versionindex_entry_t216   void add_version(index_entry_t &entry) const {
217     flst_node_t *node = entry.get_node_ptr();
218     flst_base_node_t *version_list = get_versions_ptr();
219     flst_add_first(version_list, node, m_mtr);
220   }
221 
get_versions_listindex_entry_t222   flst_base_node_t *get_versions_list() const { return (get_versions_ptr()); }
223 
224   /** Add this node as the last node in the given list.
225   @param[in]  bnode  the base node of the file list. */
push_backindex_entry_t226   void push_back(flst_base_node_t *bnode) {
227     flst_add_last(bnode, m_node, m_mtr);
228   }
229 
230   /** Get the base node of the list of versions. */
get_versions_memindex_entry_t231   flst_bnode_t get_versions_mem() const {
232     flst_base_node_t *node = get_versions_list();
233     return (flst_bnode_t(node, m_mtr));
234   }
235 
get_trx_idindex_entry_t236   trx_id_t get_trx_id() const {
237     byte *ptr = get_trxid_ptr();
238     return (mach_read_from_6(ptr));
239   }
240 
get_trx_id_modifierindex_entry_t241   trx_id_t get_trx_id_modifier() const {
242     byte *ptr = get_trxid_modifier_ptr();
243     return (mach_read_from_6(ptr));
244   }
245 
get_trx_undo_noindex_entry_t246   undo_no_t get_trx_undo_no() const {
247     byte *ptr = get_trx_undo_no_ptr();
248     return (mach_read_from_4(ptr));
249   }
250 
get_lob_versionindex_entry_t251   uint32_t get_lob_version() const {
252     byte *ptr = get_lob_version_ptr();
253     return (mach_read_from_4(ptr));
254   }
255 
256   /** Get the undo number of the modifier trx.
257   @return the undo number of the modifier trx. */
get_trx_undo_no_modifierindex_entry_t258   undo_no_t get_trx_undo_no_modifier() const {
259     byte *ptr = get_trx_undo_no_modifier_ptr();
260     return (mach_read_from_4(ptr));
261   }
262 
get_nextindex_entry_t263   fil_addr_t get_next() const {
264     ut_ad(m_node != nullptr);
265 
266     return (flst_read_addr(m_node + OFFSET_NEXT, m_mtr));
267   }
268 
269   /** Make the current index entry object to point to the next index
270   entry object.
271   @return the buffer block in which the next index entry is available.*/
nextindex_entry_t272   buf_block_t *next() {
273     fil_addr_t node_loc = get_next();
274 
275     if (node_loc.is_null()) {
276       return (nullptr);
277     }
278 
279     if (m_block == nullptr || m_block->page.id.page_no() != node_loc.page) {
280       load_x(node_loc);
281     } else {
282       /* Next entry in the same page. */
283       reset(node_loc);
284     }
285 
286     return (m_block);
287   }
288 
289   /** Get the previous index entry.
290   @return The file address of previous index entry. */
get_previndex_entry_t291   fil_addr_t get_prev() const {
292     return (flst_read_addr(m_node + OFFSET_PREV, m_mtr));
293   }
294 
295   /** Write the trx identifier to the index entry. No redo log
296   is generated for this modification.  This is meant to be used
297   during tablespace import.
298   @param[in]	id	the trx identifier.*/
set_trx_id_no_redoindex_entry_t299   void set_trx_id_no_redo(trx_id_t id) {
300     byte *ptr = get_trxid_ptr();
301     mach_write_to_6(ptr, id);
302   }
303 
304   /** Write the modifier trx identifier to the index entry. No redo log
305   is generated for this modification.  This is meant to be used
306   during tablespace import.
307   @param[in]	id	the trx identifier.*/
set_trx_id_modifier_no_redoindex_entry_t308   void set_trx_id_modifier_no_redo(trx_id_t id) {
309     byte *ptr = get_trxid_modifier_ptr();
310     mach_write_to_6(ptr, id);
311   }
312 
set_trx_idindex_entry_t313   void set_trx_id(trx_id_t id) {
314     byte *ptr = get_trxid_ptr();
315     mach_write_to_6(ptr, id);
316     mlog_log_string(ptr, 6, m_mtr);
317   }
318 
set_trx_id_modifierindex_entry_t319   void set_trx_id_modifier(trx_id_t id) {
320     ut_ad(m_mtr != nullptr);
321 
322     byte *ptr = get_trxid_modifier_ptr();
323     mach_write_to_6(ptr, id);
324     mlog_log_string(ptr, 6, m_mtr);
325   }
326 
set_trx_undo_noindex_entry_t327   void set_trx_undo_no(undo_no_t undo_no) {
328     byte *ptr = get_trx_undo_no_ptr();
329     mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr);
330   }
331 
332   /** Set the LOB version of this entry.
333   @param[in]	version		the LOB version number. */
set_lob_versionindex_entry_t334   void set_lob_version(uint32_t version) {
335     byte *ptr = get_lob_version_ptr();
336     mlog_write_ulint(ptr, version, MLOG_4BYTES, m_mtr);
337   }
338 
set_trx_undo_no_modifierindex_entry_t339   void set_trx_undo_no_modifier(undo_no_t undo_no) {
340     ut_ad(m_mtr != nullptr);
341 
342     byte *ptr = get_trx_undo_no_modifier_ptr();
343     mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr);
344   }
345 
set_page_noindex_entry_t346   void set_page_no(page_no_t num) {
347     ut_ad(num > 0);
348     byte *ptr = get_pageno_ptr();
349     return (mlog_write_ulint(ptr, num, MLOG_4BYTES, m_mtr));
350   }
351 
set_prev_nullindex_entry_t352   void set_prev_null() {
353     flst_write_addr(m_node + OFFSET_PREV, fil_addr_null, m_mtr);
354   }
355 
set_next_nullindex_entry_t356   void set_next_null() {
357     flst_write_addr(m_node + OFFSET_NEXT, fil_addr_null, m_mtr);
358   }
359 
get_page_noindex_entry_t360   page_no_t get_page_no() const {
361     byte *ptr = get_pageno_ptr();
362     return (mach_read_from_4(ptr));
363   }
364 
set_data_lenindex_entry_t365   void set_data_len(ulint len) {
366     byte *ptr = get_datalen_ptr();
367     return (mlog_write_ulint(ptr, len, MLOG_2BYTES, m_mtr));
368   }
369 
get_data_lenindex_entry_t370   ulint get_data_len() const {
371     byte *ptr = get_datalen_ptr();
372     return (mach_read_from_2(ptr));
373   }
374 
375   std::ostream &print(std::ostream &out) const;
376 
is_sameindex_entry_t377   bool is_same(const index_entry_t &that) { return (m_node == that.m_node); }
378 
379   void read(index_entry_mem_t &entry_mem) const;
380 
381   /** Load the index entry available in the given file address.
382   Take x-latch on the index page.
383   @param[in]	addr	the file address of the index entry.
384   @return the buffer block containing the index entry. */
385   buf_block_t *load_x(const fil_addr_t &addr);
386 
387   /** Load the index entry available in the given file address.
388   Take s-latch on the index page.
389   @param[in]	addr	the file location of index entry.
390   @return the buffer block. */
391   buf_block_t *load_s(const fil_addr_t &addr);
392 
insert_afterindex_entry_t393   void insert_after(flst_base_node_t *base, index_entry_t &entry) {
394     flst_insert_after(base, m_node, entry.get_node(), m_mtr);
395   }
396 
insert_beforeindex_entry_t397   void insert_before(flst_base_node_t *base, index_entry_t &entry) {
398     flst_insert_before(base, entry.get_node(), m_node, m_mtr);
399   }
400 
removeindex_entry_t401   void remove(flst_base_node_t *bnode) { flst_remove(bnode, m_node, m_mtr); }
402 
403  private:
404   /** Move the version base node from current entry to the given entry.
405   @param[in]	to_entry	The index entry to which the version
406                                   base node is moved to.*/
407   void move_version_base_node(index_entry_t &to_entry);
408 
409   /** Purge the current index entry. An index entry points to either a
410   FIRST page or DATA page.  That LOB page will be freed if it is DATA
411   page.  A FIRST page should not be freed. */
412   void purge(dict_index_t *index);
413 
get_versions_ptrindex_entry_t414   byte *get_versions_ptr() const { return (m_node + OFFSET_VERSIONS); }
415 
get_trxid_ptrindex_entry_t416   byte *get_trxid_ptr() const { return (m_node + OFFSET_TRXID); }
417 
get_trxid_modifier_ptrindex_entry_t418   byte *get_trxid_modifier_ptr() const {
419     return (m_node + OFFSET_TRXID_MODIFIER);
420   }
421 
get_trx_undo_no_ptrindex_entry_t422   byte *get_trx_undo_no_ptr() const { return (m_node + OFFSET_TRX_UNDO_NO); }
423 
get_lob_version_ptrindex_entry_t424   byte *get_lob_version_ptr() const { return (m_node + OFFSET_LOB_VERSION); }
425 
get_trx_undo_no_modifier_ptrindex_entry_t426   byte *get_trx_undo_no_modifier_ptr() const {
427     return (m_node + OFFSET_TRX_UNDO_NO_MODIFIER);
428   }
429 
get_pageno_ptrindex_entry_t430   byte *get_pageno_ptr() const { return (m_node + OFFSET_PAGE_NO); }
431 
get_datalen_ptrindex_entry_t432   byte *get_datalen_ptr() const { return (m_node + OFFSET_DATA_LEN); }
433 
get_node_ptrindex_entry_t434   byte *get_node_ptr() const { return (m_node); }
435 
get_nodeindex_entry_t436   byte *get_node() const { return (m_node); }
437 
438  private:
439   byte *m_node;
440   mtr_t *m_mtr;
441   const dict_index_t *m_index;
442   buf_block_t *m_block;
443 };
444 
445 /** Overloading the global output operator to easily print an index entry.
446 @param[in]	out	the output stream.
447 @param[in]	obj	the index entry.
448 @return	the output stream. */
449 inline std::ostream &operator<<(std::ostream &out, const index_entry_t &obj) {
450   return (obj.print(out));
451 }
452 
453 } /* namespace lob */
454 
455 #endif /* lob0index_h */
456