1/*****************************************************************************
2
3Copyright (c) 1994, 2019, 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/page0cur.ic
28 The page cursor
29
30 Created 10/4/1994 Heikki Tuuri
31 *************************************************************************/
32
33#include "buf0types.h"
34#include "page0page.h"
35
36#ifdef UNIV_DEBUG
37#ifndef UNIV_HOTBACKUP
38#include "rem0cmp.h"
39#endif /* !UNIV_HOTBACKUP */
40
41/** Gets pointer to the page frame where the cursor is positioned.
42 @return page */
43UNIV_INLINE
44page_t *page_cur_get_page(page_cur_t *cur) /*!< in: page cursor */
45{
46  ut_ad(cur);
47  ut_ad(page_align(cur->rec) == cur->block->frame);
48
49  return (page_align(cur->rec));
50}
51
52/** Gets pointer to the buffer block where the cursor is positioned.
53 @return page */
54UNIV_INLINE
55buf_block_t *page_cur_get_block(page_cur_t *cur) /*!< in: page cursor */
56{
57  ut_ad(cur);
58  ut_ad(page_align(cur->rec) == cur->block->frame);
59  return (cur->block);
60}
61
62/** Gets pointer to the page frame where the cursor is positioned.
63 @return page */
64UNIV_INLINE
65page_zip_des_t *page_cur_get_page_zip(page_cur_t *cur) /*!< in: page cursor */
66{
67  return (buf_block_get_page_zip(page_cur_get_block(cur)));
68}
69
70/** Gets the record where the cursor is positioned.
71 @return record */
72UNIV_INLINE
73rec_t *page_cur_get_rec(page_cur_t *cur) /*!< in: page cursor */
74{
75  ut_ad(cur);
76  ut_ad(page_align(cur->rec) == cur->block->frame);
77
78  return (cur->rec);
79}
80#endif /* UNIV_DEBUG */
81
82/** Sets the cursor object to point before the first user record
83 on the page. */
84UNIV_INLINE
85void page_cur_set_before_first(const buf_block_t *block, /*!< in: index page */
86                               page_cur_t *cur)          /*!< in: cursor */
87{
88  cur->block = (buf_block_t *)block;
89  cur->rec = page_get_infimum_rec(buf_block_get_frame(cur->block));
90}
91
92/** Sets the cursor object to point after the last user record on
93 the page. */
94UNIV_INLINE
95void page_cur_set_after_last(const buf_block_t *block, /*!< in: index page */
96                             page_cur_t *cur)          /*!< in: cursor */
97{
98  cur->block = (buf_block_t *)block;
99  cur->rec = page_get_supremum_rec(buf_block_get_frame(cur->block));
100}
101
102/** Returns TRUE if the cursor is before first user record on page.
103 @return true if at start */
104UNIV_INLINE
105ibool page_cur_is_before_first(const page_cur_t *cur) /*!< in: cursor */
106{
107  ut_ad(cur);
108  ut_ad(page_align(cur->rec) == cur->block->frame);
109  return (page_rec_is_infimum(cur->rec));
110}
111
112/** Returns TRUE if the cursor is after last user record.
113 @return true if at end */
114UNIV_INLINE
115ibool page_cur_is_after_last(const page_cur_t *cur) /*!< in: cursor */
116{
117  ut_ad(cur);
118  ut_ad(page_align(cur->rec) == cur->block->frame);
119  return (page_rec_is_supremum(cur->rec));
120}
121
122/** Positions the cursor on the given record. */
123UNIV_INLINE
124void page_cur_position(const rec_t *rec,         /*!< in: record on a page */
125                       const buf_block_t *block, /*!< in: buffer block
126                                                 containing the record */
127                       page_cur_t *cur)          /*!< out: page cursor */
128{
129  ut_ad(rec && block && cur);
130  ut_ad(page_align(rec) == block->frame);
131
132  cur->rec = (rec_t *)rec;
133  cur->block = (buf_block_t *)block;
134}
135
136/** Moves the cursor to the next record on page. */
137UNIV_INLINE
138void page_cur_move_to_next(
139    page_cur_t *cur) /*!< in/out: cursor; must not be after last */
140{
141  ut_ad(!page_cur_is_after_last(cur));
142
143  cur->rec = page_rec_get_next(cur->rec);
144}
145
146/** Moves the cursor to the previous record on page. */
147UNIV_INLINE
148void page_cur_move_to_prev(
149    page_cur_t *cur) /*!< in/out: page cursor, not before first */
150{
151  ut_ad(!page_cur_is_before_first(cur));
152
153  cur->rec = page_rec_get_prev(cur->rec);
154}
155
156#ifndef UNIV_HOTBACKUP
157/** Search the right position for a page cursor.
158@param[in] block buffer block
159@param[in] index index tree
160@param[in] tuple data tuple
161@param[in] mode PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE
162@param[out] cursor page cursor
163@return number of matched fields on the left */
164UNIV_INLINE
165ulint page_cur_search(const buf_block_t *block, const dict_index_t *index,
166                      const dtuple_t *tuple, page_cur_mode_t mode,
167                      page_cur_t *cursor) {
168  ulint low_match = 0;
169  ulint up_match = 0;
170
171  ut_ad(dtuple_check_typed(tuple));
172
173  page_cur_search_with_match(block, index, tuple, mode, &up_match, &low_match,
174                             cursor, nullptr);
175  return (low_match);
176}
177
178/** Search the right position for a page cursor.
179@param[in] block buffer block
180@param[in] index index tree
181@param[in] tuple data tuple
182@param[out] cursor page cursor
183@return number of matched fields on the left */
184UNIV_INLINE
185ulint page_cur_search(const buf_block_t *block, const dict_index_t *index,
186                      const dtuple_t *tuple, page_cur_t *cursor) {
187  return (page_cur_search(block, index, tuple, PAGE_CUR_LE, cursor));
188}
189
190/** Inserts a record next to page cursor. Returns pointer to inserted record if
191 succeed, i.e., enough space available, NULL otherwise. The cursor stays at
192 the same logical position, but the physical position may change if it is
193 pointing to a compressed page that was reorganized.
194
195 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
196 if this is a compressed leaf page in a secondary index.
197 This has to be done either within the same mini-transaction,
198 or by invoking ibuf_reset_free_bits() before mtr_commit().
199
200 @return pointer to record if succeed, NULL otherwise */
201UNIV_INLINE
202rec_t *page_cur_tuple_insert(
203    page_cur_t *cursor,    /*!< in/out: a page cursor */
204    const dtuple_t *tuple, /*!< in: pointer to a data tuple */
205    dict_index_t *index,   /*!< in: record descriptor */
206    ulint **offsets,       /*!< out: offsets on *rec */
207    mem_heap_t **heap,     /*!< in/out: pointer to memory heap, or NULL */
208    mtr_t *mtr,            /*!< in: mini-transaction handle, or NULL */
209    bool use_cache)
210/*!< in: if true, then use record cache to
211hold the tuple converted record. */
212{
213  rec_t *rec;
214  rec_t *insert_rec;
215
216  ulint size = rec_get_converted_size(index, tuple);
217
218  if (!*heap) {
219    *heap = mem_heap_create(
220        size + (4 + REC_OFFS_HEADER_SIZE + dtuple_get_n_fields(tuple)) *
221                   sizeof **offsets);
222  }
223
224  rec = rec_convert_dtuple_to_rec((byte *)mem_heap_alloc(*heap, size), index,
225                                  tuple);
226
227  *offsets = rec_get_offsets(rec, index, *offsets, ULINT_UNDEFINED, heap);
228
229  ut_ad(cmp_dtuple_rec(tuple, rec, index, *offsets) == 0);
230
231  if (buf_block_get_page_zip(cursor->block)) {
232    insert_rec = page_cur_insert_rec_zip(cursor, index, rec, *offsets, mtr);
233  } else {
234    insert_rec =
235        page_cur_insert_rec_low(cursor->rec, index, rec, *offsets, mtr);
236  }
237
238#ifdef UNIV_DEBUG
239  if (insert_rec != nullptr) {
240    if (cmp_dtuple_rec(tuple, insert_rec, index, *offsets) != 0) {
241      std::cerr << "tuple=" << rec_printer(tuple).str() << std::endl;
242      std::cerr << "rec=" << rec_printer(insert_rec, *offsets).str()
243                << std::endl;
244    }
245  }
246  ut_ad(!insert_rec || !cmp_dtuple_rec(tuple, insert_rec, index, *offsets));
247#endif /* UNIV_DEBUG */
248
249  return (insert_rec);
250}
251
252/** Insert a record next to page cursor. Record is directly copied to
253the page from tuple without creating intermediate copy of the record.
254
255@param[in,out]	cursor	a page cursor
256@param[in]	tuple	pointer to a data tuple
257@param[in]	index	record descriptor
258@param[in]	mtr	mini-transaction handle, or NULL
259
260@return pointer to record if succeed, NULL otherwise */
261UNIV_INLINE
262rec_t *page_cur_tuple_direct_insert(page_cur_t *cursor, const dtuple_t *tuple,
263                                    dict_index_t *index, mtr_t *mtr) {
264  rec_t *rec;
265
266  ut_ad(index->table->is_intrinsic());
267
268  rec = page_cur_direct_insert_rec_low(cursor->rec, index, tuple, mtr);
269
270  return (rec);
271}
272#endif /* !UNIV_HOTBACKUP */
273
274/** Inserts a record next to page cursor. Returns pointer to inserted record if
275 succeed, i.e., enough space available, NULL otherwise. The cursor stays at
276 the same logical position, but the physical position may change if it is
277 pointing to a compressed page that was reorganized.
278
279 IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
280 if this is a compressed leaf page in a secondary index.
281 This has to be done either within the same mini-transaction,
282 or by invoking ibuf_reset_free_bits() before mtr_commit().
283
284 @return pointer to record if succeed, NULL otherwise */
285UNIV_INLINE
286rec_t *page_cur_rec_insert(
287    page_cur_t *cursor,  /*!< in/out: a page cursor */
288    const rec_t *rec,    /*!< in: record to insert */
289    dict_index_t *index, /*!< in: record descriptor */
290    ulint *offsets,      /*!< in/out: rec_get_offsets(rec, index) */
291    mtr_t *mtr)          /*!< in: mini-transaction handle, or NULL */
292{
293  if (buf_block_get_page_zip(cursor->block)) {
294    return (page_cur_insert_rec_zip(cursor, index, rec, offsets, mtr));
295  } else {
296    return (page_cur_insert_rec_low(cursor->rec, index, rec, offsets, mtr));
297  }
298}
299