~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: Andy Lester
  • Date: 2008-08-10 02:15:48 UTC
  • mto: (266.1.31 use-replace-funcs)
  • mto: This revision was merged to the branch mainline in revision 295.
  • Revision ID: andy@petdance.com-20080810021548-0zx8nhzva6al10k3
Added a proper const qualifer.

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= NULL;
2303
 
        mem_heap_t*     heap= NULL;
2304
 
        byte*           buf= NULL;
2305
 
        ulint           count= 0;
2306
 
        ulint           own_count= 0;
2307
 
        ulint           rec_own_count= 0;
2308
 
        ulint           slot_no= 0;
2309
 
        ulint           data_size= 0;
2310
 
        rec_t*          rec= NULL;
2311
 
        rec_t*          old_rec         = NULL;
2312
 
        ulint           offs= 0;
2313
 
        ulint           n_slots= 0;
2314
 
        ibool           ret             = FALSE;
2315
 
        ulint           i= 0;
2316
 
        ulint*          offsets         = NULL;
2317
 
        ulint*          old_offsets     = NULL;
2318
 
        void* buf_ptr= NULL;
2319
 
 
2320
 
        if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
2321
 
                          != dict_table_is_comp(index->table))) {
2322
 
                fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
2323
 
                goto func_exit2;
2324
 
        }
2325
 
        if (page_is_comp(page)) {
2326
 
                if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
2327
 
                        goto func_exit2;
2328
 
                }
2329
 
        } else {
2330
 
                if (UNIV_UNLIKELY(!page_simple_validate_old(page))) {
2331
 
                        goto func_exit2;
2332
 
                }
2333
 
        }
2334
 
 
2335
 
        heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
2336
 
 
2337
 
        /* The following buffer is used to check that the
2338
 
        records in the page record heap do not overlap */
2339
 
 
2340
 
        buf_ptr= mem_heap_zalloc(heap, UNIV_PAGE_SIZE);
2341
 
        buf = static_cast<byte *>(buf_ptr);
2342
 
 
2343
 
        /* Check first that the record heap and the directory do not
2344
 
        overlap. */
2345
 
 
2346
 
        n_slots = page_dir_get_n_slots(page);
2347
 
 
2348
 
        if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
2349
 
                            <= page_dir_get_nth_slot(page, n_slots - 1)))) {
2350
 
 
2351
 
                fprintf(stderr, 
2352
 
                        "InnoDB: Record heap and dir overlap"
2353
 
                        " on space %lu page %lu index %s, %p, %p\n",
2354
 
                        (ulong) page_get_space_id(page),
2355
 
                        (ulong) page_get_page_no(page), index->name,
2356
 
                        page_header_get_ptr(page, PAGE_HEAP_TOP),
2357
 
                        page_dir_get_nth_slot(page, n_slots - 1));
2358
 
 
2359
 
                goto func_exit;
2360
 
        }
2361
 
 
2362
 
        /* Validate the record list in a loop checking also that
2363
 
        it is consistent with the directory. */
2364
 
        count = 0;
2365
 
        data_size = 0;
2366
 
        own_count = 1;
2367
 
        slot_no = 0;
2368
 
        slot = page_dir_get_nth_slot(page, slot_no);
2369
 
 
2370
 
        rec = page_get_infimum_rec(page);
2371
 
 
2372
 
        for (;;) {
2373
 
                offsets = rec_get_offsets(rec, index, offsets,
2374
 
                                          ULINT_UNDEFINED, &heap);
2375
 
 
2376
 
                if (page_is_comp(page) && page_rec_is_user_rec(rec)
2377
 
                    && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
2378
 
                                     == page_is_leaf(page))) {
2379
 
                        fputs("InnoDB: node_ptr flag mismatch\n", stderr);
2380
 
                        goto func_exit;
2381
 
                }
2382
 
 
2383
 
                if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2384
 
                        goto func_exit;
2385
 
                }
2386
 
 
2387
 
#ifndef UNIV_HOTBACKUP
2388
 
                /* Check that the records are in the ascending order */
2389
 
                if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW)
2390
 
                    && !page_rec_is_supremum(rec)) {
2391
 
                        if (UNIV_UNLIKELY
2392
 
                            (1 != cmp_rec_rec(rec, old_rec,
2393
 
                                              offsets, old_offsets, index))) {
2394
 
                                fprintf(stderr, 
2395
 
                                        "InnoDB: Records in wrong order"
2396
 
                                        " on space %lu page %lu index %s\n",
2397
 
                                        (ulong) page_get_space_id(page),
2398
 
                                        (ulong) page_get_page_no(page),
2399
 
                                        index->name);
2400
 
                                fputs("\nInnoDB: previous record ", stderr);
2401
 
                                rec_print_new(stderr, old_rec, old_offsets);
2402
 
                                fputs("\nInnoDB: record ", stderr);
2403
 
                                rec_print_new(stderr, rec, offsets);
2404
 
                                putc('\n', stderr);
2405
 
 
2406
 
                                goto func_exit;
2407
 
                        }
2408
 
                }
2409
 
#endif /* !UNIV_HOTBACKUP */
2410
 
 
2411
 
                if (page_rec_is_user_rec(rec)) {
2412
 
 
2413
 
                        data_size += rec_offs_size(offsets);
2414
 
                }
2415
 
 
2416
 
                offs = page_offset(rec_get_start(rec, offsets));
2417
 
                i = rec_offs_size(offsets);
2418
 
                if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
2419
 
                        fputs("InnoDB: record offset out of bounds\n", stderr);
2420
 
                        goto func_exit;
2421
 
                }
2422
 
 
2423
 
                while (i--) {
2424
 
                        if (UNIV_UNLIKELY(buf[offs + i])) {
2425
 
                                /* No other record may overlap this */
2426
 
 
2427
 
                                fputs("InnoDB: Record overlaps another\n",
2428
 
                                      stderr);
2429
 
                                goto func_exit;
2430
 
                        }
2431
 
 
2432
 
                        buf[offs + i] = 1;
2433
 
                }
2434
 
 
2435
 
                if (page_is_comp(page)) {
2436
 
                        rec_own_count = rec_get_n_owned_new(rec);
2437
 
                } else {
2438
 
                        rec_own_count = rec_get_n_owned_old(rec);
2439
 
                }
2440
 
 
2441
 
                if (UNIV_UNLIKELY(rec_own_count)) {
2442
 
                        /* This is a record pointed to by a dir slot */
2443
 
                        if (UNIV_UNLIKELY(rec_own_count != own_count)) {
2444
 
                                fprintf(stderr,
2445
 
                                        "InnoDB: Wrong owned count %lu, %lu\n",
2446
 
                                        (ulong) rec_own_count,
2447
 
                                        (ulong) own_count);
2448
 
                                goto func_exit;
2449
 
                        }
2450
 
 
2451
 
                        if (page_dir_slot_get_rec(slot) != rec) {
2452
 
                                fputs("InnoDB: Dir slot does not"
2453
 
                                      " point to right rec\n",
2454
 
                                      stderr);
2455
 
                                goto func_exit;
2456
 
                        }
2457
 
 
2458
 
                        page_dir_slot_check(slot);
2459
 
 
2460
 
                        own_count = 0;
2461
 
                        if (!page_rec_is_supremum(rec)) {
2462
 
                                slot_no++;
2463
 
                                slot = page_dir_get_nth_slot(page, slot_no);
2464
 
                        }
2465
 
                }
2466
 
 
2467
 
                if (page_rec_is_supremum(rec)) {
2468
 
                        break;
2469
 
                }
2470
 
 
2471
 
                count++;
2472
 
                own_count++;
2473
 
                old_rec = rec;
2474
 
                rec = page_rec_get_next(rec);
2475
 
 
2476
 
                /* set old_offsets to offsets; recycle offsets */
2477
 
                {
2478
 
                        ulint* tmp_offs = old_offsets;
2479
 
                        old_offsets = offsets;
2480
 
                        offsets = tmp_offs;
2481
 
                }
2482
 
        }
2483
 
 
2484
 
        if (page_is_comp(page)) {
2485
 
                if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2486
 
 
2487
 
                        goto n_owned_zero;
2488
 
                }
2489
 
        } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
2490
 
n_owned_zero:
2491
 
                fputs("InnoDB: n owned is zero\n", stderr);
2492
 
                goto func_exit;
2493
 
        }
2494
 
 
2495
 
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2496
 
                fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
2497
 
                        (ulong) slot_no, (ulong) (n_slots - 1));
2498
 
                goto func_exit;
2499
 
        }
2500
 
 
2501
 
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2502
 
                          + PAGE_HEAP_NO_USER_LOW
2503
 
                          != count + 1)) {
2504
 
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2505
 
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
2506
 
                        + PAGE_HEAP_NO_USER_LOW,
2507
 
                        (ulong) (count + 1));
2508
 
                goto func_exit;
2509
 
        }
2510
 
 
2511
 
        if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
2512
 
                fprintf(stderr,
2513
 
                        "InnoDB: Summed data size %lu, returned by func %lu\n",
2514
 
                        (ulong) data_size, (ulong) page_get_data_size(page));
2515
 
                goto func_exit;
2516
 
        }
2517
 
 
2518
 
        /* Check then the free list */
2519
 
        rec = page_header_get_ptr(page, PAGE_FREE);
2520
 
 
2521
 
        while (rec != NULL) {
2522
 
                offsets = rec_get_offsets(rec, index, offsets,
2523
 
                                          ULINT_UNDEFINED, &heap);
2524
 
                if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2525
 
 
2526
 
                        goto func_exit;
2527
 
                }
2528
 
 
2529
 
                count++;
2530
 
                offs = page_offset(rec_get_start(rec, offsets));
2531
 
                i = rec_offs_size(offsets);
2532
 
                if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
2533
 
                        fputs("InnoDB: record offset out of bounds\n", stderr);
2534
 
                        goto func_exit;
2535
 
                }
2536
 
 
2537
 
                while (i--) {
2538
 
 
2539
 
                        if (UNIV_UNLIKELY(buf[offs + i])) {
2540
 
                                fputs("InnoDB: Record overlaps another"
2541
 
                                      " in free list\n", stderr);
2542
 
                                goto func_exit;
2543
 
                        }
2544
 
 
2545
 
                        buf[offs + i] = 1;
2546
 
                }
2547
 
 
2548
 
                rec = page_rec_get_next(rec);
2549
 
        }
2550
 
 
2551
 
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2552
 
                fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
2553
 
                        (ulong) page_dir_get_n_heap(page),
2554
 
                        (ulong) count + 1);
2555
 
                goto func_exit;
2556
 
        }
2557
 
 
2558
 
        ret = TRUE;
2559
 
 
2560
 
func_exit:
2561
 
        mem_heap_free(heap);
2562
 
 
2563
 
        if (UNIV_UNLIKELY(ret == FALSE)) {
2564
 
func_exit2:
2565
 
                fprintf(stderr, 
2566
 
                        "InnoDB: Apparent corruption"
2567
 
                        " in space %lu page %lu index %s\n",
2568
 
                        (ulong) page_get_space_id(page),
2569
 
                        (ulong) page_get_page_no(page),
2570
 
                        index->name);
2571
 
                buf_page_print(page, 0);
2572
 
        }
2573
 
 
2574
 
        return(ret);
2575
 
}
2576
 
 
2577
 
#ifndef UNIV_HOTBACKUP
2578
 
/***************************************************************//**
2579
 
Looks in the page record list for a record with the given heap number.
2580
 
@return record, NULL if not found */
2581
 
UNIV_INTERN
2582
 
const rec_t*
2583
 
page_find_rec_with_heap_no(
2584
 
/*=======================*/
2585
 
        const page_t*   page,   /*!< in: index page */
2586
 
        ulint           heap_no)/*!< in: heap number */
2587
 
{
2588
 
        const rec_t*    rec;
2589
 
 
2590
 
        if (page_is_comp(page)) {
2591
 
                rec = page + PAGE_NEW_INFIMUM;
2592
 
 
2593
 
                for(;;) {
2594
 
                        ulint   rec_heap_no = rec_get_heap_no_new(rec);
2595
 
 
2596
 
                        if (rec_heap_no == heap_no) {
2597
 
 
2598
 
                                return(rec);
2599
 
                        } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2600
 
 
2601
 
                                return(NULL);
2602
 
                        }
2603
 
 
2604
 
                        rec = page + rec_get_next_offs(rec, TRUE);
2605
 
                }
2606
 
        } else {
2607
 
                rec = page + PAGE_OLD_INFIMUM;
2608
 
 
2609
 
                for (;;) {
2610
 
                        ulint   rec_heap_no = rec_get_heap_no_old(rec);
2611
 
 
2612
 
                        if (rec_heap_no == heap_no) {
2613
 
 
2614
 
                                return(rec);
2615
 
                        } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2616
 
 
2617
 
                                return(NULL);
2618
 
                        }
2619
 
 
2620
 
                        rec = page + rec_get_next_offs(rec, FALSE);
2621
 
                }
2622
 
        }
2623
 
}
2624
 
#endif /* !UNIV_HOTBACKUP */