~ubuntu-branches/ubuntu/trusty/mariadb-5.5/trusty-proposed

« back to all changes in this revision

Viewing changes to storage/innobase/page/page0page.c

  • Committer: Package Import Robot
  • Author(s): Otto Kekäläinen
  • Date: 2013-12-22 10:27:05 UTC
  • Revision ID: package-import@ubuntu.com-20131222102705-mndw7s12mz0szrcn
Tags: upstream-5.5.32
Import upstream version 5.5.32

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 
 
3
Copyright (c) 1994, 2012, 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 page/page0page.c
 
21
Index page routines
 
22
 
 
23
Created 2/2/1994 Heikki Tuuri
 
24
*******************************************************/
 
25
 
 
26
#define THIS_MODULE
 
27
#include "page0page.h"
 
28
#ifdef UNIV_NONINL
 
29
#include "page0page.ic"
 
30
#endif
 
31
#undef THIS_MODULE
 
32
 
 
33
#include "page0cur.h"
 
34
#include "page0zip.h"
 
35
#include "buf0buf.h"
 
36
#include "btr0btr.h"
 
37
#ifndef UNIV_HOTBACKUP
 
38
# include "srv0srv.h"
 
39
# include "lock0lock.h"
 
40
# include "fut0lst.h"
 
41
# include "btr0sea.h"
 
42
#endif /* !UNIV_HOTBACKUP */
 
43
 
 
44
/*                      THE INDEX PAGE
 
45
                        ==============
 
46
 
 
47
The index page consists of a page header which contains the page's
 
48
id and other information. On top of it are the index records
 
49
in a heap linked into a one way linear list according to alphabetic order.
 
50
 
 
51
Just below page end is an array of pointers which we call page directory,
 
52
to about every sixth record in the list. The pointers are placed in
 
53
the directory in the alphabetical order of the records pointed to,
 
54
enabling us to make binary search using the array. Each slot n:o I
 
55
in the directory points to a record, where a 4-bit field contains a count
 
56
of those records which are in the linear list between pointer I and
 
57
the pointer I - 1 in the directory, including the record
 
58
pointed to by pointer I and not including the record pointed to by I - 1.
 
59
We say that the record pointed to by slot I, or that slot I, owns
 
60
these records. The count is always kept in the range 4 to 8, with
 
61
the exception that it is 1 for the first slot, and 1--8 for the second slot.
 
62
 
 
63
An essentially binary search can be performed in the list of index
 
64
records, like we could do if we had pointer to every record in the
 
65
page directory. The data structure is, however, more efficient when
 
66
we are doing inserts, because most inserts are just pushed on a heap.
 
67
Only every 8th insert requires block move in the directory pointer
 
68
table, which itself is quite small. A record is deleted from the page
 
69
by just taking it off the linear list and updating the number of owned
 
70
records-field of the record which owns it, and updating the page directory,
 
71
if necessary. A special case is the one when the record owns itself.
 
72
Because the overhead of inserts is so small, we may also increase the
 
73
page size from the projected default of 8 kB to 64 kB without too
 
74
much loss of efficiency in inserts. Bigger page becomes actual
 
75
when the disk transfer rate compared to seek and latency time rises.
 
76
On the present system, the page size is set so that the page transfer
 
77
time (3 ms) is 20 % of the disk random access time (15 ms).
 
78
 
 
79
When the page is split, merged, or becomes full but contains deleted
 
80
records, we have to reorganize the page.
 
81
 
 
82
Assuming a page size of 8 kB, a typical index page of a secondary
 
83
index contains 300 index entries, and the size of the page directory
 
84
is 50 x 4 bytes = 200 bytes. */
 
85
 
 
86
/***************************************************************//**
 
87
Looks for the directory slot which owns the given record.
 
88
@return the directory slot number */
 
89
UNIV_INTERN
 
90
ulint
 
91
page_dir_find_owner_slot(
 
92
/*=====================*/
 
93
        const rec_t*    rec)    /*!< in: the physical record */
 
94
{
 
95
        const page_t*                   page;
 
96
        register uint16                 rec_offs_bytes;
 
97
        register const page_dir_slot_t* slot;
 
98
        register const page_dir_slot_t* first_slot;
 
99
        register const rec_t*           r = rec;
 
100
 
 
101
        ut_ad(page_rec_check(rec));
 
102
 
 
103
        page = page_align(rec);
 
104
        first_slot = page_dir_get_nth_slot(page, 0);
 
105
        slot = page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1);
 
106
 
 
107
        if (page_is_comp(page)) {
 
108
                while (rec_get_n_owned_new(r) == 0) {
 
109
                        r = rec_get_next_ptr_const(r, TRUE);
 
110
                        ut_ad(r >= page + PAGE_NEW_SUPREMUM);
 
111
                        ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
 
112
                }
 
113
        } else {
 
114
                while (rec_get_n_owned_old(r) == 0) {
 
115
                        r = rec_get_next_ptr_const(r, FALSE);
 
116
                        ut_ad(r >= page + PAGE_OLD_SUPREMUM);
 
117
                        ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
 
118
                }
 
119
        }
 
120
 
 
121
        rec_offs_bytes = mach_encode_2(r - page);
 
122
 
 
123
        while (UNIV_LIKELY(*(uint16*) slot != rec_offs_bytes)) {
 
124
 
 
125
                if (UNIV_UNLIKELY(slot == first_slot)) {
 
126
                        fprintf(stderr,
 
127
                                "InnoDB: Probable data corruption on"
 
128
                                " page %lu\n"
 
129
                                "InnoDB: Original record ",
 
130
                                (ulong) page_get_page_no(page));
 
131
 
 
132
                        if (page_is_comp(page)) {
 
133
                                fputs("(compact record)", stderr);
 
134
                        } else {
 
135
                                rec_print_old(stderr, rec);
 
136
                        }
 
137
 
 
138
                        fputs("\n"
 
139
                              "InnoDB: on that page.\n"
 
140
                              "InnoDB: Cannot find the dir slot for record ",
 
141
                              stderr);
 
142
                        if (page_is_comp(page)) {
 
143
                                fputs("(compact record)", stderr);
 
144
                        } else {
 
145
                                rec_print_old(stderr, page
 
146
                                              + mach_decode_2(rec_offs_bytes));
 
147
                        }
 
148
                        fputs("\n"
 
149
                              "InnoDB: on that page!\n", stderr);
 
150
 
 
151
                        buf_page_print(page, 0, 0);
 
152
 
 
153
                        ut_error;
 
154
                }
 
155
 
 
156
                slot += PAGE_DIR_SLOT_SIZE;
 
157
        }
 
158
 
 
159
        return(((ulint) (first_slot - slot)) / PAGE_DIR_SLOT_SIZE);
 
160
}
 
161
 
 
162
/**************************************************************//**
 
163
Used to check the consistency of a directory slot.
 
164
@return TRUE if succeed */
 
165
static
 
166
ibool
 
167
page_dir_slot_check(
 
168
/*================*/
 
169
        const page_dir_slot_t*  slot)   /*!< in: slot */
 
170
{
 
171
        const page_t*   page;
 
172
        ulint           n_slots;
 
173
        ulint           n_owned;
 
174
 
 
175
        ut_a(slot);
 
176
 
 
177
        page = page_align(slot);
 
178
 
 
179
        n_slots = page_dir_get_n_slots(page);
 
180
 
 
181
        ut_a(slot <= page_dir_get_nth_slot(page, 0));
 
182
        ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1));
 
183
 
 
184
        ut_a(page_rec_check(page_dir_slot_get_rec(slot)));
 
185
 
 
186
        if (page_is_comp(page)) {
 
187
                n_owned = rec_get_n_owned_new(page_dir_slot_get_rec(slot));
 
188
        } else {
 
189
                n_owned = rec_get_n_owned_old(page_dir_slot_get_rec(slot));
 
190
        }
 
191
 
 
192
        if (slot == page_dir_get_nth_slot(page, 0)) {
 
193
                ut_a(n_owned == 1);
 
194
        } else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) {
 
195
                ut_a(n_owned >= 1);
 
196
                ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
 
197
        } else {
 
198
                ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED);
 
199
                ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
 
200
        }
 
201
 
 
202
        return(TRUE);
 
203
}
 
204
 
 
205
/*************************************************************//**
 
206
Sets the max trx id field value. */
 
207
UNIV_INTERN
 
208
void
 
209
page_set_max_trx_id(
 
210
/*================*/
 
211
        buf_block_t*    block,  /*!< in/out: page */
 
212
        page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
 
213
        trx_id_t        trx_id, /*!< in: transaction id */
 
214
        mtr_t*          mtr)    /*!< in/out: mini-transaction, or NULL */
 
215
{
 
216
        page_t*         page            = buf_block_get_frame(block);
 
217
#ifndef UNIV_HOTBACKUP
 
218
        ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
 
219
#endif /* !UNIV_HOTBACKUP */
 
220
 
 
221
        /* It is not necessary to write this change to the redo log, as
 
222
        during a database recovery we assume that the max trx id of every
 
223
        page is the maximum trx id assigned before the crash. */
 
224
 
 
225
        if (UNIV_LIKELY_NULL(page_zip)) {
 
226
                mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
 
227
                page_zip_write_header(page_zip,
 
228
                                      page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
 
229
                                      8, mtr);
 
230
#ifndef UNIV_HOTBACKUP
 
231
        } else if (mtr) {
 
232
                mlog_write_ull(page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
 
233
                               trx_id, mtr);
 
234
#endif /* !UNIV_HOTBACKUP */
 
235
        } else {
 
236
                mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
 
237
        }
 
238
}
 
239
 
 
240
/************************************************************//**
 
241
Allocates a block of memory from the heap of an index page.
 
242
@return pointer to start of allocated buffer, or NULL if allocation fails */
 
243
UNIV_INTERN
 
244
byte*
 
245
page_mem_alloc_heap(
 
246
/*================*/
 
247
        page_t*         page,   /*!< in/out: index page */
 
248
        page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
 
249
                                space available for inserting the record,
 
250
                                or NULL */
 
251
        ulint           need,   /*!< in: total number of bytes needed */
 
252
        ulint*          heap_no)/*!< out: this contains the heap number
 
253
                                of the allocated record
 
254
                                if allocation succeeds */
 
255
{
 
256
        byte*   block;
 
257
        ulint   avl_space;
 
258
 
 
259
        ut_ad(page && heap_no);
 
260
 
 
261
        avl_space = page_get_max_insert_size(page, 1);
 
262
 
 
263
        if (avl_space >= need) {
 
264
                block = page_header_get_ptr(page, PAGE_HEAP_TOP);
 
265
 
 
266
                page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP,
 
267
                                    block + need);
 
268
                *heap_no = page_dir_get_n_heap(page);
 
269
 
 
270
                page_dir_set_n_heap(page, page_zip, 1 + *heap_no);
 
271
 
 
272
                return(block);
 
273
        }
 
274
 
 
275
        return(NULL);
 
276
}
 
277
 
 
278
#ifndef UNIV_HOTBACKUP
 
279
/**********************************************************//**
 
280
Writes a log record of page creation. */
 
281
UNIV_INLINE
 
282
void
 
283
page_create_write_log(
 
284
/*==================*/
 
285
        buf_frame_t*    frame,  /*!< in: a buffer frame where the page is
 
286
                                created */
 
287
        mtr_t*          mtr,    /*!< in: mini-transaction handle */
 
288
        ibool           comp)   /*!< in: TRUE=compact page format */
 
289
{
 
290
        mlog_write_initial_log_record(frame, comp
 
291
                                      ? MLOG_COMP_PAGE_CREATE
 
292
                                      : MLOG_PAGE_CREATE, mtr);
 
293
}
 
294
#else /* !UNIV_HOTBACKUP */
 
295
# define page_create_write_log(frame,mtr,comp) ((void) 0)
 
296
#endif /* !UNIV_HOTBACKUP */
 
297
 
 
298
/***********************************************************//**
 
299
Parses a redo log record of creating a page.
 
300
@return end of log record or NULL */
 
301
UNIV_INTERN
 
302
byte*
 
303
page_parse_create(
 
304
/*==============*/
 
305
        byte*           ptr,    /*!< in: buffer */
 
306
        byte*           end_ptr __attribute__((unused)), /*!< in: buffer end */
 
307
        ulint           comp,   /*!< in: nonzero=compact page format */
 
308
        buf_block_t*    block,  /*!< in: block or NULL */
 
309
        mtr_t*          mtr)    /*!< in: mtr or NULL */
 
310
{
 
311
        ut_ad(ptr && end_ptr);
 
312
 
 
313
        /* The record is empty, except for the record initial part */
 
314
 
 
315
        if (block) {
 
316
                page_create(block, mtr, comp);
 
317
        }
 
318
 
 
319
        return(ptr);
 
320
}
 
321
 
 
322
/**********************************************************//**
 
323
The index page creation function.
 
324
@return pointer to the page */
 
325
static
 
326
page_t*
 
327
page_create_low(
 
328
/*============*/
 
329
        buf_block_t*    block,          /*!< in: a buffer block where the
 
330
                                        page is created */
 
331
        ulint           comp)           /*!< in: nonzero=compact page format */
 
332
{
 
333
        page_dir_slot_t* slot;
 
334
        mem_heap_t*     heap;
 
335
        dtuple_t*       tuple;
 
336
        dfield_t*       field;
 
337
        byte*           heap_top;
 
338
        rec_t*          infimum_rec;
 
339
        rec_t*          supremum_rec;
 
340
        page_t*         page;
 
341
        dict_index_t*   index;
 
342
        ulint*          offsets;
 
343
 
 
344
        ut_ad(block);
 
345
#if PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA
 
346
# error "PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA"
 
347
#endif
 
348
#if PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA
 
349
# error "PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA"
 
350
#endif
 
351
 
 
352
        /* The infimum and supremum records use a dummy index. */
 
353
        if (UNIV_LIKELY(comp)) {
 
354
                index = dict_ind_compact;
 
355
        } else {
 
356
                index = dict_ind_redundant;
 
357
        }
 
358
 
 
359
        /* 1. INCREMENT MODIFY CLOCK */
 
360
        buf_block_modify_clock_inc(block);
 
361
 
 
362
        page = buf_block_get_frame(block);
 
363
 
 
364
        fil_page_set_type(page, FIL_PAGE_INDEX);
 
365
 
 
366
        heap = mem_heap_create(200);
 
367
 
 
368
        /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
 
369
 
 
370
        /* Create first a data tuple for infimum record */
 
371
        tuple = dtuple_create(heap, 1);
 
372
        dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM);
 
373
        field = dtuple_get_nth_field(tuple, 0);
 
374
 
 
375
        dfield_set_data(field, "infimum", 8);
 
376
        dtype_set(dfield_get_type(field),
 
377
                  DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8);
 
378
        /* Set the corresponding physical record to its place in the page
 
379
        record heap */
 
380
 
 
381
        heap_top = page + PAGE_DATA;
 
382
 
 
383
        infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
 
384
 
 
385
        if (UNIV_LIKELY(comp)) {
 
386
                ut_a(infimum_rec == page + PAGE_NEW_INFIMUM);
 
387
 
 
388
                rec_set_n_owned_new(infimum_rec, NULL, 1);
 
389
                rec_set_heap_no_new(infimum_rec, 0);
 
390
        } else {
 
391
                ut_a(infimum_rec == page + PAGE_OLD_INFIMUM);
 
392
 
 
393
                rec_set_n_owned_old(infimum_rec, 1);
 
394
                rec_set_heap_no_old(infimum_rec, 0);
 
395
        }
 
396
 
 
397
        offsets = rec_get_offsets(infimum_rec, index, NULL,
 
398
                                  ULINT_UNDEFINED, &heap);
 
399
 
 
400
        heap_top = rec_get_end(infimum_rec, offsets);
 
401
 
 
402
        /* Create then a tuple for supremum */
 
403
 
 
404
        tuple = dtuple_create(heap, 1);
 
405
        dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM);
 
406
        field = dtuple_get_nth_field(tuple, 0);
 
407
 
 
408
        dfield_set_data(field, "supremum", comp ? 8 : 9);
 
409
        dtype_set(dfield_get_type(field),
 
410
                  DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9);
 
411
 
 
412
        supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
 
413
 
 
414
        if (UNIV_LIKELY(comp)) {
 
415
                ut_a(supremum_rec == page + PAGE_NEW_SUPREMUM);
 
416
 
 
417
                rec_set_n_owned_new(supremum_rec, NULL, 1);
 
418
                rec_set_heap_no_new(supremum_rec, 1);
 
419
        } else {
 
420
                ut_a(supremum_rec == page + PAGE_OLD_SUPREMUM);
 
421
 
 
422
                rec_set_n_owned_old(supremum_rec, 1);
 
423
                rec_set_heap_no_old(supremum_rec, 1);
 
424
        }
 
425
 
 
426
        offsets = rec_get_offsets(supremum_rec, index, offsets,
 
427
                                  ULINT_UNDEFINED, &heap);
 
428
        heap_top = rec_get_end(supremum_rec, offsets);
 
429
 
 
430
        ut_ad(heap_top == page
 
431
              + (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END));
 
432
 
 
433
        mem_heap_free(heap);
 
434
 
 
435
        /* 4. INITIALIZE THE PAGE */
 
436
 
 
437
        page_header_set_field(page, NULL, PAGE_N_DIR_SLOTS, 2);
 
438
        page_header_set_ptr(page, NULL, PAGE_HEAP_TOP, heap_top);
 
439
        page_header_set_field(page, NULL, PAGE_N_HEAP, comp
 
440
                              ? 0x8000 | PAGE_HEAP_NO_USER_LOW
 
441
                              : PAGE_HEAP_NO_USER_LOW);
 
442
        page_header_set_ptr(page, NULL, PAGE_FREE, NULL);
 
443
        page_header_set_field(page, NULL, PAGE_GARBAGE, 0);
 
444
        page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, NULL);
 
445
        page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION);
 
446
        page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0);
 
447
        page_header_set_field(page, NULL, PAGE_N_RECS, 0);
 
448
        page_set_max_trx_id(block, NULL, 0, NULL);
 
449
        memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START
 
450
               - page_offset(heap_top));
 
451
 
 
452
        /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
 
453
 
 
454
        /* Set the slots to point to infimum and supremum. */
 
455
 
 
456
        slot = page_dir_get_nth_slot(page, 0);
 
457
        page_dir_slot_set_rec(slot, infimum_rec);
 
458
 
 
459
        slot = page_dir_get_nth_slot(page, 1);
 
460
        page_dir_slot_set_rec(slot, supremum_rec);
 
461
 
 
462
        /* Set the next pointers in infimum and supremum */
 
463
 
 
464
        if (UNIV_LIKELY(comp)) {
 
465
                rec_set_next_offs_new(infimum_rec, PAGE_NEW_SUPREMUM);
 
466
                rec_set_next_offs_new(supremum_rec, 0);
 
467
        } else {
 
468
                rec_set_next_offs_old(infimum_rec, PAGE_OLD_SUPREMUM);
 
469
                rec_set_next_offs_old(supremum_rec, 0);
 
470
        }
 
471
 
 
472
        return(page);
 
473
}
 
474
 
 
475
/**********************************************************//**
 
476
Create an uncompressed B-tree index page.
 
477
@return pointer to the page */
 
478
UNIV_INTERN
 
479
page_t*
 
480
page_create(
 
481
/*========*/
 
482
        buf_block_t*    block,          /*!< in: a buffer block where the
 
483
                                        page is created */
 
484
        mtr_t*          mtr,            /*!< in: mini-transaction handle */
 
485
        ulint           comp)           /*!< in: nonzero=compact page format */
 
486
{
 
487
        page_create_write_log(buf_block_get_frame(block), mtr, comp);
 
488
        return(page_create_low(block, comp));
 
489
}
 
490
 
 
491
/**********************************************************//**
 
492
Create a compressed B-tree index page.
 
493
@return pointer to the page */
 
494
UNIV_INTERN
 
495
page_t*
 
496
page_create_zip(
 
497
/*============*/
 
498
        buf_block_t*    block,          /*!< in/out: a buffer frame where the
 
499
                                        page is created */
 
500
        dict_index_t*   index,          /*!< in: the index of the page */
 
501
        ulint           level,          /*!< in: the B-tree level of the page */
 
502
        mtr_t*          mtr)            /*!< in: mini-transaction handle */
 
503
{
 
504
        page_t*         page;
 
505
        page_zip_des_t* page_zip        = buf_block_get_page_zip(block);
 
506
 
 
507
        ut_ad(block);
 
508
        ut_ad(page_zip);
 
509
        ut_ad(index);
 
510
        ut_ad(dict_table_is_comp(index->table));
 
511
 
 
512
        page = page_create_low(block, TRUE);
 
513
        mach_write_to_2(page + PAGE_HEADER + PAGE_LEVEL, level);
 
514
 
 
515
        if (UNIV_UNLIKELY(!page_zip_compress(page_zip, page, index, mtr))) {
 
516
                /* The compression of a newly created page
 
517
                should always succeed. */
 
518
                ut_error;
 
519
        }
 
520
 
 
521
        return(page);
 
522
}
 
523
 
 
524
/*************************************************************//**
 
525
Differs from page_copy_rec_list_end, because this function does not
 
526
touch the lock table and max trx id on page or compress the page. */
 
527
UNIV_INTERN
 
528
void
 
529
page_copy_rec_list_end_no_locks(
 
530
/*============================*/
 
531
        buf_block_t*    new_block,      /*!< in: index page to copy to */
 
532
        buf_block_t*    block,          /*!< in: index page of rec */
 
533
        rec_t*          rec,            /*!< in: record on page */
 
534
        dict_index_t*   index,          /*!< in: record descriptor */
 
535
        mtr_t*          mtr)            /*!< in: mtr */
 
536
{
 
537
        page_t*         new_page        = buf_block_get_frame(new_block);
 
538
        page_cur_t      cur1;
 
539
        rec_t*          cur2;
 
540
        mem_heap_t*     heap            = NULL;
 
541
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
542
        ulint*          offsets         = offsets_;
 
543
        rec_offs_init(offsets_);
 
544
 
 
545
        page_cur_position(rec, block, &cur1);
 
546
 
 
547
        if (page_cur_is_before_first(&cur1)) {
 
548
 
 
549
                page_cur_move_to_next(&cur1);
 
550
        }
 
551
 
 
552
        btr_assert_not_corrupted(new_block, index);
 
553
        ut_a(page_is_comp(new_page) == page_rec_is_comp(rec));
 
554
        ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == (ulint)
 
555
             (page_is_comp(new_page) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM));
 
556
 
 
557
        cur2 = page_get_infimum_rec(buf_block_get_frame(new_block));
 
558
 
 
559
        /* Copy records from the original page to the new page */
 
560
 
 
561
        while (!page_cur_is_after_last(&cur1)) {
 
562
                rec_t*  cur1_rec = page_cur_get_rec(&cur1);
 
563
                rec_t*  ins_rec;
 
564
                offsets = rec_get_offsets(cur1_rec, index, offsets,
 
565
                                          ULINT_UNDEFINED, &heap);
 
566
                ins_rec = page_cur_insert_rec_low(cur2, index,
 
567
                                                  cur1_rec, offsets, mtr);
 
568
                if (UNIV_UNLIKELY(!ins_rec)) {
 
569
                        /* Track an assertion failure reported on the mailing
 
570
                        list on June 18th, 2003 */
 
571
 
 
572
                        buf_page_print(new_page, 0,
 
573
                                       BUF_PAGE_PRINT_NO_CRASH);
 
574
                        buf_page_print(page_align(rec), 0,
 
575
                                       BUF_PAGE_PRINT_NO_CRASH);
 
576
                        ut_print_timestamp(stderr);
 
577
 
 
578
                        fprintf(stderr,
 
579
                                "InnoDB: rec offset %lu, cur1 offset %lu,"
 
580
                                " cur2 offset %lu\n",
 
581
                                (ulong) page_offset(rec),
 
582
                                (ulong) page_offset(page_cur_get_rec(&cur1)),
 
583
                                (ulong) page_offset(cur2));
 
584
                        ut_error;
 
585
                }
 
586
 
 
587
                page_cur_move_to_next(&cur1);
 
588
                cur2 = ins_rec;
 
589
        }
 
590
 
 
591
        if (UNIV_LIKELY_NULL(heap)) {
 
592
                mem_heap_free(heap);
 
593
        }
 
594
}
 
595
 
 
596
#ifndef UNIV_HOTBACKUP
 
597
/*************************************************************//**
 
598
Copies records from page to new_page, from a given record onward,
 
599
including that record. Infimum and supremum records are not copied.
 
600
The records are copied to the start of the record list on new_page.
 
601
@return pointer to the original successor of the infimum record on
 
602
new_page, or NULL on zip overflow (new_block will be decompressed) */
 
603
UNIV_INTERN
 
604
rec_t*
 
605
page_copy_rec_list_end(
 
606
/*===================*/
 
607
        buf_block_t*    new_block,      /*!< in/out: index page to copy to */
 
608
        buf_block_t*    block,          /*!< in: index page containing rec */
 
609
        rec_t*          rec,            /*!< in: record on page */
 
610
        dict_index_t*   index,          /*!< in: record descriptor */
 
611
        mtr_t*          mtr)            /*!< in: mtr */
 
612
{
 
613
        page_t*         new_page        = buf_block_get_frame(new_block);
 
614
        page_zip_des_t* new_page_zip    = buf_block_get_page_zip(new_block);
 
615
        page_t*         page            = page_align(rec);
 
616
        rec_t*          ret             = page_rec_get_next(
 
617
                page_get_infimum_rec(new_page));
 
618
        ulint           log_mode        = 0; /* remove warning */
 
619
 
 
620
#ifdef UNIV_ZIP_DEBUG
 
621
        if (new_page_zip) {
 
622
                page_zip_des_t* page_zip = buf_block_get_page_zip(block);
 
623
                ut_a(page_zip);
 
624
 
 
625
                /* Strict page_zip_validate() may fail here.
 
626
                Furthermore, btr_compress() may set FIL_PAGE_PREV to
 
627
                FIL_NULL on new_page while leaving it intact on
 
628
                new_page_zip.  So, we cannot validate new_page_zip. */
 
629
                ut_a(page_zip_validate_low(page_zip, page, index, TRUE));
 
630
        }
 
631
#endif /* UNIV_ZIP_DEBUG */
 
632
        ut_ad(buf_block_get_frame(block) == page);
 
633
        ut_ad(page_is_leaf(page) == page_is_leaf(new_page));
 
634
        ut_ad(page_is_comp(page) == page_is_comp(new_page));
 
635
        /* Here, "ret" may be pointing to a user record or the
 
636
        predefined supremum record. */
 
637
 
 
638
        if (UNIV_LIKELY_NULL(new_page_zip)) {
 
639
                log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
 
640
        }
 
641
 
 
642
        if (page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW) {
 
643
                page_copy_rec_list_end_to_created_page(new_page, rec,
 
644
                                                       index, mtr);
 
645
        } else {
 
646
                page_copy_rec_list_end_no_locks(new_block, block, rec,
 
647
                                                index, mtr);
 
648
        }
 
649
 
 
650
        /* Update PAGE_MAX_TRX_ID on the uncompressed page.
 
651
        Modifications will be redo logged and copied to the compressed
 
652
        page in page_zip_compress() or page_zip_reorganize() below. */
 
653
        if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
 
654
                page_update_max_trx_id(new_block, NULL,
 
655
                                       page_get_max_trx_id(page), mtr);
 
656
        }
 
657
 
 
658
        if (UNIV_LIKELY_NULL(new_page_zip)) {
 
659
                mtr_set_log_mode(mtr, log_mode);
 
660
 
 
661
                if (UNIV_UNLIKELY
 
662
                    (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
 
663
                        /* Before trying to reorganize the page,
 
664
                        store the number of preceding records on the page. */
 
665
                        ulint   ret_pos
 
666
                                = page_rec_get_n_recs_before(ret);
 
667
                        /* Before copying, "ret" was the successor of
 
668
                        the predefined infimum record.  It must still
 
669
                        have at least one predecessor (the predefined
 
670
                        infimum record, or a freshly copied record
 
671
                        that is smaller than "ret"). */
 
672
                        ut_a(ret_pos > 0);
 
673
 
 
674
                        if (UNIV_UNLIKELY
 
675
                            (!page_zip_reorganize(new_block, index, mtr))) {
 
676
 
 
677
                                btr_blob_dbg_remove(new_page, index,
 
678
                                                    "copy_end_reorg_fail");
 
679
                                if (UNIV_UNLIKELY
 
680
                                    (!page_zip_decompress(new_page_zip,
 
681
                                                          new_page, FALSE))) {
 
682
                                        ut_error;
 
683
                                }
 
684
                                ut_ad(page_validate(new_page, index));
 
685
                                btr_blob_dbg_add(new_page, index,
 
686
                                                 "copy_end_reorg_fail");
 
687
                                return(NULL);
 
688
                        } else {
 
689
                                /* The page was reorganized:
 
690
                                Seek to ret_pos. */
 
691
                                ret = new_page + PAGE_NEW_INFIMUM;
 
692
 
 
693
                                do {
 
694
                                        ret = rec_get_next_ptr(ret, TRUE);
 
695
                                } while (--ret_pos);
 
696
                        }
 
697
                }
 
698
        }
 
699
 
 
700
        /* Update the lock table and possible hash index */
 
701
 
 
702
        lock_move_rec_list_end(new_block, block, rec);
 
703
 
 
704
        btr_search_move_or_delete_hash_entries(new_block, block, index);
 
705
 
 
706
        return(ret);
 
707
}
 
708
 
 
709
/*************************************************************//**
 
710
Copies records from page to new_page, up to the given record,
 
711
NOT including that record. Infimum and supremum records are not copied.
 
712
The records are copied to the end of the record list on new_page.
 
713
@return pointer to the original predecessor of the supremum record on
 
714
new_page, or NULL on zip overflow (new_block will be decompressed) */
 
715
UNIV_INTERN
 
716
rec_t*
 
717
page_copy_rec_list_start(
 
718
/*=====================*/
 
719
        buf_block_t*    new_block,      /*!< in/out: index page to copy to */
 
720
        buf_block_t*    block,          /*!< in: index page containing rec */
 
721
        rec_t*          rec,            /*!< in: record on page */
 
722
        dict_index_t*   index,          /*!< in: record descriptor */
 
723
        mtr_t*          mtr)            /*!< in: mtr */
 
724
{
 
725
        page_t*         new_page        = buf_block_get_frame(new_block);
 
726
        page_zip_des_t* new_page_zip    = buf_block_get_page_zip(new_block);
 
727
        page_cur_t      cur1;
 
728
        rec_t*          cur2;
 
729
        ulint           log_mode        = 0 /* remove warning */;
 
730
        mem_heap_t*     heap            = NULL;
 
731
        rec_t*          ret
 
732
                = page_rec_get_prev(page_get_supremum_rec(new_page));
 
733
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
734
        ulint*          offsets         = offsets_;
 
735
        rec_offs_init(offsets_);
 
736
 
 
737
        /* Here, "ret" may be pointing to a user record or the
 
738
        predefined infimum record. */
 
739
 
 
740
        if (page_rec_is_infimum(rec)) {
 
741
 
 
742
                return(ret);
 
743
        }
 
744
 
 
745
        if (UNIV_LIKELY_NULL(new_page_zip)) {
 
746
                log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
 
747
        }
 
748
 
 
749
        page_cur_set_before_first(block, &cur1);
 
750
        page_cur_move_to_next(&cur1);
 
751
 
 
752
        cur2 = ret;
 
753
 
 
754
        /* Copy records from the original page to the new page */
 
755
 
 
756
        while (page_cur_get_rec(&cur1) != rec) {
 
757
                rec_t*  cur1_rec = page_cur_get_rec(&cur1);
 
758
                offsets = rec_get_offsets(cur1_rec, index, offsets,
 
759
                                          ULINT_UNDEFINED, &heap);
 
760
                cur2 = page_cur_insert_rec_low(cur2, index,
 
761
                                               cur1_rec, offsets, mtr);
 
762
                ut_a(cur2);
 
763
 
 
764
                page_cur_move_to_next(&cur1);
 
765
        }
 
766
 
 
767
        if (UNIV_LIKELY_NULL(heap)) {
 
768
                mem_heap_free(heap);
 
769
        }
 
770
 
 
771
        /* Update PAGE_MAX_TRX_ID on the uncompressed page.
 
772
        Modifications will be redo logged and copied to the compressed
 
773
        page in page_zip_compress() or page_zip_reorganize() below. */
 
774
        if (dict_index_is_sec_or_ibuf(index)
 
775
            && page_is_leaf(page_align(rec))) {
 
776
                page_update_max_trx_id(new_block, NULL,
 
777
                                       page_get_max_trx_id(page_align(rec)),
 
778
                                       mtr);
 
779
        }
 
780
 
 
781
        if (UNIV_LIKELY_NULL(new_page_zip)) {
 
782
                mtr_set_log_mode(mtr, log_mode);
 
783
 
 
784
                DBUG_EXECUTE_IF("page_copy_rec_list_start_compress_fail",
 
785
                                goto zip_reorganize;);
 
786
 
 
787
                if (UNIV_UNLIKELY
 
788
                    (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
 
789
                        ulint   ret_pos;
 
790
#ifndef DBUG_OFF
 
791
zip_reorganize:
 
792
#endif /* DBUG_OFF */
 
793
                        /* Before trying to reorganize the page,
 
794
                        store the number of preceding records on the page. */
 
795
                        ret_pos = page_rec_get_n_recs_before(ret);
 
796
                        /* Before copying, "ret" was the predecessor
 
797
                        of the predefined supremum record.  If it was
 
798
                        the predefined infimum record, then it would
 
799
                        still be the infimum, and we would have
 
800
                        ret_pos == 0. */
 
801
 
 
802
                        if (UNIV_UNLIKELY
 
803
                            (!page_zip_reorganize(new_block, index, mtr))) {
 
804
 
 
805
                                btr_blob_dbg_remove(new_page, index,
 
806
                                                    "copy_start_reorg_fail");
 
807
                                if (UNIV_UNLIKELY
 
808
                                    (!page_zip_decompress(new_page_zip,
 
809
                                                          new_page, FALSE))) {
 
810
                                        ut_error;
 
811
                                }
 
812
                                ut_ad(page_validate(new_page, index));
 
813
                                btr_blob_dbg_add(new_page, index,
 
814
                                                 "copy_start_reorg_fail");
 
815
                                return(NULL);
 
816
                        }
 
817
 
 
818
                        /* The page was reorganized: Seek to ret_pos. */
 
819
                        ret = page_rec_get_nth(new_page, ret_pos);
 
820
                }
 
821
        }
 
822
 
 
823
        /* Update the lock table and possible hash index */
 
824
 
 
825
        lock_move_rec_list_start(new_block, block, rec, ret);
 
826
 
 
827
        btr_search_move_or_delete_hash_entries(new_block, block, index);
 
828
 
 
829
        return(ret);
 
830
}
 
831
 
 
832
/**********************************************************//**
 
833
Writes a log record of a record list end or start deletion. */
 
834
UNIV_INLINE
 
835
void
 
836
page_delete_rec_list_write_log(
 
837
/*===========================*/
 
838
        rec_t*          rec,    /*!< in: record on page */
 
839
        dict_index_t*   index,  /*!< in: record descriptor */
 
840
        byte            type,   /*!< in: operation type:
 
841
                                MLOG_LIST_END_DELETE, ... */
 
842
        mtr_t*          mtr)    /*!< in: mtr */
 
843
{
 
844
        byte*   log_ptr;
 
845
        ut_ad(type == MLOG_LIST_END_DELETE
 
846
              || type == MLOG_LIST_START_DELETE
 
847
              || type == MLOG_COMP_LIST_END_DELETE
 
848
              || type == MLOG_COMP_LIST_START_DELETE);
 
849
 
 
850
        log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2);
 
851
        if (log_ptr) {
 
852
                /* Write the parameter as a 2-byte ulint */
 
853
                mach_write_to_2(log_ptr, page_offset(rec));
 
854
                mlog_close(mtr, log_ptr + 2);
 
855
        }
 
856
}
 
857
#else /* !UNIV_HOTBACKUP */
 
858
# define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0)
 
859
#endif /* !UNIV_HOTBACKUP */
 
860
 
 
861
/**********************************************************//**
 
862
Parses a log record of a record list end or start deletion.
 
863
@return end of log record or NULL */
 
864
UNIV_INTERN
 
865
byte*
 
866
page_parse_delete_rec_list(
 
867
/*=======================*/
 
868
        byte            type,   /*!< in: MLOG_LIST_END_DELETE,
 
869
                                MLOG_LIST_START_DELETE,
 
870
                                MLOG_COMP_LIST_END_DELETE or
 
871
                                MLOG_COMP_LIST_START_DELETE */
 
872
        byte*           ptr,    /*!< in: buffer */
 
873
        byte*           end_ptr,/*!< in: buffer end */
 
874
        buf_block_t*    block,  /*!< in/out: buffer block or NULL */
 
875
        dict_index_t*   index,  /*!< in: record descriptor */
 
876
        mtr_t*          mtr)    /*!< in: mtr or NULL */
 
877
{
 
878
        page_t* page;
 
879
        ulint   offset;
 
880
 
 
881
        ut_ad(type == MLOG_LIST_END_DELETE
 
882
              || type == MLOG_LIST_START_DELETE
 
883
              || type == MLOG_COMP_LIST_END_DELETE
 
884
              || type == MLOG_COMP_LIST_START_DELETE);
 
885
 
 
886
        /* Read the record offset as a 2-byte ulint */
 
887
 
 
888
        if (end_ptr < ptr + 2) {
 
889
 
 
890
                return(NULL);
 
891
        }
 
892
 
 
893
        offset = mach_read_from_2(ptr);
 
894
        ptr += 2;
 
895
 
 
896
        if (!block) {
 
897
 
 
898
                return(ptr);
 
899
        }
 
900
 
 
901
        page = buf_block_get_frame(block);
 
902
 
 
903
        ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
 
904
 
 
905
        if (type == MLOG_LIST_END_DELETE
 
906
            || type == MLOG_COMP_LIST_END_DELETE) {
 
907
                page_delete_rec_list_end(page + offset, block, index,
 
908
                                         ULINT_UNDEFINED, ULINT_UNDEFINED,
 
909
                                         mtr);
 
910
        } else {
 
911
                page_delete_rec_list_start(page + offset, block, index, mtr);
 
912
        }
 
913
 
 
914
        return(ptr);
 
915
}
 
916
 
 
917
/*************************************************************//**
 
918
Deletes records from a page from a given record onward, including that record.
 
919
The infimum and supremum records are not deleted. */
 
920
UNIV_INTERN
 
921
void
 
922
page_delete_rec_list_end(
 
923
/*=====================*/
 
924
        rec_t*          rec,    /*!< in: pointer to record on page */
 
925
        buf_block_t*    block,  /*!< in: buffer block of the page */
 
926
        dict_index_t*   index,  /*!< in: record descriptor */
 
927
        ulint           n_recs, /*!< in: number of records to delete,
 
928
                                or ULINT_UNDEFINED if not known */
 
929
        ulint           size,   /*!< in: the sum of the sizes of the
 
930
                                records in the end of the chain to
 
931
                                delete, or ULINT_UNDEFINED if not known */
 
932
        mtr_t*          mtr)    /*!< in: mtr */
 
933
{
 
934
        page_dir_slot_t*slot;
 
935
        ulint           slot_index;
 
936
        rec_t*          last_rec;
 
937
        rec_t*          prev_rec;
 
938
        ulint           n_owned;
 
939
        page_zip_des_t* page_zip        = buf_block_get_page_zip(block);
 
940
        page_t*         page            = page_align(rec);
 
941
        mem_heap_t*     heap            = NULL;
 
942
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
943
        ulint*          offsets         = offsets_;
 
944
        rec_offs_init(offsets_);
 
945
 
 
946
        ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE);
 
947
        ut_ad(!page_zip || page_rec_is_comp(rec));
 
948
#ifdef UNIV_ZIP_DEBUG
 
949
        ut_a(!page_zip || page_zip_validate(page_zip, page, index));
 
950
#endif /* UNIV_ZIP_DEBUG */
 
951
 
 
952
        if (page_rec_is_infimum(rec)) {
 
953
                rec = page_rec_get_next(rec);
 
954
        }
 
955
 
 
956
        if (page_rec_is_supremum(rec)) {
 
957
 
 
958
                return;
 
959
        }
 
960
 
 
961
        /* Reset the last insert info in the page header and increment
 
962
        the modify clock for the frame */
 
963
 
 
964
        page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL);
 
965
 
 
966
        /* The page gets invalid for optimistic searches: increment the
 
967
        frame modify clock */
 
968
 
 
969
        buf_block_modify_clock_inc(block);
 
970
 
 
971
        page_delete_rec_list_write_log(rec, index, page_is_comp(page)
 
972
                                       ? MLOG_COMP_LIST_END_DELETE
 
973
                                       : MLOG_LIST_END_DELETE, mtr);
 
974
 
 
975
        if (UNIV_LIKELY_NULL(page_zip)) {
 
976
                ulint           log_mode;
 
977
 
 
978
                ut_a(page_is_comp(page));
 
979
                /* Individual deletes are not logged */
 
980
 
 
981
                log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
 
982
 
 
983
                do {
 
984
                        page_cur_t      cur;
 
985
                        page_cur_position(rec, block, &cur);
 
986
 
 
987
                        offsets = rec_get_offsets(rec, index, offsets,
 
988
                                                  ULINT_UNDEFINED, &heap);
 
989
                        rec = rec_get_next_ptr(rec, TRUE);
 
990
#ifdef UNIV_ZIP_DEBUG
 
991
                        ut_a(page_zip_validate(page_zip, page, index));
 
992
#endif /* UNIV_ZIP_DEBUG */
 
993
                        page_cur_delete_rec(&cur, index, offsets, mtr);
 
994
                } while (page_offset(rec) != PAGE_NEW_SUPREMUM);
 
995
 
 
996
                if (UNIV_LIKELY_NULL(heap)) {
 
997
                        mem_heap_free(heap);
 
998
                }
 
999
 
 
1000
                /* Restore log mode */
 
1001
 
 
1002
                mtr_set_log_mode(mtr, log_mode);
 
1003
                return;
 
1004
        }
 
1005
 
 
1006
        prev_rec = page_rec_get_prev(rec);
 
1007
 
 
1008
        last_rec = page_rec_get_prev(page_get_supremum_rec(page));
 
1009
 
 
1010
        if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
 
1011
                rec_t*          rec2            = rec;
 
1012
                /* Calculate the sum of sizes and the number of records */
 
1013
                size = 0;
 
1014
                n_recs = 0;
 
1015
 
 
1016
                do {
 
1017
                        ulint   s;
 
1018
                        offsets = rec_get_offsets(rec2, index, offsets,
 
1019
                                                  ULINT_UNDEFINED, &heap);
 
1020
                        s = rec_offs_size(offsets);
 
1021
                        ut_ad(rec2 - page + s - rec_offs_extra_size(offsets)
 
1022
                              < UNIV_PAGE_SIZE);
 
1023
                        ut_ad(size + s < UNIV_PAGE_SIZE);
 
1024
                        size += s;
 
1025
                        n_recs++;
 
1026
 
 
1027
                        rec2 = page_rec_get_next(rec2);
 
1028
                } while (!page_rec_is_supremum(rec2));
 
1029
 
 
1030
                if (UNIV_LIKELY_NULL(heap)) {
 
1031
                        mem_heap_free(heap);
 
1032
                }
 
1033
        }
 
1034
 
 
1035
        ut_ad(size < UNIV_PAGE_SIZE);
 
1036
 
 
1037
        /* Update the page directory; there is no need to balance the number
 
1038
        of the records owned by the supremum record, as it is allowed to be
 
1039
        less than PAGE_DIR_SLOT_MIN_N_OWNED */
 
1040
 
 
1041
        if (page_is_comp(page)) {
 
1042
                rec_t*  rec2    = rec;
 
1043
                ulint   count   = 0;
 
1044
 
 
1045
                while (rec_get_n_owned_new(rec2) == 0) {
 
1046
                        count++;
 
1047
 
 
1048
                        rec2 = rec_get_next_ptr(rec2, TRUE);
 
1049
                }
 
1050
 
 
1051
                ut_ad(rec_get_n_owned_new(rec2) > count);
 
1052
 
 
1053
                n_owned = rec_get_n_owned_new(rec2) - count;
 
1054
                slot_index = page_dir_find_owner_slot(rec2);
 
1055
                ut_ad(slot_index > 0);
 
1056
                slot = page_dir_get_nth_slot(page, slot_index);
 
1057
        } else {
 
1058
                rec_t*  rec2    = rec;
 
1059
                ulint   count   = 0;
 
1060
 
 
1061
                while (rec_get_n_owned_old(rec2) == 0) {
 
1062
                        count++;
 
1063
 
 
1064
                        rec2 = rec_get_next_ptr(rec2, FALSE);
 
1065
                }
 
1066
 
 
1067
                ut_ad(rec_get_n_owned_old(rec2) > count);
 
1068
 
 
1069
                n_owned = rec_get_n_owned_old(rec2) - count;
 
1070
                slot_index = page_dir_find_owner_slot(rec2);
 
1071
                ut_ad(slot_index > 0);
 
1072
                slot = page_dir_get_nth_slot(page, slot_index);
 
1073
        }
 
1074
 
 
1075
        page_dir_slot_set_rec(slot, page_get_supremum_rec(page));
 
1076
        page_dir_slot_set_n_owned(slot, NULL, n_owned);
 
1077
 
 
1078
        page_dir_set_n_slots(page, NULL, slot_index + 1);
 
1079
 
 
1080
        /* Remove the record chain segment from the record chain */
 
1081
        page_rec_set_next(prev_rec, page_get_supremum_rec(page));
 
1082
 
 
1083
        btr_blob_dbg_op(page, rec, index, "delete_end",
 
1084
                        btr_blob_dbg_remove_rec);
 
1085
 
 
1086
        /* Catenate the deleted chain segment to the page free list */
 
1087
 
 
1088
        page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE));
 
1089
        page_header_set_ptr(page, NULL, PAGE_FREE, rec);
 
1090
 
 
1091
        page_header_set_field(page, NULL, PAGE_GARBAGE, size
 
1092
                              + page_header_get_field(page, PAGE_GARBAGE));
 
1093
 
 
1094
        page_header_set_field(page, NULL, PAGE_N_RECS,
 
1095
                              (ulint)(page_get_n_recs(page) - n_recs));
 
1096
}
 
1097
 
 
1098
/*************************************************************//**
 
1099
Deletes records from page, up to the given record, NOT including
 
1100
that record. Infimum and supremum records are not deleted. */
 
1101
UNIV_INTERN
 
1102
void
 
1103
page_delete_rec_list_start(
 
1104
/*=======================*/
 
1105
        rec_t*          rec,    /*!< in: record on page */
 
1106
        buf_block_t*    block,  /*!< in: buffer block of the page */
 
1107
        dict_index_t*   index,  /*!< in: record descriptor */
 
1108
        mtr_t*          mtr)    /*!< in: mtr */
 
1109
{
 
1110
        page_cur_t      cur1;
 
1111
        ulint           log_mode;
 
1112
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1113
        ulint*          offsets         = offsets_;
 
1114
        mem_heap_t*     heap            = NULL;
 
1115
        byte            type;
 
1116
 
 
1117
        rec_offs_init(offsets_);
 
1118
 
 
1119
        ut_ad((ibool) !!page_rec_is_comp(rec)
 
1120
              == dict_table_is_comp(index->table));
 
1121
#ifdef UNIV_ZIP_DEBUG
 
1122
        {
 
1123
                page_zip_des_t* page_zip= buf_block_get_page_zip(block);
 
1124
                page_t*         page    = buf_block_get_frame(block);
 
1125
 
 
1126
                /* page_zip_validate() would detect a min_rec_mark mismatch
 
1127
                in btr_page_split_and_insert()
 
1128
                between btr_attach_half_pages() and insert_page = ...
 
1129
                when btr_page_get_split_rec_to_left() holds
 
1130
                (direction == FSP_DOWN). */
 
1131
                ut_a(!page_zip
 
1132
                     || page_zip_validate_low(page_zip, page, index, TRUE));
 
1133
        }
 
1134
#endif /* UNIV_ZIP_DEBUG */
 
1135
 
 
1136
        if (page_rec_is_infimum(rec)) {
 
1137
 
 
1138
                return;
 
1139
        }
 
1140
 
 
1141
        if (page_rec_is_comp(rec)) {
 
1142
                type = MLOG_COMP_LIST_START_DELETE;
 
1143
        } else {
 
1144
                type = MLOG_LIST_START_DELETE;
 
1145
        }
 
1146
 
 
1147
        page_delete_rec_list_write_log(rec, index, type, mtr);
 
1148
 
 
1149
        page_cur_set_before_first(block, &cur1);
 
1150
        page_cur_move_to_next(&cur1);
 
1151
 
 
1152
        /* Individual deletes are not logged */
 
1153
 
 
1154
        log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
 
1155
 
 
1156
        while (page_cur_get_rec(&cur1) != rec) {
 
1157
                offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
 
1158
                                          offsets, ULINT_UNDEFINED, &heap);
 
1159
                page_cur_delete_rec(&cur1, index, offsets, mtr);
 
1160
        }
 
1161
 
 
1162
        if (UNIV_LIKELY_NULL(heap)) {
 
1163
                mem_heap_free(heap);
 
1164
        }
 
1165
 
 
1166
        /* Restore log mode */
 
1167
 
 
1168
        mtr_set_log_mode(mtr, log_mode);
 
1169
}
 
1170
 
 
1171
#ifndef UNIV_HOTBACKUP
 
1172
/*************************************************************//**
 
1173
Moves record list end to another page. Moved records include
 
1174
split_rec.
 
1175
@return TRUE on success; FALSE on compression failure (new_block will
 
1176
be decompressed) */
 
1177
UNIV_INTERN
 
1178
ibool
 
1179
page_move_rec_list_end(
 
1180
/*===================*/
 
1181
        buf_block_t*    new_block,      /*!< in/out: index page where to move */
 
1182
        buf_block_t*    block,          /*!< in: index page from where to move */
 
1183
        rec_t*          split_rec,      /*!< in: first record to move */
 
1184
        dict_index_t*   index,          /*!< in: record descriptor */
 
1185
        mtr_t*          mtr)            /*!< in: mtr */
 
1186
{
 
1187
        page_t*         new_page        = buf_block_get_frame(new_block);
 
1188
        ulint           old_data_size;
 
1189
        ulint           new_data_size;
 
1190
        ulint           old_n_recs;
 
1191
        ulint           new_n_recs;
 
1192
 
 
1193
        old_data_size = page_get_data_size(new_page);
 
1194
        old_n_recs = page_get_n_recs(new_page);
 
1195
#ifdef UNIV_ZIP_DEBUG
 
1196
        {
 
1197
                page_zip_des_t* new_page_zip
 
1198
                        = buf_block_get_page_zip(new_block);
 
1199
                page_zip_des_t* page_zip
 
1200
                        = buf_block_get_page_zip(block);
 
1201
                ut_a(!new_page_zip == !page_zip);
 
1202
                ut_a(!new_page_zip
 
1203
                     || page_zip_validate(new_page_zip, new_page, index));
 
1204
                ut_a(!page_zip
 
1205
                     || page_zip_validate(page_zip, page_align(split_rec),
 
1206
                                          index));
 
1207
        }
 
1208
#endif /* UNIV_ZIP_DEBUG */
 
1209
 
 
1210
        if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block,
 
1211
                                                  split_rec, index, mtr))) {
 
1212
                return(FALSE);
 
1213
        }
 
1214
 
 
1215
        new_data_size = page_get_data_size(new_page);
 
1216
        new_n_recs = page_get_n_recs(new_page);
 
1217
 
 
1218
        ut_ad(new_data_size >= old_data_size);
 
1219
 
 
1220
        page_delete_rec_list_end(split_rec, block, index,
 
1221
                                 new_n_recs - old_n_recs,
 
1222
                                 new_data_size - old_data_size, mtr);
 
1223
 
 
1224
        return(TRUE);
 
1225
}
 
1226
 
 
1227
/*************************************************************//**
 
1228
Moves record list start to another page. Moved records do not include
 
1229
split_rec.
 
1230
@return TRUE on success; FALSE on compression failure */
 
1231
UNIV_INTERN
 
1232
ibool
 
1233
page_move_rec_list_start(
 
1234
/*=====================*/
 
1235
        buf_block_t*    new_block,      /*!< in/out: index page where to move */
 
1236
        buf_block_t*    block,          /*!< in/out: page containing split_rec */
 
1237
        rec_t*          split_rec,      /*!< in: first record not to move */
 
1238
        dict_index_t*   index,          /*!< in: record descriptor */
 
1239
        mtr_t*          mtr)            /*!< in: mtr */
 
1240
{
 
1241
        if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block,
 
1242
                                                    split_rec, index, mtr))) {
 
1243
                return(FALSE);
 
1244
        }
 
1245
 
 
1246
        page_delete_rec_list_start(split_rec, block, index, mtr);
 
1247
 
 
1248
        return(TRUE);
 
1249
}
 
1250
#endif /* !UNIV_HOTBACKUP */
 
1251
 
 
1252
/**************************************************************//**
 
1253
Used to delete n slots from the directory. This function updates
 
1254
also n_owned fields in the records, so that the first slot after
 
1255
the deleted ones inherits the records of the deleted slots. */
 
1256
UNIV_INLINE
 
1257
void
 
1258
page_dir_delete_slot(
 
1259
/*=================*/
 
1260
        page_t*         page,   /*!< in/out: the index page */
 
1261
        page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
 
1262
        ulint           slot_no)/*!< in: slot to be deleted */
 
1263
{
 
1264
        page_dir_slot_t*        slot;
 
1265
        ulint                   n_owned;
 
1266
        ulint                   i;
 
1267
        ulint                   n_slots;
 
1268
 
 
1269
        ut_ad(!page_zip || page_is_comp(page));
 
1270
        ut_ad(slot_no > 0);
 
1271
        ut_ad(slot_no + 1 < page_dir_get_n_slots(page));
 
1272
 
 
1273
        n_slots = page_dir_get_n_slots(page);
 
1274
 
 
1275
        /* 1. Reset the n_owned fields of the slots to be
 
1276
        deleted */
 
1277
        slot = page_dir_get_nth_slot(page, slot_no);
 
1278
        n_owned = page_dir_slot_get_n_owned(slot);
 
1279
        page_dir_slot_set_n_owned(slot, page_zip, 0);
 
1280
 
 
1281
        /* 2. Update the n_owned value of the first non-deleted slot */
 
1282
 
 
1283
        slot = page_dir_get_nth_slot(page, slot_no + 1);
 
1284
        page_dir_slot_set_n_owned(slot, page_zip,
 
1285
                                  n_owned + page_dir_slot_get_n_owned(slot));
 
1286
 
 
1287
        /* 3. Destroy the slot by copying slots */
 
1288
        for (i = slot_no + 1; i < n_slots; i++) {
 
1289
                rec_t*  rec = (rec_t*)
 
1290
                        page_dir_slot_get_rec(page_dir_get_nth_slot(page, i));
 
1291
                page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec);
 
1292
        }
 
1293
 
 
1294
        /* 4. Zero out the last slot, which will be removed */
 
1295
        mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0);
 
1296
 
 
1297
        /* 5. Update the page header */
 
1298
        page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1);
 
1299
}
 
1300
 
 
1301
/**************************************************************//**
 
1302
Used to add n slots to the directory. Does not set the record pointers
 
1303
in the added slots or update n_owned values: this is the responsibility
 
1304
of the caller. */
 
1305
UNIV_INLINE
 
1306
void
 
1307
page_dir_add_slot(
 
1308
/*==============*/
 
1309
        page_t*         page,   /*!< in/out: the index page */
 
1310
        page_zip_des_t* page_zip,/*!< in/out: comprssed page, or NULL */
 
1311
        ulint           start)  /*!< in: the slot above which the new slots
 
1312
                                are added */
 
1313
{
 
1314
        page_dir_slot_t*        slot;
 
1315
        ulint                   n_slots;
 
1316
 
 
1317
        n_slots = page_dir_get_n_slots(page);
 
1318
 
 
1319
        ut_ad(start < n_slots - 1);
 
1320
 
 
1321
        /* Update the page header */
 
1322
        page_dir_set_n_slots(page, page_zip, n_slots + 1);
 
1323
 
 
1324
        /* Move slots up */
 
1325
        slot = page_dir_get_nth_slot(page, n_slots);
 
1326
        memmove(slot, slot + PAGE_DIR_SLOT_SIZE,
 
1327
                (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE);
 
1328
}
 
1329
 
 
1330
/****************************************************************//**
 
1331
Splits a directory slot which owns too many records. */
 
1332
UNIV_INTERN
 
1333
void
 
1334
page_dir_split_slot(
 
1335
/*================*/
 
1336
        page_t*         page,   /*!< in/out: index page */
 
1337
        page_zip_des_t* page_zip,/*!< in/out: compressed page whose
 
1338
                                uncompressed part will be written, or NULL */
 
1339
        ulint           slot_no)/*!< in: the directory slot */
 
1340
{
 
1341
        rec_t*                  rec;
 
1342
        page_dir_slot_t*        new_slot;
 
1343
        page_dir_slot_t*        prev_slot;
 
1344
        page_dir_slot_t*        slot;
 
1345
        ulint                   i;
 
1346
        ulint                   n_owned;
 
1347
 
 
1348
        ut_ad(page);
 
1349
        ut_ad(!page_zip || page_is_comp(page));
 
1350
        ut_ad(slot_no > 0);
 
1351
 
 
1352
        slot = page_dir_get_nth_slot(page, slot_no);
 
1353
 
 
1354
        n_owned = page_dir_slot_get_n_owned(slot);
 
1355
        ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
 
1356
 
 
1357
        /* 1. We loop to find a record approximately in the middle of the
 
1358
        records owned by the slot. */
 
1359
 
 
1360
        prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
 
1361
        rec = (rec_t*) page_dir_slot_get_rec(prev_slot);
 
1362
 
 
1363
        for (i = 0; i < n_owned / 2; i++) {
 
1364
                rec = page_rec_get_next(rec);
 
1365
        }
 
1366
 
 
1367
        ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
 
1368
 
 
1369
        /* 2. We add one directory slot immediately below the slot to be
 
1370
        split. */
 
1371
 
 
1372
        page_dir_add_slot(page, page_zip, slot_no - 1);
 
1373
 
 
1374
        /* The added slot is now number slot_no, and the old slot is
 
1375
        now number slot_no + 1 */
 
1376
 
 
1377
        new_slot = page_dir_get_nth_slot(page, slot_no);
 
1378
        slot = page_dir_get_nth_slot(page, slot_no + 1);
 
1379
 
 
1380
        /* 3. We store the appropriate values to the new slot. */
 
1381
 
 
1382
        page_dir_slot_set_rec(new_slot, rec);
 
1383
        page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2);
 
1384
 
 
1385
        /* 4. Finally, we update the number of records field of the
 
1386
        original slot */
 
1387
 
 
1388
        page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2));
 
1389
}
 
1390
 
 
1391
/*************************************************************//**
 
1392
Tries to balance the given directory slot with too few records with the upper
 
1393
neighbor, so that there are at least the minimum number of records owned by
 
1394
the slot; this may result in the merging of two slots. */
 
1395
UNIV_INTERN
 
1396
void
 
1397
page_dir_balance_slot(
 
1398
/*==================*/
 
1399
        page_t*         page,   /*!< in/out: index page */
 
1400
        page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
 
1401
        ulint           slot_no)/*!< in: the directory slot */
 
1402
{
 
1403
        page_dir_slot_t*        slot;
 
1404
        page_dir_slot_t*        up_slot;
 
1405
        ulint                   n_owned;
 
1406
        ulint                   up_n_owned;
 
1407
        rec_t*                  old_rec;
 
1408
        rec_t*                  new_rec;
 
1409
 
 
1410
        ut_ad(page);
 
1411
        ut_ad(!page_zip || page_is_comp(page));
 
1412
        ut_ad(slot_no > 0);
 
1413
 
 
1414
        slot = page_dir_get_nth_slot(page, slot_no);
 
1415
 
 
1416
        /* The last directory slot cannot be balanced with the upper
 
1417
        neighbor, as there is none. */
 
1418
 
 
1419
        if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) {
 
1420
 
 
1421
                return;
 
1422
        }
 
1423
 
 
1424
        up_slot = page_dir_get_nth_slot(page, slot_no + 1);
 
1425
 
 
1426
        n_owned = page_dir_slot_get_n_owned(slot);
 
1427
        up_n_owned = page_dir_slot_get_n_owned(up_slot);
 
1428
 
 
1429
        ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
 
1430
 
 
1431
        /* If the upper slot has the minimum value of n_owned, we will merge
 
1432
        the two slots, therefore we assert: */
 
1433
        ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
 
1434
 
 
1435
        if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
 
1436
 
 
1437
                /* In this case we can just transfer one record owned
 
1438
                by the upper slot to the property of the lower slot */
 
1439
                old_rec = (rec_t*) page_dir_slot_get_rec(slot);
 
1440
 
 
1441
                if (page_is_comp(page)) {
 
1442
                        new_rec = rec_get_next_ptr(old_rec, TRUE);
 
1443
 
 
1444
                        rec_set_n_owned_new(old_rec, page_zip, 0);
 
1445
                        rec_set_n_owned_new(new_rec, page_zip, n_owned + 1);
 
1446
                } else {
 
1447
                        new_rec = rec_get_next_ptr(old_rec, FALSE);
 
1448
 
 
1449
                        rec_set_n_owned_old(old_rec, 0);
 
1450
                        rec_set_n_owned_old(new_rec, n_owned + 1);
 
1451
                }
 
1452
 
 
1453
                page_dir_slot_set_rec(slot, new_rec);
 
1454
 
 
1455
                page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1);
 
1456
        } else {
 
1457
                /* In this case we may merge the two slots */
 
1458
                page_dir_delete_slot(page, page_zip, slot_no);
 
1459
        }
 
1460
}
 
1461
 
 
1462
/************************************************************//**
 
1463
Returns the nth record of the record list.
 
1464
This is the inverse function of page_rec_get_n_recs_before().
 
1465
@return nth record */
 
1466
UNIV_INTERN
 
1467
const rec_t*
 
1468
page_rec_get_nth_const(
 
1469
/*===================*/
 
1470
        const page_t*   page,   /*!< in: page */
 
1471
        ulint           nth)    /*!< in: nth record */
 
1472
{
 
1473
        const page_dir_slot_t*  slot;
 
1474
        ulint                   i;
 
1475
        ulint                   n_owned;
 
1476
        const rec_t*            rec;
 
1477
 
 
1478
        if (nth == 0) {
 
1479
                return(page_get_infimum_rec(page));
 
1480
        }
 
1481
 
 
1482
        ut_ad(nth < UNIV_PAGE_SIZE / (REC_N_NEW_EXTRA_BYTES + 1));
 
1483
 
 
1484
        for (i = 0;; i++) {
 
1485
 
 
1486
                slot = page_dir_get_nth_slot(page, i);
 
1487
                n_owned = page_dir_slot_get_n_owned(slot);
 
1488
 
 
1489
                if (n_owned > nth) {
 
1490
                        break;
 
1491
                } else {
 
1492
                        nth -= n_owned;
 
1493
                }
 
1494
        }
 
1495
 
 
1496
        ut_ad(i > 0);
 
1497
        slot = page_dir_get_nth_slot(page, i - 1);
 
1498
        rec = page_dir_slot_get_rec(slot);
 
1499
 
 
1500
        if (page_is_comp(page)) {
 
1501
                do {
 
1502
                        rec = page_rec_get_next_low(rec, TRUE);
 
1503
                        ut_ad(rec);
 
1504
                } while (nth--);
 
1505
        } else {
 
1506
                do {
 
1507
                        rec = page_rec_get_next_low(rec, FALSE);
 
1508
                        ut_ad(rec);
 
1509
                } while (nth--);
 
1510
        }
 
1511
 
 
1512
        return(rec);
 
1513
}
 
1514
 
 
1515
/***************************************************************//**
 
1516
Returns the number of records before the given record in chain.
 
1517
The number includes infimum and supremum records.
 
1518
@return number of records */
 
1519
UNIV_INTERN
 
1520
ulint
 
1521
page_rec_get_n_recs_before(
 
1522
/*=======================*/
 
1523
        const rec_t*    rec)    /*!< in: the physical record */
 
1524
{
 
1525
        const page_dir_slot_t*  slot;
 
1526
        const rec_t*            slot_rec;
 
1527
        const page_t*           page;
 
1528
        ulint                   i;
 
1529
        lint                    n       = 0;
 
1530
 
 
1531
        ut_ad(page_rec_check(rec));
 
1532
 
 
1533
        page = page_align(rec);
 
1534
        if (page_is_comp(page)) {
 
1535
                while (rec_get_n_owned_new(rec) == 0) {
 
1536
 
 
1537
                        rec = rec_get_next_ptr_const(rec, TRUE);
 
1538
                        n--;
 
1539
                }
 
1540
 
 
1541
                for (i = 0; ; i++) {
 
1542
                        slot = page_dir_get_nth_slot(page, i);
 
1543
                        slot_rec = page_dir_slot_get_rec(slot);
 
1544
 
 
1545
                        n += rec_get_n_owned_new(slot_rec);
 
1546
 
 
1547
                        if (rec == slot_rec) {
 
1548
 
 
1549
                                break;
 
1550
                        }
 
1551
                }
 
1552
        } else {
 
1553
                while (rec_get_n_owned_old(rec) == 0) {
 
1554
 
 
1555
                        rec = rec_get_next_ptr_const(rec, FALSE);
 
1556
                        n--;
 
1557
                }
 
1558
 
 
1559
                for (i = 0; ; i++) {
 
1560
                        slot = page_dir_get_nth_slot(page, i);
 
1561
                        slot_rec = page_dir_slot_get_rec(slot);
 
1562
 
 
1563
                        n += rec_get_n_owned_old(slot_rec);
 
1564
 
 
1565
                        if (rec == slot_rec) {
 
1566
 
 
1567
                                break;
 
1568
                        }
 
1569
                }
 
1570
        }
 
1571
 
 
1572
        n--;
 
1573
 
 
1574
        ut_ad(n >= 0);
 
1575
        ut_ad(n < UNIV_PAGE_SIZE / (REC_N_NEW_EXTRA_BYTES + 1));
 
1576
 
 
1577
        return((ulint) n);
 
1578
}
 
1579
 
 
1580
#ifndef UNIV_HOTBACKUP
 
1581
/************************************************************//**
 
1582
Prints record contents including the data relevant only in
 
1583
the index page context. */
 
1584
UNIV_INTERN
 
1585
void
 
1586
page_rec_print(
 
1587
/*===========*/
 
1588
        const rec_t*    rec,    /*!< in: physical record */
 
1589
        const ulint*    offsets)/*!< in: record descriptor */
 
1590
{
 
1591
        ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
 
1592
        rec_print_new(stderr, rec, offsets);
 
1593
        if (page_rec_is_comp(rec)) {
 
1594
                fprintf(stderr,
 
1595
                        " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
 
1596
                        (ulong) rec_get_n_owned_new(rec),
 
1597
                        (ulong) rec_get_heap_no_new(rec),
 
1598
                        (ulong) rec_get_next_offs(rec, TRUE));
 
1599
        } else {
 
1600
                fprintf(stderr,
 
1601
                        " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
 
1602
                        (ulong) rec_get_n_owned_old(rec),
 
1603
                        (ulong) rec_get_heap_no_old(rec),
 
1604
                        (ulong) rec_get_next_offs(rec, FALSE));
 
1605
        }
 
1606
 
 
1607
        page_rec_check(rec);
 
1608
        rec_validate(rec, offsets);
 
1609
}
 
1610
 
 
1611
# ifdef UNIV_BTR_PRINT
 
1612
/***************************************************************//**
 
1613
This is used to print the contents of the directory for
 
1614
debugging purposes. */
 
1615
UNIV_INTERN
 
1616
void
 
1617
page_dir_print(
 
1618
/*===========*/
 
1619
        page_t* page,   /*!< in: index page */
 
1620
        ulint   pr_n)   /*!< in: print n first and n last entries */
 
1621
{
 
1622
        ulint                   n;
 
1623
        ulint                   i;
 
1624
        page_dir_slot_t*        slot;
 
1625
 
 
1626
        n = page_dir_get_n_slots(page);
 
1627
 
 
1628
        fprintf(stderr, "--------------------------------\n"
 
1629
                "PAGE DIRECTORY\n"
 
1630
                "Page address %p\n"
 
1631
                "Directory stack top at offs: %lu; number of slots: %lu\n",
 
1632
                page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)),
 
1633
                (ulong) n);
 
1634
        for (i = 0; i < n; i++) {
 
1635
                slot = page_dir_get_nth_slot(page, i);
 
1636
                if ((i == pr_n) && (i < n - pr_n)) {
 
1637
                        fputs("    ...   \n", stderr);
 
1638
                }
 
1639
                if ((i < pr_n) || (i >= n - pr_n)) {
 
1640
                        fprintf(stderr,
 
1641
                                "Contents of slot: %lu: n_owned: %lu,"
 
1642
                                " rec offs: %lu\n",
 
1643
                                (ulong) i,
 
1644
                                (ulong) page_dir_slot_get_n_owned(slot),
 
1645
                                (ulong)
 
1646
                                page_offset(page_dir_slot_get_rec(slot)));
 
1647
                }
 
1648
        }
 
1649
        fprintf(stderr, "Total of %lu records\n"
 
1650
                "--------------------------------\n",
 
1651
                (ulong) (PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page)));
 
1652
}
 
1653
 
 
1654
/***************************************************************//**
 
1655
This is used to print the contents of the page record list for
 
1656
debugging purposes. */
 
1657
UNIV_INTERN
 
1658
void
 
1659
page_print_list(
 
1660
/*============*/
 
1661
        buf_block_t*    block,  /*!< in: index page */
 
1662
        dict_index_t*   index,  /*!< in: dictionary index of the page */
 
1663
        ulint           pr_n)   /*!< in: print n first and n last entries */
 
1664
{
 
1665
        page_t*         page            = block->frame;
 
1666
        page_cur_t      cur;
 
1667
        ulint           count;
 
1668
        ulint           n_recs;
 
1669
        mem_heap_t*     heap            = NULL;
 
1670
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1671
        ulint*          offsets         = offsets_;
 
1672
        rec_offs_init(offsets_);
 
1673
 
 
1674
        ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table));
 
1675
 
 
1676
        fprintf(stderr,
 
1677
                "--------------------------------\n"
 
1678
                "PAGE RECORD LIST\n"
 
1679
                "Page address %p\n", page);
 
1680
 
 
1681
        n_recs = page_get_n_recs(page);
 
1682
 
 
1683
        page_cur_set_before_first(block, &cur);
 
1684
        count = 0;
 
1685
        for (;;) {
 
1686
                offsets = rec_get_offsets(cur.rec, index, offsets,
 
1687
                                          ULINT_UNDEFINED, &heap);
 
1688
                page_rec_print(cur.rec, offsets);
 
1689
 
 
1690
                if (count == pr_n) {
 
1691
                        break;
 
1692
                }
 
1693
                if (page_cur_is_after_last(&cur)) {
 
1694
                        break;
 
1695
                }
 
1696
                page_cur_move_to_next(&cur);
 
1697
                count++;
 
1698
        }
 
1699
 
 
1700
        if (n_recs > 2 * pr_n) {
 
1701
                fputs(" ... \n", stderr);
 
1702
        }
 
1703
 
 
1704
        while (!page_cur_is_after_last(&cur)) {
 
1705
                page_cur_move_to_next(&cur);
 
1706
 
 
1707
                if (count + pr_n >= n_recs) {
 
1708
                        offsets = rec_get_offsets(cur.rec, index, offsets,
 
1709
                                                  ULINT_UNDEFINED, &heap);
 
1710
                        page_rec_print(cur.rec, offsets);
 
1711
                }
 
1712
                count++;
 
1713
        }
 
1714
 
 
1715
        fprintf(stderr,
 
1716
                "Total of %lu records \n"
 
1717
                "--------------------------------\n",
 
1718
                (ulong) (count + 1));
 
1719
 
 
1720
        if (UNIV_LIKELY_NULL(heap)) {
 
1721
                mem_heap_free(heap);
 
1722
        }
 
1723
}
 
1724
 
 
1725
/***************************************************************//**
 
1726
Prints the info in a page header. */
 
1727
UNIV_INTERN
 
1728
void
 
1729
page_header_print(
 
1730
/*==============*/
 
1731
        const page_t*   page)
 
1732
{
 
1733
        fprintf(stderr,
 
1734
                "--------------------------------\n"
 
1735
                "PAGE HEADER INFO\n"
 
1736
                "Page address %p, n records %lu (%s)\n"
 
1737
                "n dir slots %lu, heap top %lu\n"
 
1738
                "Page n heap %lu, free %lu, garbage %lu\n"
 
1739
                "Page last insert %lu, direction %lu, n direction %lu\n",
 
1740
                page, (ulong) page_header_get_field(page, PAGE_N_RECS),
 
1741
                page_is_comp(page) ? "compact format" : "original format",
 
1742
                (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
 
1743
                (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
 
1744
                (ulong) page_dir_get_n_heap(page),
 
1745
                (ulong) page_header_get_field(page, PAGE_FREE),
 
1746
                (ulong) page_header_get_field(page, PAGE_GARBAGE),
 
1747
                (ulong) page_header_get_field(page, PAGE_LAST_INSERT),
 
1748
                (ulong) page_header_get_field(page, PAGE_DIRECTION),
 
1749
                (ulong) page_header_get_field(page, PAGE_N_DIRECTION));
 
1750
}
 
1751
 
 
1752
/***************************************************************//**
 
1753
This is used to print the contents of the page for
 
1754
debugging purposes. */
 
1755
UNIV_INTERN
 
1756
void
 
1757
page_print(
 
1758
/*=======*/
 
1759
        buf_block_t*    block,  /*!< in: index page */
 
1760
        dict_index_t*   index,  /*!< in: dictionary index of the page */
 
1761
        ulint           dn,     /*!< in: print dn first and last entries
 
1762
                                in directory */
 
1763
        ulint           rn)     /*!< in: print rn first and last records
 
1764
                                in directory */
 
1765
{
 
1766
        page_t* page = block->frame;
 
1767
 
 
1768
        page_header_print(page);
 
1769
        page_dir_print(page, dn);
 
1770
        page_print_list(block, index, rn);
 
1771
}
 
1772
# endif /* UNIV_BTR_PRINT */
 
1773
#endif /* !UNIV_HOTBACKUP */
 
1774
 
 
1775
/***************************************************************//**
 
1776
The following is used to validate a record on a page. This function
 
1777
differs from rec_validate as it can also check the n_owned field and
 
1778
the heap_no field.
 
1779
@return TRUE if ok */
 
1780
UNIV_INTERN
 
1781
ibool
 
1782
page_rec_validate(
 
1783
/*==============*/
 
1784
        const rec_t*    rec,    /*!< in: physical record */
 
1785
        const ulint*    offsets)/*!< in: array returned by rec_get_offsets() */
 
1786
{
 
1787
        ulint           n_owned;
 
1788
        ulint           heap_no;
 
1789
        const page_t*   page;
 
1790
 
 
1791
        page = page_align(rec);
 
1792
        ut_a(!page_is_comp(page) == !rec_offs_comp(offsets));
 
1793
 
 
1794
        page_rec_check(rec);
 
1795
        rec_validate(rec, offsets);
 
1796
 
 
1797
        if (page_rec_is_comp(rec)) {
 
1798
                n_owned = rec_get_n_owned_new(rec);
 
1799
                heap_no = rec_get_heap_no_new(rec);
 
1800
        } else {
 
1801
                n_owned = rec_get_n_owned_old(rec);
 
1802
                heap_no = rec_get_heap_no_old(rec);
 
1803
        }
 
1804
 
 
1805
        if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) {
 
1806
                fprintf(stderr,
 
1807
                        "InnoDB: Dir slot of rec %lu, n owned too big %lu\n",
 
1808
                        (ulong) page_offset(rec), (ulong) n_owned);
 
1809
                return(FALSE);
 
1810
        }
 
1811
 
 
1812
        if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) {
 
1813
                fprintf(stderr,
 
1814
                        "InnoDB: Heap no of rec %lu too big %lu %lu\n",
 
1815
                        (ulong) page_offset(rec), (ulong) heap_no,
 
1816
                        (ulong) page_dir_get_n_heap(page));
 
1817
                return(FALSE);
 
1818
        }
 
1819
 
 
1820
        return(TRUE);
 
1821
}
 
1822
 
 
1823
#ifndef UNIV_HOTBACKUP
 
1824
/***************************************************************//**
 
1825
Checks that the first directory slot points to the infimum record and
 
1826
the last to the supremum. This function is intended to track if the
 
1827
bug fixed in 4.0.14 has caused corruption to users' databases. */
 
1828
UNIV_INTERN
 
1829
void
 
1830
page_check_dir(
 
1831
/*===========*/
 
1832
        const page_t*   page)   /*!< in: index page */
 
1833
{
 
1834
        ulint   n_slots;
 
1835
        ulint   infimum_offs;
 
1836
        ulint   supremum_offs;
 
1837
 
 
1838
        n_slots = page_dir_get_n_slots(page);
 
1839
        infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0));
 
1840
        supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page,
 
1841
                                                               n_slots - 1));
 
1842
 
 
1843
        if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) {
 
1844
 
 
1845
                fprintf(stderr,
 
1846
                        "InnoDB: Page directory corruption:"
 
1847
                        " infimum not pointed to\n");
 
1848
                buf_page_print(page, 0, 0);
 
1849
        }
 
1850
 
 
1851
        if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) {
 
1852
 
 
1853
                fprintf(stderr,
 
1854
                        "InnoDB: Page directory corruption:"
 
1855
                        " supremum not pointed to\n");
 
1856
                buf_page_print(page, 0, 0);
 
1857
        }
 
1858
}
 
1859
#endif /* !UNIV_HOTBACKUP */
 
1860
 
 
1861
/***************************************************************//**
 
1862
This function checks the consistency of an index page when we do not
 
1863
know the index. This is also resilient so that this should never crash
 
1864
even if the page is total garbage.
 
1865
@return TRUE if ok */
 
1866
UNIV_INTERN
 
1867
ibool
 
1868
page_simple_validate_old(
 
1869
/*=====================*/
 
1870
        const page_t*   page)   /*!< in: index page in ROW_FORMAT=REDUNDANT */
 
1871
{
 
1872
        const page_dir_slot_t*  slot;
 
1873
        ulint                   slot_no;
 
1874
        ulint                   n_slots;
 
1875
        const rec_t*            rec;
 
1876
        const byte*             rec_heap_top;
 
1877
        ulint                   count;
 
1878
        ulint                   own_count;
 
1879
        ibool                   ret     = FALSE;
 
1880
 
 
1881
        ut_a(!page_is_comp(page));
 
1882
 
 
1883
        /* Check first that the record heap and the directory do not
 
1884
        overlap. */
 
1885
 
 
1886
        n_slots = page_dir_get_n_slots(page);
 
1887
 
 
1888
        if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
 
1889
                fprintf(stderr,
 
1890
                        "InnoDB: Nonsensical number %lu of page dir slots\n",
 
1891
                        (ulong) n_slots);
 
1892
 
 
1893
                goto func_exit;
 
1894
        }
 
1895
 
 
1896
        rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
 
1897
 
 
1898
        if (UNIV_UNLIKELY(rec_heap_top
 
1899
                          > page_dir_get_nth_slot(page, n_slots - 1))) {
 
1900
 
 
1901
                fprintf(stderr,
 
1902
                        "InnoDB: Record heap and dir overlap on a page,"
 
1903
                        " heap top %lu, dir %lu\n",
 
1904
                        (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
 
1905
                        (ulong)
 
1906
                        page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
 
1907
 
 
1908
                goto func_exit;
 
1909
        }
 
1910
 
 
1911
        /* Validate the record list in a loop checking also that it is
 
1912
        consistent with the page record directory. */
 
1913
 
 
1914
        count = 0;
 
1915
        own_count = 1;
 
1916
        slot_no = 0;
 
1917
        slot = page_dir_get_nth_slot(page, slot_no);
 
1918
 
 
1919
        rec = page_get_infimum_rec(page);
 
1920
 
 
1921
        for (;;) {
 
1922
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
 
1923
                        fprintf(stderr,
 
1924
                                "InnoDB: Record %lu is above"
 
1925
                                " rec heap top %lu\n",
 
1926
                                (ulong)(rec - page),
 
1927
                                (ulong)(rec_heap_top - page));
 
1928
 
 
1929
                        goto func_exit;
 
1930
                }
 
1931
 
 
1932
                if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) {
 
1933
                        /* This is a record pointed to by a dir slot */
 
1934
                        if (UNIV_UNLIKELY(rec_get_n_owned_old(rec)
 
1935
                                          != own_count)) {
 
1936
 
 
1937
                                fprintf(stderr,
 
1938
                                        "InnoDB: Wrong owned count %lu, %lu,"
 
1939
                                        " rec %lu\n",
 
1940
                                        (ulong) rec_get_n_owned_old(rec),
 
1941
                                        (ulong) own_count,
 
1942
                                        (ulong)(rec - page));
 
1943
 
 
1944
                                goto func_exit;
 
1945
                        }
 
1946
 
 
1947
                        if (UNIV_UNLIKELY
 
1948
                            (page_dir_slot_get_rec(slot) != rec)) {
 
1949
                                fprintf(stderr,
 
1950
                                        "InnoDB: Dir slot does not point"
 
1951
                                        " to right rec %lu\n",
 
1952
                                        (ulong)(rec - page));
 
1953
 
 
1954
                                goto func_exit;
 
1955
                        }
 
1956
 
 
1957
                        own_count = 0;
 
1958
 
 
1959
                        if (!page_rec_is_supremum(rec)) {
 
1960
                                slot_no++;
 
1961
                                slot = page_dir_get_nth_slot(page, slot_no);
 
1962
                        }
 
1963
                }
 
1964
 
 
1965
                if (page_rec_is_supremum(rec)) {
 
1966
 
 
1967
                        break;
 
1968
                }
 
1969
 
 
1970
                if (UNIV_UNLIKELY
 
1971
                    (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA
 
1972
                     || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) {
 
1973
                        fprintf(stderr,
 
1974
                                "InnoDB: Next record offset"
 
1975
                                " nonsensical %lu for rec %lu\n",
 
1976
                                (ulong) rec_get_next_offs(rec, FALSE),
 
1977
                                (ulong) (rec - page));
 
1978
 
 
1979
                        goto func_exit;
 
1980
                }
 
1981
 
 
1982
                count++;
 
1983
 
 
1984
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
 
1985
                        fprintf(stderr,
 
1986
                                "InnoDB: Page record list appears"
 
1987
                                " to be circular %lu\n",
 
1988
                                (ulong) count);
 
1989
                        goto func_exit;
 
1990
                }
 
1991
 
 
1992
                rec = page_rec_get_next_const(rec);
 
1993
                own_count++;
 
1994
        }
 
1995
 
 
1996
        if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
 
1997
                fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n");
 
1998
 
 
1999
                goto func_exit;
 
2000
        }
 
2001
 
 
2002
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
 
2003
                fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
 
2004
                        (ulong) slot_no, (ulong) (n_slots - 1));
 
2005
                goto func_exit;
 
2006
        }
 
2007
 
 
2008
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
 
2009
                          + PAGE_HEAP_NO_USER_LOW
 
2010
                          != count + 1)) {
 
2011
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
 
2012
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
 
2013
                        + PAGE_HEAP_NO_USER_LOW,
 
2014
                        (ulong) (count + 1));
 
2015
 
 
2016
                goto func_exit;
 
2017
        }
 
2018
 
 
2019
        /* Check then the free list */
 
2020
        rec = page_header_get_ptr(page, PAGE_FREE);
 
2021
 
 
2022
        while (rec != NULL) {
 
2023
                if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
 
2024
                                  || rec >= page + UNIV_PAGE_SIZE)) {
 
2025
                        fprintf(stderr,
 
2026
                                "InnoDB: Free list record has"
 
2027
                                " a nonsensical offset %lu\n",
 
2028
                                (ulong) (rec - page));
 
2029
 
 
2030
                        goto func_exit;
 
2031
                }
 
2032
 
 
2033
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
 
2034
                        fprintf(stderr,
 
2035
                                "InnoDB: Free list record %lu"
 
2036
                                " is above rec heap top %lu\n",
 
2037
                                (ulong) (rec - page),
 
2038
                                (ulong) (rec_heap_top - page));
 
2039
 
 
2040
                        goto func_exit;
 
2041
                }
 
2042
 
 
2043
                count++;
 
2044
 
 
2045
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
 
2046
                        fprintf(stderr,
 
2047
                                "InnoDB: Page free list appears"
 
2048
                                " to be circular %lu\n",
 
2049
                                (ulong) count);
 
2050
                        goto func_exit;
 
2051
                }
 
2052
 
 
2053
                rec = page_rec_get_next_const(rec);
 
2054
        }
 
2055
 
 
2056
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
 
2057
 
 
2058
                fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
 
2059
                        (ulong) page_dir_get_n_heap(page),
 
2060
                        (ulong) (count + 1));
 
2061
 
 
2062
                goto func_exit;
 
2063
        }
 
2064
 
 
2065
        ret = TRUE;
 
2066
 
 
2067
func_exit:
 
2068
        return(ret);
 
2069
}
 
2070
 
 
2071
/***************************************************************//**
 
2072
This function checks the consistency of an index page when we do not
 
2073
know the index. This is also resilient so that this should never crash
 
2074
even if the page is total garbage.
 
2075
@return TRUE if ok */
 
2076
UNIV_INTERN
 
2077
ibool
 
2078
page_simple_validate_new(
 
2079
/*=====================*/
 
2080
        const page_t*   page)   /*!< in: index page in ROW_FORMAT!=REDUNDANT */
 
2081
{
 
2082
        const page_dir_slot_t*  slot;
 
2083
        ulint                   slot_no;
 
2084
        ulint                   n_slots;
 
2085
        const rec_t*            rec;
 
2086
        const byte*             rec_heap_top;
 
2087
        ulint                   count;
 
2088
        ulint                   own_count;
 
2089
        ibool                   ret     = FALSE;
 
2090
 
 
2091
        ut_a(page_is_comp(page));
 
2092
 
 
2093
        /* Check first that the record heap and the directory do not
 
2094
        overlap. */
 
2095
 
 
2096
        n_slots = page_dir_get_n_slots(page);
 
2097
 
 
2098
        if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
 
2099
                fprintf(stderr,
 
2100
                        "InnoDB: Nonsensical number %lu"
 
2101
                        " of page dir slots\n", (ulong) n_slots);
 
2102
 
 
2103
                goto func_exit;
 
2104
        }
 
2105
 
 
2106
        rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
 
2107
 
 
2108
        if (UNIV_UNLIKELY(rec_heap_top
 
2109
                          > page_dir_get_nth_slot(page, n_slots - 1))) {
 
2110
 
 
2111
                fprintf(stderr,
 
2112
                        "InnoDB: Record heap and dir overlap on a page,"
 
2113
                        " heap top %lu, dir %lu\n",
 
2114
                        (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
 
2115
                        (ulong)
 
2116
                        page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
 
2117
 
 
2118
                goto func_exit;
 
2119
        }
 
2120
 
 
2121
        /* Validate the record list in a loop checking also that it is
 
2122
        consistent with the page record directory. */
 
2123
 
 
2124
        count = 0;
 
2125
        own_count = 1;
 
2126
        slot_no = 0;
 
2127
        slot = page_dir_get_nth_slot(page, slot_no);
 
2128
 
 
2129
        rec = page_get_infimum_rec(page);
 
2130
 
 
2131
        for (;;) {
 
2132
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
 
2133
                        fprintf(stderr,
 
2134
                                "InnoDB: Record %lu is above rec"
 
2135
                                " heap top %lu\n",
 
2136
                                (ulong) page_offset(rec),
 
2137
                                (ulong) page_offset(rec_heap_top));
 
2138
 
 
2139
                        goto func_exit;
 
2140
                }
 
2141
 
 
2142
                if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) {
 
2143
                        /* This is a record pointed to by a dir slot */
 
2144
                        if (UNIV_UNLIKELY(rec_get_n_owned_new(rec)
 
2145
                                          != own_count)) {
 
2146
 
 
2147
                                fprintf(stderr,
 
2148
                                        "InnoDB: Wrong owned count %lu, %lu,"
 
2149
                                        " rec %lu\n",
 
2150
                                        (ulong) rec_get_n_owned_new(rec),
 
2151
                                        (ulong) own_count,
 
2152
                                        (ulong) page_offset(rec));
 
2153
 
 
2154
                                goto func_exit;
 
2155
                        }
 
2156
 
 
2157
                        if (UNIV_UNLIKELY
 
2158
                            (page_dir_slot_get_rec(slot) != rec)) {
 
2159
                                fprintf(stderr,
 
2160
                                        "InnoDB: Dir slot does not point"
 
2161
                                        " to right rec %lu\n",
 
2162
                                        (ulong) page_offset(rec));
 
2163
 
 
2164
                                goto func_exit;
 
2165
                        }
 
2166
 
 
2167
                        own_count = 0;
 
2168
 
 
2169
                        if (!page_rec_is_supremum(rec)) {
 
2170
                                slot_no++;
 
2171
                                slot = page_dir_get_nth_slot(page, slot_no);
 
2172
                        }
 
2173
                }
 
2174
 
 
2175
                if (page_rec_is_supremum(rec)) {
 
2176
 
 
2177
                        break;
 
2178
                }
 
2179
 
 
2180
                if (UNIV_UNLIKELY
 
2181
                    (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA
 
2182
                     || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) {
 
2183
                        fprintf(stderr,
 
2184
                                "InnoDB: Next record offset nonsensical %lu"
 
2185
                                " for rec %lu\n",
 
2186
                                (ulong) rec_get_next_offs(rec, TRUE),
 
2187
                                (ulong) page_offset(rec));
 
2188
 
 
2189
                        goto func_exit;
 
2190
                }
 
2191
 
 
2192
                count++;
 
2193
 
 
2194
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
 
2195
                        fprintf(stderr,
 
2196
                                "InnoDB: Page record list appears"
 
2197
                                " to be circular %lu\n",
 
2198
                                (ulong) count);
 
2199
                        goto func_exit;
 
2200
                }
 
2201
 
 
2202
                rec = page_rec_get_next_const(rec);
 
2203
                own_count++;
 
2204
        }
 
2205
 
 
2206
        if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
 
2207
                fprintf(stderr, "InnoDB: n owned is zero"
 
2208
                        " in a supremum rec\n");
 
2209
 
 
2210
                goto func_exit;
 
2211
        }
 
2212
 
 
2213
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
 
2214
                fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
 
2215
                        (ulong) slot_no, (ulong) (n_slots - 1));
 
2216
                goto func_exit;
 
2217
        }
 
2218
 
 
2219
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
 
2220
                          + PAGE_HEAP_NO_USER_LOW
 
2221
                          != count + 1)) {
 
2222
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
 
2223
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
 
2224
                        + PAGE_HEAP_NO_USER_LOW,
 
2225
                        (ulong) (count + 1));
 
2226
 
 
2227
                goto func_exit;
 
2228
        }
 
2229
 
 
2230
        /* Check then the free list */
 
2231
        rec = page_header_get_ptr(page, PAGE_FREE);
 
2232
 
 
2233
        while (rec != NULL) {
 
2234
                if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
 
2235
                                  || rec >= page + UNIV_PAGE_SIZE)) {
 
2236
                        fprintf(stderr,
 
2237
                                "InnoDB: Free list record has"
 
2238
                                " a nonsensical offset %lu\n",
 
2239
                                (ulong) page_offset(rec));
 
2240
 
 
2241
                        goto func_exit;
 
2242
                }
 
2243
 
 
2244
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
 
2245
                        fprintf(stderr,
 
2246
                                "InnoDB: Free list record %lu"
 
2247
                                " is above rec heap top %lu\n",
 
2248
                                (ulong) page_offset(rec),
 
2249
                                (ulong) page_offset(rec_heap_top));
 
2250
 
 
2251
                        goto func_exit;
 
2252
                }
 
2253
 
 
2254
                count++;
 
2255
 
 
2256
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
 
2257
                        fprintf(stderr,
 
2258
                                "InnoDB: Page free list appears"
 
2259
                                " to be circular %lu\n",
 
2260
                                (ulong) count);
 
2261
                        goto func_exit;
 
2262
                }
 
2263
 
 
2264
                rec = page_rec_get_next_const(rec);
 
2265
        }
 
2266
 
 
2267
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
 
2268
 
 
2269
                fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
 
2270
                        (ulong) page_dir_get_n_heap(page),
 
2271
                        (ulong) (count + 1));
 
2272
 
 
2273
                goto func_exit;
 
2274
        }
 
2275
 
 
2276
        ret = TRUE;
 
2277
 
 
2278
func_exit:
 
2279
        return(ret);
 
2280
}
 
2281
 
 
2282
/***************************************************************//**
 
2283
This function checks the consistency of an index page.
 
2284
@return TRUE if ok */
 
2285
UNIV_INTERN
 
2286
ibool
 
2287
page_validate(
 
2288
/*==========*/
 
2289
        const page_t*   page,   /*!< in: index page */
 
2290
        dict_index_t*   index)  /*!< in: data dictionary index containing
 
2291
                                the page record type definition */
 
2292
{
 
2293
        const page_dir_slot_t*  slot;
 
2294
        mem_heap_t*             heap;
 
2295
        byte*                   buf;
 
2296
        ulint                   count;
 
2297
        ulint                   own_count;
 
2298
        ulint                   rec_own_count;
 
2299
        ulint                   slot_no;
 
2300
        ulint                   data_size;
 
2301
        const rec_t*            rec;
 
2302
        const rec_t*            old_rec         = NULL;
 
2303
        ulint                   offs;
 
2304
        ulint                   n_slots;
 
2305
        ibool                   ret             = FALSE;
 
2306
        ulint                   i;
 
2307
        ulint*                  offsets         = NULL;
 
2308
        ulint*                  old_offsets     = NULL;
 
2309
 
 
2310
        if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
 
2311
                          != dict_table_is_comp(index->table))) {
 
2312
                fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
 
2313
                goto func_exit2;
 
2314
        }
 
2315
        if (page_is_comp(page)) {
 
2316
                if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
 
2317
                        goto func_exit2;
 
2318
                }
 
2319
        } else {
 
2320
                if (UNIV_UNLIKELY(!page_simple_validate_old(page))) {
 
2321
                        goto func_exit2;
 
2322
                }
 
2323
        }
 
2324
 
 
2325
        heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
 
2326
 
 
2327
        /* The following buffer is used to check that the
 
2328
        records in the page record heap do not overlap */
 
2329
 
 
2330
        buf = mem_heap_zalloc(heap, UNIV_PAGE_SIZE);
 
2331
 
 
2332
        /* Check first that the record heap and the directory do not
 
2333
        overlap. */
 
2334
 
 
2335
        n_slots = page_dir_get_n_slots(page);
 
2336
 
 
2337
        if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
 
2338
                            <= page_dir_get_nth_slot(page, n_slots - 1)))) {
 
2339
 
 
2340
                fprintf(stderr, 
 
2341
                        "InnoDB: Record heap and dir overlap"
 
2342
                        " on space %lu page %lu index %s, %p, %p\n",
 
2343
                        (ulong) page_get_space_id(page),
 
2344
                        (ulong) page_get_page_no(page), index->name,
 
2345
                        page_header_get_ptr(page, PAGE_HEAP_TOP),
 
2346
                        page_dir_get_nth_slot(page, n_slots - 1));
 
2347
 
 
2348
                goto func_exit;
 
2349
        }
 
2350
 
 
2351
        /* Validate the record list in a loop checking also that
 
2352
        it is consistent with the directory. */
 
2353
        count = 0;
 
2354
        data_size = 0;
 
2355
        own_count = 1;
 
2356
        slot_no = 0;
 
2357
        slot = page_dir_get_nth_slot(page, slot_no);
 
2358
 
 
2359
        rec = page_get_infimum_rec(page);
 
2360
 
 
2361
        for (;;) {
 
2362
                offsets = rec_get_offsets(rec, index, offsets,
 
2363
                                          ULINT_UNDEFINED, &heap);
 
2364
 
 
2365
                if (page_is_comp(page) && page_rec_is_user_rec(rec)
 
2366
                    && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
 
2367
                                     == page_is_leaf(page))) {
 
2368
                        fputs("InnoDB: node_ptr flag mismatch\n", stderr);
 
2369
                        goto func_exit;
 
2370
                }
 
2371
 
 
2372
                if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
 
2373
                        goto func_exit;
 
2374
                }
 
2375
 
 
2376
#ifndef UNIV_HOTBACKUP
 
2377
                /* Check that the records are in the ascending order */
 
2378
                if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW)
 
2379
                    && !page_rec_is_supremum(rec)) {
 
2380
                        if (UNIV_UNLIKELY
 
2381
                            (1 != cmp_rec_rec(rec, old_rec,
 
2382
                                              offsets, old_offsets, index))) {
 
2383
                                fprintf(stderr, 
 
2384
                                        "InnoDB: Records in wrong order"
 
2385
                                        " on space %lu page %lu index %s\n",
 
2386
                                        (ulong) page_get_space_id(page),
 
2387
                                        (ulong) page_get_page_no(page),
 
2388
                                        index->name);
 
2389
                                fputs("\nInnoDB: previous record ", stderr);
 
2390
                                rec_print_new(stderr, old_rec, old_offsets);
 
2391
                                fputs("\nInnoDB: record ", stderr);
 
2392
                                rec_print_new(stderr, rec, offsets);
 
2393
                                putc('\n', stderr);
 
2394
 
 
2395
                                goto func_exit;
 
2396
                        }
 
2397
                }
 
2398
#endif /* !UNIV_HOTBACKUP */
 
2399
 
 
2400
                if (page_rec_is_user_rec(rec)) {
 
2401
 
 
2402
                        data_size += rec_offs_size(offsets);
 
2403
                }
 
2404
 
 
2405
                offs = page_offset(rec_get_start(rec, offsets));
 
2406
                i = rec_offs_size(offsets);
 
2407
                if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
 
2408
                        fputs("InnoDB: record offset out of bounds\n", stderr);
 
2409
                        goto func_exit;
 
2410
                }
 
2411
 
 
2412
                while (i--) {
 
2413
                        if (UNIV_UNLIKELY(buf[offs + i])) {
 
2414
                                /* No other record may overlap this */
 
2415
 
 
2416
                                fputs("InnoDB: Record overlaps another\n",
 
2417
                                      stderr);
 
2418
                                goto func_exit;
 
2419
                        }
 
2420
 
 
2421
                        buf[offs + i] = 1;
 
2422
                }
 
2423
 
 
2424
                if (page_is_comp(page)) {
 
2425
                        rec_own_count = rec_get_n_owned_new(rec);
 
2426
                } else {
 
2427
                        rec_own_count = rec_get_n_owned_old(rec);
 
2428
                }
 
2429
 
 
2430
                if (UNIV_UNLIKELY(rec_own_count)) {
 
2431
                        /* This is a record pointed to by a dir slot */
 
2432
                        if (UNIV_UNLIKELY(rec_own_count != own_count)) {
 
2433
                                fprintf(stderr,
 
2434
                                        "InnoDB: Wrong owned count %lu, %lu\n",
 
2435
                                        (ulong) rec_own_count,
 
2436
                                        (ulong) own_count);
 
2437
                                goto func_exit;
 
2438
                        }
 
2439
 
 
2440
                        if (page_dir_slot_get_rec(slot) != rec) {
 
2441
                                fputs("InnoDB: Dir slot does not"
 
2442
                                      " point to right rec\n",
 
2443
                                      stderr);
 
2444
                                goto func_exit;
 
2445
                        }
 
2446
 
 
2447
                        page_dir_slot_check(slot);
 
2448
 
 
2449
                        own_count = 0;
 
2450
                        if (!page_rec_is_supremum(rec)) {
 
2451
                                slot_no++;
 
2452
                                slot = page_dir_get_nth_slot(page, slot_no);
 
2453
                        }
 
2454
                }
 
2455
 
 
2456
                if (page_rec_is_supremum(rec)) {
 
2457
                        break;
 
2458
                }
 
2459
 
 
2460
                count++;
 
2461
                own_count++;
 
2462
                old_rec = rec;
 
2463
                rec = page_rec_get_next_const(rec);
 
2464
 
 
2465
                /* set old_offsets to offsets; recycle offsets */
 
2466
                {
 
2467
                        ulint* offs = old_offsets;
 
2468
                        old_offsets = offsets;
 
2469
                        offsets = offs;
 
2470
                }
 
2471
        }
 
2472
 
 
2473
        if (page_is_comp(page)) {
 
2474
                if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
 
2475
 
 
2476
                        goto n_owned_zero;
 
2477
                }
 
2478
        } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
 
2479
n_owned_zero:
 
2480
                fputs("InnoDB: n owned is zero\n", stderr);
 
2481
                goto func_exit;
 
2482
        }
 
2483
 
 
2484
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
 
2485
                fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
 
2486
                        (ulong) slot_no, (ulong) (n_slots - 1));
 
2487
                goto func_exit;
 
2488
        }
 
2489
 
 
2490
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
 
2491
                          + PAGE_HEAP_NO_USER_LOW
 
2492
                          != count + 1)) {
 
2493
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
 
2494
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
 
2495
                        + PAGE_HEAP_NO_USER_LOW,
 
2496
                        (ulong) (count + 1));
 
2497
                goto func_exit;
 
2498
        }
 
2499
 
 
2500
        if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
 
2501
                fprintf(stderr,
 
2502
                        "InnoDB: Summed data size %lu, returned by func %lu\n",
 
2503
                        (ulong) data_size, (ulong) page_get_data_size(page));
 
2504
                goto func_exit;
 
2505
        }
 
2506
 
 
2507
        /* Check then the free list */
 
2508
        rec = page_header_get_ptr(page, PAGE_FREE);
 
2509
 
 
2510
        while (rec != NULL) {
 
2511
                offsets = rec_get_offsets(rec, index, offsets,
 
2512
                                          ULINT_UNDEFINED, &heap);
 
2513
                if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
 
2514
 
 
2515
                        goto func_exit;
 
2516
                }
 
2517
 
 
2518
                count++;
 
2519
                offs = page_offset(rec_get_start(rec, offsets));
 
2520
                i = rec_offs_size(offsets);
 
2521
                if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
 
2522
                        fputs("InnoDB: record offset out of bounds\n", stderr);
 
2523
                        goto func_exit;
 
2524
                }
 
2525
 
 
2526
                while (i--) {
 
2527
 
 
2528
                        if (UNIV_UNLIKELY(buf[offs + i])) {
 
2529
                                fputs("InnoDB: Record overlaps another"
 
2530
                                      " in free list\n", stderr);
 
2531
                                goto func_exit;
 
2532
                        }
 
2533
 
 
2534
                        buf[offs + i] = 1;
 
2535
                }
 
2536
 
 
2537
                rec = page_rec_get_next_const(rec);
 
2538
        }
 
2539
 
 
2540
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
 
2541
                fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
 
2542
                        (ulong) page_dir_get_n_heap(page),
 
2543
                        (ulong) count + 1);
 
2544
                goto func_exit;
 
2545
        }
 
2546
 
 
2547
        ret = TRUE;
 
2548
 
 
2549
func_exit:
 
2550
        mem_heap_free(heap);
 
2551
 
 
2552
        if (UNIV_UNLIKELY(ret == FALSE)) {
 
2553
func_exit2:
 
2554
                fprintf(stderr, 
 
2555
                        "InnoDB: Apparent corruption"
 
2556
                        " in space %lu page %lu index %s\n",
 
2557
                        (ulong) page_get_space_id(page),
 
2558
                        (ulong) page_get_page_no(page),
 
2559
                        index->name);
 
2560
                buf_page_print(page, 0, 0);
 
2561
        }
 
2562
 
 
2563
        return(ret);
 
2564
}
 
2565
 
 
2566
#ifndef UNIV_HOTBACKUP
 
2567
/***************************************************************//**
 
2568
Looks in the page record list for a record with the given heap number.
 
2569
@return record, NULL if not found */
 
2570
UNIV_INTERN
 
2571
const rec_t*
 
2572
page_find_rec_with_heap_no(
 
2573
/*=======================*/
 
2574
        const page_t*   page,   /*!< in: index page */
 
2575
        ulint           heap_no)/*!< in: heap number */
 
2576
{
 
2577
        const rec_t*    rec;
 
2578
 
 
2579
        if (page_is_comp(page)) {
 
2580
                rec = page + PAGE_NEW_INFIMUM;
 
2581
 
 
2582
                for(;;) {
 
2583
                        ulint   rec_heap_no = rec_get_heap_no_new(rec);
 
2584
 
 
2585
                        if (rec_heap_no == heap_no) {
 
2586
 
 
2587
                                return(rec);
 
2588
                        } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
 
2589
 
 
2590
                                return(NULL);
 
2591
                        }
 
2592
 
 
2593
                        rec = page + rec_get_next_offs(rec, TRUE);
 
2594
                }
 
2595
        } else {
 
2596
                rec = page + PAGE_OLD_INFIMUM;
 
2597
 
 
2598
                for (;;) {
 
2599
                        ulint   rec_heap_no = rec_get_heap_no_old(rec);
 
2600
 
 
2601
                        if (rec_heap_no == heap_no) {
 
2602
 
 
2603
                                return(rec);
 
2604
                        } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
 
2605
 
 
2606
                                return(NULL);
 
2607
                        }
 
2608
 
 
2609
                        rec = page + rec_get_next_offs(rec, FALSE);
 
2610
                }
 
2611
        }
 
2612
}
 
2613
#endif /* !UNIV_HOTBACKUP */