1
/******************************************************
4
(c) 1994-1996 Innobase Oy
6
Created 2/2/1994 Heikki Tuuri
7
*******************************************************/
13
#ifdef UNIV_MATERIALIZE
18
/****************************************************************
19
Gets the start of a page. */
24
/* out: start of the page */
25
void* ptr) /* in: pointer to page frame */
27
return((page_t*) ut_align_down(ptr, UNIV_PAGE_SIZE));
29
/****************************************************************
30
Gets the offset within a page. */
35
/* out: offset from the start of the page */
36
const void* ptr) /* in: pointer to page frame */
38
return(ut_align_offset(ptr, UNIV_PAGE_SIZE));
40
/*****************************************************************
41
Returns the max trx id field value. */
46
page_t* page) /* in: page */
50
return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID));
53
/*****************************************************************
54
Sets the max trx id field value if trx_id is bigger than the previous
58
page_update_max_trx_id(
59
/*===================*/
60
page_t* page, /* in: page */
61
dulint trx_id) /* in: transaction id */
65
if (ut_dulint_cmp(page_get_max_trx_id(page), trx_id) < 0) {
67
page_set_max_trx_id(page, trx_id);
71
/*****************************************************************
72
Reads the given header field. */
75
page_header_get_field(
76
/*==================*/
77
page_t* page, /* in: page */
78
ulint field) /* in: PAGE_LEVEL, ... */
81
ut_ad(field <= PAGE_INDEX_ID);
83
return(mach_read_from_2(page + PAGE_HEADER + field));
86
/*****************************************************************
87
Sets the given header field. */
90
page_header_set_field(
91
/*==================*/
92
page_t* page, /* in: page */
93
ulint field, /* in: PAGE_LEVEL, ... */
94
ulint val) /* in: value */
97
ut_ad(field <= PAGE_N_RECS);
98
ut_ad(field == PAGE_N_HEAP || val < UNIV_PAGE_SIZE);
99
ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < UNIV_PAGE_SIZE);
101
mach_write_to_2(page + PAGE_HEADER + field, val);
104
/*****************************************************************
105
Returns the pointer stored in the given header field. */
110
/* out: pointer or NULL */
111
page_t* page, /* in: page */
112
ulint field) /* in: PAGE_FREE, ... */
117
ut_ad((field == PAGE_FREE)
118
|| (field == PAGE_LAST_INSERT)
119
|| (field == PAGE_HEAP_TOP));
121
offs = page_header_get_field(page, field);
123
ut_ad((field != PAGE_HEAP_TOP) || offs);
133
/*****************************************************************
134
Sets the pointer stored in the given header field. */
139
page_t* page, /* in: page */
140
ulint field, /* in: PAGE_FREE, ... */
141
byte* ptr) /* in: pointer or NULL*/
146
ut_ad((field == PAGE_FREE)
147
|| (field == PAGE_LAST_INSERT)
148
|| (field == PAGE_HEAP_TOP));
156
ut_ad((field != PAGE_HEAP_TOP) || offs);
158
page_header_set_field(page, field, offs);
161
/*****************************************************************
162
Resets the last insert info field in the page header. Writes to mlog
163
about this operation. */
166
page_header_reset_last_insert(
167
/*==========================*/
168
page_t* page, /* in: page */
169
mtr_t* mtr) /* in: mtr */
173
mlog_write_ulint(page + PAGE_HEADER + PAGE_LAST_INSERT, 0,
177
/****************************************************************
178
Determine whether the page is in new-style compact format. */
183
/* out: nonzero if the page is in compact
184
format, zero if it is in old-style format */
185
page_t* page) /* in: index page */
187
return(UNIV_EXPECT(page_header_get_field(page, PAGE_N_HEAP) & 0x8000,
191
/****************************************************************
192
TRUE if the record is on a page in compact format. */
197
/* out: nonzero if in compact format */
198
const rec_t* rec) /* in: record */
200
return(page_is_comp(page_align((rec_t*) rec)));
203
/****************************************************************
204
Gets the first record on the page. */
207
page_get_infimum_rec(
208
/*=================*/
209
/* out: the first record in record list */
210
page_t* page) /* in: page which must have record(s) */
214
if (page_is_comp(page)) {
215
return(page + PAGE_NEW_INFIMUM);
217
return(page + PAGE_OLD_INFIMUM);
221
/****************************************************************
222
Gets the last record on the page. */
225
page_get_supremum_rec(
226
/*==================*/
227
/* out: the last record in record list */
228
page_t* page) /* in: page which must have record(s) */
232
if (page_is_comp(page)) {
233
return(page + PAGE_NEW_SUPREMUM);
235
return(page + PAGE_OLD_SUPREMUM);
239
/****************************************************************
240
TRUE if the record is a user record on the page. */
243
page_rec_is_user_rec_low(
244
/*=====================*/
245
/* out: TRUE if a user record */
246
ulint offset) /* in: record offset on page */
248
ut_ad(offset >= PAGE_NEW_INFIMUM);
249
#if PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM
250
# error "PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM"
252
#if PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM
253
# error "PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM"
255
#if PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM
256
# error "PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM"
258
#if PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM
259
# error "PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM"
261
#if PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END
262
# error "PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END"
264
#if PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END
265
# error "PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END"
267
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
269
return(UNIV_LIKELY(offset != PAGE_NEW_SUPREMUM)
270
&& UNIV_LIKELY(offset != PAGE_NEW_INFIMUM)
271
&& UNIV_LIKELY(offset != PAGE_OLD_INFIMUM)
272
&& UNIV_LIKELY(offset != PAGE_OLD_SUPREMUM));
275
/****************************************************************
276
TRUE if the record is the supremum record on a page. */
279
page_rec_is_supremum_low(
280
/*=====================*/
281
/* out: TRUE if the supremum record */
282
ulint offset) /* in: record offset on page */
284
ut_ad(offset >= PAGE_NEW_INFIMUM);
285
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
287
return(UNIV_UNLIKELY(offset == PAGE_NEW_SUPREMUM)
288
|| UNIV_UNLIKELY(offset == PAGE_OLD_SUPREMUM));
291
/****************************************************************
292
TRUE if the record is the infimum record on a page. */
295
page_rec_is_infimum_low(
296
/*====================*/
297
/* out: TRUE if the infimum record */
298
ulint offset) /* in: record offset on page */
300
ut_ad(offset >= PAGE_NEW_INFIMUM);
301
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
303
return(UNIV_UNLIKELY(offset == PAGE_NEW_INFIMUM)
304
|| UNIV_UNLIKELY(offset == PAGE_OLD_INFIMUM));
307
/****************************************************************
308
TRUE if the record is a user record on the page. */
311
page_rec_is_user_rec(
312
/*=================*/
313
/* out: TRUE if a user record */
314
const rec_t* rec) /* in: record */
316
return(page_rec_is_user_rec_low(page_offset(rec)));
319
/****************************************************************
320
TRUE if the record is the supremum record on a page. */
323
page_rec_is_supremum(
324
/*=================*/
325
/* out: TRUE if the supremum record */
326
const rec_t* rec) /* in: record */
328
return(page_rec_is_supremum_low(page_offset(rec)));
331
/****************************************************************
332
TRUE if the record is the infimum record on a page. */
337
/* out: TRUE if the infimum record */
338
const rec_t* rec) /* in: record */
340
return(page_rec_is_infimum_low(page_offset(rec)));
343
/*****************************************************************
344
Compares a data tuple to a physical record. Differs from the function
345
cmp_dtuple_rec_with_match in the way that the record must reside on an
346
index page, and also page infimum and supremum records can be given in
347
the parameter rec. These are considered as the negative infinity and
348
the positive infinity in the alphabetical order. */
351
page_cmp_dtuple_rec_with_match(
352
/*===========================*/
353
/* out: 1, 0, -1, if dtuple is greater, equal,
354
less than rec, respectively, when only the
355
common first fields are compared */
356
dtuple_t* dtuple, /* in: data tuple */
357
rec_t* rec, /* in: physical record on a page; may also
358
be page infimum or supremum, in which case
359
matched-parameter values below are not
361
const ulint* offsets,/* in: array returned by rec_get_offsets() */
362
ulint* matched_fields, /* in/out: number of already completely
363
matched fields; when function returns
364
contains the value for current comparison */
365
ulint* matched_bytes) /* in/out: number of already matched
366
bytes within the first field not completely
367
matched; when function returns contains the
368
value for current comparison */
372
ut_ad(dtuple_check_typed(dtuple));
373
ut_ad(rec_offs_validate(rec, NULL, offsets));
374
ut_ad(!rec_offs_comp(offsets) == !page_rec_is_comp(rec));
376
rec_offset = page_offset(rec);
378
if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_INFIMUM)
379
|| UNIV_UNLIKELY(rec_offset == PAGE_OLD_INFIMUM)) {
382
if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_SUPREMUM)
383
|| UNIV_UNLIKELY(rec_offset == PAGE_OLD_SUPREMUM)) {
387
return(cmp_dtuple_rec_with_match(dtuple, rec, offsets,
392
/*****************************************************************
393
Gets the number of user records on page (infimum and supremum records
394
are not user records). */
399
/* out: number of user records */
400
page_t* page) /* in: index page */
402
return(page_header_get_field(page, PAGE_N_RECS));
405
/*****************************************************************
406
Gets the number of dir slots in directory. */
409
page_dir_get_n_slots(
410
/*=================*/
411
/* out: number of slots */
412
page_t* page) /* in: index page */
414
return(page_header_get_field(page, PAGE_N_DIR_SLOTS));
416
/*****************************************************************
417
Sets the number of dir slots in directory. */
420
page_dir_set_n_slots(
421
/*=================*/
422
/* out: number of slots */
423
page_t* page, /* in: index page */
424
ulint n_slots)/* in: number of slots */
426
page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots);
429
/*****************************************************************
430
Gets the number of records in the heap. */
435
/* out: number of user records */
436
page_t* page) /* in: index page */
438
return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
441
/*****************************************************************
442
Sets the number of records in the heap. */
447
page_t* page, /* in: index page */
448
ulint n_heap) /* in: number of records */
450
ut_ad(n_heap < 0x8000);
452
page_header_set_field(page, PAGE_N_HEAP, n_heap
454
& page_header_get_field(page, PAGE_N_HEAP)));
457
/*****************************************************************
458
Gets pointer to nth directory slot. */
461
page_dir_get_nth_slot(
462
/*==================*/
463
/* out: pointer to dir slot */
464
page_t* page, /* in: index page */
465
ulint n) /* in: position */
467
ut_ad(page_dir_get_n_slots(page) > n);
469
return(page + UNIV_PAGE_SIZE - PAGE_DIR
470
- (n + 1) * PAGE_DIR_SLOT_SIZE);
473
/******************************************************************
474
Used to check the consistency of a record on a page. */
479
/* out: TRUE if succeed */
480
rec_t* rec) /* in: record */
486
page = buf_frame_align(rec);
488
ut_a(rec <= page_header_get_ptr(page, PAGE_HEAP_TOP));
489
ut_a(rec >= page + PAGE_DATA);
494
/*******************************************************************
495
Gets the record pointed to by a directory slot. */
498
page_dir_slot_get_rec(
499
/*==================*/
500
/* out: pointer to record */
501
page_dir_slot_t* slot) /* in: directory slot */
503
return(buf_frame_align(slot) + mach_read_from_2(slot));
506
/*******************************************************************
507
This is used to set the record offset in a directory slot. */
510
page_dir_slot_set_rec(
511
/*==================*/
512
page_dir_slot_t* slot, /* in: directory slot */
513
rec_t* rec) /* in: record on the page */
515
ut_ad(page_rec_check(rec));
517
mach_write_to_2(slot, page_offset(rec));
520
/*******************************************************************
521
Gets the number of records owned by a directory slot. */
524
page_dir_slot_get_n_owned(
525
/*======================*/
526
/* out: number of records */
527
page_dir_slot_t* slot) /* in: page directory slot */
529
rec_t* rec = page_dir_slot_get_rec(slot);
530
return(rec_get_n_owned(rec, page_rec_is_comp(rec)));
533
/*******************************************************************
534
This is used to set the owned records field of a directory slot. */
537
page_dir_slot_set_n_owned(
538
/*======================*/
539
page_dir_slot_t* slot, /* in: directory slot */
540
ulint n) /* in: number of records owned
543
rec_t* rec = page_dir_slot_get_rec(slot);
544
rec_set_n_owned(rec, page_rec_is_comp(rec), n);
547
/****************************************************************
548
Calculates the space reserved for directory slots of a given number of
549
records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE /
550
PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */
553
page_dir_calc_reserved_space(
554
/*=========================*/
555
ulint n_recs) /* in: number of records */
557
return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1)
558
/ PAGE_DIR_SLOT_MIN_N_OWNED);
561
/****************************************************************
562
Gets the pointer to the next record on the page. */
567
/* out: pointer to next record */
568
rec_t* rec) /* in: pointer to record */
573
ut_ad(page_rec_check(rec));
575
page = page_align(rec);
577
offs = rec_get_next_offs(rec, page_is_comp(page));
579
if (UNIV_UNLIKELY(offs >= UNIV_PAGE_SIZE)) {
581
"InnoDB: Next record offset is nonsensical %lu"
582
" in record at offset %lu\n"
583
"InnoDB: rec address %p, first buffer frame %p\n"
584
"InnoDB: buffer pool high end %p, buf fix count %lu\n",
585
(ulong)offs, (ulong)(rec - page),
586
(void*) rec, (void*) buf_pool->frame_zero,
587
(void*) buf_pool->high_end,
588
(ulong) buf_block_align(rec)->buf_fix_count);
589
buf_page_print(page);
594
if (UNIV_UNLIKELY(offs == 0)) {
602
/****************************************************************
603
Sets the pointer to the next record on the page. */
608
rec_t* rec, /* in: pointer to record, must not be page supremum */
609
rec_t* next) /* in: pointer to next record, must not be page
615
ut_ad(page_rec_check(rec));
616
ut_ad(!page_rec_is_supremum(rec));
617
page = page_align(rec);
620
ut_ad(!page_rec_is_infimum(next));
621
ut_ad(page == page_align(next));
622
offs = (ulint) (next - page);
627
rec_set_next_offs(rec, page_is_comp(page), offs);
630
/****************************************************************
631
Gets the pointer to the previous record. */
636
/* out: pointer to previous record */
637
rec_t* rec) /* in: pointer to record, must not be page
640
page_dir_slot_t* slot;
643
rec_t* prev_rec = NULL;
646
ut_ad(page_rec_check(rec));
648
page = page_align(rec);
650
ut_ad(!page_rec_is_infimum(rec));
652
slot_no = page_dir_find_owner_slot(rec);
656
slot = page_dir_get_nth_slot(page, slot_no - 1);
658
rec2 = page_dir_slot_get_rec(slot);
660
while (rec != rec2) {
662
rec2 = page_rec_get_next(rec2);
670
/*******************************************************************
671
Looks for the record which owns the given record. */
674
page_rec_find_owner_rec(
675
/*====================*/
676
/* out: the owner record */
677
rec_t* rec) /* in: the physical record */
679
ut_ad(page_rec_check(rec));
681
if (page_rec_is_comp(rec)) {
682
while (rec_get_n_owned(rec, TRUE) == 0) {
683
rec = page_rec_get_next(rec);
686
while (rec_get_n_owned(rec, FALSE) == 0) {
687
rec = page_rec_get_next(rec);
694
/****************************************************************
695
Returns the sum of the sizes of the records in the record list, excluding
696
the infimum and supremum records. */
701
/* out: data in bytes */
702
page_t* page) /* in: index page */
706
ret = (ulint)(page_header_get_field(page, PAGE_HEAP_TOP)
707
- (page_is_comp(page)
708
? PAGE_NEW_SUPREMUM_END
709
: PAGE_OLD_SUPREMUM_END)
710
- page_header_get_field(page, PAGE_GARBAGE));
712
ut_ad(ret < UNIV_PAGE_SIZE);
717
/*****************************************************************
718
Calculates free space if a page is emptied. */
721
page_get_free_space_of_empty(
722
/*=========================*/
723
/* out: free space */
724
ulint comp) /* in: nonzero=compact page layout */
726
if (UNIV_LIKELY(comp)) {
727
return((ulint)(UNIV_PAGE_SIZE
728
- PAGE_NEW_SUPREMUM_END
730
- 2 * PAGE_DIR_SLOT_SIZE));
733
return((ulint)(UNIV_PAGE_SIZE
734
- PAGE_OLD_SUPREMUM_END
736
- 2 * PAGE_DIR_SLOT_SIZE));
739
/****************************************************************
740
Each user record on a page, and also the deleted user records in the heap
741
takes its size plus the fraction of the dir cell size /
742
PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the
743
value of page_get_free_space_of_empty, the insert is impossible, otherwise
744
it is allowed. This function returns the maximum combined size of records
745
which can be inserted on top of the record heap. */
748
page_get_max_insert_size(
749
/*=====================*/
750
/* out: maximum combined size for inserted records */
751
page_t* page, /* in: index page */
752
ulint n_recs) /* in: number of records */
757
if (page_is_comp(page)) {
758
occupied = page_header_get_field(page, PAGE_HEAP_TOP)
759
- PAGE_NEW_SUPREMUM_END
760
+ page_dir_calc_reserved_space(
761
n_recs + page_dir_get_n_heap(page) - 2);
763
free_space = page_get_free_space_of_empty(TRUE);
765
occupied = page_header_get_field(page, PAGE_HEAP_TOP)
766
- PAGE_OLD_SUPREMUM_END
767
+ page_dir_calc_reserved_space(
768
n_recs + page_dir_get_n_heap(page) - 2);
770
free_space = page_get_free_space_of_empty(FALSE);
773
/* Above the 'n_recs +' part reserves directory space for the new
774
inserted records; the '- 2' excludes page infimum and supremum
777
if (occupied > free_space) {
782
return(free_space - occupied);
785
/****************************************************************
786
Returns the maximum combined size of records which can be inserted on top
787
of the record heap if a page is first reorganized. */
790
page_get_max_insert_size_after_reorganize(
791
/*======================================*/
792
/* out: maximum combined size for inserted records */
793
page_t* page, /* in: index page */
794
ulint n_recs) /* in: number of records */
799
occupied = page_get_data_size(page)
800
+ page_dir_calc_reserved_space(n_recs + page_get_n_recs(page));
802
free_space = page_get_free_space_of_empty(page_is_comp(page));
804
if (occupied > free_space) {
809
return(free_space - occupied);
812
/****************************************************************
813
Puts a record to free list. */
818
page_t* page, /* in: index page */
819
rec_t* rec, /* in: pointer to the (origin of) record */
820
const ulint* offsets)/* in: array returned by rec_get_offsets() */
825
ut_ad(rec_offs_validate(rec, NULL, offsets));
826
ut_ad(!rec_offs_comp(offsets) == !page_rec_is_comp(rec));
827
free = page_header_get_ptr(page, PAGE_FREE);
829
page_rec_set_next(rec, free);
830
page_header_set_ptr(page, PAGE_FREE, rec);
832
#if 0 /* It's better not to destroy the user's data. */
834
/* Clear the data bytes of the deleted record in order to improve
835
the compression ratio of the page and to make it easier to read
836
page dumps in corruption reports. The extra bytes of the record
837
cannot be cleared, because page_mem_alloc() needs them in order
838
to determine the size of the deleted record. */
839
memset(rec, 0, rec_offs_data_size(offsets));
842
garbage = page_header_get_field(page, PAGE_GARBAGE);
844
page_header_set_field(page, PAGE_GARBAGE,
845
garbage + rec_offs_size(offsets));
848
#ifdef UNIV_MATERIALIZE
850
#define UNIV_INLINE UNIV_INLINE_ORIGINAL