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 
27 #ifndef lob0first_h
28 #define lob0first_h
29 
30 #include "btr0btr.h"
31 #include "buf0buf.h"
32 #include "dict0dict.h"
33 #include "fut0lst.h"
34 #include "lob0index.h"
35 #include "lob0lob.h"
36 #include "lob0util.h"
37 #include "mtr0log.h"
38 
39 namespace lob {
40 
41 /** The first page of an uncompressed LOB. */
42 struct first_page_t : public basic_page_t {
43   /** Version information. One byte. */
44   static const ulint OFFSET_VERSION = FIL_PAGE_DATA;
45 
46   /** One byte of flag bits.  Currently only one bit (the least
47   significant bit) is used, other 7 bits are available for future use.*/
48   static const ulint OFFSET_FLAGS = FIL_PAGE_DATA + 1;
49 
50   /** LOB version. 4 bytes.*/
51   static const uint32_t OFFSET_LOB_VERSION = OFFSET_FLAGS + 1;
52 
53   /** The latest transaction that modified this LOB. */
54   static const ulint OFFSET_LAST_TRX_ID = OFFSET_LOB_VERSION + 4;
55 
56   /** The latest transaction undo_no that modified this LOB. */
57   static const ulint OFFSET_LAST_UNDO_NO = OFFSET_LAST_TRX_ID + 6;
58 
59   /** Length of data stored in this page.  4 bytes. */
60   static const ulint OFFSET_DATA_LEN = OFFSET_LAST_UNDO_NO + 4;
61 
62   /** The trx that created the data stored in this page. */
63   static const ulint OFFSET_TRX_ID = OFFSET_DATA_LEN + 4;
64 
65   /** The offset where the list base node is located.  This is the list
66   of LOB pages. */
67   static const ulint OFFSET_INDEX_LIST = OFFSET_TRX_ID + 6;
68 
69   /** The offset where the list base node is located.  This is the list
70   of free nodes. */
71   static const ulint OFFSET_INDEX_FREE_NODES =
72       OFFSET_INDEX_LIST + FLST_BASE_NODE_SIZE;
73 
74   /** The offset where the contents of the first page begins. */
75   static const ulint LOB_PAGE_DATA =
76       OFFSET_INDEX_FREE_NODES + FLST_BASE_NODE_SIZE;
77 
78   static const ulint LOB_PAGE_TRAILER_LEN = FIL_PAGE_DATA_END;
79 
80   /** The default constructor. */
first_page_tfirst_page_t81   first_page_t() {}
82 
83   /** Constructor.
84   @param[in]	block	the buffer block of the first page.
85   @param[in]	mtr	the mini-transaction context. */
first_page_tfirst_page_t86   first_page_t(buf_block_t *block, mtr_t *mtr) : basic_page_t(block, mtr) {}
87 
88   /** Constructor.
89   @param[in]	block	the buffer block of the first page.*/
first_page_tfirst_page_t90   first_page_t(buf_block_t *block) : basic_page_t(block, nullptr) {}
91 
92   /** Constructor.
93   @param[in]	block	the buffer block of the first page.
94   @param[in]	mtr	the mini-transaction context.
95   @param[in]	index	the clustered index containing the LOB. */
first_page_tfirst_page_t96   first_page_t(buf_block_t *block, mtr_t *mtr, dict_index_t *index)
97       : basic_page_t(block, mtr, index) {}
98 
99   /** Constructor.
100   @param[in]	mtr	the mini-transaction context.
101   @param[in]	index	the clustered index containing the LOB. */
first_page_tfirst_page_t102   first_page_t(mtr_t *mtr, dict_index_t *index)
103       : basic_page_t(nullptr, mtr, index) {}
104 
105   /** Set the LOB format version number to 0. */
set_version_0first_page_t106   void set_version_0() {
107     mlog_write_ulint(frame() + OFFSET_VERSION, 0, MLOG_1BYTE, m_mtr);
108   }
109 
110   /** Obtain the flags value. This has 8 bits of which only the first
111   bit is used.
112   @return one byte flag */
get_flagsfirst_page_t113   uint8_t get_flags() { return (mach_read_from_1(frame() + OFFSET_FLAGS)); }
114 
115   /** When the bit is set, the LOB is not partially updatable anymore.
116   @return true, if partially updatable.
117   @return false, if partially NOT updatable. */
can_be_partially_updatedfirst_page_t118   bool can_be_partially_updated() {
119     uint8_t flags = get_flags();
120     return (!(flags & 0x01));
121   }
122 
123   /** Do tablespace import. */
124   void import(trx_id_t trx_id);
125 
126   /** When the bit is set, the LOB is not partially updatable anymore.
127   Enable the bit.
128   @param[in]	trx	the current transaction. */
129   void mark_cannot_be_partially_updated(trx_t *trx);
130 
131   /** Allocate the first page for uncompressed LOB.
132   @param[in,out]	alloc_mtr	the allocation mtr.
133   @param[in]	is_bulk		true if it is bulk operation
134                                   (OPCODE_INSERT_BULK)
135   @return the allocated buffer block.*/
136   buf_block_t *alloc(mtr_t *alloc_mtr, bool is_bulk);
137 
138   /** Free all the index pages.  The list of index pages can be accessed
139   by traversing via the FIL_PAGE_NEXT field.*/
140   void free_all_index_pages();
141 
142   /** Free all the data pages. The data pages can be accessed through
143   index entry. */
144   void free_all_data_pages();
145 
146   /** Load the first page of LOB with s-latch.
147   @param[in]   page_id    the page identifier of the first page.
148   @param[in]   page_size  the page size information.
149   @return the buffer block of the first page. */
load_sfirst_page_t150   buf_block_t *load_s(page_id_t page_id, page_size_t page_size) {
151     m_block = buf_page_get(page_id, page_size, RW_S_LATCH, m_mtr);
152     return (m_block);
153   }
154 
155   /** Load the first page of LOB with x-latch.
156   @param[in]   page_id    the page identifier of the first page.
157   @param[in]   page_size  the page size information.
158   @param[in]   mtr        the mini transaction context for latch.
159   @return the buffer block of the first page. */
160   buf_block_t *load_x(const page_id_t &page_id, const page_size_t &page_size,
161                       mtr_t *mtr);
162 
163   /** Load the first page of LOB with x-latch in the given mtr context.
164   The first page must already be x-latched by the m_mtr.
165   @param[in]   mtr        the mini transaction context for latch.
166   @return the buffer block of the first page. */
load_xfirst_page_t167   buf_block_t *load_x(mtr_t *mtr) const {
168     ut_ad(mtr_memo_contains(m_mtr, m_block, MTR_MEMO_PAGE_X_FIX));
169     buf_block_t *tmp = buf_page_get(m_block->page.id, m_index->get_page_size(),
170                                     RW_X_LATCH, mtr);
171     ut_ad(tmp == m_block);
172     return (tmp);
173   }
174 
175   /** Load the first page of LOB with x-latch.
176   @param[in]   page_id    the page identifier of the first page.
177   @param[in]   page_size  the page size information.
178   @return the buffer block of the first page. */
load_xfirst_page_t179   buf_block_t *load_x(const page_id_t &page_id, const page_size_t &page_size) {
180     return (load_x(page_id, page_size, m_mtr));
181   }
182 
183   /** Get the buffer block of the LOB first page.
184   @return the buffer block. */
get_blockfirst_page_t185   buf_block_t *get_block() { return (m_block); }
186 
187   /** Load the file list node from the given location.  An x-latch is taken
188   on the page containing the file list node.
189   @param[in]	addr	the location of file list node.
190   @param[in]	mtr     the mini transaction context to be used.
191   @return		the file list node.*/
addr2ptr_xfirst_page_t192   flst_node_t *addr2ptr_x(fil_addr_t &addr, mtr_t *mtr) const {
193     space_id_t space = dict_index_get_space(m_index);
194     const page_size_t page_size = dict_table_page_size(m_index->table);
195     return (fut_get_ptr(space, page_size, addr, RW_X_LATCH, mtr));
196   }
197 
198   /** Load the file list node from the given location.  An x-latch is taken
199   on the page containing the file list node.
200   @param[in]	addr	the location of file list node.
201   @return		the file list node.*/
addr2ptr_xfirst_page_t202   flst_node_t *addr2ptr_x(fil_addr_t &addr) const {
203     return (addr2ptr_x(addr, m_mtr));
204   }
205 
206   /** Load the file list node from the given location, assuming that it
207   exists in the first page itself.
208   @param[in]	addr	the location of file list node.
209   @return		the file list node.*/
addr2ptrfirst_page_t210   flst_node_t *addr2ptr(const fil_addr_t &addr) {
211     ut_ad(m_block->page.id.page_no() == addr.page);
212     return (buf_block_get_frame(m_block) + addr.boffset);
213   }
214 
215   /** Load the file list node from the given location.  An s-latch is taken
216   on the page containing the file list node.
217   @param[in]	addr	the location of file list node.
218   @return		the file list node.*/
addr2ptr_sfirst_page_t219   flst_node_t *addr2ptr_s(fil_addr_t &addr) {
220     space_id_t space = dict_index_get_space(m_index);
221     const page_size_t page_size = dict_table_page_size(m_index->table);
222     return (fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr));
223   }
224 
225   /** Load the file list node from the given location.  An s-latch is taken
226   on the page containing the file list node. The given cache is checked to
227   see if the page is already loaded.
228   @param[in]	cache	cache of loaded buffer blocks.
229   @param[in]	addr	the location of file list node.
230   @return		the file list node.*/
addr2ptr_s_cachefirst_page_t231   flst_node_t *addr2ptr_s_cache(std::map<page_no_t, buf_block_t *> &cache,
232                                 fil_addr_t &addr) const {
233     byte *result;
234     space_id_t space = dict_index_get_space(m_index);
235     const page_size_t page_size = dict_table_page_size(m_index->table);
236 
237     auto iter = cache.find(addr.page);
238 
239     if (iter == cache.end()) {
240       /* Not there in cached blocks.  Add the loaded block to cache. */
241       buf_block_t *block = nullptr;
242       result = fut_get_ptr(space, page_size, addr, RW_S_LATCH, m_mtr, &block);
243       cache.insert(std::make_pair(addr.page, block));
244     } else {
245       buf_block_t *block = iter->second;
246       ut_ad(block->page.id.page_no() == addr.page);
247       result = buf_block_get_frame(block) + addr.boffset;
248     }
249     return (result);
250   }
251 
252   /** Free the first page.  This is done when all other LOB pages have
253   been freed. */
254   void dealloc();
255 
256   /** Free all the pages associated with this LOB. */
257   void destroy();
258 
259   /** Check if the index list is empty or not.
260   @return true if empty, false otherwise. */
is_emptyfirst_page_t261   bool is_empty() const {
262     flst_base_node_t *base = index_list();
263     ut_ad(base != nullptr);
264     return (flst_get_len(base) == 0);
265   }
266 
267   /** Allocate one index entry.  If required an index page (of type
268   FIL_PAGE_TYPE_LOB_INDEX) will be allocated.
269   @param[in]	bulk	true if it is a bulk operation
270                           (OPCODE_INSERT_BULK), false otherwise.
271   @return the file list node of the index entry. */
272   flst_node_t *alloc_index_entry(bool bulk);
273 
274   /** Get a pointer to the beginning of the index entry nodes in the
275   first part of the page.
276   @return	the first index entry node. */
nodes_beginfirst_page_t277   byte *nodes_begin() const { return (frame() + LOB_PAGE_DATA); }
278 
279   /** Calculate and return the payload.
280   @return the payload possible in this page. */
payloadfirst_page_t281   static ulint payload() {
282     return (UNIV_PAGE_SIZE - LOB_PAGE_DATA - LOB_PAGE_TRAILER_LEN);
283   }
284 
285   /** Set the transaction identifier in the first page header without
286   generating redo logs.
287   @param[in]	id	the transaction identifier. */
set_trx_id_no_redofirst_page_t288   void set_trx_id_no_redo(trx_id_t id) {
289     byte *ptr = frame() + OFFSET_TRX_ID;
290     mach_write_to_6(ptr, id);
291   }
292 
293   /** Set the transaction identifier in the first page header.
294   @param[in]	id	the transaction identifier. */
set_trx_idfirst_page_t295   void set_trx_id(trx_id_t id) {
296     byte *ptr = frame() + OFFSET_TRX_ID;
297     mach_write_to_6(ptr, id);
298     mlog_log_string(ptr, 6, m_mtr);
299   }
300 
301   /** Initialize the LOB version to 1. */
init_lob_versionfirst_page_t302   void init_lob_version() {
303     ut_ad(m_mtr != nullptr);
304 
305     mlog_write_ulint(frame() + OFFSET_LOB_VERSION, 1, MLOG_4BYTES, m_mtr);
306   }
307 
308   /** Get the lob version number.
309   @return the lob version. */
get_lob_versionfirst_page_t310   uint32_t get_lob_version() {
311     return (mach_read_from_4(frame() + OFFSET_LOB_VERSION));
312   }
313 
314   /** Increment the lob version by 1. */
315   uint32_t incr_lob_version();
316 
317   /** Set the last transaction identifier, without generating redo log
318   records.
319   @param[in]	id	the trx identifier. */
set_last_trx_id_no_redofirst_page_t320   void set_last_trx_id_no_redo(trx_id_t id) {
321     byte *ptr = frame() + OFFSET_LAST_TRX_ID;
322     mach_write_to_6(ptr, id);
323   }
324 
325   /** Set the last transaction identifier.
326   @param[in]	id	the trx identifier. */
set_last_trx_idfirst_page_t327   void set_last_trx_id(trx_id_t id) {
328     byte *ptr = frame() + OFFSET_LAST_TRX_ID;
329     mach_write_to_6(ptr, id);
330     mlog_log_string(ptr, 6, m_mtr);
331   }
332 
333   /** Set the last transaction undo number.
334   @param[in]	undo_no	the trx undo number. */
set_last_trx_undo_nofirst_page_t335   void set_last_trx_undo_no(undo_no_t undo_no) {
336     ut_ad(m_mtr != nullptr);
337 
338     byte *ptr = frame() + OFFSET_LAST_UNDO_NO;
339     mlog_write_ulint(ptr, undo_no, MLOG_4BYTES, m_mtr);
340   }
341 
342   /** Get the last transaction identifier.
343   @return the transaction identifier. */
get_last_trx_idfirst_page_t344   trx_id_t get_last_trx_id() const {
345     byte *ptr = frame() + OFFSET_LAST_TRX_ID;
346     return (mach_read_from_6(ptr));
347   }
348 
349   /** Get the last transaction undo number.
350   @return the transaction undo number. */
get_last_trx_undo_nofirst_page_t351   undo_no_t get_last_trx_undo_no() const {
352     byte *ptr = frame() + OFFSET_LAST_UNDO_NO;
353     return (mach_read_from_4(ptr));
354   }
355 
356   /** Set the length of data stored in bytes.
357   @param[in]	len	amount of data stored in bytes. */
set_data_lenfirst_page_t358   void set_data_len(ulint len) {
359     ut_ad(m_mtr != nullptr);
360 
361     mlog_write_ulint(frame() + OFFSET_DATA_LEN, len, MLOG_4BYTES, m_mtr);
362   }
363 
364   /** Write as much as possible of the given data into the page.
365   @param[in]	trxid	the current transaction.
366   @param[in]	data	the data to be written.
367   @param[in]	len	the length of the given data.
368   @return number of bytes actually written. */
369   ulint write(trx_id_t trxid, const byte *&data, ulint &len);
370 
371   /** Replace data in the page by making a copy-on-write.
372   @param[in]	trx	the current transaction.
373   @param[in]	offset	the location where replace operation starts.
374   @param[in,out]	ptr	the buffer containing new data. after the
375                           call it will point to remaining data.
376   @param[in,out]	want	requested amount of data to be replaced.
377                           after the call it will contain amount of
378                           data yet to be replaced.
379   @param[in]	mtr	the mini-transaction context.
380   @return	the newly allocated buffer block. */
381   buf_block_t *replace(trx_t *trx, ulint offset, const byte *&ptr, ulint &want,
382                        mtr_t *mtr);
383 
384   /** Replace data in the page inline.
385   @param[in]	trx	the current transaction.
386   @param[in]	offset	the location where replace operation starts.
387   @param[in,out]	ptr	the buffer containing new data. after the
388                           call it will point to remaining data.
389   @param[in,out]	want	requested amount of data to be replaced.
390                           after the call it will contain amount of
391                           data yet to be replaced.
392   @param[in]	mtr	the mini-transaction context.*/
393   void replace_inline(trx_t *trx, ulint offset, const byte *&ptr, ulint &want,
394                       mtr_t *mtr);
395 
get_data_lenfirst_page_t396   ulint get_data_len() const {
397     return (mach_read_from_4(frame() + OFFSET_DATA_LEN));
398   }
399 
400   /** Read data from the first page.
401   @param[in]	offset	the offset from where read starts.
402   @param[out]	ptr	the output buffer
403   @param[in]	want	number of bytes to read.
404   @return number of bytes read. */
405   ulint read(ulint offset, byte *ptr, ulint want);
406 
set_page_typefirst_page_t407   void set_page_type() {
408     ut_ad(m_mtr != nullptr);
409 
410     mlog_write_ulint(frame() + FIL_PAGE_TYPE, FIL_PAGE_TYPE_LOB_FIRST,
411                      MLOG_2BYTES, m_mtr);
412   }
413 
index_listfirst_page_t414   flst_base_node_t *index_list() const { return (frame() + OFFSET_INDEX_LIST); }
415 
free_listfirst_page_t416   flst_base_node_t *free_list() const {
417     return (frame() + OFFSET_INDEX_FREE_NODES);
418   }
419 
420   /** Get the number of bytes used to store LOB data in the first page
421   of uncompressed LOB.
422   @return Number of bytes available for LOB data. */
max_space_availablefirst_page_t423   static ulint max_space_available() {
424     const ulint index_array_size = node_count() * index_entry_t::SIZE;
425 
426     return (payload() - index_array_size);
427   }
428 
429   /** Get the number of index entries this page can hold.
430   @return Number of index entries this page can hold. */
node_countfirst_page_t431   constexpr static ulint node_count() {
432     /* Each index entry is of size 60 bytes.  We store only 10
433     index entries in the first page of the LOB.  This means that
434     only 600 bytes are used for index data in the first page of
435     LOB. This will help to reserve more space in the first page
436     for the LOB data.*/
437     return (10);
438   }
439 
440   std::ostream &print_index_entries(std::ostream &out) const;
441 
442   std::ostream &print_index_entries_cache_s(std::ostream &out,
443                                             BlockCache &cache) const;
444 
445   /** Obtain the location where the data begins.
446   @return pointer to location within page where data begins. */
data_beginfirst_page_t447   byte *data_begin() const {
448     ut_ad(buf_block_get_page_zip(m_block) == NULL);
449 
450     constexpr ulint index_array_size = node_count() * index_entry_t::SIZE;
451 
452     return (frame() + LOB_PAGE_DATA + index_array_size);
453   }
454 
455   /** Append data into a LOB first page. */
456   ulint append(trx_id_t trxid, byte *&data, ulint &len);
457 
458 #ifdef UNIV_DEBUG
459   /** Validate the first page. */
460   bool validate() const;
461 #endif /* UNIV_DEBUG */
462 
get_page_typefirst_page_t463   page_type_t get_page_type() { return (basic_page_t::get_page_type()); }
464 
get_page_typefirst_page_t465   static page_type_t get_page_type(dict_index_t *index,
466                                    const page_id_t &page_id,
467                                    const page_size_t &page_size) {
468     mtr_t local_mtr;
469     mtr_start(&local_mtr);
470     first_page_t first(&local_mtr, index);
471     first.load_x(page_id, page_size);
472     page_type_t page_type = first.get_page_type();
473     mtr_commit(&local_mtr);
474     return (page_type);
475   }
476 
477  public:
478   /** Restart the given mtr. The first page must already be x-latched by
479   the m_mtr.
480   @param[in]   mtr   the mini transaction context which is to be restarted. */
restart_mtrfirst_page_t481   void restart_mtr(mtr_t *mtr) {
482     ut_ad(mtr != m_mtr);
483     mtr_commit(mtr);
484     mtr_start(mtr);
485     mtr->set_log_mode(m_mtr->get_log_mode());
486     load_x(mtr);
487   }
488 };
489 
490 } /* namespace lob */
491 
492 #endif /* lob0first_h */
493