1/*****************************************************************************
2
3Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License, version 2.0,
7as published by the Free Software Foundation.
8
9This program is also distributed with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation.  The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have included with MySQL.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License, version 2.0, for 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 Street, Suite 500, Boston, MA 02110-1335 USA
24
25*****************************************************************************/
26
27/********************************************************************//**
28@file include/page0cur.ic
29The page cursor
30
31Created 10/4/1994 Heikki Tuuri
32*************************************************************************/
33
34#include "page0page.h"
35#include "buf0types.h"
36
37#ifdef UNIV_DEBUG
38# include "rem0cmp.h"
39
40/*********************************************************//**
41Gets pointer to the page frame where the cursor is positioned.
42@return	page */
43UNIV_INLINE
44page_t*
45page_cur_get_page(
46/*==============*/
47	page_cur_t*	cur)	/*!< in: page cursor */
48{
49	ut_ad(cur);
50	ut_ad(page_align(cur->rec) == cur->block->frame);
51
52	return(page_align(cur->rec));
53}
54
55/*********************************************************//**
56Gets pointer to the buffer block where the cursor is positioned.
57@return	page */
58UNIV_INLINE
59buf_block_t*
60page_cur_get_block(
61/*===============*/
62	page_cur_t*	cur)	/*!< in: page cursor */
63{
64	ut_ad(cur);
65	ut_ad(page_align(cur->rec) == cur->block->frame);
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	ut_ad(page_align(cur->rec) == cur->block->frame);
92
93	return(cur->rec);
94}
95#endif /* UNIV_DEBUG */
96
97/*********************************************************//**
98Sets the cursor object to point before the first user record
99on the page. */
100UNIV_INLINE
101void
102page_cur_set_before_first(
103/*======================*/
104	const buf_block_t*	block,	/*!< in: index page */
105	page_cur_t*		cur)	/*!< in: cursor */
106{
107	cur->block = (buf_block_t*) block;
108	cur->rec = page_get_infimum_rec(buf_block_get_frame(cur->block));
109}
110
111/*********************************************************//**
112Sets the cursor object to point after the last user record on
113the page. */
114UNIV_INLINE
115void
116page_cur_set_after_last(
117/*====================*/
118	const buf_block_t*	block,	/*!< in: index page */
119	page_cur_t*		cur)	/*!< in: cursor */
120{
121	cur->block = (buf_block_t*) block;
122	cur->rec = page_get_supremum_rec(buf_block_get_frame(cur->block));
123}
124
125/*********************************************************//**
126Returns TRUE if the cursor is before first user record on page.
127@return	TRUE if at start */
128UNIV_INLINE
129ibool
130page_cur_is_before_first(
131/*=====================*/
132	const page_cur_t*	cur)	/*!< in: cursor */
133{
134	ut_ad(cur);
135	ut_ad(page_align(cur->rec) == cur->block->frame);
136	return(page_rec_is_infimum(cur->rec));
137}
138
139/*********************************************************//**
140Returns TRUE if the cursor is after last user record.
141@return	TRUE if at end */
142UNIV_INLINE
143ibool
144page_cur_is_after_last(
145/*===================*/
146	const page_cur_t*	cur)	/*!< in: cursor */
147{
148	ut_ad(cur);
149	ut_ad(page_align(cur->rec) == cur->block->frame);
150	return(page_rec_is_supremum(cur->rec));
151}
152
153/**********************************************************//**
154Positions the cursor on the given record. */
155UNIV_INLINE
156void
157page_cur_position(
158/*==============*/
159	const rec_t*		rec,	/*!< in: record on a page */
160	const buf_block_t*	block,	/*!< in: buffer block containing
161					the record */
162	page_cur_t*		cur)	/*!< out: page cursor */
163{
164	ut_ad(rec && block && cur);
165	ut_ad(page_align(rec) == block->frame);
166
167	cur->rec = (rec_t*) rec;
168	cur->block = (buf_block_t*) block;
169}
170
171/**********************************************************//**
172Invalidates a page cursor by setting the record pointer NULL. */
173UNIV_INLINE
174void
175page_cur_invalidate(
176/*================*/
177	page_cur_t*	cur)	/*!< out: page cursor */
178{
179	ut_ad(cur);
180
181	cur->rec = NULL;
182	cur->block = NULL;
183}
184
185/**********************************************************//**
186Moves the cursor to the next record on page. */
187UNIV_INLINE
188void
189page_cur_move_to_next(
190/*==================*/
191	page_cur_t*	cur)	/*!< in/out: cursor; must not be after last */
192{
193	ut_ad(!page_cur_is_after_last(cur));
194
195	cur->rec = page_rec_get_next(cur->rec);
196}
197
198/**********************************************************//**
199Moves the cursor to the previous record on page. */
200UNIV_INLINE
201void
202page_cur_move_to_prev(
203/*==================*/
204	page_cur_t*	cur)	/*!< in/out: page cursor, not before first */
205{
206	ut_ad(!page_cur_is_before_first(cur));
207
208	cur->rec = page_rec_get_prev(cur->rec);
209}
210
211#ifndef UNIV_HOTBACKUP
212/****************************************************************//**
213Searches the right position for a page cursor.
214@return	number of matched fields on the left */
215UNIV_INLINE
216ulint
217page_cur_search(
218/*============*/
219	const buf_block_t*	block,	/*!< in: buffer block */
220	const dict_index_t*	index,	/*!< in: record descriptor */
221	const dtuple_t*		tuple,	/*!< in: data tuple */
222	ulint			mode,	/*!< in: PAGE_CUR_L,
223					PAGE_CUR_LE, PAGE_CUR_G, or
224					PAGE_CUR_GE */
225	page_cur_t*		cursor)	/*!< out: page cursor */
226{
227	ulint		low_matched_fields = 0;
228	ulint		low_matched_bytes = 0;
229	ulint		up_matched_fields = 0;
230	ulint		up_matched_bytes = 0;
231
232	ut_ad(dtuple_check_typed(tuple));
233
234	page_cur_search_with_match(block, index, tuple, mode,
235				   &up_matched_fields,
236				   &up_matched_bytes,
237				   &low_matched_fields,
238				   &low_matched_bytes,
239				   cursor);
240	return(low_matched_fields);
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	ulint		size
268		= rec_get_converted_size(index, tuple, n_ext);
269	rec_t*		rec;
270
271	if (!*heap) {
272		*heap = mem_heap_create(size
273					+ (4 + REC_OFFS_HEADER_SIZE
274					   + dtuple_get_n_fields(tuple))
275					* sizeof **offsets);
276	}
277
278	rec = rec_convert_dtuple_to_rec((byte*) mem_heap_alloc(*heap, size),
279					index, tuple, n_ext);
280	*offsets = rec_get_offsets(
281		rec, index, *offsets, ULINT_UNDEFINED, heap);
282
283	if (buf_block_get_page_zip(cursor->block)) {
284		rec = page_cur_insert_rec_zip(
285			cursor, index, rec, *offsets, mtr);
286	} else {
287		rec = page_cur_insert_rec_low(cursor->rec,
288					      index, rec, *offsets, mtr);
289	}
290
291	ut_ad(!rec || !cmp_dtuple_rec(tuple, rec, *offsets));
292	return(rec);
293}
294#endif /* !UNIV_HOTBACKUP */
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