1 /*****************************************************************************
2 
3 Copyright (c) 2016, 2018, 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 _lob0int_h_
27 #define _lob0int_h_
28 
29 #include <string.h>
30 
31 #include "buf0buf.h"
32 #include "fut0fut.h"
33 #include "fut0lst.h"
34 #include "lot0buf.h"
35 #include "lot0types.h"
36 #include "trx0types.h"
37 
38 namespace lob {
39 const ulint LOB_HDR_PART_LEN = 0;
40 const ulint LOB_HDR_TRX_ID = 4;
41 const ulint LOB_HDR_SIZE = 10;
42 const ulint LOB_PAGE_DATA = FIL_PAGE_DATA + LOB_HDR_SIZE;
43 
44 /** The offset where the list base node is located.  This is the list
45 of LOB pages. */
46 const ulint LOB_INDEX_LIST = LOB_PAGE_DATA;
47 
48 /** The offset where the list base node is located.  This is the list
49 of free pages. */
50 const ulint LOB_INDEX_FREE_NODES = LOB_PAGE_DATA + 16;
51 
52 const ulint LOB_PAGE_TRAILER_LEN = 8;
53 
54 /** This page type refers to the first page of an LOB, which contains
55 half data and half meta data. */
56 #define FIL_PAGE_TYPE_LOB_BASE 21
57 
58 struct base_node_page_t;
59 
60 /** An index entry pointing to an LOB page. */
61 struct index_entry_t {
index_entry_tindex_entry_t62   index_entry_t(flst_node_t *node) : m_node(node) {}
resetindex_entry_t63   void reset(flst_node_t *node) { m_node = node; }
64 
65   /** Index entry offsets within node. */
66   static const ulint OFFSET_PREV = 0;
67   static const ulint OFFSET_NEXT = 6;
68 
69   /** Points to base node of the list of versions. The size of base node is
70   16 bytes. */
71   static const ulint OFFSET_VERSIONS = 12;
72   static const ulint OFFSET_TRXID = 28;
73   static const ulint OFFSET_PAGE_NO = 34;
74   static const ulint OFFSET_DATA_LEN = 38;
75 
76   /** Total length of an index node. */
77   static const ulint SIZE = 40;
78 
79   /** The versions base node is set to NULL. */
set_versions_nullindex_entry_t80   void set_versions_null() {
81     byte *base_node = get_versions_ptr();
82     flst_init(base_node);
83   }
84 
85   /** Can this index entry be purged.
86   @param[in] trxid the transaction that is being purged.
87   @return true if this entry can be purged, false otherwise. */
can_be_purgedindex_entry_t88   bool can_be_purged(trx_id_t trxid) { return (trxid == get_trx_id()); }
89 
90   /* The given entry becomes the old version of the current entry.
91   Move the version base node from old entry to current entry.
92   @param[in]  entry  the old entry */
set_old_versionindex_entry_t93   void set_old_version(index_entry_t &entry) {
94     flst_node_t *node = entry.get_node_ptr();
95     flst_base_node_t *version_list = get_versions_ptr();
96     ut_ad(flst_get_len(version_list) == 0);
97 
98     entry.move_version_base_node(*this);
99     flst_add_first(version_list, node);
100   }
101 
102   /** The current index entry points to a latest LOB page.  It may or may
103   not have older versions.  If older version is there, bring it back to the
104   index list from the versions list.  Then remove the current entry from
105   the index list.  Move the versions list from current entry to older entry.
106   @param[in]  trxid  The transaction identifier.
107   @param[in]  first_page  The first lob page containing index list and free
108                           list. */
109   void make_old_version_current(trx_id_t trxid, base_node_page_t &first_page);
110 
111   fil_addr_t purge_version(trx_id_t trxid, flst_base_node_t *ver_list,
112                            flst_base_node_t *free_list);
113 
add_versionindex_entry_t114   void add_version(index_entry_t &entry) const {
115     flst_node_t *node = entry.get_node_ptr();
116     flst_base_node_t *version_list = get_versions_ptr();
117     flst_add_first(version_list, node);
118   }
119 
get_versions_listindex_entry_t120   flst_base_node_t *get_versions_list() const { return (get_versions_ptr()); }
121 
get_trx_idindex_entry_t122   trx_id_t get_trx_id() const {
123     byte *ptr = get_trxid_ptr();
124     return (mach_read_from_6(ptr));
125   }
126 
set_trx_idindex_entry_t127   void set_trx_id(trx_id_t id) {
128     byte *ptr = get_trxid_ptr();
129     return (mach_write_to_6(ptr, id));
130   }
131 
set_page_noindex_entry_t132   void set_page_no(page_no_t num) {
133     byte *ptr = get_pageno_ptr();
134     return (mach_write_to_4(ptr, num));
135   }
136 
set_prev_nullindex_entry_t137   void set_prev_null() { flst_write_addr(m_node + OFFSET_PREV, fil_addr_null); }
138 
set_next_nullindex_entry_t139   void set_next_null() { flst_write_addr(m_node + OFFSET_NEXT, fil_addr_null); }
140 
get_page_noindex_entry_t141   page_no_t get_page_no() {
142     byte *ptr = get_pageno_ptr();
143     return (mach_read_from_4(ptr));
144   }
145 
set_data_lenindex_entry_t146   void set_data_len(ulint len) {
147     byte *ptr = get_datalen_ptr();
148     return (mach_write_to_2(ptr, len));
149   }
150 
get_data_lenindex_entry_t151   ulint get_data_len() {
152     byte *ptr = get_datalen_ptr();
153     return (mach_read_from_2(ptr));
154   }
155 
printindex_entry_t156   std::ostream &print(std::ostream &out) {
157     out << "[index_entry_t: trxid=" << get_trx_id()
158         << ", page_no=" << get_page_no() << ", data_len=" << get_data_len()
159         << "]";
160     return (out);
161   }
162 
is_sameindex_entry_t163   bool is_same(const index_entry_t &that) { return (m_node == that.m_node); }
164 
165  private:
166   /** Move the version base node from current entry to the given entry.
167   @param[in]  entry  The index entry to which the version base node is moved
168                      to. */
169   void move_version_base_node(index_entry_t &entry);
170 
171   /** Purge the current index entry. An index entry points to either a FIRST
172   page or DATA page.  That LOB page will be freed if it is DATA page.  A FIRST
173   page should not be freed. */
174   void purge();
175 
get_versions_ptrindex_entry_t176   byte *get_versions_ptr() const { return (m_node + OFFSET_VERSIONS); }
get_trxid_ptrindex_entry_t177   byte *get_trxid_ptr() const { return (m_node + OFFSET_TRXID); }
get_pageno_ptrindex_entry_t178   byte *get_pageno_ptr() const { return (m_node + OFFSET_PAGE_NO); }
get_datalen_ptrindex_entry_t179   byte *get_datalen_ptr() const { return (m_node + OFFSET_DATA_LEN); }
get_node_ptrindex_entry_t180   byte *get_node_ptr() const { return (m_node); }
181 
182   byte *m_node;
183 };
184 
185 struct page_t {
page_tpage_t186   page_t() : m_block(nullptr) {}
page_tpage_t187   page_t(buf_block_t *block) : m_block(block) {}
188 
get_trx_idpage_t189   trx_id_t get_trx_id() const {
190     return (mach_read_from_6(frame() + FIL_PAGE_DATA + LOB_HDR_TRX_ID));
191   }
192 
get_page_nopage_t193   page_no_t get_page_no() const {
194     return (mach_read_from_4(frame() + FIL_PAGE_OFFSET));
195   }
196 
set_data_lenpage_t197   void set_data_len(ulint len) {
198     mach_write_to_4(frame() + FIL_PAGE_DATA + LOB_HDR_PART_LEN, len);
199   }
200 
set_next_pagepage_t201   void set_next_page(page_no_t page_no) {
202     mach_write_to_4(frame() + FIL_PAGE_NEXT, page_no);
203   }
204 
set_next_page_nullpage_t205   void set_next_page_null() {
206     mach_write_to_4(frame() + FIL_PAGE_NEXT, FIL_NULL);
207   }
208 
get_next_pagepage_t209   page_no_t get_next_page() {
210     return (mach_read_from_4(frame() + FIL_PAGE_NEXT));
211   }
212 
get_data_lenpage_t213   ulint get_data_len() {
214     return (mach_read_from_4(frame() + FIL_PAGE_DATA + LOB_HDR_PART_LEN));
215   }
216 
framepage_t217   byte *frame() const { return (buf_block_get_frame(m_block)); }
218 
219   static ulint payload();
220   ulint max_space_available();
221 
222  protected:
223   buf_block_t *m_block;
224 };
225 
226 struct data_page_t : public page_t {
data_page_tdata_page_t227   data_page_t() {}
data_page_tdata_page_t228   data_page_t(buf_block_t *block) : page_t(block) {}
229 
allocdata_page_t230   buf_block_t *alloc() {
231     ut_ad(m_block == nullptr);
232     m_block = btr_page_alloc();
233     set_page_type();
234     set_next_page_null();
235     return (m_block);
236   }
237 
set_page_typedata_page_t238   void set_page_type() {
239     mach_write_to_2(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_DATA);
240   }
241 
set_trx_iddata_page_t242   void set_trx_id(trx_id_t id) {
243     mach_write_to_6(frame() + FIL_PAGE_DATA + LOB_HDR_TRX_ID, id);
244   }
245 
payloaddata_page_t246   static ulint payload() {
247     return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN);
248   }
249 
data_begindata_page_t250   byte *data_begin() const { return (frame() + LOB_PAGE_DATA); }
251 
252   buf_block_t *replace(trx_id_t trxid, ulint offset, byte *&ptr, ulint &want);
253 
readdata_page_t254   ulint read(trx_id_t trxid, ulint offset, byte *ptr, ulint want) {
255     byte *start = data_begin();
256     start += offset;
257     ulint avail_data = get_data_len() - offset;
258 
259     ulint copy_len = want < avail_data ? want : avail_data;
260     memcpy(ptr, start, copy_len);
261     return (copy_len);
262   }
263 
writedata_page_t264   ulint write(trx_id_t trxid, byte *&data, ulint &len) {
265     byte *ptr = data_begin();
266     ulint written = (len > payload()) ? payload() : len;
267     memcpy(ptr, data, written);
268     set_data_len(written);
269 
270     data += written;
271     len -= written;
272 
273     return (written);
274   }
275 
276   ulint append(trx_id_t trxid, byte *&data, ulint &len);
277 
278   std::pair<ulint, byte *> insert_middle(trx_id_t trxid, ulint offset,
279                                          byte *&data, ulint &len,
280                                          buf_block_t *&new_block);
281 
282   buf_block_t *remove_middle(trx_id_t trxid, ulint offset, ulint &len);
283 
max_space_availabledata_page_t284   ulint max_space_available() const { return (payload()); }
285 };
286 
287 struct base_node_page_t : public page_t {
base_node_page_tbase_node_page_t288   base_node_page_t() {}
base_node_page_tbase_node_page_t289   base_node_page_t(buf_block_t *block) : page_t(block) {}
290 
291   buf_block_t *alloc();
292 
deallocbase_node_page_t293   void dealloc() {
294     btr_page_free(m_block);
295     m_block = nullptr;
296   }
297 
emptybase_node_page_t298   bool empty() const {
299     flst_base_node_t *base = index_list();
300     return (flst_get_len(base) == 0);
301   }
302 
303   /** Allocate one index entry. */
304   flst_node_t *alloc_index_entry();
305 
nodes_beginbase_node_page_t306   byte *nodes_begin() const {
307     return (frame() + LOB_PAGE_DATA + FLST_BASE_NODE_SIZE +
308             FLST_BASE_NODE_SIZE);
309   }
310 
payloadbase_node_page_t311   static ulint payload() {
312     return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN -
313             FLST_BASE_NODE_SIZE - FLST_BASE_NODE_SIZE);
314   }
315 
set_trx_idbase_node_page_t316   void set_trx_id(trx_id_t id) {
317     mach_write_to_6(frame() + FIL_PAGE_DATA + LOB_HDR_TRX_ID, id);
318   }
319 
320   std::pair<ulint, byte *> insert_middle(trx_id_t trxid, ulint offset,
321                                          byte *&data, ulint &len,
322                                          buf_block_t *&new_block);
323 
324   buf_block_t *remove_middle(trx_id_t trxid, ulint offset, ulint &len);
325 
326   /** Write as much as possible of the given data into the page. */
writebase_node_page_t327   ulint write(trx_id_t trxid, byte *&data, ulint &len) {
328     byte *ptr = data_begin();
329     ulint space_available = payload() / 2;
330     ulint written = (len > space_available) ? space_available : len;
331     memcpy(ptr, data, written);
332     set_data_len(written);
333     set_trx_id(trxid);
334 
335     data += written;
336     len -= written;
337 
338     return (written);
339   }
340 
341   buf_block_t *replace(trx_id_t trxid, ulint offset, byte *&ptr, ulint &want);
342 
readbase_node_page_t343   ulint read(trx_id_t trxid, ulint offset, byte *ptr, ulint want) {
344     byte *start = data_begin();
345     start += offset;
346     ulint avail_data = get_data_len() - offset;
347 
348     ulint copy_len = want < avail_data ? want : avail_data;
349     memcpy(ptr, start, copy_len);
350     return (copy_len);
351   }
352 
set_page_typebase_node_page_t353   void set_page_type() {
354     mach_write_to_2(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_FIRST);
355   }
356 
index_listbase_node_page_t357   flst_base_node_t *index_list() const { return (frame() + LOB_INDEX_LIST); }
358 
free_listbase_node_page_t359   flst_base_node_t *free_list() const {
360     return (frame() + LOB_INDEX_FREE_NODES);
361   }
362 
max_space_availablebase_node_page_t363   static ulint max_space_available() { return (payload() / 2); }
364 
365   /** Get the number of index entries this page can hold.
366   @return Number of index entries this page can hold. */
node_countbase_node_page_t367   static ulint node_count() {
368     return (max_space_available() / index_entry_t::SIZE);
369   }
370 
print_index_entriesbase_node_page_t371   std::ostream &print_index_entries(std::ostream &out) const {
372     flst_base_node_t *base = index_list();
373     fil_addr_t node_loc = flst_get_first(base);
374 
375     while (!fil_addr_is_null(node_loc)) {
376       flst_node_t *node = fut_get_ptr(node_loc);
377       out << (void *)node << std::endl;
378       index_entry_t entry(node);
379       entry.print(out) << std::endl;
380       node_loc = flst_get_next_addr(node);
381     }
382     return (out);
383   }
384 
data_beginbase_node_page_t385   byte *data_begin() const {
386     ulint space_available = payload() / 2;
387     return (frame() + UNIV_PAGE_SIZE - LOB_PAGE_TRAILER_LEN - space_available);
388   }
389 
390   /** Append data into a LOB first page. */
391   ulint append(trx_id_t trxid, byte *&data, ulint &len);
392 };
393 
394 struct node_page_t : public page_t {
node_page_tnode_page_t395   node_page_t() {}
node_page_tnode_page_t396   node_page_t(buf_block_t *block) : page_t(block) {}
397 
398   buf_block_t *alloc(base_node_page_t &first_page);
399 
payloadnode_page_t400   static ulint payload() {
401     return (UNIV_PAGE_SIZE - FIL_PAGE_DATA - FIL_PAGE_DATA_END);
402   }
403 
max_space_availablenode_page_t404   static ulint max_space_available() { return (payload()); }
405 
406   /** Get the number of index entries this page can hold.
407   @return Number of index entries this page can hold. */
node_countnode_page_t408   static ulint node_count() {
409     return (max_space_available() / index_entry_t::SIZE);
410   }
411 
set_page_typenode_page_t412   void set_page_type() {
413     mach_write_to_2(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_INDEX);
414   }
415 
nodes_beginnode_page_t416   byte *nodes_begin() const { return (frame() + LOB_PAGE_DATA); }
417 };
418 
419 }  // namespace lob
420 #endif  // _lob0int_h_
421