~ubuntu-branches/ubuntu/trusty/mysql-5.6/trusty

« back to all changes in this revision

Viewing changes to storage/innobase/include/page0page.ic

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-02-12 11:54:27 UTC
  • Revision ID: package-import@ubuntu.com-20140212115427-oq6tfsqxl1wuwehi
Tags: upstream-5.6.15
ImportĀ upstreamĀ versionĀ 5.6.15

Show diffs side-by-side

added added

removed removed

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