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 
27 #include <list>
28 #include "btr0btr.h"
29 #include "buf0buf.h"
30 #include "dict0dict.h"
31 #include "lob0first.h"
32 #include "lob0index.h"
33 #include "lob0lob.h"
34 #include "table.h"
35 #include "trx0trx.h"
36 
37 namespace lob {
38 
39 /** Allocate one LOB page.
40 @param[in]	index	the index in which LOB exists.
41 @param[in]	lob_mtr	the mini-transaction context.
42 @param[in]	hint	the hint page number for allocation.
43 @param[in]	bulk	true if operation is OPCODE_INSERT_BULK,
44                         false otherwise.
45 @return the allocated block of the BLOB page or nullptr. */
alloc_lob_page(dict_index_t * index,mtr_t * lob_mtr,page_no_t hint,bool bulk)46 buf_block_t *alloc_lob_page(dict_index_t *index, mtr_t *lob_mtr, page_no_t hint,
47                             bool bulk) {
48   ulint r_extents;
49   mtr_t mtr_bulk;
50   mtr_t *alloc_mtr;
51   buf_block_t *block = nullptr;
52 
53   space_id_t space_id = dict_index_get_space(index);
54 
55   ut_ad(fsp_check_tablespace_size(space_id));
56 
57   if (bulk) {
58     mtr_start(&mtr_bulk);
59     alloc_mtr = &mtr_bulk;
60   } else {
61     alloc_mtr = lob_mtr;
62   }
63 
64   bool success =
65       fsp_reserve_free_extents(&r_extents, space_id, 1, FSP_BLOB, alloc_mtr, 1);
66 
67   DBUG_EXECUTE_IF("innodb_alloc_lob_page_failed", success = false;);
68 
69   if (!success) {
70     if (bulk) {
71       ut_ad(alloc_mtr == &mtr_bulk);
72       alloc_mtr->commit();
73     }
74     return (nullptr);
75   }
76 
77   block = btr_page_alloc(index, hint, FSP_NO_DIR, 0, alloc_mtr, lob_mtr);
78 
79   fil_space_release_free_extents(space_id, r_extents);
80 
81   if (bulk) {
82     ut_ad(alloc_mtr == &mtr_bulk);
83     alloc_mtr->commit();
84   }
85 
86   return (block);
87 }
88 
get_affected_index_entries(const ref_t & ref,dict_index_t * index,const Binary_diff & bdiff,List_iem_t & entries,mtr_t * mtr)89 dberr_t get_affected_index_entries(const ref_t &ref, dict_index_t *index,
90                                    const Binary_diff &bdiff,
91                                    List_iem_t &entries, mtr_t *mtr) {
92   page_no_t first_page_no = ref.page_no();
93   space_id_t space_id = ref.space_id();
94   const page_size_t page_size = dict_table_page_size(index->table);
95   const page_id_t first_page_id(space_id, first_page_no);
96   size_t offset = bdiff.offset();
97   ulint data_len = 0;
98 
99   if (!dict_table_has_atomic_blobs(index->table)) {
100     /* For compact and redundant row format, remove the local
101     prefix length from the offset. */
102 
103     ut_ad(offset >= DICT_ANTELOPE_MAX_INDEX_COL_LEN);
104     offset -= DICT_ANTELOPE_MAX_INDEX_COL_LEN;
105   }
106 
107   /* Currently only working with uncompressed LOB */
108   ut_ad(!page_size.is_compressed());
109 
110   first_page_t first_page(mtr, index);
111   first_page.load_x(first_page_id, page_size);
112 
113 #ifdef UNIV_DEBUG
114   {
115     page_type_t page_type = first_page.get_page_type();
116     ut_ad(page_type == FIL_PAGE_TYPE_LOB_FIRST);
117   }
118 #endif /* UNIV_DEBUG */
119 
120   /* Obtain the first index entry. */
121   flst_base_node_t *base_node = first_page.index_list();
122   fil_addr_t node_loc = flst_get_first(base_node, mtr);
123 
124   buf_block_t *block = nullptr;
125   index_entry_t entry(mtr, index);
126 
127   while (!fil_addr_is_null(node_loc)) {
128     if (block == nullptr) {
129       block = entry.load_x(node_loc);
130     } else if (block->page.id.page_no() != node_loc.page) {
131       block = entry.load_x(node_loc);
132     } else {
133       /* Next entry in the same page. */
134       ut_ad(block == entry.get_block());
135       entry.reset(node_loc);
136     }
137 
138     /* Get the amount of data */
139     data_len = entry.get_data_len();
140 
141     if (offset < data_len) {
142       index_entry_mem_t entry_mem;
143       entry.read(entry_mem);
144       entries.push_back(entry_mem);
145 
146       size_t remain = data_len - offset;
147 
148       if (bdiff.length() > remain) {
149         block = entry.next();
150 
151         if (block != nullptr) {
152           entry.read(entry_mem);
153           entries.push_back(entry_mem);
154         }
155       }
156 
157       break;
158     }
159 
160     offset -= data_len;
161 
162     /* The next node should not be the same as the current node. */
163     ut_ad(!node_loc.is_equal(entry.get_next()));
164 
165     node_loc = entry.get_next();
166   }
167 
168   ut_ad(entries.size() == 1 || entries.size() == 2);
169 
170   return (DB_SUCCESS);
171 }
172 
173 /** Get information about the given LOB.
174 @param[in]	ref	the LOB reference.
175 @param[in]	index	the clustered index to which LOB belongs.
176 @param[out]	lob_version	the lob version number.
177 @param[out]	last_trx_id	the trx_id that modified the lob last.
178 @param[out]	last_undo_no	the trx undo no that modified the lob last.
179 @param[out]	page_type	the page type of first lob page.
180 @param[in]	mtr		the mini transaction context.
181 @return always returns DB_SUCCESS. */
get_info(ref_t & ref,dict_index_t * index,ulint & lob_version,trx_id_t & last_trx_id,undo_no_t & last_undo_no,page_type_t & page_type,mtr_t * mtr)182 dberr_t get_info(ref_t &ref, dict_index_t *index, ulint &lob_version,
183                  trx_id_t &last_trx_id, undo_no_t &last_undo_no,
184                  page_type_t &page_type, mtr_t *mtr) {
185   page_no_t first_page_no = ref.page_no();
186   space_id_t space_id = ref.space_id();
187   const page_size_t page_size = dict_table_page_size(index->table);
188   const page_id_t first_page_id(space_id, first_page_no);
189 
190   /* Currently only working with uncompressed LOB */
191   ut_ad(!page_size.is_compressed());
192 
193   first_page_t first_page(mtr, index);
194   first_page.load_x(first_page_id, page_size);
195 
196   page_type = first_page.get_page_type();
197   lob_version = first_page.get_lob_version();
198   last_trx_id = first_page.get_last_trx_id();
199   last_undo_no = first_page.get_last_trx_undo_no();
200 
201   return (DB_SUCCESS);
202 }
203 
204 } /* namespace lob */
205