1 /*****************************************************************************
2 
3 Copyright (c) 1994, 2020, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 #include <stddef.h>
28 #include <sys/types.h>
29 
30 /** @file include/page0page.h
31  Index page routines
32 
33  Created 2/2/1994 Heikki Tuuri
34  *******************************************************/
35 
36 #ifndef page0page_h
37 #define page0page_h
38 
39 #include "univ.i"
40 
41 #include "buf0buf.h"
42 #include "data0data.h"
43 #include "dict0dict.h"
44 #include "fil0fil.h"
45 #include "fsp0fsp.h"
46 #include "mtr0mtr.h"
47 #include "page0types.h"
48 #include "rem0rec.h"
49 
50 #ifdef UNIV_MATERIALIZE
51 #undef UNIV_INLINE
52 #define UNIV_INLINE
53 #endif
54 
55 /*			PAGE DIRECTORY
56                         ==============
57 */
58 
59 typedef byte page_dir_slot_t;
60 typedef page_dir_slot_t page_dir_t;
61 
62 /* Offset of the directory start down from the page end. We call the
63 slot with the highest file address directory start, as it points to
64 the first record in the list of records. */
65 #define PAGE_DIR FIL_PAGE_DATA_END
66 
67 /* We define a slot in the page directory as two bytes */
68 #define PAGE_DIR_SLOT_SIZE 2
69 
70 /* The offset of the physically lower end of the directory, counted from
71 page end, when the page is empty */
72 #define PAGE_EMPTY_DIR_START (PAGE_DIR + 2 * PAGE_DIR_SLOT_SIZE)
73 
74 /* The maximum and minimum number of records owned by a directory slot. The
75 number may drop below the minimum in the first and the last slot in the
76 directory. */
77 #define PAGE_DIR_SLOT_MAX_N_OWNED 8
78 #define PAGE_DIR_SLOT_MIN_N_OWNED 4
79 
80 /* The infimum and supremum records are omitted from the compressed page.
81 On compress, we compare that the records are there, and on uncompress we
82 restore the records. */
83 /** Extra bytes of an infimum record */
84 static const byte infimum_extra[] = {
85     0x01,          /* info_bits=0, n_owned=1 */
86     0x00, 0x02     /* heap_no=0, status=2 */
87     /* ?, ?	*/ /* next=(first user rec, or supremum) */
88 };
89 /** Data bytes of an infimum record */
90 static const byte infimum_data[] = {
91     0x69, 0x6e, 0x66, 0x69, 0x6d, 0x75, 0x6d, 0x00 /* "infimum\0" */
92 };
93 /** Extra bytes and data bytes of a supremum record */
94 static const byte supremum_extra_data[] = {
95     /* 0x0?, */ /* info_bits=0, n_owned=1..8 */
96     0x00,
97     0x0b, /* heap_no=1, status=3 */
98     0x00,
99     0x00, /* next=0 */
100     0x73,
101     0x75,
102     0x70,
103     0x72,
104     0x65,
105     0x6d,
106     0x75,
107     0x6d /* "supremum" */
108 };
109 
110 /** Gets the start of a page.
111 @param[in]  ptr     pointer to page frame
112 @return start of the page */
113 UNIV_INLINE
114 page_t *page_align(const void *ptr);
115 
116 /** Gets the offset within a page.
117 @param[in]  ptr     pointer to page frame
118 @return offset from the start of the page */
119 UNIV_INLINE
120 ulint page_offset(const void *ptr);
121 
122 /** Returns the max trx id field value. */
123 UNIV_INLINE
124 trx_id_t page_get_max_trx_id(const page_t *page); /*!< in: page */
125 /** Sets the max trx id field value. */
126 void page_set_max_trx_id(
127     buf_block_t *block,       /*!< in/out: page */
128     page_zip_des_t *page_zip, /*!< in/out: compressed page, or NULL */
129     trx_id_t trx_id,          /*!< in: transaction id */
130     mtr_t *mtr);              /*!< in/out: mini-transaction, or NULL */
131 
132 /** Sets the max trx id field value if trx_id is bigger than the previous
133 value.
134 @param[in,out]	block		page
135 @param[in,out]	page_zip	compressed page whose uncompressed part will
136                                 be updated, or NULL
137 @param[in]	trx_id		transaction id
138 @param[in,out]	mtr		mini-transaction */
139 UNIV_INLINE
140 void page_update_max_trx_id(buf_block_t *block, page_zip_des_t *page_zip,
141                             trx_id_t trx_id, mtr_t *mtr);
142 
143 /** Returns the RTREE SPLIT SEQUENCE NUMBER (FIL_RTREE_SPLIT_SEQ_NUM).
144 @param[in]	page	page
145 @return SPLIT SEQUENCE NUMBER */
146 UNIV_INLINE
147 node_seq_t page_get_ssn_id(const page_t *page);
148 
149 /** Sets the RTREE SPLIT SEQUENCE NUMBER field value
150 @param[in,out]	block		page
151 @param[in,out]	page_zip	compressed page whose uncompressed part will
152                                 be updated, or NULL
153 @param[in]	ssn_id		split sequence id
154 @param[in,out]	mtr		mini-transaction */
155 UNIV_INLINE
156 void page_set_ssn_id(buf_block_t *block, page_zip_des_t *page_zip,
157                      node_seq_t ssn_id, mtr_t *mtr);
158 
159 /** Reads the given header field.
160 @param[in]	page	page
161 @param[in]	field	PAGE_N_DIR_SLOTS, ... */
162 UNIV_INLINE
163 ulint page_header_get_field(const page_t *page, ulint field);
164 
165 /** Sets the given header field.
166 @param[in,out]	page		page
167 @param[in,out]	page_zip	compressed page whose uncompressed part will
168                                 be updated, or NULL
169 @param[in]	field		PAGE_N_DIR_SLOTS, ...
170 @param[in]	val		value */
171 UNIV_INLINE
172 void page_header_set_field(page_t *page, page_zip_des_t *page_zip, ulint field,
173                            ulint val);
174 
175 /** Returns the offset stored in the given header field.
176  @return offset from the start of the page, or 0 */
177 UNIV_INLINE
178 ulint page_header_get_offs(const page_t *page, /*!< in: page */
179                            ulint field)        /*!< in: PAGE_FREE, ... */
180     MY_ATTRIBUTE((warn_unused_result));
181 
182 /** Returns the pointer stored in the given header field, or NULL. */
183 #define page_header_get_ptr(page, field)          \
184   (page_header_get_offs(page, field)              \
185        ? page + page_header_get_offs(page, field) \
186        : NULL)
187 
188 /** Sets the pointer stored in the given header field.
189 @param[in,out]	page		page
190 @param[in,out]	page_zip	compressed page whose uncompressed part will
191                                 be updated, or NULL
192 @param[in,out]	field		PAGE_FREE, ...
193 @param[in]	ptr		pointer or NULL */
194 UNIV_INLINE
195 void page_header_set_ptr(page_t *page, page_zip_des_t *page_zip, ulint field,
196                          const byte *ptr);
197 #ifndef UNIV_HOTBACKUP
198 
199 /** Resets the last insert info field in the page header. Writes to mlog about
200 this operation.
201 @param[in]	page		page
202 @param[in,out]	page_zip	compressed page whose uncompressed part will
203                                 be updated, or NULL
204 @param[in]	mtr		mtr */
205 UNIV_INLINE
206 void page_header_reset_last_insert(page_t *page, page_zip_des_t *page_zip,
207                                    mtr_t *mtr);
208 #endif /* !UNIV_HOTBACKUP */
209 
210 /** Gets the offset of the first record on the page.
211  @return offset of the first record in record list, relative from page */
212 UNIV_INLINE
213 ulint page_get_infimum_offset(
214     const page_t *page); /*!< in: page which must have record(s) */
215 /** Gets the offset of the last record on the page.
216  @return offset of the last record in record list, relative from page */
217 UNIV_INLINE
218 ulint page_get_supremum_offset(
219     const page_t *page); /*!< in: page which must have record(s) */
220 #define page_get_infimum_rec(page) ((page) + page_get_infimum_offset(page))
221 #define page_get_supremum_rec(page) ((page) + page_get_supremum_offset(page))
222 
223 /** Returns the nth record of the record list.
224  This is the inverse function of page_rec_get_n_recs_before().
225  @return nth record */
226 const rec_t *page_rec_get_nth_const(const page_t *page, /*!< in: page */
227                                     ulint nth)          /*!< in: nth record */
228     MY_ATTRIBUTE((warn_unused_result));
229 
230 /** Returns the nth record of the record list.
231 This is the inverse function of page_rec_get_n_recs_before().
232 @param[in]	page	page
233 @param[in]	nth	nth record
234 @return nth record */
235 UNIV_INLINE
236 rec_t *page_rec_get_nth(page_t *page, ulint nth)
237     MY_ATTRIBUTE((warn_unused_result));
238 
239 #ifndef UNIV_HOTBACKUP
240 /** Returns the middle record of the records on the page. If there is an
241  even number of records in the list, returns the first record of the
242  upper half-list.
243  @return middle record */
244 UNIV_INLINE
245 rec_t *page_get_middle_rec(page_t *page) /*!< in: page */
246     MY_ATTRIBUTE((warn_unused_result));
247 #endif /* !UNIV_HOTBACKUP */
248 /** Gets the page number.
249  @return page number */
250 UNIV_INLINE
251 page_no_t page_get_page_no(const page_t *page); /*!< in: page */
252 /** Gets the tablespace identifier.
253  @return space id */
254 UNIV_INLINE
255 space_id_t page_get_space_id(const page_t *page); /*!< in: page */
256 
257 /** Gets the space id and page number identifying the page.
258  @return page number */
259 UNIV_INLINE
260 page_id_t page_get_page_id(const page_t *page);
261 
262 /** Gets the number of user records on page (the infimum and supremum records
263  are not user records).
264  @return number of user records */
265 UNIV_INLINE
266 ulint page_get_n_recs(const page_t *page); /*!< in: index page */
267 /** Returns the number of records before the given record in chain.
268  The number includes infimum and supremum records.
269  This is the inverse function of page_rec_get_nth().
270  @return number of records */
271 ulint page_rec_get_n_recs_before(
272     const rec_t *rec); /*!< in: the physical record */
273 /** Gets the number of records in the heap.
274  @return number of user records */
275 UNIV_INLINE
276 ulint page_dir_get_n_heap(const page_t *page); /*!< in: index page */
277 
278 /** Sets the number of records in the heap.
279 @param[in,out]	page		index page
280 @param[in,out]	page_zip	compressed page whose uncompressed part will
281                                 be updated, or NULL. Note that the size of the
282                                 dense page directory in the compressed page
283                                 trailer is n_heap * PAGE_ZIP_DIR_SLOT_SIZE.
284 @param[in]	n_heap		number of records*/
285 UNIV_INLINE
286 void page_dir_set_n_heap(page_t *page, page_zip_des_t *page_zip, ulint n_heap);
287 
288 /** Gets the number of dir slots in directory.
289  @return number of slots */
290 UNIV_INLINE
291 ulint page_dir_get_n_slots(const page_t *page); /*!< in: index page */
292 
293 /** Sets the number of dir slots in directory.
294 @param[in,out]	page		page
295 @param[in,out]	page_zip	compressed page whose uncompressed part will
296                                 be updated, or NULL
297 @param[in]	n_slots		number of slots */
298 UNIV_INLINE
299 void page_dir_set_n_slots(page_t *page, page_zip_des_t *page_zip,
300                           ulint n_slots);
301 
302 #ifdef UNIV_DEBUG
303 /** Gets pointer to nth directory slot.
304 @param[in]	page	index page
305 @param[in]	n	position
306 @return pointer to dir slot */
307 UNIV_INLINE
308 page_dir_slot_t *page_dir_get_nth_slot(const page_t *page, ulint n);
309 #else /* UNIV_DEBUG */
310 #define page_dir_get_nth_slot(page, n) \
311   ((page) + (UNIV_PAGE_SIZE - PAGE_DIR - (n + 1) * PAGE_DIR_SLOT_SIZE))
312 #endif /* UNIV_DEBUG */
313 
314 /** Used to check the consistency of a record on a page.
315  @return true if succeed */
316 UNIV_INLINE
317 ibool page_rec_check(const rec_t *rec); /*!< in: record */
318 /** Gets the record pointed to by a directory slot.
319  @return pointer to record */
320 UNIV_INLINE
321 const rec_t *page_dir_slot_get_rec(
322     const page_dir_slot_t *slot); /*!< in: directory slot */
323 
324 /** This is used to set the record offset in a directory slot.
325 @param[in]	rec	record on the page
326 @param[in]	slot	directory slot */
327 UNIV_INLINE
328 void page_dir_slot_set_rec(page_dir_slot_t *slot, rec_t *rec);
329 
330 /** Gets the number of records owned by a directory slot.
331  @return number of records */
332 UNIV_INLINE
333 ulint page_dir_slot_get_n_owned(
334     const page_dir_slot_t *slot); /*!< in: page directory slot */
335 
336 /** This is used to set the owned records field of a directory slot.
337 @param[in,out]	slot		directory slot
338 @param[in,out]	page_zip	compressed page, or NULL
339 @param[in]	n		number of records owned by the slot */
340 UNIV_INLINE
341 void page_dir_slot_set_n_owned(page_dir_slot_t *slot, page_zip_des_t *page_zip,
342                                ulint n);
343 
344 /** Calculates the space reserved for directory slots of a given
345  number of records. The exact value is a fraction number
346  n * PAGE_DIR_SLOT_SIZE / PAGE_DIR_SLOT_MIN_N_OWNED, and it is
347  rounded upwards to an integer. */
348 UNIV_INLINE
349 ulint page_dir_calc_reserved_space(ulint n_recs); /*!< in: number of records */
350 /** Looks for the directory slot which owns the given record.
351  @return the directory slot number */
352 ulint page_dir_find_owner_slot(
353     const rec_t *rec); /*!< in: the physical record */
354 /** Determine whether the page is in new-style compact format.
355  @return nonzero if the page is in compact format, zero if it is in
356  old-style format */
357 UNIV_INLINE
358 ulint page_is_comp(const page_t *page); /*!< in: index page */
359 /** TRUE if the record is on a page in compact format.
360  @return nonzero if in compact format */
361 UNIV_INLINE
362 ulint page_rec_is_comp(const rec_t *rec); /*!< in: record */
363 /** Returns the heap number of a record.
364  @return heap number */
365 UNIV_INLINE
366 ulint page_rec_get_heap_no(const rec_t *rec); /*!< in: the physical record */
367 /** Determine whether the page is a B-tree leaf.
368  @return true if the page is a B-tree leaf (PAGE_LEVEL = 0) */
369 UNIV_INLINE
370 bool page_is_leaf(const page_t *page) /*!< in: page */
371     MY_ATTRIBUTE((warn_unused_result));
372 /** Determine whether the page is empty.
373  @return true if the page is empty (PAGE_N_RECS = 0) */
374 UNIV_INLINE
375 bool page_is_empty(const page_t *page) /*!< in: page */
376     MY_ATTRIBUTE((warn_unused_result));
377 /** Determine whether a page is an index root page.
378 @param[in]	page	page frame
379 @return true if the page is a root page of an index */
380 UNIV_INLINE
381 bool page_is_root(const page_t *page) MY_ATTRIBUTE((warn_unused_result));
382 /** Determine whether the page contains garbage.
383  @return true if the page contains garbage (PAGE_GARBAGE is not 0) */
384 UNIV_INLINE
385 bool page_has_garbage(const page_t *page) /*!< in: page */
386     MY_ATTRIBUTE((warn_unused_result));
387 
388 /** Gets the pointer to the next record on the page.
389 @param[in]	rec	pointer to record
390 @param[in]	comp	nonzero=compact page layout
391 @return pointer to next record */
392 UNIV_INLINE
393 const rec_t *page_rec_get_next_low(const rec_t *rec, ulint comp);
394 
395 /** Gets the pointer to the next record on the page.
396  @return pointer to next record */
397 UNIV_INLINE
398 rec_t *page_rec_get_next(rec_t *rec); /*!< in: pointer to record */
399 /** Gets the pointer to the next record on the page.
400  @return pointer to next record */
401 UNIV_INLINE
402 const rec_t *page_rec_get_next_const(
403     const rec_t *rec); /*!< in: pointer to record */
404 /** Gets the pointer to the next non delete-marked record on the page.
405  If all subsequent records are delete-marked, then this function
406  will return the supremum record.
407  @return pointer to next non delete-marked record or pointer to supremum */
408 UNIV_INLINE
409 const rec_t *page_rec_get_next_non_del_marked(
410     const rec_t *rec); /*!< in: pointer to record */
411 
412 /** Sets the pointer to the next record on the page.
413 @param[in]	rec	pointer to record, must not be page supremum
414 @param[in]	next	pointer to next record, must not be page infimum */
415 UNIV_INLINE
416 void page_rec_set_next(rec_t *rec, const rec_t *next);
417 
418 /** Gets the pointer to the previous record.
419  @return pointer to previous record */
420 UNIV_INLINE
421 const rec_t *page_rec_get_prev_const(
422     const rec_t *rec); /*!< in: pointer to record, must not be page
423                        infimum */
424 /** Gets the pointer to the previous record.
425  @return pointer to previous record */
426 UNIV_INLINE
427 rec_t *page_rec_get_prev(rec_t *rec); /*!< in: pointer to record,
428                                       must not be page infimum */
429 /** TRUE if the record is a user record on the page.
430 @param[in]  offset      record offset on page
431 @return true if a user record */
432 UNIV_INLINE
433 ibool page_rec_is_user_rec_low(ulint offset);
434 
435 /** TRUE if the record is the supremum record on a page.
436 @param[in]  offset      record offset on page
437 @return true if the supremum record */
438 UNIV_INLINE
439 ibool page_rec_is_supremum_low(ulint offset);
440 
441 /** TRUE if the record is the infimum record on a page.
442 @param[in]  offset      record offset on page
443 @return true if the infimum record */
444 UNIV_INLINE
445 ibool page_rec_is_infimum_low(ulint offset);
446 
447 /** TRUE if the record is a user record on the page.
448  @return true if a user record */
449 UNIV_INLINE
450 ibool page_rec_is_user_rec(const rec_t *rec) /*!< in: record */
451     MY_ATTRIBUTE((warn_unused_result));
452 /** TRUE if the record is the supremum record on a page.
453  @return true if the supremum record */
454 UNIV_INLINE
455 ibool page_rec_is_supremum(const rec_t *rec) /*!< in: record */
456     MY_ATTRIBUTE((warn_unused_result));
457 
458 /** TRUE if the record is the infimum record on a page.
459  @return true if the infimum record */
460 UNIV_INLINE
461 ibool page_rec_is_infimum(const rec_t *rec) /*!< in: record */
462     MY_ATTRIBUTE((warn_unused_result));
463 
464 /** true if the record is the first user record on a page.
465  @return true if the first user record */
466 UNIV_INLINE
467 bool page_rec_is_first(const rec_t *rec,   /*!< in: record */
468                        const page_t *page) /*!< in: page */
469     MY_ATTRIBUTE((warn_unused_result));
470 
471 /** true if the record is the second user record on a page.
472  @return true if the second user record */
473 UNIV_INLINE
474 bool page_rec_is_second(const rec_t *rec,   /*!< in: record */
475                         const page_t *page) /*!< in: page */
476     MY_ATTRIBUTE((warn_unused_result));
477 
478 /** true if the record is the last user record on a page.
479  @return true if the last user record */
480 UNIV_INLINE
481 bool page_rec_is_last(const rec_t *rec,   /*!< in: record */
482                       const page_t *page) /*!< in: page */
483     MY_ATTRIBUTE((warn_unused_result));
484 
485 /** true if distance between the records (measured in number of times we have to
486 move to the next record) is at most the specified value
487 @param[in]  left_rec    lefter record
488 @param[in]  right_rec   righter record
489 @param[in]  val         specified value to compare
490 @return true if the distance is smaller than the value */
491 UNIV_INLINE
492 bool page_rec_distance_is_at_most(const rec_t *left_rec, const rec_t *right_rec,
493                                   ulint val) MY_ATTRIBUTE((warn_unused_result));
494 
495 /** true if the record is the second last user record on a page.
496  @return true if the second last user record */
497 UNIV_INLINE
498 bool page_rec_is_second_last(const rec_t *rec,   /*!< in: record */
499                              const page_t *page) /*!< in: page */
500     MY_ATTRIBUTE((warn_unused_result));
501 
502 /** Looks for the record which owns the given record.
503  @return the owner record */
504 UNIV_INLINE
505 rec_t *page_rec_find_owner_rec(rec_t *rec); /*!< in: the physical record */
506 #ifndef UNIV_HOTBACKUP
507 
508 /** Write a 32-bit field in a data dictionary record.
509 @param[in,out]	rec	record to update
510 @param[in]	i	index of the field to update
511 @param[in]	val	value to write
512 @param[in,out]	mtr	mini-transaction */
513 UNIV_INLINE
514 void page_rec_write_field(rec_t *rec, ulint i, ulint val, mtr_t *mtr);
515 #endif /* !UNIV_HOTBACKUP */
516 
517 /** Returns the maximum combined size of records which can be inserted on top
518 of record heap.
519 @param[in]	page	index page
520 @param[in]	n_recs	number of records
521 @return maximum combined size for inserted records */
522 UNIV_INLINE
523 ulint page_get_max_insert_size(const page_t *page, ulint n_recs);
524 
525 /** Returns the maximum combined size of records which can be inserted on top
526 of record heap if page is first reorganized.
527 @param[in]	page	index page
528 @param[in]	n_recs	number of records
529 @return maximum combined size for inserted records */
530 UNIV_INLINE
531 ulint page_get_max_insert_size_after_reorganize(const page_t *page,
532                                                 ulint n_recs);
533 
534 /** Calculates free space if a page is emptied.
535 @param[in]  comp    nonzero=compact page format
536 @return free space */
537 UNIV_INLINE
538 ulint page_get_free_space_of_empty(ulint comp);
539 
540 /** Returns the base extra size of a physical record.  This is the
541  size of the fixed header, independent of the record size.
542  @return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */
543 UNIV_INLINE
544 ulint page_rec_get_base_extra_size(
545     const rec_t *rec); /*!< in: physical record */
546 /** Returns the sum of the sizes of the records in the record list
547  excluding the infimum and supremum records.
548  @return data in bytes */
549 UNIV_INLINE
550 ulint page_get_data_size(const page_t *page); /*!< in: index page */
551 
552 /** Allocates a block of memory from the head of the free list of an index
553 page.
554 @param[in,out]	page		index page
555 @param[in,out]	page_zip	compressed page with enough space available
556                                 for inserting the record, or NULL
557 @param[in]	next_rec	pointer to the new head of the free record
558                                 list
559 @param[in]	need		number of bytes allocated */
560 UNIV_INLINE
561 void page_mem_alloc_free(page_t *page, page_zip_des_t *page_zip,
562                          rec_t *next_rec, ulint need);
563 
564 /** Allocates a block of memory from the heap of an index page.
565  @return pointer to start of allocated buffer, or NULL if allocation fails */
566 byte *page_mem_alloc_heap(
567     page_t *page,             /*!< in/out: index page */
568     page_zip_des_t *page_zip, /*!< in/out: compressed page with enough
569                              space available for inserting the record,
570                              or NULL */
571     ulint need,               /*!< in: total number of bytes needed */
572     ulint *heap_no);          /*!< out: this contains the heap number
573                              of the allocated record
574                              if allocation succeeds */
575 
576 /** Puts a record to free list.
577 @param[in,out]	page		index page
578 @param[in,out]	page_zip	compressed page, or NULL
579 @param[in]	rec		pointer to the (origin of) record
580 @param[in]	index		index of rec
581 @param[in]	offsets		array returned by rec_get_offsets() */
582 UNIV_INLINE
583 void page_mem_free(page_t *page, page_zip_des_t *page_zip, rec_t *rec,
584                    const dict_index_t *index, const ulint *offsets);
585 
586 /** Create an uncompressed B-tree or R-tree or SDI index page.
587 @param[in]	block		a buffer block where the page is created
588 @param[in]	mtr		mini-transaction handle
589 @param[in]	comp		nonzero=compact page format
590 @param[in]	page_type	page type
591 @return pointer to the page */
592 page_t *page_create(buf_block_t *block, mtr_t *mtr, ulint comp,
593                     page_type_t page_type);
594 
595 /** Create a compressed B-tree index page.
596 @param[in,out]	block		buffer frame where the page is created
597 @param[in]	index		index of the page, or NULL when applying
598                                 TRUNCATE log record during recovery
599 @param[in]	level		the B-tree level of the page
600 @param[in]	max_trx_id	PAGE_MAX_TRX_ID
601 @param[in]	mtr		mini-transaction handle
602 @param[in]	page_type	page_type to be created. Only FIL_PAGE_INDEX,
603                                 FIL_PAGE_RTREE, FIL_PAGE_SDI allowed */
604 page_t *page_create_zip(buf_block_t *block, dict_index_t *index, ulint level,
605                         trx_id_t max_trx_id, mtr_t *mtr, page_type_t page_type);
606 
607 /** Empty a previously created B-tree index page. */
608 void page_create_empty(buf_block_t *block,  /*!< in/out: B-tree block */
609                        dict_index_t *index, /*!< in: the index of the page */
610                        mtr_t *mtr);         /*!< in/out: mini-transaction */
611 /** Differs from page_copy_rec_list_end, because this function does not
612  touch the lock table and max trx id on page or compress the page.
613 
614  IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
615  if new_block is a compressed leaf page in a secondary index.
616  This has to be done either within the same mini-transaction,
617  or by invoking ibuf_reset_free_bits() before mtr_commit(). */
618 void page_copy_rec_list_end_no_locks(
619     buf_block_t *new_block, /*!< in: index page to copy to */
620     buf_block_t *block,     /*!< in: index page of rec */
621     rec_t *rec,             /*!< in: record on page */
622     dict_index_t *index,    /*!< in: record descriptor */
623     mtr_t *mtr);            /*!< in: mtr */
624 /** Copies records from page to new_page, from the given record onward,
625  including that record. Infimum and supremum records are not copied.
626  The records are copied to the start of the record list on new_page.
627 
628  IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
629  if new_block is a compressed leaf page in a secondary index.
630  This has to be done either within the same mini-transaction,
631  or by invoking ibuf_reset_free_bits() before mtr_commit().
632 
633  @return pointer to the original successor of the infimum record on
634  new_page, or NULL on zip overflow (new_block will be decompressed) */
635 rec_t *page_copy_rec_list_end(
636     buf_block_t *new_block, /*!< in/out: index page to copy to */
637     buf_block_t *block,     /*!< in: index page containing rec */
638     rec_t *rec,             /*!< in: record on page */
639     dict_index_t *index,    /*!< in: record descriptor */
640     mtr_t *mtr);            /*!< in: mtr */
641 /** Copies records from page to new_page, up to the given record, NOT
642  including that record. Infimum and supremum records are not copied.
643  The records are copied to the end of the record list on new_page.
644 
645  IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
646  if new_block is a compressed leaf page in a secondary index.
647  This has to be done either within the same mini-transaction,
648  or by invoking ibuf_reset_free_bits() before mtr_commit().
649 
650  @return pointer to the original predecessor of the supremum record on
651  new_page, or NULL on zip overflow (new_block will be decompressed) */
652 rec_t *page_copy_rec_list_start(
653     buf_block_t *new_block, /*!< in/out: index page to copy to */
654     buf_block_t *block,     /*!< in: index page containing rec */
655     rec_t *rec,             /*!< in: record on page */
656     dict_index_t *index,    /*!< in: record descriptor */
657     mtr_t *mtr);            /*!< in: mtr */
658 /** Deletes records from a page from a given record onward, including that
659  record. The infimum and supremum records are not deleted. */
660 void page_delete_rec_list_end(
661     rec_t *rec,          /*!< in: pointer to record on page */
662     buf_block_t *block,  /*!< in: buffer block of the page */
663     dict_index_t *index, /*!< in: record descriptor */
664     ulint n_recs,        /*!< in: number of records to delete,
665                          or ULINT_UNDEFINED if not known */
666     ulint size,          /*!< in: the sum of the sizes of the
667                          records in the end of the chain to
668                          delete, or ULINT_UNDEFINED if not known */
669     mtr_t *mtr);         /*!< in: mtr */
670 /** Deletes records from page, up to the given record, NOT including
671  that record. Infimum and supremum records are not deleted. */
672 void page_delete_rec_list_start(
673     rec_t *rec,          /*!< in: record on page */
674     buf_block_t *block,  /*!< in: buffer block of the page */
675     dict_index_t *index, /*!< in: record descriptor */
676     mtr_t *mtr);         /*!< in: mtr */
677 /** Moves record list end to another page. Moved records include
678  split_rec.
679 
680  IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
681  if new_block is a compressed leaf page in a secondary index.
682  This has to be done either within the same mini-transaction,
683  or by invoking ibuf_reset_free_bits() before mtr_commit().
684 
685  @return true on success; false on compression failure (new_block will
686  be decompressed) */
687 ibool page_move_rec_list_end(
688     buf_block_t *new_block, /*!< in/out: index page where to move */
689     buf_block_t *block,     /*!< in: index page from where to move */
690     rec_t *split_rec,       /*!< in: first record to move */
691     dict_index_t *index,    /*!< in: record descriptor */
692     mtr_t *mtr);            /*!< in: mtr */
693 /** Moves record list start to another page. Moved records do not include
694  split_rec.
695 
696  IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
697  if new_block is a compressed leaf page in a secondary index.
698  This has to be done either within the same mini-transaction,
699  or by invoking ibuf_reset_free_bits() before mtr_commit().
700 
701  @return true on success; false on compression failure */
702 ibool page_move_rec_list_start(
703     buf_block_t *new_block, /*!< in/out: index page where to move */
704     buf_block_t *block,     /*!< in/out: page containing split_rec */
705     rec_t *split_rec,       /*!< in: first record not to move */
706     dict_index_t *index,    /*!< in: record descriptor */
707     mtr_t *mtr);            /*!< in: mtr */
708 /** Splits a directory slot which owns too many records. */
709 void page_dir_split_slot(
710     page_t *page,             /*!< in: index page */
711     page_zip_des_t *page_zip, /*!< in/out: compressed page whose
712                              uncompressed part will be written, or NULL */
713     ulint slot_no);           /*!< in: the directory slot */
714 /** Tries to balance the given directory slot with too few records
715  with the upper neighbor, so that there are at least the minimum number
716  of records owned by the slot; this may result in the merging of
717  two slots. */
718 void page_dir_balance_slot(
719     page_t *page,             /*!< in/out: index page */
720     page_zip_des_t *page_zip, /*!< in/out: compressed page, or NULL */
721     ulint slot_no);           /*!< in: the directory slot */
722 /** Parses a log record of a record list end or start deletion.
723  @return end of log record or NULL */
724 byte *page_parse_delete_rec_list(
725     mlog_id_t type,      /*!< in: MLOG_LIST_END_DELETE,
726                          MLOG_LIST_START_DELETE,
727                          MLOG_COMP_LIST_END_DELETE or
728                          MLOG_COMP_LIST_START_DELETE */
729     byte *ptr,           /*!< in: buffer */
730     byte *end_ptr,       /*!< in: buffer end */
731     buf_block_t *block,  /*!< in/out: buffer block or NULL */
732     dict_index_t *index, /*!< in: record descriptor */
733     mtr_t *mtr);         /*!< in: mtr or NULL */
734 
735 /** Parses a redo log record of creating a page.
736 @param[in,out]	block		buffer block, or NULL
737 @param[in]	comp		nonzero=compact page format
738 @param[in]	page_type	page type */
739 void page_parse_create(buf_block_t *block, ulint comp, page_type_t page_type);
740 #ifndef UNIV_HOTBACKUP
741 /** Prints record contents including the data relevant only in
742  the index page context. */
743 void page_rec_print(const rec_t *rec,      /*!< in: physical record */
744                     const ulint *offsets); /*!< in: record descriptor */
745 #ifdef UNIV_BTR_PRINT
746 /** This is used to print the contents of the directory for
747  debugging purposes. */
748 void page_dir_print(page_t *page, /*!< in: index page */
749                     ulint pr_n);  /*!< in: print n first and n last entries */
750 /** This is used to print the contents of the page record list for
751  debugging purposes. */
752 void page_print_list(
753     buf_block_t *block,  /*!< in: index page */
754     dict_index_t *index, /*!< in: dictionary index of the page */
755     ulint pr_n);         /*!< in: print n first and n last entries */
756 /** Prints the info in a page header. */
757 void page_header_print(const page_t *page); /*!< in: index page */
758 /** This is used to print the contents of the page for
759  debugging purposes. */
760 void page_print(buf_block_t *block,  /*!< in: index page */
761                 dict_index_t *index, /*!< in: dictionary index of the page */
762                 ulint dn,            /*!< in: print dn first and last entries
763                                      in directory */
764                 ulint rn);           /*!< in: print rn first and last records
765                                      in directory */
766 #endif                               /* UNIV_BTR_PRINT */
767 #endif                               /* !UNIV_HOTBACKUP */
768 /** The following is used to validate a record on a page. This function
769  differs from rec_validate as it can also check the n_owned field and
770  the heap_no field.
771  @return true if ok */
772 ibool page_rec_validate(
773     const rec_t *rec,      /*!< in: physical record */
774     const ulint *offsets); /*!< in: array returned by rec_get_offsets() */
775 #ifdef UNIV_DEBUG
776 /** Checks that the first directory slot points to the infimum record and
777  the last to the supremum. This function is intended to track if the
778  bug fixed in 4.0.14 has caused corruption to users' databases. */
779 void page_check_dir(const page_t *page); /*!< in: index page */
780 #endif                                   /* UNIV_DEBUG */
781 /** This function checks the consistency of an index page when we do not
782  know the index. This is also resilient so that this should never crash
783  even if the page is total garbage.
784  @return true if ok */
785 ibool page_simple_validate_old(
786     const page_t *page); /*!< in: index page in ROW_FORMAT=REDUNDANT */
787 /** This function checks the consistency of an index page when we do not
788  know the index. This is also resilient so that this should never crash
789  even if the page is total garbage.
790  @return true if ok */
791 ibool page_simple_validate_new(
792     const page_t *page); /*!< in: index page in ROW_FORMAT!=REDUNDANT */
793 /** This function checks the consistency of an index page.
794  @return true if ok */
795 ibool page_validate(
796     const page_t *page,   /*!< in: index page */
797     dict_index_t *index); /*!< in: data dictionary index containing
798                           the page record type definition */
799 /** Looks in the page record list for a record with the given heap number.
800  @return record, NULL if not found */
801 const rec_t *page_find_rec_with_heap_no(
802     const page_t *page, /*!< in: index page */
803     ulint heap_no);     /*!< in: heap number */
804 /** Get the last non-delete-marked record on a page.
805 @param[in]	page	index tree leaf page
806 @return the last record, not delete-marked
807 @retval infimum record if all records are delete-marked */
808 const rec_t *page_find_rec_last_not_deleted(const page_t *page);
809 
810 /** Issue a warning when the checksum that is stored in the page is valid,
811 but different than the global setting innodb_checksum_algorithm.
812 @param[in]	curr_algo	current checksum algorithm
813 @param[in]	page_checksum	page valid checksum
814 @param[in]	page_id		page identifier */
815 void page_warn_strict_checksum(srv_checksum_algorithm_t curr_algo,
816                                srv_checksum_algorithm_t page_checksum,
817                                const page_id_t &page_id);
818 
819 /** Check that a page_size is correct for InnoDB.
820 If correct, set the associated page_size_shift which is the power of 2
821 for this page size.
822 @param[in]	page_size	Page Size to evaluate
823 @return an associated page_size_shift if valid, 0 if invalid. */
824 inline ulong page_size_validate(ulong page_size);
825 
826 /** This function checks if the page in which record is present is a
827 non-leaf node of a spatial index.
828 param[in]       rec     Btree record
829 param[in]       index   index
830 @return TRUE if ok */
831 bool page_is_spatial_non_leaf(const rec_t *rec, dict_index_t *index);
832 
833 #ifdef UNIV_MATERIALIZE
834 #undef UNIV_INLINE
835 #define UNIV_INLINE UNIV_INLINE_ORIGINAL
836 #endif
837 
838 #include "page0page.ic"
839 
840 #endif
841