1/*****************************************************************************
2
3Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2015, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/********************************************************************//**
21@file include/page0cur.ic
22The page cursor
23
24Created 10/4/1994 Heikki Tuuri
25*************************************************************************/
26
27#include "page0page.h"
28#include "buf0types.h"
29
30#ifdef UNIV_DEBUG
31# include "rem0cmp.h"
32
33/*********************************************************//**
34Gets pointer to the page frame where the cursor is positioned.
35@return page */
36UNIV_INLINE
37page_t*
38page_cur_get_page(
39/*==============*/
40 page_cur_t* cur) /*!< in: page cursor */
41{
42 ut_ad(cur);
43
44 if (cur->rec) {
45 ut_ad(page_align(cur->rec) == cur->block->frame);
46 }
47
48 return(page_align(cur->rec));
49}
50
51/*********************************************************//**
52Gets pointer to the buffer block where the cursor is positioned.
53@return page */
54UNIV_INLINE
55buf_block_t*
56page_cur_get_block(
57/*===============*/
58 page_cur_t* cur) /*!< in: page cursor */
59{
60 ut_ad(cur);
61
62 if (cur->rec) {
63 ut_ad(page_align(cur->rec) == cur->block->frame);
64 }
65
66 return(cur->block);
67}
68
69/*********************************************************//**
70Gets pointer to the page frame where the cursor is positioned.
71@return page */
72UNIV_INLINE
73page_zip_des_t*
74page_cur_get_page_zip(
75/*==================*/
76 page_cur_t* cur) /*!< in: page cursor */
77{
78 return(buf_block_get_page_zip(page_cur_get_block(cur)));
79}
80
81/*********************************************************//**
82Gets the record where the cursor is positioned.
83@return record */
84UNIV_INLINE
85rec_t*
86page_cur_get_rec(
87/*=============*/
88 page_cur_t* cur) /*!< in: page cursor */
89{
90 ut_ad(cur);
91
92 if (cur->rec) {
93 ut_ad(page_align(cur->rec) == cur->block->frame);
94 }
95
96 return(cur->rec);
97}
98#endif /* UNIV_DEBUG */
99
100/*********************************************************//**
101Sets the cursor object to point before the first user record
102on the page. */
103UNIV_INLINE
104void
105page_cur_set_before_first(
106/*======================*/
107 const buf_block_t* block, /*!< in: index page */
108 page_cur_t* cur) /*!< in: cursor */
109{
110 cur->block = (buf_block_t*) block;
111 cur->rec = page_get_infimum_rec(buf_block_get_frame(cur->block));
112}
113
114/*********************************************************//**
115Sets the cursor object to point after the last user record on
116the page. */
117UNIV_INLINE
118void
119page_cur_set_after_last(
120/*====================*/
121 const buf_block_t* block, /*!< in: index page */
122 page_cur_t* cur) /*!< in: cursor */
123{
124 cur->block = (buf_block_t*) block;
125 cur->rec = page_get_supremum_rec(buf_block_get_frame(cur->block));
126}
127
128/*********************************************************//**
129Returns TRUE if the cursor is before first user record on page.
130@return TRUE if at start */
131UNIV_INLINE
132ibool
133page_cur_is_before_first(
134/*=====================*/
135 const page_cur_t* cur) /*!< in: cursor */
136{
137 ut_ad(cur);
138 ut_ad(page_align(cur->rec) == cur->block->frame);
139 return(page_rec_is_infimum(cur->rec));
140}
141
142/*********************************************************//**
143Returns TRUE if the cursor is after last user record.
144@return TRUE if at end */
145UNIV_INLINE
146ibool
147page_cur_is_after_last(
148/*===================*/
149 const page_cur_t* cur) /*!< in: cursor */
150{
151 ut_ad(cur);
152 ut_ad(page_align(cur->rec) == cur->block->frame);
153 return(page_rec_is_supremum(cur->rec));
154}
155
156/**********************************************************//**
157Positions the cursor on the given record. */
158UNIV_INLINE
159void
160page_cur_position(
161/*==============*/
162 const rec_t* rec, /*!< in: record on a page */
163 const buf_block_t* block, /*!< in: buffer block containing
164 the record */
165 page_cur_t* cur) /*!< out: page cursor */
166{
167 ut_ad(rec && block && cur);
168 ut_ad(page_align(rec) == block->frame);
169
170 cur->rec = (rec_t*) rec;
171 cur->block = (buf_block_t*) block;
172}
173
174/**********************************************************//**
175Moves the cursor to the next record on page. */
176UNIV_INLINE
177void
178page_cur_move_to_next(
179/*==================*/
180 page_cur_t* cur) /*!< in/out: cursor; must not be after last */
181{
182 ut_ad(!page_cur_is_after_last(cur));
183
184 cur->rec = page_rec_get_next(cur->rec);
185}
186
187/**********************************************************//**
188Moves the cursor to the previous record on page. */
189UNIV_INLINE
190void
191page_cur_move_to_prev(
192/*==================*/
193 page_cur_t* cur) /*!< in/out: page cursor, not before first */
194{
195 ut_ad(!page_cur_is_before_first(cur));
196
197 cur->rec = page_rec_get_prev(cur->rec);
198}
199
200/** Search the right position for a page cursor.
201@param[in] block buffer block
202@param[in] index index tree
203@param[in] tuple data tuple
204@param[in] mode PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE
205@param[out] cursor page cursor
206@return number of matched fields on the left */
207UNIV_INLINE
208ulint
209page_cur_search(
210 const buf_block_t* block,
211 const dict_index_t* index,
212 const dtuple_t* tuple,
213 page_cur_mode_t mode,
214 page_cur_t* cursor)
215{
216 ulint low_match = 0;
217 ulint up_match = 0;
218
219 ut_ad(dtuple_check_typed(tuple));
220
221 page_cur_search_with_match(block, index, tuple, mode,
222 &up_match, &low_match, cursor, NULL);
223 return(low_match);
224}
225
226/** Search the right position for a page cursor.
227@param[in] block buffer block
228@param[in] index index tree
229@param[in] tuple data tuple
230@param[out] cursor page cursor
231@return number of matched fields on the left */
232UNIV_INLINE
233ulint
234page_cur_search(
235 const buf_block_t* block,
236 const dict_index_t* index,
237 const dtuple_t* tuple,
238 page_cur_t* cursor)
239{
240 return(page_cur_search(block, index, tuple, PAGE_CUR_LE, cursor));
241}
242
243/***********************************************************//**
244Inserts a record next to page cursor. Returns pointer to inserted record if
245succeed, i.e., enough space available, NULL otherwise. The cursor stays at
246the same logical position, but the physical position may change if it is
247pointing to a compressed page that was reorganized.
248
249IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
250if this is a compressed leaf page in a secondary index.
251This has to be done either within the same mini-transaction,
252or by invoking ibuf_reset_free_bits() before mtr_commit().
253
254@return pointer to record if succeed, NULL otherwise */
255UNIV_INLINE
256rec_t*
257page_cur_tuple_insert(
258/*==================*/
259 page_cur_t* cursor, /*!< in/out: a page cursor */
260 const dtuple_t* tuple, /*!< in: pointer to a data tuple */
261 dict_index_t* index, /*!< in: record descriptor */
262 ulint** offsets,/*!< out: offsets on *rec */
263 mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */
264 ulint n_ext, /*!< in: number of externally stored columns */
265 mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
266{
267 rec_t* rec;
268 ulint size = rec_get_converted_size(index, tuple, n_ext);
269
270 if (!*heap) {
271 *heap = mem_heap_create(size
272 + (4 + REC_OFFS_HEADER_SIZE
273 + dtuple_get_n_fields(tuple))
274 * sizeof **offsets);
275 }
276
277 rec = rec_convert_dtuple_to_rec((byte*) mem_heap_alloc(*heap, size),
278 index, tuple, n_ext);
279
280 *offsets = rec_get_offsets(rec, index, *offsets,
281 page_is_leaf(cursor->block->frame),
282 ULINT_UNDEFINED, heap);
283
284 if (buf_block_get_page_zip(cursor->block)) {
285 rec = page_cur_insert_rec_zip(
286 cursor, index, rec, *offsets, mtr);
287 } else {
288 rec = page_cur_insert_rec_low(cursor->rec,
289 index, rec, *offsets, mtr);
290 }
291
292 ut_ad(!rec || !cmp_dtuple_rec(tuple, rec, *offsets));
293 return(rec);
294}
295
296/***********************************************************//**
297Inserts a record next to page cursor. Returns pointer to inserted record if
298succeed, i.e., enough space available, NULL otherwise. The cursor stays at
299the same logical position, but the physical position may change if it is
300pointing to a compressed page that was reorganized.
301
302IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
303if this is a compressed leaf page in a secondary index.
304This has to be done either within the same mini-transaction,
305or by invoking ibuf_reset_free_bits() before mtr_commit().
306
307@return pointer to record if succeed, NULL otherwise */
308UNIV_INLINE
309rec_t*
310page_cur_rec_insert(
311/*================*/
312 page_cur_t* cursor, /*!< in/out: a page cursor */
313 const rec_t* rec, /*!< in: record to insert */
314 dict_index_t* index, /*!< in: record descriptor */
315 ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */
316 mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
317{
318 if (buf_block_get_page_zip(cursor->block)) {
319 return(page_cur_insert_rec_zip(
320 cursor, index, rec, offsets, mtr));
321 } else {
322 return(page_cur_insert_rec_low(cursor->rec,
323 index, rec, offsets, mtr));
324 }
325}
326