1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2015, 2018, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /********************************************************************//** |
21 | @file include/page0cur.ic |
22 | The page cursor |
23 | |
24 | Created 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 | /*********************************************************//** |
34 | Gets pointer to the page frame where the cursor is positioned. |
35 | @return page */ |
36 | UNIV_INLINE |
37 | page_t* |
38 | page_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 | /*********************************************************//** |
52 | Gets pointer to the buffer block where the cursor is positioned. |
53 | @return page */ |
54 | UNIV_INLINE |
55 | buf_block_t* |
56 | page_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 | /*********************************************************//** |
70 | Gets pointer to the page frame where the cursor is positioned. |
71 | @return page */ |
72 | UNIV_INLINE |
73 | page_zip_des_t* |
74 | page_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 | /*********************************************************//** |
82 | Gets the record where the cursor is positioned. |
83 | @return record */ |
84 | UNIV_INLINE |
85 | rec_t* |
86 | page_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 | /*********************************************************//** |
101 | Sets the cursor object to point before the first user record |
102 | on the page. */ |
103 | UNIV_INLINE |
104 | void |
105 | page_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 | /*********************************************************//** |
115 | Sets the cursor object to point after the last user record on |
116 | the page. */ |
117 | UNIV_INLINE |
118 | void |
119 | page_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 | /*********************************************************//** |
129 | Returns TRUE if the cursor is before first user record on page. |
130 | @return TRUE if at start */ |
131 | UNIV_INLINE |
132 | ibool |
133 | page_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 | /*********************************************************//** |
143 | Returns TRUE if the cursor is after last user record. |
144 | @return TRUE if at end */ |
145 | UNIV_INLINE |
146 | ibool |
147 | page_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 | /**********************************************************//** |
157 | Positions the cursor on the given record. */ |
158 | UNIV_INLINE |
159 | void |
160 | page_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 | /**********************************************************//** |
175 | Moves the cursor to the next record on page. */ |
176 | UNIV_INLINE |
177 | void |
178 | page_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 | /**********************************************************//** |
188 | Moves the cursor to the previous record on page. */ |
189 | UNIV_INLINE |
190 | void |
191 | page_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 */ |
207 | UNIV_INLINE |
208 | ulint |
209 | page_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 */ |
232 | UNIV_INLINE |
233 | ulint |
234 | page_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 | /***********************************************************//** |
244 | Inserts a record next to page cursor. Returns pointer to inserted record if |
245 | succeed, i.e., enough space available, NULL otherwise. The cursor stays at |
246 | the same logical position, but the physical position may change if it is |
247 | pointing to a compressed page that was reorganized. |
248 | |
249 | IMPORTANT: The caller will have to update IBUF_BITMAP_FREE |
250 | if this is a compressed leaf page in a secondary index. |
251 | This has to be done either within the same mini-transaction, |
252 | or by invoking ibuf_reset_free_bits() before mtr_commit(). |
253 | |
254 | @return pointer to record if succeed, NULL otherwise */ |
255 | UNIV_INLINE |
256 | rec_t* |
257 | page_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 | /***********************************************************//** |
297 | Inserts a record next to page cursor. Returns pointer to inserted record if |
298 | succeed, i.e., enough space available, NULL otherwise. The cursor stays at |
299 | the same logical position, but the physical position may change if it is |
300 | pointing to a compressed page that was reorganized. |
301 | |
302 | IMPORTANT: The caller will have to update IBUF_BITMAP_FREE |
303 | if this is a compressed leaf page in a secondary index. |
304 | This has to be done either within the same mini-transaction, |
305 | or by invoking ibuf_reset_free_bits() before mtr_commit(). |
306 | |
307 | @return pointer to record if succeed, NULL otherwise */ |
308 | UNIV_INLINE |
309 | rec_t* |
310 | page_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 | |