1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2016, 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/page0page.ic |
22 | Index page routines |
23 | |
24 | Created 2/2/1994 Heikki Tuuri |
25 | *******************************************************/ |
26 | |
27 | #ifndef page0page_ic |
28 | #define page0page_ic |
29 | |
30 | #ifndef UNIV_INNOCHECKSUM |
31 | #include "mach0data.h" |
32 | #ifdef UNIV_DEBUG |
33 | # include "log0recv.h" |
34 | #endif /* !UNIV_DEBUG */ |
35 | #include "rem0cmp.h" |
36 | #include "mtr0log.h" |
37 | #include "page0zip.h" |
38 | |
39 | #ifdef UNIV_MATERIALIZE |
40 | #undef UNIV_INLINE |
41 | #define UNIV_INLINE |
42 | #endif |
43 | |
44 | /*************************************************************//** |
45 | Returns the max trx id field value. */ |
46 | UNIV_INLINE |
47 | trx_id_t |
48 | page_get_max_trx_id( |
49 | /*================*/ |
50 | const page_t* page) /*!< in: page */ |
51 | { |
52 | ut_ad(page); |
53 | |
54 | return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID)); |
55 | } |
56 | |
57 | /*************************************************************//** |
58 | Sets the max trx id field value if trx_id is bigger than the previous |
59 | value. */ |
60 | UNIV_INLINE |
61 | void |
62 | page_update_max_trx_id( |
63 | /*===================*/ |
64 | buf_block_t* block, /*!< in/out: page */ |
65 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
66 | uncompressed part will be updated, or NULL */ |
67 | trx_id_t trx_id, /*!< in: transaction id */ |
68 | mtr_t* mtr) /*!< in/out: mini-transaction */ |
69 | { |
70 | ut_ad(block); |
71 | ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); |
72 | /* During crash recovery, this function may be called on |
73 | something else than a leaf page of a secondary index or the |
74 | insert buffer index tree (dict_index_is_sec_or_ibuf() returns |
75 | TRUE for the dummy indexes constructed during redo log |
76 | application). In that case, PAGE_MAX_TRX_ID is unused, |
77 | and trx_id is usually zero. */ |
78 | ut_ad(trx_id || recv_recovery_is_on()); |
79 | ut_ad(page_is_leaf(buf_block_get_frame(block))); |
80 | |
81 | if (page_get_max_trx_id(buf_block_get_frame(block)) < trx_id) { |
82 | |
83 | page_set_max_trx_id(block, page_zip, trx_id, mtr); |
84 | } |
85 | } |
86 | |
87 | /** Read the AUTO_INCREMENT value from a clustered index root page. |
88 | @param[in] page clustered index root page |
89 | @return the persisted AUTO_INCREMENT value */ |
90 | UNIV_INLINE |
91 | ib_uint64_t |
92 | page_get_autoinc(const page_t* page) |
93 | { |
94 | ut_ad(page_is_root(page)); |
95 | return(mach_read_from_8(PAGE_HEADER + PAGE_ROOT_AUTO_INC + page)); |
96 | } |
97 | |
98 | /*************************************************************//** |
99 | Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM). |
100 | @return SPLIT SEQUENCE NUMBER */ |
101 | UNIV_INLINE |
102 | node_seq_t |
103 | page_get_ssn_id( |
104 | /*============*/ |
105 | const page_t* page) /*!< in: page */ |
106 | { |
107 | ut_ad(page); |
108 | |
109 | return(static_cast<node_seq_t>( |
110 | mach_read_from_8(page + FIL_RTREE_SPLIT_SEQ_NUM))); |
111 | } |
112 | |
113 | /*************************************************************//** |
114 | Sets the RTREE SPLIT SEQUENCE NUMBER field value */ |
115 | UNIV_INLINE |
116 | void |
117 | page_set_ssn_id( |
118 | /*============*/ |
119 | buf_block_t* block, /*!< in/out: page */ |
120 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
121 | uncompressed part will be updated, or NULL */ |
122 | node_seq_t ssn_id, /*!< in: transaction id */ |
123 | mtr_t* mtr) /*!< in/out: mini-transaction */ |
124 | { |
125 | page_t* page = buf_block_get_frame(block); |
126 | |
127 | ut_ad(!mtr || mtr_memo_contains_flagged(mtr, block, |
128 | MTR_MEMO_PAGE_SX_FIX |
129 | | MTR_MEMO_PAGE_X_FIX)); |
130 | |
131 | if (page_zip) { |
132 | mach_write_to_8(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id); |
133 | page_zip_write_header(page_zip, |
134 | page + FIL_RTREE_SPLIT_SEQ_NUM, |
135 | 8, mtr); |
136 | } else if (mtr) { |
137 | mlog_write_ull(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id, mtr); |
138 | } else { |
139 | mach_write_to_8(page + FIL_RTREE_SPLIT_SEQ_NUM, ssn_id); |
140 | } |
141 | } |
142 | |
143 | #endif /* !UNIV_INNOCHECKSUM */ |
144 | |
145 | /*************************************************************//** |
146 | Reads the given header field. */ |
147 | UNIV_INLINE |
148 | uint16_t |
149 | ( |
150 | /*==================*/ |
151 | const page_t* page, /*!< in: page */ |
152 | ulint field) /*!< in: PAGE_LEVEL, ... */ |
153 | { |
154 | ut_ad(page); |
155 | ut_ad(field <= PAGE_INDEX_ID); |
156 | |
157 | return(mach_read_from_2(page + PAGE_HEADER + field)); |
158 | } |
159 | |
160 | #ifndef UNIV_INNOCHECKSUM |
161 | /*************************************************************//** |
162 | Sets the given header field. */ |
163 | UNIV_INLINE |
164 | void |
165 | page_header_set_field( |
166 | /*==================*/ |
167 | page_t* page, /*!< in/out: page */ |
168 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
169 | uncompressed part will be updated, or NULL */ |
170 | ulint field, /*!< in: PAGE_N_DIR_SLOTS, ... */ |
171 | ulint val) /*!< in: value */ |
172 | { |
173 | ut_ad(page); |
174 | ut_ad(field <= PAGE_N_RECS); |
175 | ut_ad(field == PAGE_N_HEAP || val < srv_page_size); |
176 | ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < srv_page_size); |
177 | |
178 | mach_write_to_2(page + PAGE_HEADER + field, val); |
179 | if (page_zip) { |
180 | page_zip_write_header(page_zip, |
181 | page + PAGE_HEADER + field, 2, NULL); |
182 | } |
183 | } |
184 | |
185 | /*************************************************************//** |
186 | Returns the offset stored in the given header field. |
187 | @return offset from the start of the page, or 0 */ |
188 | UNIV_INLINE |
189 | uint16_t |
190 | page_header_get_offs( |
191 | /*=================*/ |
192 | const page_t* page, /*!< in: page */ |
193 | ulint field) /*!< in: PAGE_FREE, ... */ |
194 | { |
195 | ut_ad((field == PAGE_FREE) |
196 | || (field == PAGE_LAST_INSERT) |
197 | || (field == PAGE_HEAP_TOP)); |
198 | |
199 | uint16_t offs = page_header_get_field(page, field); |
200 | |
201 | ut_ad((field != PAGE_HEAP_TOP) || offs); |
202 | |
203 | return(offs); |
204 | } |
205 | |
206 | /*************************************************************//** |
207 | Sets the pointer stored in the given header field. */ |
208 | UNIV_INLINE |
209 | void |
210 | page_header_set_ptr( |
211 | /*================*/ |
212 | page_t* page, /*!< in: page */ |
213 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
214 | uncompressed part will be updated, or NULL */ |
215 | ulint field, /*!< in: PAGE_FREE, ... */ |
216 | const byte* ptr) /*!< in: pointer or NULL*/ |
217 | { |
218 | ulint offs; |
219 | |
220 | ut_ad(page); |
221 | ut_ad((field == PAGE_FREE) |
222 | || (field == PAGE_LAST_INSERT) |
223 | || (field == PAGE_HEAP_TOP)); |
224 | |
225 | if (ptr == NULL) { |
226 | offs = 0; |
227 | } else { |
228 | offs = ulint(ptr - page); |
229 | } |
230 | |
231 | ut_ad((field != PAGE_HEAP_TOP) || offs); |
232 | |
233 | page_header_set_field(page, page_zip, field, offs); |
234 | } |
235 | |
236 | /*************************************************************//** |
237 | Resets the last insert info field in the page header. Writes to mlog |
238 | about this operation. */ |
239 | UNIV_INLINE |
240 | void |
241 | page_header_reset_last_insert( |
242 | /*==========================*/ |
243 | page_t* page, /*!< in/out: page */ |
244 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
245 | uncompressed part will be updated, or NULL */ |
246 | mtr_t* mtr) /*!< in: mtr */ |
247 | { |
248 | ut_ad(page != NULL); |
249 | ut_ad(mtr != NULL); |
250 | |
251 | if (page_zip) { |
252 | mach_write_to_2(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0); |
253 | page_zip_write_header(page_zip, |
254 | page + (PAGE_HEADER + PAGE_LAST_INSERT), |
255 | 2, mtr); |
256 | } else { |
257 | mlog_write_ulint(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0, |
258 | MLOG_2BYTES, mtr); |
259 | } |
260 | } |
261 | |
262 | /***************************************************************//** |
263 | Returns the heap number of a record. |
264 | @return heap number */ |
265 | UNIV_INLINE |
266 | ulint |
267 | page_rec_get_heap_no( |
268 | /*=================*/ |
269 | const rec_t* rec) /*!< in: the physical record */ |
270 | { |
271 | if (page_rec_is_comp(rec)) { |
272 | return(rec_get_heap_no_new(rec)); |
273 | } else { |
274 | return(rec_get_heap_no_old(rec)); |
275 | } |
276 | } |
277 | |
278 | /** Determine whether an index page record is a user record. |
279 | @param[in] rec record in an index page |
280 | @return true if a user record */ |
281 | inline |
282 | bool |
283 | page_rec_is_user_rec(const rec_t* rec) |
284 | { |
285 | ut_ad(page_rec_check(rec)); |
286 | return(page_rec_is_user_rec_low(page_offset(rec))); |
287 | } |
288 | |
289 | /** Determine whether an index page record is the supremum record. |
290 | @param[in] rec record in an index page |
291 | @return true if the supremum record */ |
292 | inline |
293 | bool |
294 | page_rec_is_supremum(const rec_t* rec) |
295 | { |
296 | ut_ad(page_rec_check(rec)); |
297 | return(page_rec_is_supremum_low(page_offset(rec))); |
298 | } |
299 | |
300 | /** Determine whether an index page record is the infimum record. |
301 | @param[in] rec record in an index page |
302 | @return true if the infimum record */ |
303 | inline |
304 | bool |
305 | page_rec_is_infimum(const rec_t* rec) |
306 | { |
307 | ut_ad(page_rec_check(rec)); |
308 | return(page_rec_is_infimum_low(page_offset(rec))); |
309 | } |
310 | |
311 | /************************************************************//** |
312 | true if the record is the first user record on a page. |
313 | @return true if the first user record */ |
314 | UNIV_INLINE |
315 | bool |
316 | page_rec_is_first( |
317 | /*==============*/ |
318 | const rec_t* rec, /*!< in: record */ |
319 | const page_t* page) /*!< in: page */ |
320 | { |
321 | ut_ad(page_get_n_recs(page) > 0); |
322 | |
323 | return(page_rec_get_next_const(page_get_infimum_rec(page)) == rec); |
324 | } |
325 | |
326 | /************************************************************//** |
327 | true if the record is the second user record on a page. |
328 | @return true if the second user record */ |
329 | UNIV_INLINE |
330 | bool |
331 | page_rec_is_second( |
332 | /*===============*/ |
333 | const rec_t* rec, /*!< in: record */ |
334 | const page_t* page) /*!< in: page */ |
335 | { |
336 | ut_ad(page_get_n_recs(page) > 1); |
337 | |
338 | return(page_rec_get_next_const( |
339 | page_rec_get_next_const(page_get_infimum_rec(page))) == rec); |
340 | } |
341 | |
342 | /************************************************************//** |
343 | true if the record is the last user record on a page. |
344 | @return true if the last user record */ |
345 | UNIV_INLINE |
346 | bool |
347 | page_rec_is_last( |
348 | /*=============*/ |
349 | const rec_t* rec, /*!< in: record */ |
350 | const page_t* page) /*!< in: page */ |
351 | { |
352 | ut_ad(page_get_n_recs(page) > 0); |
353 | |
354 | return(page_rec_get_next_const(rec) == page_get_supremum_rec(page)); |
355 | } |
356 | |
357 | /************************************************************//** |
358 | true if the record is the second last user record on a page. |
359 | @return true if the second last user record */ |
360 | UNIV_INLINE |
361 | bool |
362 | page_rec_is_second_last( |
363 | /*====================*/ |
364 | const rec_t* rec, /*!< in: record */ |
365 | const page_t* page) /*!< in: page */ |
366 | { |
367 | ut_ad(page_get_n_recs(page) > 1); |
368 | ut_ad(!page_rec_is_last(rec, page)); |
369 | |
370 | return(page_rec_get_next_const( |
371 | page_rec_get_next_const(rec)) == page_get_supremum_rec(page)); |
372 | } |
373 | |
374 | /************************************************************//** |
375 | Returns the nth record of the record list. |
376 | This is the inverse function of page_rec_get_n_recs_before(). |
377 | @return nth record */ |
378 | UNIV_INLINE |
379 | rec_t* |
380 | page_rec_get_nth( |
381 | /*=============*/ |
382 | page_t* page, /*!< in: page */ |
383 | ulint nth) /*!< in: nth record */ |
384 | { |
385 | return((rec_t*) page_rec_get_nth_const(page, nth)); |
386 | } |
387 | |
388 | /************************************************************//** |
389 | Returns the middle record of the records on the page. If there is an |
390 | even number of records in the list, returns the first record of the |
391 | upper half-list. |
392 | @return middle record */ |
393 | UNIV_INLINE |
394 | rec_t* |
395 | page_get_middle_rec( |
396 | /*================*/ |
397 | page_t* page) /*!< in: page */ |
398 | { |
399 | ulint middle = (ulint(page_get_n_recs(page)) |
400 | + PAGE_HEAP_NO_USER_LOW) / 2; |
401 | |
402 | return(page_rec_get_nth(page, middle)); |
403 | } |
404 | |
405 | #endif /* !UNIV_INNOCHECKSUM */ |
406 | |
407 | /*************************************************************//** |
408 | Gets the page number. |
409 | @return page number */ |
410 | UNIV_INLINE |
411 | ulint |
412 | page_get_page_no( |
413 | /*=============*/ |
414 | const page_t* page) /*!< in: page */ |
415 | { |
416 | ut_ad(page == page_align((page_t*) page)); |
417 | return(mach_read_from_4(page + FIL_PAGE_OFFSET)); |
418 | } |
419 | |
420 | #ifndef UNIV_INNOCHECKSUM |
421 | /*************************************************************//** |
422 | Gets the tablespace identifier. |
423 | @return space id */ |
424 | UNIV_INLINE |
425 | ulint |
426 | page_get_space_id( |
427 | /*==============*/ |
428 | const page_t* page) /*!< in: page */ |
429 | { |
430 | ut_ad(page == page_align((page_t*) page)); |
431 | return(mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)); |
432 | } |
433 | |
434 | #endif /* !UNIV_INNOCHECKSUM */ |
435 | |
436 | /*************************************************************//** |
437 | Gets the number of user records on page (infimum and supremum records |
438 | are not user records). |
439 | @return number of user records */ |
440 | UNIV_INLINE |
441 | uint16_t |
442 | page_get_n_recs( |
443 | /*============*/ |
444 | const page_t* page) /*!< in: index page */ |
445 | { |
446 | return(page_header_get_field(page, PAGE_N_RECS)); |
447 | } |
448 | |
449 | #ifndef UNIV_INNOCHECKSUM |
450 | /*************************************************************//** |
451 | Gets the number of dir slots in directory. |
452 | @return number of slots */ |
453 | UNIV_INLINE |
454 | uint16_t |
455 | page_dir_get_n_slots( |
456 | /*=================*/ |
457 | const page_t* page) /*!< in: index page */ |
458 | { |
459 | return(page_header_get_field(page, PAGE_N_DIR_SLOTS)); |
460 | } |
461 | /*************************************************************//** |
462 | Sets the number of dir slots in directory. */ |
463 | UNIV_INLINE |
464 | void |
465 | page_dir_set_n_slots( |
466 | /*=================*/ |
467 | page_t* page, /*!< in/out: page */ |
468 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
469 | uncompressed part will be updated, or NULL */ |
470 | ulint n_slots)/*!< in: number of slots */ |
471 | { |
472 | page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots); |
473 | } |
474 | |
475 | /*************************************************************//** |
476 | Gets the number of records in the heap. |
477 | @return number of user records */ |
478 | UNIV_INLINE |
479 | uint16_t |
480 | page_dir_get_n_heap( |
481 | /*================*/ |
482 | const page_t* page) /*!< in: index page */ |
483 | { |
484 | return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff); |
485 | } |
486 | |
487 | /*************************************************************//** |
488 | Sets the number of records in the heap. */ |
489 | UNIV_INLINE |
490 | void |
491 | page_dir_set_n_heap( |
492 | /*================*/ |
493 | page_t* page, /*!< in/out: index page */ |
494 | page_zip_des_t* page_zip,/*!< in/out: compressed page whose |
495 | uncompressed part will be updated, or NULL. |
496 | Note that the size of the dense page directory |
497 | in the compressed page trailer is |
498 | n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */ |
499 | ulint n_heap) /*!< in: number of records */ |
500 | { |
501 | ut_ad(n_heap < 0x8000); |
502 | ut_ad(!page_zip || uint16_t(n_heap) |
503 | == (page_header_get_field(page, PAGE_N_HEAP) & 0x7fff) + 1); |
504 | |
505 | page_header_set_field(page, page_zip, PAGE_N_HEAP, n_heap |
506 | | (0x8000 |
507 | & page_header_get_field(page, PAGE_N_HEAP))); |
508 | } |
509 | |
510 | #ifdef UNIV_DEBUG |
511 | /*************************************************************//** |
512 | Gets pointer to nth directory slot. |
513 | @return pointer to dir slot */ |
514 | UNIV_INLINE |
515 | page_dir_slot_t* |
516 | page_dir_get_nth_slot( |
517 | /*==================*/ |
518 | const page_t* page, /*!< in: index page */ |
519 | ulint n) /*!< in: position */ |
520 | { |
521 | ut_ad(page_dir_get_n_slots(page) > n); |
522 | |
523 | return((page_dir_slot_t*) |
524 | page + srv_page_size - PAGE_DIR |
525 | - (n + 1) * PAGE_DIR_SLOT_SIZE); |
526 | } |
527 | #endif /* UNIV_DEBUG */ |
528 | |
529 | /**************************************************************//** |
530 | Used to check the consistency of a record on a page. |
531 | @return TRUE if succeed */ |
532 | UNIV_INLINE |
533 | ibool |
534 | page_rec_check( |
535 | /*===========*/ |
536 | const rec_t* rec) /*!< in: record */ |
537 | { |
538 | const page_t* page = page_align(rec); |
539 | |
540 | ut_a(rec); |
541 | |
542 | ut_a(page_offset(rec) <= page_header_get_field(page, PAGE_HEAP_TOP)); |
543 | ut_a(page_offset(rec) >= PAGE_DATA); |
544 | |
545 | return(TRUE); |
546 | } |
547 | |
548 | /***************************************************************//** |
549 | Gets the record pointed to by a directory slot. |
550 | @return pointer to record */ |
551 | UNIV_INLINE |
552 | const rec_t* |
553 | page_dir_slot_get_rec( |
554 | /*==================*/ |
555 | const page_dir_slot_t* slot) /*!< in: directory slot */ |
556 | { |
557 | return(page_align(slot) + mach_read_from_2(slot)); |
558 | } |
559 | |
560 | /***************************************************************//** |
561 | This is used to set the record offset in a directory slot. */ |
562 | UNIV_INLINE |
563 | void |
564 | page_dir_slot_set_rec( |
565 | /*==================*/ |
566 | page_dir_slot_t* slot, /*!< in: directory slot */ |
567 | rec_t* rec) /*!< in: record on the page */ |
568 | { |
569 | ut_ad(page_rec_check(rec)); |
570 | |
571 | mach_write_to_2(slot, page_offset(rec)); |
572 | } |
573 | |
574 | /***************************************************************//** |
575 | Gets the number of records owned by a directory slot. |
576 | @return number of records */ |
577 | UNIV_INLINE |
578 | ulint |
579 | page_dir_slot_get_n_owned( |
580 | /*======================*/ |
581 | const page_dir_slot_t* slot) /*!< in: page directory slot */ |
582 | { |
583 | const rec_t* rec = page_dir_slot_get_rec(slot); |
584 | if (page_rec_is_comp(slot)) { |
585 | return(rec_get_n_owned_new(rec)); |
586 | } else { |
587 | return(rec_get_n_owned_old(rec)); |
588 | } |
589 | } |
590 | |
591 | /***************************************************************//** |
592 | This is used to set the owned records field of a directory slot. */ |
593 | UNIV_INLINE |
594 | void |
595 | page_dir_slot_set_n_owned( |
596 | /*======================*/ |
597 | page_dir_slot_t*slot, /*!< in/out: directory slot */ |
598 | page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ |
599 | ulint n) /*!< in: number of records owned by the slot */ |
600 | { |
601 | rec_t* rec = (rec_t*) page_dir_slot_get_rec(slot); |
602 | if (page_rec_is_comp(slot)) { |
603 | rec_set_n_owned_new(rec, page_zip, n); |
604 | } else { |
605 | ut_ad(!page_zip); |
606 | rec_set_n_owned_old(rec, n); |
607 | } |
608 | } |
609 | |
610 | /************************************************************//** |
611 | Calculates the space reserved for directory slots of a given number of |
612 | records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE / |
613 | PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */ |
614 | UNIV_INLINE |
615 | ulint |
616 | page_dir_calc_reserved_space( |
617 | /*=========================*/ |
618 | ulint n_recs) /*!< in: number of records */ |
619 | { |
620 | return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1) |
621 | / PAGE_DIR_SLOT_MIN_N_OWNED); |
622 | } |
623 | |
624 | /************************************************************//** |
625 | Gets the pointer to the next record on the page. |
626 | @return pointer to next record */ |
627 | UNIV_INLINE |
628 | const rec_t* |
629 | page_rec_get_next_low( |
630 | /*==================*/ |
631 | const rec_t* rec, /*!< in: pointer to record */ |
632 | ulint comp) /*!< in: nonzero=compact page layout */ |
633 | { |
634 | ulint offs; |
635 | const page_t* page; |
636 | |
637 | ut_ad(page_rec_check(rec)); |
638 | |
639 | page = page_align(rec); |
640 | |
641 | offs = rec_get_next_offs(rec, comp); |
642 | |
643 | if (offs >= srv_page_size) { |
644 | fprintf(stderr, |
645 | "InnoDB: Next record offset is nonsensical %lu" |
646 | " in record at offset %lu\n" |
647 | "InnoDB: rec address %p, space id %lu, page %lu\n" , |
648 | (ulong) offs, (ulong) page_offset(rec), |
649 | (void*) rec, |
650 | (ulong) page_get_space_id(page), |
651 | (ulong) page_get_page_no(page)); |
652 | ut_error; |
653 | } else if (offs == 0) { |
654 | |
655 | return(NULL); |
656 | } |
657 | |
658 | return(page + offs); |
659 | } |
660 | |
661 | /************************************************************//** |
662 | Gets the pointer to the next record on the page. |
663 | @return pointer to next record */ |
664 | UNIV_INLINE |
665 | rec_t* |
666 | page_rec_get_next( |
667 | /*==============*/ |
668 | rec_t* rec) /*!< in: pointer to record */ |
669 | { |
670 | return((rec_t*) page_rec_get_next_low(rec, page_rec_is_comp(rec))); |
671 | } |
672 | |
673 | /************************************************************//** |
674 | Gets the pointer to the next record on the page. |
675 | @return pointer to next record */ |
676 | UNIV_INLINE |
677 | const rec_t* |
678 | page_rec_get_next_const( |
679 | /*====================*/ |
680 | const rec_t* rec) /*!< in: pointer to record */ |
681 | { |
682 | return(page_rec_get_next_low(rec, page_rec_is_comp(rec))); |
683 | } |
684 | |
685 | /************************************************************//** |
686 | Gets the pointer to the next non delete-marked record on the page. |
687 | If all subsequent records are delete-marked, then this function |
688 | will return the supremum record. |
689 | @return pointer to next non delete-marked record or pointer to supremum */ |
690 | UNIV_INLINE |
691 | const rec_t* |
692 | page_rec_get_next_non_del_marked( |
693 | /*=============================*/ |
694 | const rec_t* rec) /*!< in: pointer to record */ |
695 | { |
696 | const rec_t* r; |
697 | ulint page_is_compact = page_rec_is_comp(rec); |
698 | |
699 | for (r = page_rec_get_next_const(rec); |
700 | !page_rec_is_supremum(r) |
701 | && rec_get_deleted_flag(r, page_is_compact); |
702 | r = page_rec_get_next_const(r)) { |
703 | /* noop */ |
704 | } |
705 | |
706 | return(r); |
707 | } |
708 | |
709 | /************************************************************//** |
710 | Sets the pointer to the next record on the page. */ |
711 | UNIV_INLINE |
712 | void |
713 | page_rec_set_next( |
714 | /*==============*/ |
715 | rec_t* rec, /*!< in: pointer to record, |
716 | must not be page supremum */ |
717 | const rec_t* next) /*!< in: pointer to next record, |
718 | must not be page infimum */ |
719 | { |
720 | ulint offs; |
721 | |
722 | ut_ad(page_rec_check(rec)); |
723 | ut_ad(!page_rec_is_supremum(rec)); |
724 | ut_ad(rec != next); |
725 | |
726 | ut_ad(!next || !page_rec_is_infimum(next)); |
727 | ut_ad(!next || page_align(rec) == page_align(next)); |
728 | |
729 | offs = next != NULL ? page_offset(next) : 0; |
730 | |
731 | if (page_rec_is_comp(rec)) { |
732 | rec_set_next_offs_new(rec, offs); |
733 | } else { |
734 | rec_set_next_offs_old(rec, offs); |
735 | } |
736 | } |
737 | |
738 | /************************************************************//** |
739 | Gets the pointer to the previous record. |
740 | @return pointer to previous record */ |
741 | UNIV_INLINE |
742 | const rec_t* |
743 | page_rec_get_prev_const( |
744 | /*====================*/ |
745 | const rec_t* rec) /*!< in: pointer to record, must not be page |
746 | infimum */ |
747 | { |
748 | const page_dir_slot_t* slot; |
749 | ulint slot_no; |
750 | const rec_t* rec2; |
751 | const rec_t* prev_rec = NULL; |
752 | const page_t* page; |
753 | |
754 | ut_ad(page_rec_check(rec)); |
755 | |
756 | page = page_align(rec); |
757 | |
758 | ut_ad(!page_rec_is_infimum(rec)); |
759 | |
760 | slot_no = page_dir_find_owner_slot(rec); |
761 | |
762 | ut_a(slot_no != 0); |
763 | |
764 | slot = page_dir_get_nth_slot(page, slot_no - 1); |
765 | |
766 | rec2 = page_dir_slot_get_rec(slot); |
767 | |
768 | if (page_is_comp(page)) { |
769 | while (rec != rec2) { |
770 | prev_rec = rec2; |
771 | rec2 = page_rec_get_next_low(rec2, TRUE); |
772 | } |
773 | } else { |
774 | while (rec != rec2) { |
775 | prev_rec = rec2; |
776 | rec2 = page_rec_get_next_low(rec2, FALSE); |
777 | } |
778 | } |
779 | |
780 | ut_a(prev_rec); |
781 | |
782 | return(prev_rec); |
783 | } |
784 | |
785 | /************************************************************//** |
786 | Gets the pointer to the previous record. |
787 | @return pointer to previous record */ |
788 | UNIV_INLINE |
789 | rec_t* |
790 | page_rec_get_prev( |
791 | /*==============*/ |
792 | rec_t* rec) /*!< in: pointer to record, must not be page |
793 | infimum */ |
794 | { |
795 | return((rec_t*) page_rec_get_prev_const(rec)); |
796 | } |
797 | |
798 | /***************************************************************//** |
799 | Looks for the record which owns the given record. |
800 | @return the owner record */ |
801 | UNIV_INLINE |
802 | rec_t* |
803 | page_rec_find_owner_rec( |
804 | /*====================*/ |
805 | rec_t* rec) /*!< in: the physical record */ |
806 | { |
807 | ut_ad(page_rec_check(rec)); |
808 | |
809 | if (page_rec_is_comp(rec)) { |
810 | while (rec_get_n_owned_new(rec) == 0) { |
811 | rec = page_rec_get_next(rec); |
812 | } |
813 | } else { |
814 | while (rec_get_n_owned_old(rec) == 0) { |
815 | rec = page_rec_get_next(rec); |
816 | } |
817 | } |
818 | |
819 | return(rec); |
820 | } |
821 | |
822 | /**********************************************************//** |
823 | Returns the base extra size of a physical record. This is the |
824 | size of the fixed header, independent of the record size. |
825 | @return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */ |
826 | UNIV_INLINE |
827 | ulint |
828 | page_rec_get_base_extra_size( |
829 | /*=========================*/ |
830 | const rec_t* rec) /*!< in: physical record */ |
831 | { |
832 | compile_time_assert(REC_N_NEW_EXTRA_BYTES + 1 |
833 | == REC_N_OLD_EXTRA_BYTES); |
834 | return(REC_N_NEW_EXTRA_BYTES + (ulint) !page_rec_is_comp(rec)); |
835 | } |
836 | |
837 | #endif /* UNIV_INNOCHECKSUM */ |
838 | |
839 | /************************************************************//** |
840 | Returns the sum of the sizes of the records in the record list, excluding |
841 | the infimum and supremum records. |
842 | @return data in bytes */ |
843 | UNIV_INLINE |
844 | uint16_t |
845 | page_get_data_size( |
846 | /*===============*/ |
847 | const page_t* page) /*!< in: index page */ |
848 | { |
849 | uint16_t ret = page_header_get_field(page, PAGE_HEAP_TOP) |
850 | - (page_is_comp(page) |
851 | ? PAGE_NEW_SUPREMUM_END |
852 | : PAGE_OLD_SUPREMUM_END) |
853 | - page_header_get_field(page, PAGE_GARBAGE); |
854 | ut_ad(ret < srv_page_size); |
855 | return(ret); |
856 | } |
857 | |
858 | #ifndef UNIV_INNOCHECKSUM |
859 | /************************************************************//** |
860 | Allocates a block of memory from the free list of an index page. */ |
861 | UNIV_INLINE |
862 | void |
863 | page_mem_alloc_free( |
864 | /*================*/ |
865 | page_t* page, /*!< in/out: index page */ |
866 | page_zip_des_t* page_zip,/*!< in/out: compressed page with enough |
867 | space available for inserting the record, |
868 | or NULL */ |
869 | rec_t* next_rec,/*!< in: pointer to the new head of the |
870 | free record list */ |
871 | ulint need) /*!< in: number of bytes allocated */ |
872 | { |
873 | ulint garbage; |
874 | |
875 | #ifdef UNIV_DEBUG |
876 | const rec_t* old_rec = page_header_get_ptr(page, PAGE_FREE); |
877 | ulint next_offs; |
878 | |
879 | ut_ad(old_rec); |
880 | next_offs = rec_get_next_offs(old_rec, page_is_comp(page)); |
881 | ut_ad(next_rec == (next_offs ? page + next_offs : NULL)); |
882 | #endif |
883 | |
884 | page_header_set_ptr(page, page_zip, PAGE_FREE, next_rec); |
885 | |
886 | garbage = page_header_get_field(page, PAGE_GARBAGE); |
887 | ut_ad(garbage >= need); |
888 | |
889 | page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage - need); |
890 | } |
891 | |
892 | /*************************************************************//** |
893 | Calculates free space if a page is emptied. |
894 | @return free space */ |
895 | UNIV_INLINE |
896 | ulint |
897 | page_get_free_space_of_empty( |
898 | /*=========================*/ |
899 | ulint comp) /*!< in: nonzero=compact page layout */ |
900 | { |
901 | if (comp) { |
902 | return((ulint)(srv_page_size |
903 | - PAGE_NEW_SUPREMUM_END |
904 | - PAGE_DIR |
905 | - 2 * PAGE_DIR_SLOT_SIZE)); |
906 | } |
907 | |
908 | return((ulint)(srv_page_size |
909 | - PAGE_OLD_SUPREMUM_END |
910 | - PAGE_DIR |
911 | - 2 * PAGE_DIR_SLOT_SIZE)); |
912 | } |
913 | |
914 | /***********************************************************************//** |
915 | Write a 32-bit field in a data dictionary record. */ |
916 | UNIV_INLINE |
917 | void |
918 | page_rec_write_field( |
919 | /*=================*/ |
920 | rec_t* rec, /*!< in/out: record to update */ |
921 | ulint i, /*!< in: index of the field to update */ |
922 | ulint val, /*!< in: value to write */ |
923 | mtr_t* mtr) /*!< in/out: mini-transaction */ |
924 | { |
925 | byte* data; |
926 | ulint len; |
927 | |
928 | data = rec_get_nth_field_old(rec, i, &len); |
929 | |
930 | ut_ad(len == 4); |
931 | |
932 | mlog_write_ulint(data, val, MLOG_4BYTES, mtr); |
933 | } |
934 | |
935 | /************************************************************//** |
936 | Each user record on a page, and also the deleted user records in the heap |
937 | takes its size plus the fraction of the dir cell size / |
938 | PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the |
939 | value of page_get_free_space_of_empty, the insert is impossible, otherwise |
940 | it is allowed. This function returns the maximum combined size of records |
941 | which can be inserted on top of the record heap. |
942 | @return maximum combined size for inserted records */ |
943 | UNIV_INLINE |
944 | ulint |
945 | page_get_max_insert_size( |
946 | /*=====================*/ |
947 | const page_t* page, /*!< in: index page */ |
948 | ulint n_recs) /*!< in: number of records */ |
949 | { |
950 | ulint occupied; |
951 | ulint free_space; |
952 | |
953 | if (page_is_comp(page)) { |
954 | occupied = page_header_get_field(page, PAGE_HEAP_TOP) |
955 | - PAGE_NEW_SUPREMUM_END |
956 | + page_dir_calc_reserved_space( |
957 | n_recs + page_dir_get_n_heap(page) - 2); |
958 | |
959 | free_space = page_get_free_space_of_empty(TRUE); |
960 | } else { |
961 | occupied = page_header_get_field(page, PAGE_HEAP_TOP) |
962 | - PAGE_OLD_SUPREMUM_END |
963 | + page_dir_calc_reserved_space( |
964 | n_recs + page_dir_get_n_heap(page) - 2); |
965 | |
966 | free_space = page_get_free_space_of_empty(FALSE); |
967 | } |
968 | |
969 | /* Above the 'n_recs +' part reserves directory space for the new |
970 | inserted records; the '- 2' excludes page infimum and supremum |
971 | records */ |
972 | |
973 | if (occupied > free_space) { |
974 | |
975 | return(0); |
976 | } |
977 | |
978 | return(free_space - occupied); |
979 | } |
980 | |
981 | /************************************************************//** |
982 | Returns the maximum combined size of records which can be inserted on top |
983 | of the record heap if a page is first reorganized. |
984 | @return maximum combined size for inserted records */ |
985 | UNIV_INLINE |
986 | ulint |
987 | page_get_max_insert_size_after_reorganize( |
988 | /*======================================*/ |
989 | const page_t* page, /*!< in: index page */ |
990 | ulint n_recs) /*!< in: number of records */ |
991 | { |
992 | ulint occupied; |
993 | ulint free_space; |
994 | |
995 | occupied = page_get_data_size(page) |
996 | + page_dir_calc_reserved_space(n_recs + page_get_n_recs(page)); |
997 | |
998 | free_space = page_get_free_space_of_empty(page_is_comp(page)); |
999 | |
1000 | if (occupied > free_space) { |
1001 | |
1002 | return(0); |
1003 | } |
1004 | |
1005 | return(free_space - occupied); |
1006 | } |
1007 | |
1008 | /************************************************************//** |
1009 | Puts a record to free list. */ |
1010 | UNIV_INLINE |
1011 | void |
1012 | page_mem_free( |
1013 | /*==========*/ |
1014 | page_t* page, /*!< in/out: index page */ |
1015 | page_zip_des_t* page_zip, /*!< in/out: compressed page, |
1016 | or NULL */ |
1017 | rec_t* rec, /*!< in: pointer to the |
1018 | (origin of) record */ |
1019 | const dict_index_t* index, /*!< in: index of rec */ |
1020 | const ulint* offsets) /*!< in: array returned by |
1021 | rec_get_offsets() */ |
1022 | { |
1023 | rec_t* free; |
1024 | ulint garbage; |
1025 | |
1026 | ut_ad(rec_offs_validate(rec, index, offsets)); |
1027 | free = page_header_get_ptr(page, PAGE_FREE); |
1028 | |
1029 | if (srv_immediate_scrub_data_uncompressed) { |
1030 | /* scrub record */ |
1031 | memset(rec, 0, rec_offs_data_size(offsets)); |
1032 | } |
1033 | |
1034 | page_rec_set_next(rec, free); |
1035 | page_header_set_ptr(page, page_zip, PAGE_FREE, rec); |
1036 | |
1037 | garbage = page_header_get_field(page, PAGE_GARBAGE); |
1038 | |
1039 | page_header_set_field(page, page_zip, PAGE_GARBAGE, |
1040 | garbage + rec_offs_size(offsets)); |
1041 | |
1042 | if (page_zip) { |
1043 | page_zip_dir_delete(page_zip, rec, index, offsets, free); |
1044 | } else { |
1045 | page_header_set_field(page, page_zip, PAGE_N_RECS, |
1046 | ulint(page_get_n_recs(page)) - 1); |
1047 | } |
1048 | } |
1049 | |
1050 | /** Read the PAGE_DIRECTION field from a byte. |
1051 | @param[in] ptr pointer to PAGE_DIRECTION_B |
1052 | @return the value of the PAGE_DIRECTION field */ |
1053 | inline |
1054 | byte |
1055 | page_ptr_get_direction(const byte* ptr) |
1056 | { |
1057 | ut_ad(page_offset(ptr) == PAGE_HEADER + PAGE_DIRECTION_B); |
1058 | return *ptr & ((1U << 3) - 1); |
1059 | } |
1060 | |
1061 | /** Set the PAGE_DIRECTION field. |
1062 | @param[in] ptr pointer to PAGE_DIRECTION_B |
1063 | @param[in] dir the value of the PAGE_DIRECTION field */ |
1064 | inline |
1065 | void |
1066 | page_ptr_set_direction(byte* ptr, byte dir) |
1067 | { |
1068 | ut_ad(page_offset(ptr) == PAGE_HEADER + PAGE_DIRECTION_B); |
1069 | ut_ad(dir >= PAGE_LEFT); |
1070 | ut_ad(dir <= PAGE_NO_DIRECTION); |
1071 | *ptr = (*ptr & ~((1U << 3) - 1)) | dir; |
1072 | } |
1073 | |
1074 | /** Read the PAGE_INSTANT field. |
1075 | @param[in] page index page |
1076 | @return the value of the PAGE_INSTANT field */ |
1077 | inline |
1078 | uint16_t |
1079 | page_get_instant(const page_t* page) |
1080 | { |
1081 | uint16_t i = page_header_get_field(page, PAGE_INSTANT); |
1082 | #ifdef UNIV_DEBUG |
1083 | switch (fil_page_get_type(page)) { |
1084 | case FIL_PAGE_TYPE_INSTANT: |
1085 | ut_ad(page_get_direction(page) <= PAGE_NO_DIRECTION); |
1086 | ut_ad(i >> 3); |
1087 | break; |
1088 | case FIL_PAGE_INDEX: |
1089 | ut_ad(i <= PAGE_NO_DIRECTION || !page_is_comp(page)); |
1090 | break; |
1091 | case FIL_PAGE_RTREE: |
1092 | ut_ad(i <= PAGE_NO_DIRECTION); |
1093 | break; |
1094 | default: |
1095 | ut_ad(!"invalid page type" ); |
1096 | break; |
1097 | } |
1098 | #endif /* UNIV_DEBUG */ |
1099 | return(i >> 3); |
1100 | } |
1101 | |
1102 | /** Assign the PAGE_INSTANT field. |
1103 | @param[in,out] page clustered index root page |
1104 | @param[in] n original number of clustered index fields |
1105 | @param[in,out] mtr mini-transaction */ |
1106 | inline |
1107 | void |
1108 | page_set_instant(page_t* page, unsigned n, mtr_t* mtr) |
1109 | { |
1110 | ut_ad(fil_page_get_type(page) == FIL_PAGE_TYPE_INSTANT); |
1111 | ut_ad(n > 0); |
1112 | ut_ad(n < REC_MAX_N_FIELDS); |
1113 | uint16_t i = page_header_get_field(page, PAGE_INSTANT); |
1114 | ut_ad(i <= PAGE_NO_DIRECTION); |
1115 | i |= n << 3; |
1116 | mlog_write_ulint(PAGE_HEADER + PAGE_INSTANT + page, i, |
1117 | MLOG_2BYTES, mtr); |
1118 | } |
1119 | #endif /* !UNIV_INNOCHECKSUM */ |
1120 | |
1121 | #ifdef UNIV_MATERIALIZE |
1122 | #undef UNIV_INLINE |
1123 | #define UNIV_INLINE UNIV_INLINE_ORIGINAL |
1124 | #endif |
1125 | |
1126 | #endif |
1127 | |