1/*****************************************************************************
2
3Copyright (c) 1997, 2018, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is also distributed with certain software (including but not
10limited to OpenSSL) that is licensed under separate terms, as designated in a
11particular file or component or in included license documentation. The authors
12of MySQL hereby grant you an additional permission to link the program and
13your derivative works with the separately licensed software that they have
14included with MySQL.
15
16This program is distributed in the hope that it will be useful, but WITHOUT
17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24
25*****************************************************************************/
26
27/** @file include/ibuf0ibuf.ic
28 Insert buffer
29
30 Created 7/19/1997 Heikki Tuuri
31 *******************************************************/
32
33#include "fsp0types.h"
34#include "page0page.h"
35#include "page0zip.h"
36#ifndef UNIV_HOTBACKUP
37#include "buf0lru.h"
38
39/** An index page must contain at least UNIV_PAGE_SIZE /
40IBUF_PAGE_SIZE_PER_FREE_SPACE bytes of free space for ibuf to try to
41buffer inserts to this page.  If there is this much of free space, the
42corresponding bits are set in the ibuf bitmap. */
43#define IBUF_PAGE_SIZE_PER_FREE_SPACE 32
44
45/** Starts an insert buffer mini-transaction. */
46UNIV_INLINE
47void ibuf_mtr_start(mtr_t *mtr) /*!< out: mini-transaction */
48{
49  mtr_start(mtr);
50  mtr->enter_ibuf();
51}
52/** Commits an insert buffer mini-transaction. */
53UNIV_INLINE
54void ibuf_mtr_commit(mtr_t *mtr) /*!< in/out: mini-transaction */
55{
56  ut_ad(mtr->is_inside_ibuf());
57  ut_d(mtr->exit_ibuf());
58
59  mtr_commit(mtr);
60}
61
62/** Insert buffer struct */
63struct ibuf_t {
64  ulint size;          /*!< current size of the ibuf index
65                       tree, in pages */
66  ulint max_size;      /*!< recommended maximum size of the
67                       ibuf index tree, in pages */
68  ulint seg_size;      /*!< allocated pages of the file
69                       segment containing ibuf header and
70                       tree */
71  bool empty;          /*!< Protected by the page
72                       latch of the root page of the
73                       insert buffer tree
74                       (FSP_IBUF_TREE_ROOT_PAGE_NO). true
75                       if and only if the insert
76                       buffer tree is empty. */
77  ulint free_list_len; /*!< length of the free list */
78  ulint height;        /*!< tree height */
79  dict_index_t *index; /*!< insert buffer index */
80
81  ulint n_merges; /*!< number of pages merged */
82  ulint n_merged_ops[IBUF_OP_COUNT];
83  /*!< number of operations of each type
84  merged to index pages */
85  ulint n_discarded_ops[IBUF_OP_COUNT];
86  /*!< number of operations of each type
87  discarded without merging due to the
88  tablespace being deleted or the
89  index being dropped */
90};
91
92/** Sets the free bit of the page in the ibuf bitmap. This is done in a separate
93 mini-transaction, hence this operation does not restrict further work to only
94 ibuf bitmap operations, which would result if the latch to the bitmap page
95 were kept. */
96void ibuf_set_free_bits_func(
97    buf_block_t *block, /*!< in: index page of a non-clustered index;
98                        free bit is reset if page level is 0 */
99#ifdef UNIV_IBUF_DEBUG
100    ulint max_val, /*!< in: ULINT_UNDEFINED or a maximum
101                   value which the bits must have before
102                   setting; this is for debugging */
103#endif             /* UNIV_IBUF_DEBUG */
104    ulint val);    /*!< in: value to set: < 4 */
105#ifdef UNIV_IBUF_DEBUG
106#define ibuf_set_free_bits(b, v, max) ibuf_set_free_bits_func(b, max, v)
107#else /* UNIV_IBUF_DEBUG */
108#define ibuf_set_free_bits(b, v, max) ibuf_set_free_bits_func(b, v)
109#endif /* UNIV_IBUF_DEBUG */
110
111/** A basic partial test if an insert to the insert buffer could be possible and
112 recommended. */
113UNIV_INLINE
114ibool ibuf_should_try(dict_index_t *index,     /*!< in: index where to insert */
115                      ulint ignore_sec_unique) /*!< in: if != 0, we should
116                                               ignore UNIQUE constraint on
117                                               a secondary index when we
118                                               decide */
119{
120  return (innodb_change_buffering != IBUF_USE_NONE && ibuf->max_size != 0 &&
121          index->space != dict_sys_t::s_space_id && !index->is_clustered() &&
122          !dict_index_is_spatial(index) && !dict_index_has_desc(index) &&
123          index->table->quiesce == QUIESCE_NONE &&
124          (ignore_sec_unique || !dict_index_is_unique(index)) &&
125          srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE);
126}
127
128/** Returns TRUE if the current OS thread is performing an insert buffer
129 routine.
130
131 For instance, a read-ahead of non-ibuf pages is forbidden by threads
132 that are executing an insert buffer routine.
133 @return true if inside an insert buffer routine */
134UNIV_INLINE
135ibool ibuf_inside(const mtr_t *mtr) /*!< in: mini-transaction */
136{
137  return (mtr->is_inside_ibuf());
138}
139
140/** Checks if a page address is an ibuf bitmap page (level 3 page) address.
141@param[in]	page_id		page id
142@param[in]	page_size	page size
143@return true if a bitmap page */
144UNIV_INLINE
145ibool ibuf_bitmap_page(const page_id_t &page_id, const page_size_t &page_size) {
146  return ((page_id.page_no() & (page_size.physical() - 1)) ==
147          FSP_IBUF_BITMAP_OFFSET);
148}
149
150/** Translates the free space on a page to a value in the ibuf bitmap.
151@param[in]	page_size	page size in bytes
152@param[in]	max_ins_size	maximum insert size after reorganize for
153the page
154@return value for ibuf bitmap bits */
155UNIV_INLINE
156ulint ibuf_index_page_calc_free_bits(ulint page_size, ulint max_ins_size) {
157  ulint n;
158  ut_ad(ut_is_2pow(page_size));
159  ut_ad(page_size > IBUF_PAGE_SIZE_PER_FREE_SPACE);
160
161  n = max_ins_size / (page_size / IBUF_PAGE_SIZE_PER_FREE_SPACE);
162
163  if (n == 3) {
164    n = 2;
165  }
166
167  if (n > 3) {
168    n = 3;
169  }
170
171  return (n);
172}
173
174/** Translates the ibuf free bits to the free space on a page in bytes.
175@param[in]	page_size	page_size
176@param[in]	bits		value for ibuf bitmap bits
177@return maximum insert size after reorganize for the page */
178UNIV_INLINE
179ulint ibuf_index_page_calc_free_from_bits(const page_size_t &page_size,
180                                          ulint bits) {
181  ut_ad(bits < 4);
182  ut_ad(!page_size.is_compressed() ||
183        page_size.physical() > IBUF_PAGE_SIZE_PER_FREE_SPACE);
184
185  if (bits == 3) {
186    return (4 * page_size.physical() / IBUF_PAGE_SIZE_PER_FREE_SPACE);
187  }
188
189  return (bits * (page_size.physical() / IBUF_PAGE_SIZE_PER_FREE_SPACE));
190}
191
192/** Translates the free space on a compressed page to a value in the ibuf
193 bitmap.
194 @return value for ibuf bitmap bits */
195UNIV_INLINE
196ulint ibuf_index_page_calc_free_zip(
197    const buf_block_t *block) /*!< in: buffer block */
198{
199  ulint max_ins_size;
200  const page_zip_des_t *page_zip;
201  lint zip_max_ins;
202
203  ut_ad(block->page.size.is_compressed());
204
205  /* Consider the maximum insert size on the uncompressed page
206  without reorganizing the page. We must not assume anything
207  about the compression ratio. If zip_max_ins > max_ins_size and
208  there is 1/4 garbage on the page, recompression after the
209  reorganize could fail, in theory. So, let us guarantee that
210  merging a buffered insert to a compressed page will always
211  succeed without reorganizing or recompressing the page, just
212  by using the page modification log. */
213  max_ins_size = page_get_max_insert_size(buf_block_get_frame(block), 1);
214
215  page_zip = buf_block_get_page_zip(block);
216  zip_max_ins = page_zip_max_ins_size(page_zip, FALSE /* not clustered */);
217
218  if (zip_max_ins < 0) {
219    return (0);
220  } else if (max_ins_size > (ulint)zip_max_ins) {
221    max_ins_size = (ulint)zip_max_ins;
222  }
223
224  return (ibuf_index_page_calc_free_bits(block->page.size.physical(),
225                                         max_ins_size));
226}
227
228/** Translates the free space on a page to a value in the ibuf bitmap.
229 @return value for ibuf bitmap bits */
230UNIV_INLINE
231ulint ibuf_index_page_calc_free(
232    const buf_block_t *block) /*!< in: buffer block */
233{
234  if (!block->page.size.is_compressed()) {
235    ulint max_ins_size;
236
237    max_ins_size = page_get_max_insert_size_after_reorganize(
238        buf_block_get_frame(block), 1);
239
240    return (ibuf_index_page_calc_free_bits(block->page.size.physical(),
241                                           max_ins_size));
242  } else {
243    return (ibuf_index_page_calc_free_zip(block));
244  }
245}
246
247/** Updates the free bits of an uncompressed page in the ibuf bitmap if
248 there is not enough free on the page any more.  This is done in a
249 separate mini-transaction, hence this operation does not restrict
250 further work to only ibuf bitmap operations, which would result if the
251 latch to the bitmap page were kept.  NOTE: The free bits in the insert
252 buffer bitmap must never exceed the free space on a page.  It is
253 unsafe to increment the bits in a separately committed
254 mini-transaction, because in crash recovery, the free bits could
255 momentarily be set too high.  It is only safe to use this function for
256 decrementing the free bits.  Should more free space become available,
257 we must not update the free bits here, because that would break crash
258 recovery. */
259UNIV_INLINE
260void ibuf_update_free_bits_if_full(
261    buf_block_t *block, /*!< in: index page to which we have added new
262                        records; the free bits are updated if the
263                        index is non-clustered and non-unique and
264                        the page level is 0, and the page becomes
265                        fuller */
266    ulint max_ins_size, /*!< in: value of maximum insert size with
267                   reorganize before the latest operation
268                   performed to the page */
269    ulint increase)     /*!< in: upper limit for the additional space
270                       used in the latest operation, if known, or
271                       ULINT_UNDEFINED */
272{
273  ulint before;
274  ulint after;
275
276  ut_ad(buf_block_get_page_zip(block) == NULL);
277
278  before =
279      ibuf_index_page_calc_free_bits(block->page.size.physical(), max_ins_size);
280
281  if (max_ins_size >= increase) {
282    static_assert(UINT32_UNDEFINED > UNIV_PAGE_SIZE_MAX,
283                  "UINT32_UNDEFINED <= UNIV_PAGE_SIZE_MAX");
284
285    after = ibuf_index_page_calc_free_bits(block->page.size.physical(),
286                                           max_ins_size - increase);
287#ifdef UNIV_IBUF_DEBUG
288    ut_a(after <= ibuf_index_page_calc_free(block));
289#endif /* UNIV_IBUF_DEBUG */
290
291  } else {
292    after = ibuf_index_page_calc_free(block);
293  }
294
295  if (after == 0) {
296    /* We move the page to the front of the buffer pool LRU list:
297    the purpose of this is to prevent those pages to which we
298    cannot make inserts using the insert buffer from slipping
299    out of the buffer pool */
300
301    buf_page_make_young(&block->page);
302  }
303
304  if (before > after) {
305    ibuf_set_free_bits(block, after, before);
306  }
307}
308#endif /* !UNIV_HOTBACKUP */
309