1/*****************************************************************************
2
3Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2015, 2021, 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, Fifth Floor, 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	rec_offs**	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				   ? index->n_core_fields : 0,
283				   ULINT_UNDEFINED, heap);
284
285	if (buf_block_get_page_zip(cursor->block)) {
286		rec = page_cur_insert_rec_zip(
287			cursor, index, rec, *offsets, mtr);
288	} else {
289		rec = page_cur_insert_rec_low(cursor->rec,
290					      index, rec, *offsets, mtr);
291	}
292
293	ut_ad(!rec || !cmp_dtuple_rec(tuple, rec, *offsets));
294	return(rec);
295}
296
297/***********************************************************//**
298Inserts a record next to page cursor. Returns pointer to inserted record if
299succeed, i.e., enough space available, NULL otherwise. The cursor stays at
300the same logical position, but the physical position may change if it is
301pointing to a compressed page that was reorganized.
302
303IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
304if this is a compressed leaf page in a secondary index.
305This has to be done either within the same mini-transaction,
306or by invoking ibuf_reset_free_bits() before mtr_commit().
307
308@return pointer to record if succeed, NULL otherwise */
309UNIV_INLINE
310rec_t*
311page_cur_rec_insert(
312/*================*/
313	page_cur_t*	cursor,	/*!< in/out: a page cursor */
314	const rec_t*	rec,	/*!< in: record to insert */
315	dict_index_t*	index,	/*!< in: record descriptor */
316	rec_offs*	offsets,/*!< in/out: rec_get_offsets(rec, index) */
317	mtr_t*		mtr)	/*!< in: mini-transaction handle, or NULL */
318{
319	if (buf_block_get_page_zip(cursor->block)) {
320		return(page_cur_insert_rec_zip(
321			       cursor, index, rec, offsets, mtr));
322	} else {
323		return(page_cur_insert_rec_low(cursor->rec,
324					       index, rec, offsets, mtr));
325	}
326}
327