~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/innobase/page/page0page.cc

  • Committer: Monty Taylor
  • Date: 2010-12-24 02:13:05 UTC
  • mto: This revision was merged to the branch mainline in revision 2038.
  • Revision ID: mordred@inaugust.com-20101224021305-e3slv1cyjczqorij
Changed the bzrignore file.

Show diffs side-by-side

added added

removed removed

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