~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*****************************************************************************
2
 
 
3
 
Copyright (c) 1994, 2009, 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., 59 Temple
15
 
Place, Suite 330, Boston, MA 02111-1307 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_dulint(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, ut_dulint_zero, 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
 
        if (UNIV_LIKELY_NULL(new_page_zip)) {
660
 
                mtr_set_log_mode(mtr, log_mode);
661
 
 
662
 
                if (UNIV_UNLIKELY
663
 
                    (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
664
 
                        /* Before trying to reorganize the page,
665
 
                        store the number of preceding records on the page. */
666
 
                        ulint   ret_pos
667
 
                                = page_rec_get_n_recs_before(ret);
668
 
                        /* Before copying, "ret" was the successor of
669
 
                        the predefined infimum record.  It must still
670
 
                        have at least one predecessor (the predefined
671
 
                        infimum record, or a freshly copied record
672
 
                        that is smaller than "ret"). */
673
 
                        ut_a(ret_pos > 0);
674
 
 
675
 
                        if (UNIV_UNLIKELY
676
 
                            (!page_zip_reorganize(new_block, index, mtr))) {
677
 
 
678
 
                                if (UNIV_UNLIKELY
679
 
                                    (!page_zip_decompress(new_page_zip,
680
 
                                                          new_page))) {
681
 
                                        ut_error;
682
 
                                }
683
 
                                ut_ad(page_validate(new_page, index));
684
 
                                return(NULL);
685
 
                        } else {
686
 
                                /* The page was reorganized:
687
 
                                Seek to ret_pos. */
688
 
                                ret = new_page + PAGE_NEW_INFIMUM;
689
 
 
690
 
                                do {
691
 
                                        ret = rec_get_next_ptr(ret, TRUE);
692
 
                                } while (--ret_pos);
693
 
                        }
694
 
                }
695
 
        }
696
 
 
697
 
        /* Update the lock table, MAX_TRX_ID, and possible hash index */
698
 
 
699
 
        lock_move_rec_list_end(new_block, block, rec);
700
 
 
701
 
        if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
702
 
                page_update_max_trx_id(new_block, new_page_zip,
703
 
                                       page_get_max_trx_id(page), mtr);
704
 
        }
705
 
 
706
 
        btr_search_move_or_delete_hash_entries(new_block, block, index);
707
 
 
708
 
        return(ret);
709
 
}
710
 
 
711
 
/*************************************************************//**
712
 
Copies records from page to new_page, up to the given record,
713
 
NOT including that record. Infimum and supremum records are not copied.
714
 
The records are copied to the end of the record list on new_page.
715
 
@return pointer to the original predecessor of the supremum record on
716
 
new_page, or NULL on zip overflow (new_block will be decompressed) */
717
 
UNIV_INTERN
718
 
rec_t*
719
 
page_copy_rec_list_start(
720
 
/*=====================*/
721
 
        buf_block_t*    new_block,      /*!< in/out: index page to copy to */
722
 
        buf_block_t*    block,          /*!< in: index page containing rec */
723
 
        rec_t*          rec,            /*!< in: record on page */
724
 
        dict_index_t*   index,          /*!< in: record descriptor */
725
 
        mtr_t*          mtr)            /*!< in: mtr */
726
 
{
727
 
        page_t*         new_page        = buf_block_get_frame(new_block);
728
 
        page_zip_des_t* new_page_zip    = buf_block_get_page_zip(new_block);
729
 
        page_cur_t      cur1;
730
 
        rec_t*          cur2;
731
 
        ulint           log_mode        = 0 /* remove warning */;
732
 
        mem_heap_t*     heap            = NULL;
733
 
        rec_t*          ret
734
 
                = page_rec_get_prev(page_get_supremum_rec(new_page));
735
 
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
736
 
        ulint*          offsets         = offsets_;
737
 
        rec_offs_init(offsets_);
738
 
 
739
 
        /* Here, "ret" may be pointing to a user record or the
740
 
        predefined infimum record. */
741
 
 
742
 
        if (page_rec_is_infimum(rec)) {
743
 
 
744
 
                return(ret);
745
 
        }
746
 
 
747
 
        if (UNIV_LIKELY_NULL(new_page_zip)) {
748
 
                log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
749
 
        }
750
 
 
751
 
        page_cur_set_before_first(block, &cur1);
752
 
        page_cur_move_to_next(&cur1);
753
 
 
754
 
        cur2 = ret;
755
 
 
756
 
        /* Copy records from the original page to the new page */
757
 
 
758
 
        while (page_cur_get_rec(&cur1) != rec) {
759
 
                rec_t*  cur1_rec = page_cur_get_rec(&cur1);
760
 
                offsets = rec_get_offsets(cur1_rec, index, offsets,
761
 
                                          ULINT_UNDEFINED, &heap);
762
 
                cur2 = page_cur_insert_rec_low(cur2, index,
763
 
                                               cur1_rec, offsets, mtr);
764
 
                ut_a(cur2);
765
 
 
766
 
                page_cur_move_to_next(&cur1);
767
 
        }
768
 
 
769
 
        if (UNIV_LIKELY_NULL(heap)) {
770
 
                mem_heap_free(heap);
771
 
        }
772
 
 
773
 
        if (UNIV_LIKELY_NULL(new_page_zip)) {
774
 
                mtr_set_log_mode(mtr, log_mode);
775
 
 
776
 
                if (UNIV_UNLIKELY
777
 
                    (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
778
 
                        /* Before trying to reorganize the page,
779
 
                        store the number of preceding records on the page. */
780
 
                        ulint   ret_pos
781
 
                                = page_rec_get_n_recs_before(ret);
782
 
                        /* Before copying, "ret" was the predecessor
783
 
                        of the predefined supremum record.  If it was
784
 
                        the predefined infimum record, then it would
785
 
                        still be the infimum.  Thus, the assertion
786
 
                        ut_a(ret_pos > 0) would fail here. */
787
 
 
788
 
                        if (UNIV_UNLIKELY
789
 
                            (!page_zip_reorganize(new_block, index, mtr))) {
790
 
 
791
 
                                if (UNIV_UNLIKELY
792
 
                                    (!page_zip_decompress(new_page_zip,
793
 
                                                          new_page))) {
794
 
                                        ut_error;
795
 
                                }
796
 
                                ut_ad(page_validate(new_page, index));
797
 
                                return(NULL);
798
 
                        } else {
799
 
                                /* The page was reorganized:
800
 
                                Seek to ret_pos. */
801
 
                                ret = new_page + PAGE_NEW_INFIMUM;
802
 
 
803
 
                                do {
804
 
                                        ret = rec_get_next_ptr(ret, TRUE);
805
 
                                } while (--ret_pos);
806
 
                        }
807
 
                }
808
 
        }
809
 
 
810
 
        /* Update MAX_TRX_ID, the lock table, and possible hash index */
811
 
 
812
 
        if (dict_index_is_sec_or_ibuf(index)
813
 
            && page_is_leaf(page_align(rec))) {
814
 
                page_update_max_trx_id(new_block, new_page_zip,
815
 
                                       page_get_max_trx_id(page_align(rec)),
816
 
                                       mtr);
817
 
        }
818
 
 
819
 
        lock_move_rec_list_start(new_block, block, rec, ret);
820
 
 
821
 
        btr_search_move_or_delete_hash_entries(new_block, block, index);
822
 
 
823
 
        return(ret);
824
 
}
825
 
 
826
 
/**********************************************************//**
827
 
Writes a log record of a record list end or start deletion. */
828
 
UNIV_INLINE
829
 
void
830
 
page_delete_rec_list_write_log(
831
 
/*===========================*/
832
 
        rec_t*          rec,    /*!< in: record on page */
833
 
        dict_index_t*   index,  /*!< in: record descriptor */
834
 
        byte            type,   /*!< in: operation type:
835
 
                                MLOG_LIST_END_DELETE, ... */
836
 
        mtr_t*          mtr)    /*!< in: mtr */
837
 
{
838
 
        byte*   log_ptr;
839
 
        ut_ad(type == MLOG_LIST_END_DELETE
840
 
              || type == MLOG_LIST_START_DELETE
841
 
              || type == MLOG_COMP_LIST_END_DELETE
842
 
              || type == MLOG_COMP_LIST_START_DELETE);
843
 
 
844
 
        log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2);
845
 
        if (log_ptr) {
846
 
                /* Write the parameter as a 2-byte ulint */
847
 
                mach_write_to_2(log_ptr, page_offset(rec));
848
 
                mlog_close(mtr, log_ptr + 2);
849
 
        }
850
 
}
851
 
#else /* !UNIV_HOTBACKUP */
852
 
# define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0)
853
 
#endif /* !UNIV_HOTBACKUP */
854
 
 
855
 
/**********************************************************//**
856
 
Parses a log record of a record list end or start deletion.
857
 
@return end of log record or NULL */
858
 
UNIV_INTERN
859
 
byte*
860
 
page_parse_delete_rec_list(
861
 
/*=======================*/
862
 
        byte            type,   /*!< in: MLOG_LIST_END_DELETE,
863
 
                                MLOG_LIST_START_DELETE,
864
 
                                MLOG_COMP_LIST_END_DELETE or
865
 
                                MLOG_COMP_LIST_START_DELETE */
866
 
        byte*           ptr,    /*!< in: buffer */
867
 
        byte*           end_ptr,/*!< in: buffer end */
868
 
        buf_block_t*    block,  /*!< in/out: buffer block or NULL */
869
 
        dict_index_t*   index,  /*!< in: record descriptor */
870
 
        mtr_t*          mtr)    /*!< in: mtr or NULL */
871
 
{
872
 
        page_t* page;
873
 
        ulint   offset;
874
 
 
875
 
        ut_ad(type == MLOG_LIST_END_DELETE
876
 
              || type == MLOG_LIST_START_DELETE
877
 
              || type == MLOG_COMP_LIST_END_DELETE
878
 
              || type == MLOG_COMP_LIST_START_DELETE);
879
 
 
880
 
        /* Read the record offset as a 2-byte ulint */
881
 
 
882
 
        if (end_ptr < ptr + 2) {
883
 
 
884
 
                return(NULL);
885
 
        }
886
 
 
887
 
        offset = mach_read_from_2(ptr);
888
 
        ptr += 2;
889
 
 
890
 
        if (!block) {
891
 
 
892
 
                return(ptr);
893
 
        }
894
 
 
895
 
        page = buf_block_get_frame(block);
896
 
 
897
 
        ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
898
 
 
899
 
        if (type == MLOG_LIST_END_DELETE
900
 
            || type == MLOG_COMP_LIST_END_DELETE) {
901
 
                page_delete_rec_list_end(page + offset, block, index,
902
 
                                         ULINT_UNDEFINED, ULINT_UNDEFINED,
903
 
                                         mtr);
904
 
        } else {
905
 
                page_delete_rec_list_start(page + offset, block, index, mtr);
906
 
        }
907
 
 
908
 
        return(ptr);
909
 
}
910
 
 
911
 
/*************************************************************//**
912
 
Deletes records from a page from a given record onward, including that record.
913
 
The infimum and supremum records are not deleted. */
914
 
UNIV_INTERN
915
 
void
916
 
page_delete_rec_list_end(
917
 
/*=====================*/
918
 
        rec_t*          rec,    /*!< in: pointer to record on page */
919
 
        buf_block_t*    block,  /*!< in: buffer block of the page */
920
 
        dict_index_t*   index,  /*!< in: record descriptor */
921
 
        ulint           n_recs, /*!< in: number of records to delete,
922
 
                                or ULINT_UNDEFINED if not known */
923
 
        ulint           size,   /*!< in: the sum of the sizes of the
924
 
                                records in the end of the chain to
925
 
                                delete, or ULINT_UNDEFINED if not known */
926
 
        mtr_t*          mtr)    /*!< in: mtr */
927
 
{
928
 
        page_dir_slot_t*slot;
929
 
        ulint           slot_index;
930
 
        rec_t*          last_rec;
931
 
        rec_t*          prev_rec;
932
 
        ulint           n_owned;
933
 
        page_zip_des_t* page_zip        = buf_block_get_page_zip(block);
934
 
        page_t*         page            = page_align(rec);
935
 
        mem_heap_t*     heap            = NULL;
936
 
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
937
 
        ulint*          offsets         = offsets_;
938
 
        rec_offs_init(offsets_);
939
 
 
940
 
        ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE);
941
 
        ut_ad(!page_zip || page_rec_is_comp(rec));
942
 
#ifdef UNIV_ZIP_DEBUG
943
 
        ut_a(!page_zip || page_zip_validate(page_zip, page));
944
 
#endif /* UNIV_ZIP_DEBUG */
945
 
 
946
 
        if (page_rec_is_infimum(rec)) {
947
 
                rec = page_rec_get_next(rec);
948
 
        }
949
 
 
950
 
        if (page_rec_is_supremum(rec)) {
951
 
 
952
 
                return;
953
 
        }
954
 
 
955
 
        /* Reset the last insert info in the page header and increment
956
 
        the modify clock for the frame */
957
 
 
958
 
        page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL);
959
 
 
960
 
        /* The page gets invalid for optimistic searches: increment the
961
 
        frame modify clock */
962
 
 
963
 
        buf_block_modify_clock_inc(block);
964
 
 
965
 
        page_delete_rec_list_write_log(rec, index, page_is_comp(page)
966
 
                                       ? MLOG_COMP_LIST_END_DELETE
967
 
                                       : MLOG_LIST_END_DELETE, mtr);
968
 
 
969
 
        if (UNIV_LIKELY_NULL(page_zip)) {
970
 
                ulint           log_mode;
971
 
 
972
 
                ut_a(page_is_comp(page));
973
 
                /* Individual deletes are not logged */
974
 
 
975
 
                log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
976
 
 
977
 
                do {
978
 
                        page_cur_t      cur;
979
 
                        page_cur_position(rec, block, &cur);
980
 
 
981
 
                        offsets = rec_get_offsets(rec, index, offsets,
982
 
                                                  ULINT_UNDEFINED, &heap);
983
 
                        rec = rec_get_next_ptr(rec, TRUE);
984
 
#ifdef UNIV_ZIP_DEBUG
985
 
                        ut_a(page_zip_validate(page_zip, page));
986
 
#endif /* UNIV_ZIP_DEBUG */
987
 
                        page_cur_delete_rec(&cur, index, offsets, mtr);
988
 
                } while (page_offset(rec) != PAGE_NEW_SUPREMUM);
989
 
 
990
 
                if (UNIV_LIKELY_NULL(heap)) {
991
 
                        mem_heap_free(heap);
992
 
                }
993
 
 
994
 
                /* Restore log mode */
995
 
 
996
 
                mtr_set_log_mode(mtr, log_mode);
997
 
                return;
998
 
        }
999
 
 
1000
 
        prev_rec = page_rec_get_prev(rec);
1001
 
 
1002
 
        last_rec = page_rec_get_prev(page_get_supremum_rec(page));
1003
 
 
1004
 
        if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
1005
 
                rec_t*          rec2            = rec;
1006
 
                /* Calculate the sum of sizes and the number of records */
1007
 
                size = 0;
1008
 
                n_recs = 0;
1009
 
 
1010
 
                do {
1011
 
                        ulint   s;
1012
 
                        offsets = rec_get_offsets(rec2, index, offsets,
1013
 
                                                  ULINT_UNDEFINED, &heap);
1014
 
                        s = rec_offs_size(offsets);
1015
 
                        ut_ad(rec2 - page + s - rec_offs_extra_size(offsets)
1016
 
                              < UNIV_PAGE_SIZE);
1017
 
                        ut_ad(size + s < UNIV_PAGE_SIZE);
1018
 
                        size += s;
1019
 
                        n_recs++;
1020
 
 
1021
 
                        rec2 = page_rec_get_next(rec2);
1022
 
                } while (!page_rec_is_supremum(rec2));
1023
 
 
1024
 
                if (UNIV_LIKELY_NULL(heap)) {
1025
 
                        mem_heap_free(heap);
1026
 
                }
1027
 
        }
1028
 
 
1029
 
        ut_ad(size < UNIV_PAGE_SIZE);
1030
 
 
1031
 
        /* Update the page directory; there is no need to balance the number
1032
 
        of the records owned by the supremum record, as it is allowed to be
1033
 
        less than PAGE_DIR_SLOT_MIN_N_OWNED */
1034
 
 
1035
 
        if (page_is_comp(page)) {
1036
 
                rec_t*  rec2    = rec;
1037
 
                ulint   count   = 0;
1038
 
 
1039
 
                while (rec_get_n_owned_new(rec2) == 0) {
1040
 
                        count++;
1041
 
 
1042
 
                        rec2 = rec_get_next_ptr(rec2, TRUE);
1043
 
                }
1044
 
 
1045
 
                ut_ad(rec_get_n_owned_new(rec2) > count);
1046
 
 
1047
 
                n_owned = rec_get_n_owned_new(rec2) - count;
1048
 
                slot_index = page_dir_find_owner_slot(rec2);
1049
 
                slot = page_dir_get_nth_slot(page, slot_index);
1050
 
        } else {
1051
 
                rec_t*  rec2    = rec;
1052
 
                ulint   count   = 0;
1053
 
 
1054
 
                while (rec_get_n_owned_old(rec2) == 0) {
1055
 
                        count++;
1056
 
 
1057
 
                        rec2 = rec_get_next_ptr(rec2, FALSE);
1058
 
                }
1059
 
 
1060
 
                ut_ad(rec_get_n_owned_old(rec2) > count);
1061
 
 
1062
 
                n_owned = rec_get_n_owned_old(rec2) - count;
1063
 
                slot_index = page_dir_find_owner_slot(rec2);
1064
 
                slot = page_dir_get_nth_slot(page, slot_index);
1065
 
        }
1066
 
 
1067
 
        page_dir_slot_set_rec(slot, page_get_supremum_rec(page));
1068
 
        page_dir_slot_set_n_owned(slot, NULL, n_owned);
1069
 
 
1070
 
        page_dir_set_n_slots(page, NULL, slot_index + 1);
1071
 
 
1072
 
        /* Remove the record chain segment from the record chain */
1073
 
        page_rec_set_next(prev_rec, page_get_supremum_rec(page));
1074
 
 
1075
 
        /* Catenate the deleted chain segment to the page free list */
1076
 
 
1077
 
        page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE));
1078
 
        page_header_set_ptr(page, NULL, PAGE_FREE, rec);
1079
 
 
1080
 
        page_header_set_field(page, NULL, PAGE_GARBAGE, size
1081
 
                              + page_header_get_field(page, PAGE_GARBAGE));
1082
 
 
1083
 
        page_header_set_field(page, NULL, PAGE_N_RECS,
1084
 
                              (ulint)(page_get_n_recs(page) - n_recs));
1085
 
}
1086
 
 
1087
 
/*************************************************************//**
1088
 
Deletes records from page, up to the given record, NOT including
1089
 
that record. Infimum and supremum records are not deleted. */
1090
 
UNIV_INTERN
1091
 
void
1092
 
page_delete_rec_list_start(
1093
 
/*=======================*/
1094
 
        rec_t*          rec,    /*!< in: record on page */
1095
 
        buf_block_t*    block,  /*!< in: buffer block of the page */
1096
 
        dict_index_t*   index,  /*!< in: record descriptor */
1097
 
        mtr_t*          mtr)    /*!< in: mtr */
1098
 
{
1099
 
        page_cur_t      cur1;
1100
 
        ulint           log_mode;
1101
 
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
1102
 
        ulint*          offsets         = offsets_;
1103
 
        mem_heap_t*     heap            = NULL;
1104
 
        byte            type;
1105
 
 
1106
 
        rec_offs_init(offsets_);
1107
 
 
1108
 
        ut_ad((ibool) !!page_rec_is_comp(rec)
1109
 
              == dict_table_is_comp(index->table));
1110
 
#ifdef UNIV_ZIP_DEBUG
1111
 
        {
1112
 
                page_zip_des_t* page_zip= buf_block_get_page_zip(block);
1113
 
                page_t*         page    = buf_block_get_frame(block);
1114
 
 
1115
 
                /* page_zip_validate() would detect a min_rec_mark mismatch
1116
 
                in btr_page_split_and_insert()
1117
 
                between btr_attach_half_pages() and insert_page = ...
1118
 
                when btr_page_get_split_rec_to_left() holds
1119
 
                (direction == FSP_DOWN). */
1120
 
                ut_a(!page_zip || page_zip_validate_low(page_zip, page, TRUE));
1121
 
        }
1122
 
#endif /* UNIV_ZIP_DEBUG */
1123
 
 
1124
 
        if (page_rec_is_infimum(rec)) {
1125
 
 
1126
 
                return;
1127
 
        }
1128
 
 
1129
 
        if (page_rec_is_comp(rec)) {
1130
 
                type = MLOG_COMP_LIST_START_DELETE;
1131
 
        } else {
1132
 
                type = MLOG_LIST_START_DELETE;
1133
 
        }
1134
 
 
1135
 
        page_delete_rec_list_write_log(rec, index, type, mtr);
1136
 
 
1137
 
        page_cur_set_before_first(block, &cur1);
1138
 
        page_cur_move_to_next(&cur1);
1139
 
 
1140
 
        /* Individual deletes are not logged */
1141
 
 
1142
 
        log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
1143
 
 
1144
 
        while (page_cur_get_rec(&cur1) != rec) {
1145
 
                offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
1146
 
                                          offsets, ULINT_UNDEFINED, &heap);
1147
 
                page_cur_delete_rec(&cur1, index, offsets, mtr);
1148
 
        }
1149
 
 
1150
 
        if (UNIV_LIKELY_NULL(heap)) {
1151
 
                mem_heap_free(heap);
1152
 
        }
1153
 
 
1154
 
        /* Restore log mode */
1155
 
 
1156
 
        mtr_set_log_mode(mtr, log_mode);
1157
 
}
1158
 
 
1159
 
#ifndef UNIV_HOTBACKUP
1160
 
/*************************************************************//**
1161
 
Moves record list end to another page. Moved records include
1162
 
split_rec.
1163
 
@return TRUE on success; FALSE on compression failure (new_block will
1164
 
be decompressed) */
1165
 
UNIV_INTERN
1166
 
ibool
1167
 
page_move_rec_list_end(
1168
 
/*===================*/
1169
 
        buf_block_t*    new_block,      /*!< in/out: index page where to move */
1170
 
        buf_block_t*    block,          /*!< in: index page from where to move */
1171
 
        rec_t*          split_rec,      /*!< in: first record to move */
1172
 
        dict_index_t*   index,          /*!< in: record descriptor */
1173
 
        mtr_t*          mtr)            /*!< in: mtr */
1174
 
{
1175
 
        page_t*         new_page        = buf_block_get_frame(new_block);
1176
 
        ulint           old_data_size;
1177
 
        ulint           new_data_size;
1178
 
        ulint           old_n_recs;
1179
 
        ulint           new_n_recs;
1180
 
 
1181
 
        old_data_size = page_get_data_size(new_page);
1182
 
        old_n_recs = page_get_n_recs(new_page);
1183
 
#ifdef UNIV_ZIP_DEBUG
1184
 
        {
1185
 
                page_zip_des_t* new_page_zip
1186
 
                        = buf_block_get_page_zip(new_block);
1187
 
                page_zip_des_t* page_zip
1188
 
                        = buf_block_get_page_zip(block);
1189
 
                ut_a(!new_page_zip == !page_zip);
1190
 
                ut_a(!new_page_zip
1191
 
                     || page_zip_validate(new_page_zip, new_page));
1192
 
                ut_a(!page_zip
1193
 
                     || page_zip_validate(page_zip, page_align(split_rec)));
1194
 
        }
1195
 
#endif /* UNIV_ZIP_DEBUG */
1196
 
 
1197
 
        if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block,
1198
 
                                                  split_rec, index, mtr))) {
1199
 
                return(FALSE);
1200
 
        }
1201
 
 
1202
 
        new_data_size = page_get_data_size(new_page);
1203
 
        new_n_recs = page_get_n_recs(new_page);
1204
 
 
1205
 
        ut_ad(new_data_size >= old_data_size);
1206
 
 
1207
 
        page_delete_rec_list_end(split_rec, block, index,
1208
 
                                 new_n_recs - old_n_recs,
1209
 
                                 new_data_size - old_data_size, mtr);
1210
 
 
1211
 
        return(TRUE);
1212
 
}
1213
 
 
1214
 
/*************************************************************//**
1215
 
Moves record list start to another page. Moved records do not include
1216
 
split_rec.
1217
 
@return TRUE on success; FALSE on compression failure */
1218
 
UNIV_INTERN
1219
 
ibool
1220
 
page_move_rec_list_start(
1221
 
/*=====================*/
1222
 
        buf_block_t*    new_block,      /*!< in/out: index page where to move */
1223
 
        buf_block_t*    block,          /*!< in/out: page containing split_rec */
1224
 
        rec_t*          split_rec,      /*!< in: first record not to move */
1225
 
        dict_index_t*   index,          /*!< in: record descriptor */
1226
 
        mtr_t*          mtr)            /*!< in: mtr */
1227
 
{
1228
 
        if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block,
1229
 
                                                    split_rec, index, mtr))) {
1230
 
                return(FALSE);
1231
 
        }
1232
 
 
1233
 
        page_delete_rec_list_start(split_rec, block, index, mtr);
1234
 
 
1235
 
        return(TRUE);
1236
 
}
1237
 
 
1238
 
/***********************************************************************//**
1239
 
This is a low-level operation which is used in a database index creation
1240
 
to update the page number of a created B-tree to a data dictionary record. */
1241
 
UNIV_INTERN
1242
 
void
1243
 
page_rec_write_index_page_no(
1244
 
/*=========================*/
1245
 
        rec_t*  rec,    /*!< in: record to update */
1246
 
        ulint   i,      /*!< in: index of the field to update */
1247
 
        ulint   page_no,/*!< in: value to write */
1248
 
        mtr_t*  mtr)    /*!< in: mtr */
1249
 
{
1250
 
        byte*   data;
1251
 
        ulint   len;
1252
 
 
1253
 
        data = rec_get_nth_field_old(rec, i, &len);
1254
 
 
1255
 
        ut_ad(len == 4);
1256
 
 
1257
 
        mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr);
1258
 
}
1259
 
#endif /* !UNIV_HOTBACKUP */
1260
 
 
1261
 
/**************************************************************//**
1262
 
Used to delete n slots from the directory. This function updates
1263
 
also n_owned fields in the records, so that the first slot after
1264
 
the deleted ones inherits the records of the deleted slots. */
1265
 
UNIV_INLINE
1266
 
void
1267
 
page_dir_delete_slot(
1268
 
/*=================*/
1269
 
        page_t*         page,   /*!< in/out: the index page */
1270
 
        page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
1271
 
        ulint           slot_no)/*!< in: slot to be deleted */
1272
 
{
1273
 
        page_dir_slot_t*        slot;
1274
 
        ulint                   n_owned;
1275
 
        ulint                   i;
1276
 
        ulint                   n_slots;
1277
 
 
1278
 
        ut_ad(!page_zip || page_is_comp(page));
1279
 
        ut_ad(slot_no > 0);
1280
 
        ut_ad(slot_no + 1 < page_dir_get_n_slots(page));
1281
 
 
1282
 
        n_slots = page_dir_get_n_slots(page);
1283
 
 
1284
 
        /* 1. Reset the n_owned fields of the slots to be
1285
 
        deleted */
1286
 
        slot = page_dir_get_nth_slot(page, slot_no);
1287
 
        n_owned = page_dir_slot_get_n_owned(slot);
1288
 
        page_dir_slot_set_n_owned(slot, page_zip, 0);
1289
 
 
1290
 
        /* 2. Update the n_owned value of the first non-deleted slot */
1291
 
 
1292
 
        slot = page_dir_get_nth_slot(page, slot_no + 1);
1293
 
        page_dir_slot_set_n_owned(slot, page_zip,
1294
 
                                  n_owned + page_dir_slot_get_n_owned(slot));
1295
 
 
1296
 
        /* 3. Destroy the slot by copying slots */
1297
 
        for (i = slot_no + 1; i < n_slots; i++) {
1298
 
                rec_t*  rec = (rec_t*)
1299
 
                        page_dir_slot_get_rec(page_dir_get_nth_slot(page, i));
1300
 
                page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec);
1301
 
        }
1302
 
 
1303
 
        /* 4. Zero out the last slot, which will be removed */
1304
 
        mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0);
1305
 
 
1306
 
        /* 5. Update the page header */
1307
 
        page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1);
1308
 
}
1309
 
 
1310
 
/**************************************************************//**
1311
 
Used to add n slots to the directory. Does not set the record pointers
1312
 
in the added slots or update n_owned values: this is the responsibility
1313
 
of the caller. */
1314
 
UNIV_INLINE
1315
 
void
1316
 
page_dir_add_slot(
1317
 
/*==============*/
1318
 
        page_t*         page,   /*!< in/out: the index page */
1319
 
        page_zip_des_t* page_zip,/*!< in/out: comprssed page, or NULL */
1320
 
        ulint           start)  /*!< in: the slot above which the new slots
1321
 
                                are added */
1322
 
{
1323
 
        page_dir_slot_t*        slot;
1324
 
        ulint                   n_slots;
1325
 
 
1326
 
        n_slots = page_dir_get_n_slots(page);
1327
 
 
1328
 
        ut_ad(start < n_slots - 1);
1329
 
 
1330
 
        /* Update the page header */
1331
 
        page_dir_set_n_slots(page, page_zip, n_slots + 1);
1332
 
 
1333
 
        /* Move slots up */
1334
 
        slot = page_dir_get_nth_slot(page, n_slots);
1335
 
        memmove(slot, slot + PAGE_DIR_SLOT_SIZE,
1336
 
                (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE);
1337
 
}
1338
 
 
1339
 
/****************************************************************//**
1340
 
Splits a directory slot which owns too many records. */
1341
 
UNIV_INTERN
1342
 
void
1343
 
page_dir_split_slot(
1344
 
/*================*/
1345
 
        page_t*         page,   /*!< in/out: index page */
1346
 
        page_zip_des_t* page_zip,/*!< in/out: compressed page whose
1347
 
                                uncompressed part will be written, or NULL */
1348
 
        ulint           slot_no)/*!< in: the directory slot */
1349
 
{
1350
 
        rec_t*                  rec;
1351
 
        page_dir_slot_t*        new_slot;
1352
 
        page_dir_slot_t*        prev_slot;
1353
 
        page_dir_slot_t*        slot;
1354
 
        ulint                   i;
1355
 
        ulint                   n_owned;
1356
 
 
1357
 
        ut_ad(page);
1358
 
        ut_ad(!page_zip || page_is_comp(page));
1359
 
        ut_ad(slot_no > 0);
1360
 
 
1361
 
        slot = page_dir_get_nth_slot(page, slot_no);
1362
 
 
1363
 
        n_owned = page_dir_slot_get_n_owned(slot);
1364
 
        ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
1365
 
 
1366
 
        /* 1. We loop to find a record approximately in the middle of the
1367
 
        records owned by the slot. */
1368
 
 
1369
 
        prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
1370
 
        rec = (rec_t*) page_dir_slot_get_rec(prev_slot);
1371
 
 
1372
 
        for (i = 0; i < n_owned / 2; i++) {
1373
 
                rec = page_rec_get_next(rec);
1374
 
        }
1375
 
 
1376
 
        ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
1377
 
 
1378
 
        /* 2. We add one directory slot immediately below the slot to be
1379
 
        split. */
1380
 
 
1381
 
        page_dir_add_slot(page, page_zip, slot_no - 1);
1382
 
 
1383
 
        /* The added slot is now number slot_no, and the old slot is
1384
 
        now number slot_no + 1 */
1385
 
 
1386
 
        new_slot = page_dir_get_nth_slot(page, slot_no);
1387
 
        slot = page_dir_get_nth_slot(page, slot_no + 1);
1388
 
 
1389
 
        /* 3. We store the appropriate values to the new slot. */
1390
 
 
1391
 
        page_dir_slot_set_rec(new_slot, rec);
1392
 
        page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2);
1393
 
 
1394
 
        /* 4. Finally, we update the number of records field of the
1395
 
        original slot */
1396
 
 
1397
 
        page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2));
1398
 
}
1399
 
 
1400
 
/*************************************************************//**
1401
 
Tries to balance the given directory slot with too few records with the upper
1402
 
neighbor, so that there are at least the minimum number of records owned by
1403
 
the slot; this may result in the merging of two slots. */
1404
 
UNIV_INTERN
1405
 
void
1406
 
page_dir_balance_slot(
1407
 
/*==================*/
1408
 
        page_t*         page,   /*!< in/out: index page */
1409
 
        page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
1410
 
        ulint           slot_no)/*!< in: the directory slot */
1411
 
{
1412
 
        page_dir_slot_t*        slot;
1413
 
        page_dir_slot_t*        up_slot;
1414
 
        ulint                   n_owned;
1415
 
        ulint                   up_n_owned;
1416
 
        rec_t*                  old_rec;
1417
 
        rec_t*                  new_rec;
1418
 
 
1419
 
        ut_ad(page);
1420
 
        ut_ad(!page_zip || page_is_comp(page));
1421
 
        ut_ad(slot_no > 0);
1422
 
 
1423
 
        slot = page_dir_get_nth_slot(page, slot_no);
1424
 
 
1425
 
        /* The last directory slot cannot be balanced with the upper
1426
 
        neighbor, as there is none. */
1427
 
 
1428
 
        if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) {
1429
 
 
1430
 
                return;
1431
 
        }
1432
 
 
1433
 
        up_slot = page_dir_get_nth_slot(page, slot_no + 1);
1434
 
 
1435
 
        n_owned = page_dir_slot_get_n_owned(slot);
1436
 
        up_n_owned = page_dir_slot_get_n_owned(up_slot);
1437
 
 
1438
 
        ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
1439
 
 
1440
 
        /* If the upper slot has the minimum value of n_owned, we will merge
1441
 
        the two slots, therefore we assert: */
1442
 
        ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
1443
 
 
1444
 
        if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
1445
 
 
1446
 
                /* In this case we can just transfer one record owned
1447
 
                by the upper slot to the property of the lower slot */
1448
 
                old_rec = (rec_t*) page_dir_slot_get_rec(slot);
1449
 
 
1450
 
                if (page_is_comp(page)) {
1451
 
                        new_rec = rec_get_next_ptr(old_rec, TRUE);
1452
 
 
1453
 
                        rec_set_n_owned_new(old_rec, page_zip, 0);
1454
 
                        rec_set_n_owned_new(new_rec, page_zip, n_owned + 1);
1455
 
                } else {
1456
 
                        new_rec = rec_get_next_ptr(old_rec, FALSE);
1457
 
 
1458
 
                        rec_set_n_owned_old(old_rec, 0);
1459
 
                        rec_set_n_owned_old(new_rec, n_owned + 1);
1460
 
                }
1461
 
 
1462
 
                page_dir_slot_set_rec(slot, new_rec);
1463
 
 
1464
 
                page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1);
1465
 
        } else {
1466
 
                /* In this case we may merge the two slots */
1467
 
                page_dir_delete_slot(page, page_zip, slot_no);
1468
 
        }
1469
 
}
1470
 
 
1471
 
#ifndef UNIV_HOTBACKUP
1472
 
/************************************************************//**
1473
 
Returns the middle record of the record list. If there are an even number
1474
 
of records in the list, returns the first record of the upper half-list.
1475
 
@return middle record */
1476
 
UNIV_INTERN
1477
 
rec_t*
1478
 
page_get_middle_rec(
1479
 
/*================*/
1480
 
        page_t* page)   /*!< in: page */
1481
 
{
1482
 
        page_dir_slot_t*        slot;
1483
 
        ulint                   middle;
1484
 
        ulint                   i;
1485
 
        ulint                   n_owned;
1486
 
        ulint                   count;
1487
 
        rec_t*                  rec;
1488
 
 
1489
 
        /* This many records we must leave behind */
1490
 
        middle = (page_get_n_recs(page) + PAGE_HEAP_NO_USER_LOW) / 2;
1491
 
 
1492
 
        count = 0;
1493
 
 
1494
 
        for (i = 0;; i++) {
1495
 
 
1496
 
                slot = page_dir_get_nth_slot(page, i);
1497
 
                n_owned = page_dir_slot_get_n_owned(slot);
1498
 
 
1499
 
                if (count + n_owned > middle) {
1500
 
                        break;
1501
 
                } else {
1502
 
                        count += n_owned;
1503
 
                }
1504
 
        }
1505
 
 
1506
 
        ut_ad(i > 0);
1507
 
        slot = page_dir_get_nth_slot(page, i - 1);
1508
 
        rec = (rec_t*) page_dir_slot_get_rec(slot);
1509
 
        rec = page_rec_get_next(rec);
1510
 
 
1511
 
        /* There are now count records behind rec */
1512
 
 
1513
 
        for (i = 0; i < middle - count; i++) {
1514
 
                rec = page_rec_get_next(rec);
1515
 
        }
1516
 
 
1517
 
        return(rec);
1518
 
}
1519
 
#endif /* !UNIV_HOTBACKUP */
1520
 
 
1521
 
/***************************************************************//**
1522
 
Returns the number of records before the given record in chain.
1523
 
The number includes infimum and supremum records.
1524
 
@return number of records */
1525
 
UNIV_INTERN
1526
 
ulint
1527
 
page_rec_get_n_recs_before(
1528
 
/*=======================*/
1529
 
        const rec_t*    rec)    /*!< in: the physical record */
1530
 
{
1531
 
        const page_dir_slot_t*  slot;
1532
 
        const rec_t*            slot_rec;
1533
 
        const page_t*           page;
1534
 
        ulint                   i;
1535
 
        lint                    n       = 0;
1536
 
 
1537
 
        ut_ad(page_rec_check(rec));
1538
 
 
1539
 
        page = page_align(rec);
1540
 
        if (page_is_comp(page)) {
1541
 
                while (rec_get_n_owned_new(rec) == 0) {
1542
 
 
1543
 
                        rec = rec_get_next_ptr_const(rec, TRUE);
1544
 
                        n--;
1545
 
                }
1546
 
 
1547
 
                for (i = 0; ; i++) {
1548
 
                        slot = page_dir_get_nth_slot(page, i);
1549
 
                        slot_rec = page_dir_slot_get_rec(slot);
1550
 
 
1551
 
                        n += rec_get_n_owned_new(slot_rec);
1552
 
 
1553
 
                        if (rec == slot_rec) {
1554
 
 
1555
 
                                break;
1556
 
                        }
1557
 
                }
1558
 
        } else {
1559
 
                while (rec_get_n_owned_old(rec) == 0) {
1560
 
 
1561
 
                        rec = rec_get_next_ptr_const(rec, FALSE);
1562
 
                        n--;
1563
 
                }
1564
 
 
1565
 
                for (i = 0; ; i++) {
1566
 
                        slot = page_dir_get_nth_slot(page, i);
1567
 
                        slot_rec = page_dir_slot_get_rec(slot);
1568
 
 
1569
 
                        n += rec_get_n_owned_old(slot_rec);
1570
 
 
1571
 
                        if (rec == slot_rec) {
1572
 
 
1573
 
                                break;
1574
 
                        }
1575
 
                }
1576
 
        }
1577
 
 
1578
 
        n--;
1579
 
 
1580
 
        ut_ad(n >= 0);
1581
 
 
1582
 
        return((ulint) n);
1583
 
}
1584
 
 
1585
 
#ifndef UNIV_HOTBACKUP
1586
 
/************************************************************//**
1587
 
Prints record contents including the data relevant only in
1588
 
the index page context. */
1589
 
UNIV_INTERN
1590
 
void
1591
 
page_rec_print(
1592
 
/*===========*/
1593
 
        const rec_t*    rec,    /*!< in: physical record */
1594
 
        const ulint*    offsets)/*!< in: record descriptor */
1595
 
{
1596
 
        ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
1597
 
        rec_print_new(stderr, rec, offsets);
1598
 
        if (page_rec_is_comp(rec)) {
1599
 
                fprintf(stderr,
1600
 
                        " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1601
 
                        (ulong) rec_get_n_owned_new(rec),
1602
 
                        (ulong) rec_get_heap_no_new(rec),
1603
 
                        (ulong) rec_get_next_offs(rec, TRUE));
1604
 
        } else {
1605
 
                fprintf(stderr,
1606
 
                        " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1607
 
                        (ulong) rec_get_n_owned_old(rec),
1608
 
                        (ulong) rec_get_heap_no_old(rec),
1609
 
                        (ulong) rec_get_next_offs(rec, TRUE));
1610
 
        }
1611
 
 
1612
 
        page_rec_check(rec);
1613
 
        rec_validate(rec, offsets);
1614
 
}
1615
 
 
1616
 
/***************************************************************//**
1617
 
This is used to print the contents of the directory for
1618
 
debugging purposes. */
1619
 
UNIV_INTERN
1620
 
void
1621
 
page_dir_print(
1622
 
/*===========*/
1623
 
        page_t* page,   /*!< in: index page */
1624
 
        ulint   pr_n)   /*!< in: print n first and n last entries */
1625
 
{
1626
 
        ulint                   n;
1627
 
        ulint                   i;
1628
 
        page_dir_slot_t*        slot;
1629
 
 
1630
 
        n = page_dir_get_n_slots(page);
1631
 
 
1632
 
        fprintf(stderr, "--------------------------------\n"
1633
 
                "PAGE DIRECTORY\n"
1634
 
                "Page address %p\n"
1635
 
                "Directory stack top at offs: %lu; number of slots: %lu\n",
1636
 
                page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)),
1637
 
                (ulong) n);
1638
 
        for (i = 0; i < n; i++) {
1639
 
                slot = page_dir_get_nth_slot(page, i);
1640
 
                if ((i == pr_n) && (i < n - pr_n)) {
1641
 
                        fputs("    ...   \n", stderr);
1642
 
                }
1643
 
                if ((i < pr_n) || (i >= n - pr_n)) {
1644
 
                        fprintf(stderr,
1645
 
                                "Contents of slot: %lu: n_owned: %lu,"
1646
 
                                " rec offs: %lu\n",
1647
 
                                (ulong) i,
1648
 
                                (ulong) page_dir_slot_get_n_owned(slot),
1649
 
                                (ulong)
1650
 
                                page_offset(page_dir_slot_get_rec(slot)));
1651
 
                }
1652
 
        }
1653
 
        fprintf(stderr, "Total of %lu records\n"
1654
 
                "--------------------------------\n",
1655
 
                (ulong) (PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page)));
1656
 
}
1657
 
 
1658
 
/***************************************************************//**
1659
 
This is used to print the contents of the page record list for
1660
 
debugging purposes. */
1661
 
UNIV_INTERN
1662
 
void
1663
 
page_print_list(
1664
 
/*============*/
1665
 
        buf_block_t*    block,  /*!< in: index page */
1666
 
        dict_index_t*   index,  /*!< in: dictionary index of the page */
1667
 
        ulint           pr_n)   /*!< in: print n first and n last entries */
1668
 
{
1669
 
        page_t*         page            = block->frame;
1670
 
        page_cur_t      cur;
1671
 
        ulint           count;
1672
 
        ulint           n_recs;
1673
 
        mem_heap_t*     heap            = NULL;
1674
 
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
1675
 
        ulint*          offsets         = offsets_;
1676
 
        rec_offs_init(offsets_);
1677
 
 
1678
 
        ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table));
1679
 
 
1680
 
        fprintf(stderr,
1681
 
                "--------------------------------\n"
1682
 
                "PAGE RECORD LIST\n"
1683
 
                "Page address %p\n", page);
1684
 
 
1685
 
        n_recs = page_get_n_recs(page);
1686
 
 
1687
 
        page_cur_set_before_first(block, &cur);
1688
 
        count = 0;
1689
 
        for (;;) {
1690
 
                offsets = rec_get_offsets(cur.rec, index, offsets,
1691
 
                                          ULINT_UNDEFINED, &heap);
1692
 
                page_rec_print(cur.rec, offsets);
1693
 
 
1694
 
                if (count == pr_n) {
1695
 
                        break;
1696
 
                }
1697
 
                if (page_cur_is_after_last(&cur)) {
1698
 
                        break;
1699
 
                }
1700
 
                page_cur_move_to_next(&cur);
1701
 
                count++;
1702
 
        }
1703
 
 
1704
 
        if (n_recs > 2 * pr_n) {
1705
 
                fputs(" ... \n", stderr);
1706
 
        }
1707
 
 
1708
 
        while (!page_cur_is_after_last(&cur)) {
1709
 
                page_cur_move_to_next(&cur);
1710
 
 
1711
 
                if (count + pr_n >= n_recs) {
1712
 
                        offsets = rec_get_offsets(cur.rec, index, offsets,
1713
 
                                                  ULINT_UNDEFINED, &heap);
1714
 
                        page_rec_print(cur.rec, offsets);
1715
 
                }
1716
 
                count++;
1717
 
        }
1718
 
 
1719
 
        fprintf(stderr,
1720
 
                "Total of %lu records \n"
1721
 
                "--------------------------------\n",
1722
 
                (ulong) (count + 1));
1723
 
 
1724
 
        if (UNIV_LIKELY_NULL(heap)) {
1725
 
                mem_heap_free(heap);
1726
 
        }
1727
 
}
1728
 
 
1729
 
/***************************************************************//**
1730
 
Prints the info in a page header. */
1731
 
UNIV_INTERN
1732
 
void
1733
 
page_header_print(
1734
 
/*==============*/
1735
 
        const page_t*   page)
1736
 
{
1737
 
        fprintf(stderr,
1738
 
                "--------------------------------\n"
1739
 
                "PAGE HEADER INFO\n"
1740
 
                "Page address %p, n records %lu (%s)\n"
1741
 
                "n dir slots %lu, heap top %lu\n"
1742
 
                "Page n heap %lu, free %lu, garbage %lu\n"
1743
 
                "Page last insert %lu, direction %lu, n direction %lu\n",
1744
 
                page, (ulong) page_header_get_field(page, PAGE_N_RECS),
1745
 
                page_is_comp(page) ? "compact format" : "original format",
1746
 
                (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
1747
 
                (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1748
 
                (ulong) page_dir_get_n_heap(page),
1749
 
                (ulong) page_header_get_field(page, PAGE_FREE),
1750
 
                (ulong) page_header_get_field(page, PAGE_GARBAGE),
1751
 
                (ulong) page_header_get_field(page, PAGE_LAST_INSERT),
1752
 
                (ulong) page_header_get_field(page, PAGE_DIRECTION),
1753
 
                (ulong) page_header_get_field(page, PAGE_N_DIRECTION));
1754
 
}
1755
 
 
1756
 
/***************************************************************//**
1757
 
This is used to print the contents of the page for
1758
 
debugging purposes. */
1759
 
UNIV_INTERN
1760
 
void
1761
 
page_print(
1762
 
/*=======*/
1763
 
        buf_block_t*    block,  /*!< in: index page */
1764
 
        dict_index_t*   index,  /*!< in: dictionary index of the page */
1765
 
        ulint           dn,     /*!< in: print dn first and last entries
1766
 
                                in directory */
1767
 
        ulint           rn)     /*!< in: print rn first and last records
1768
 
                                in directory */
1769
 
{
1770
 
        page_t* page = block->frame;
1771
 
 
1772
 
        page_header_print(page);
1773
 
        page_dir_print(page, dn);
1774
 
        page_print_list(block, index, rn);
1775
 
}
1776
 
#endif /* !UNIV_HOTBACKUP */
1777
 
 
1778
 
/***************************************************************//**
1779
 
The following is used to validate a record on a page. This function
1780
 
differs from rec_validate as it can also check the n_owned field and
1781
 
the heap_no field.
1782
 
@return TRUE if ok */
1783
 
UNIV_INTERN
1784
 
ibool
1785
 
page_rec_validate(
1786
 
/*==============*/
1787
 
        rec_t*          rec,    /*!< in: physical record */
1788
 
        const ulint*    offsets)/*!< in: array returned by rec_get_offsets() */
1789
 
{
1790
 
        ulint   n_owned;
1791
 
        ulint   heap_no;
1792
 
        page_t* page;
1793
 
 
1794
 
        page = page_align(rec);
1795
 
        ut_a(!page_is_comp(page) == !rec_offs_comp(offsets));
1796
 
 
1797
 
        page_rec_check(rec);
1798
 
        rec_validate(rec, offsets);
1799
 
 
1800
 
        if (page_rec_is_comp(rec)) {
1801
 
                n_owned = rec_get_n_owned_new(rec);
1802
 
                heap_no = rec_get_heap_no_new(rec);
1803
 
        } else {
1804
 
                n_owned = rec_get_n_owned_old(rec);
1805
 
                heap_no = rec_get_heap_no_old(rec);
1806
 
        }
1807
 
 
1808
 
        if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) {
1809
 
                fprintf(stderr,
1810
 
                        "InnoDB: Dir slot of rec %lu, n owned too big %lu\n",
1811
 
                        (ulong) page_offset(rec), (ulong) n_owned);
1812
 
                return(FALSE);
1813
 
        }
1814
 
 
1815
 
        if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) {
1816
 
                fprintf(stderr,
1817
 
                        "InnoDB: Heap no of rec %lu too big %lu %lu\n",
1818
 
                        (ulong) page_offset(rec), (ulong) heap_no,
1819
 
                        (ulong) page_dir_get_n_heap(page));
1820
 
                return(FALSE);
1821
 
        }
1822
 
 
1823
 
        return(TRUE);
1824
 
}
1825
 
 
1826
 
#ifndef UNIV_HOTBACKUP
1827
 
/***************************************************************//**
1828
 
Checks that the first directory slot points to the infimum record and
1829
 
the last to the supremum. This function is intended to track if the
1830
 
bug fixed in 4.0.14 has caused corruption to users' databases. */
1831
 
UNIV_INTERN
1832
 
void
1833
 
page_check_dir(
1834
 
/*===========*/
1835
 
        const page_t*   page)   /*!< in: index page */
1836
 
{
1837
 
        ulint   n_slots;
1838
 
        ulint   infimum_offs;
1839
 
        ulint   supremum_offs;
1840
 
 
1841
 
        n_slots = page_dir_get_n_slots(page);
1842
 
        infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0));
1843
 
        supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page,
1844
 
                                                               n_slots - 1));
1845
 
 
1846
 
        if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) {
1847
 
 
1848
 
                fprintf(stderr,
1849
 
                        "InnoDB: Page directory corruption:"
1850
 
                        " infimum not pointed to\n");
1851
 
                buf_page_print(page, 0);
1852
 
        }
1853
 
 
1854
 
        if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) {
1855
 
 
1856
 
                fprintf(stderr,
1857
 
                        "InnoDB: Page directory corruption:"
1858
 
                        " supremum not pointed to\n");
1859
 
                buf_page_print(page, 0);
1860
 
        }
1861
 
}
1862
 
#endif /* !UNIV_HOTBACKUP */
1863
 
 
1864
 
/***************************************************************//**
1865
 
This function checks the consistency of an index page when we do not
1866
 
know the index. This is also resilient so that this should never crash
1867
 
even if the page is total garbage.
1868
 
@return TRUE if ok */
1869
 
UNIV_INTERN
1870
 
ibool
1871
 
page_simple_validate_old(
1872
 
/*=====================*/
1873
 
        page_t* page)   /*!< in: old-style index page */
1874
 
{
1875
 
        page_dir_slot_t* slot;
1876
 
        ulint           slot_no;
1877
 
        ulint           n_slots;
1878
 
        rec_t*          rec;
1879
 
        byte*           rec_heap_top;
1880
 
        ulint           count;
1881
 
        ulint           own_count;
1882
 
        ibool           ret     = FALSE;
1883
 
 
1884
 
        ut_a(!page_is_comp(page));
1885
 
 
1886
 
        /* Check first that the record heap and the directory do not
1887
 
        overlap. */
1888
 
 
1889
 
        n_slots = page_dir_get_n_slots(page);
1890
 
 
1891
 
        if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
1892
 
                fprintf(stderr,
1893
 
                        "InnoDB: Nonsensical number %lu of page dir slots\n",
1894
 
                        (ulong) n_slots);
1895
 
 
1896
 
                goto func_exit;
1897
 
        }
1898
 
 
1899
 
        rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
1900
 
 
1901
 
        if (UNIV_UNLIKELY(rec_heap_top
1902
 
                          > page_dir_get_nth_slot(page, n_slots - 1))) {
1903
 
 
1904
 
                fprintf(stderr,
1905
 
                        "InnoDB: Record heap and dir overlap on a page,"
1906
 
                        " heap top %lu, dir %lu\n",
1907
 
                        (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1908
 
                        (ulong)
1909
 
                        page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
1910
 
 
1911
 
                goto func_exit;
1912
 
        }
1913
 
 
1914
 
        /* Validate the record list in a loop checking also that it is
1915
 
        consistent with the page record directory. */
1916
 
 
1917
 
        count = 0;
1918
 
        own_count = 1;
1919
 
        slot_no = 0;
1920
 
        slot = page_dir_get_nth_slot(page, slot_no);
1921
 
 
1922
 
        rec = page_get_infimum_rec(page);
1923
 
 
1924
 
        for (;;) {
1925
 
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
1926
 
                        fprintf(stderr,
1927
 
                                "InnoDB: Record %lu is above"
1928
 
                                " rec heap top %lu\n",
1929
 
                                (ulong)(rec - page),
1930
 
                                (ulong)(rec_heap_top - page));
1931
 
 
1932
 
                        goto func_exit;
1933
 
                }
1934
 
 
1935
 
                if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) {
1936
 
                        /* This is a record pointed to by a dir slot */
1937
 
                        if (UNIV_UNLIKELY(rec_get_n_owned_old(rec)
1938
 
                                          != own_count)) {
1939
 
 
1940
 
                                fprintf(stderr,
1941
 
                                        "InnoDB: Wrong owned count %lu, %lu,"
1942
 
                                        " rec %lu\n",
1943
 
                                        (ulong) rec_get_n_owned_old(rec),
1944
 
                                        (ulong) own_count,
1945
 
                                        (ulong)(rec - page));
1946
 
 
1947
 
                                goto func_exit;
1948
 
                        }
1949
 
 
1950
 
                        if (UNIV_UNLIKELY
1951
 
                            (page_dir_slot_get_rec(slot) != rec)) {
1952
 
                                fprintf(stderr,
1953
 
                                        "InnoDB: Dir slot does not point"
1954
 
                                        " to right rec %lu\n",
1955
 
                                        (ulong)(rec - page));
1956
 
 
1957
 
                                goto func_exit;
1958
 
                        }
1959
 
 
1960
 
                        own_count = 0;
1961
 
 
1962
 
                        if (!page_rec_is_supremum(rec)) {
1963
 
                                slot_no++;
1964
 
                                slot = page_dir_get_nth_slot(page, slot_no);
1965
 
                        }
1966
 
                }
1967
 
 
1968
 
                if (page_rec_is_supremum(rec)) {
1969
 
 
1970
 
                        break;
1971
 
                }
1972
 
 
1973
 
                if (UNIV_UNLIKELY
1974
 
                    (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA
1975
 
                     || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) {
1976
 
                        fprintf(stderr,
1977
 
                                "InnoDB: Next record offset"
1978
 
                                " nonsensical %lu for rec %lu\n",
1979
 
                                (ulong) rec_get_next_offs(rec, FALSE),
1980
 
                                (ulong) (rec - page));
1981
 
 
1982
 
                        goto func_exit;
1983
 
                }
1984
 
 
1985
 
                count++;
1986
 
 
1987
 
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
1988
 
                        fprintf(stderr,
1989
 
                                "InnoDB: Page record list appears"
1990
 
                                " to be circular %lu\n",
1991
 
                                (ulong) count);
1992
 
                        goto func_exit;
1993
 
                }
1994
 
 
1995
 
                rec = page_rec_get_next(rec);
1996
 
                own_count++;
1997
 
        }
1998
 
 
1999
 
        if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
2000
 
                fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n");
2001
 
 
2002
 
                goto func_exit;
2003
 
        }
2004
 
 
2005
 
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2006
 
                fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
2007
 
                        (ulong) slot_no, (ulong) (n_slots - 1));
2008
 
                goto func_exit;
2009
 
        }
2010
 
 
2011
 
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2012
 
                          + PAGE_HEAP_NO_USER_LOW
2013
 
                          != count + 1)) {
2014
 
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2015
 
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
2016
 
                        + PAGE_HEAP_NO_USER_LOW,
2017
 
                        (ulong) (count + 1));
2018
 
 
2019
 
                goto func_exit;
2020
 
        }
2021
 
 
2022
 
        /* Check then the free list */
2023
 
        rec = page_header_get_ptr(page, PAGE_FREE);
2024
 
 
2025
 
        while (rec != NULL) {
2026
 
                if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
2027
 
                                  || rec >= page + UNIV_PAGE_SIZE)) {
2028
 
                        fprintf(stderr,
2029
 
                                "InnoDB: Free list record has"
2030
 
                                " a nonsensical offset %lu\n",
2031
 
                                (ulong) (rec - page));
2032
 
 
2033
 
                        goto func_exit;
2034
 
                }
2035
 
 
2036
 
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2037
 
                        fprintf(stderr,
2038
 
                                "InnoDB: Free list record %lu"
2039
 
                                " is above rec heap top %lu\n",
2040
 
                                (ulong) (rec - page),
2041
 
                                (ulong) (rec_heap_top - page));
2042
 
 
2043
 
                        goto func_exit;
2044
 
                }
2045
 
 
2046
 
                count++;
2047
 
 
2048
 
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2049
 
                        fprintf(stderr,
2050
 
                                "InnoDB: Page free list appears"
2051
 
                                " to be circular %lu\n",
2052
 
                                (ulong) count);
2053
 
                        goto func_exit;
2054
 
                }
2055
 
 
2056
 
                rec = page_rec_get_next(rec);
2057
 
        }
2058
 
 
2059
 
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2060
 
 
2061
 
                fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
2062
 
                        (ulong) page_dir_get_n_heap(page),
2063
 
                        (ulong) (count + 1));
2064
 
 
2065
 
                goto func_exit;
2066
 
        }
2067
 
 
2068
 
        ret = TRUE;
2069
 
 
2070
 
func_exit:
2071
 
        return(ret);
2072
 
}
2073
 
 
2074
 
/***************************************************************//**
2075
 
This function checks the consistency of an index page when we do not
2076
 
know the index. This is also resilient so that this should never crash
2077
 
even if the page is total garbage.
2078
 
@return TRUE if ok */
2079
 
UNIV_INTERN
2080
 
ibool
2081
 
page_simple_validate_new(
2082
 
/*=====================*/
2083
 
        page_t* page)   /*!< in: new-style index page */
2084
 
{
2085
 
        page_dir_slot_t* slot;
2086
 
        ulint           slot_no;
2087
 
        ulint           n_slots;
2088
 
        rec_t*          rec;
2089
 
        byte*           rec_heap_top;
2090
 
        ulint           count;
2091
 
        ulint           own_count;
2092
 
        ibool           ret     = FALSE;
2093
 
 
2094
 
        ut_a(page_is_comp(page));
2095
 
 
2096
 
        /* Check first that the record heap and the directory do not
2097
 
        overlap. */
2098
 
 
2099
 
        n_slots = page_dir_get_n_slots(page);
2100
 
 
2101
 
        if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
2102
 
                fprintf(stderr,
2103
 
                        "InnoDB: Nonsensical number %lu"
2104
 
                        " of page dir slots\n", (ulong) n_slots);
2105
 
 
2106
 
                goto func_exit;
2107
 
        }
2108
 
 
2109
 
        rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
2110
 
 
2111
 
        if (UNIV_UNLIKELY(rec_heap_top
2112
 
                          > page_dir_get_nth_slot(page, n_slots - 1))) {
2113
 
 
2114
 
                fprintf(stderr,
2115
 
                        "InnoDB: Record heap and dir overlap on a page,"
2116
 
                        " heap top %lu, dir %lu\n",
2117
 
                        (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
2118
 
                        (ulong)
2119
 
                        page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
2120
 
 
2121
 
                goto func_exit;
2122
 
        }
2123
 
 
2124
 
        /* Validate the record list in a loop checking also that it is
2125
 
        consistent with the page record directory. */
2126
 
 
2127
 
        count = 0;
2128
 
        own_count = 1;
2129
 
        slot_no = 0;
2130
 
        slot = page_dir_get_nth_slot(page, slot_no);
2131
 
 
2132
 
        rec = page_get_infimum_rec(page);
2133
 
 
2134
 
        for (;;) {
2135
 
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2136
 
                        fprintf(stderr,
2137
 
                                "InnoDB: Record %lu is above rec"
2138
 
                                " heap top %lu\n",
2139
 
                                (ulong) page_offset(rec),
2140
 
                                (ulong) page_offset(rec_heap_top));
2141
 
 
2142
 
                        goto func_exit;
2143
 
                }
2144
 
 
2145
 
                if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) {
2146
 
                        /* This is a record pointed to by a dir slot */
2147
 
                        if (UNIV_UNLIKELY(rec_get_n_owned_new(rec)
2148
 
                                          != own_count)) {
2149
 
 
2150
 
                                fprintf(stderr,
2151
 
                                        "InnoDB: Wrong owned count %lu, %lu,"
2152
 
                                        " rec %lu\n",
2153
 
                                        (ulong) rec_get_n_owned_new(rec),
2154
 
                                        (ulong) own_count,
2155
 
                                        (ulong) page_offset(rec));
2156
 
 
2157
 
                                goto func_exit;
2158
 
                        }
2159
 
 
2160
 
                        if (UNIV_UNLIKELY
2161
 
                            (page_dir_slot_get_rec(slot) != rec)) {
2162
 
                                fprintf(stderr,
2163
 
                                        "InnoDB: Dir slot does not point"
2164
 
                                        " to right rec %lu\n",
2165
 
                                        (ulong) page_offset(rec));
2166
 
 
2167
 
                                goto func_exit;
2168
 
                        }
2169
 
 
2170
 
                        own_count = 0;
2171
 
 
2172
 
                        if (!page_rec_is_supremum(rec)) {
2173
 
                                slot_no++;
2174
 
                                slot = page_dir_get_nth_slot(page, slot_no);
2175
 
                        }
2176
 
                }
2177
 
 
2178
 
                if (page_rec_is_supremum(rec)) {
2179
 
 
2180
 
                        break;
2181
 
                }
2182
 
 
2183
 
                if (UNIV_UNLIKELY
2184
 
                    (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA
2185
 
                     || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) {
2186
 
                        fprintf(stderr,
2187
 
                                "InnoDB: Next record offset nonsensical %lu"
2188
 
                                " for rec %lu\n",
2189
 
                                (ulong) rec_get_next_offs(rec, TRUE),
2190
 
                                (ulong) page_offset(rec));
2191
 
 
2192
 
                        goto func_exit;
2193
 
                }
2194
 
 
2195
 
                count++;
2196
 
 
2197
 
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2198
 
                        fprintf(stderr,
2199
 
                                "InnoDB: Page record list appears"
2200
 
                                " to be circular %lu\n",
2201
 
                                (ulong) count);
2202
 
                        goto func_exit;
2203
 
                }
2204
 
 
2205
 
                rec = page_rec_get_next(rec);
2206
 
                own_count++;
2207
 
        }
2208
 
 
2209
 
        if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2210
 
                fprintf(stderr, "InnoDB: n owned is zero"
2211
 
                        " in a supremum rec\n");
2212
 
 
2213
 
                goto func_exit;
2214
 
        }
2215
 
 
2216
 
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2217
 
                fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
2218
 
                        (ulong) slot_no, (ulong) (n_slots - 1));
2219
 
                goto func_exit;
2220
 
        }
2221
 
 
2222
 
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2223
 
                          + PAGE_HEAP_NO_USER_LOW
2224
 
                          != count + 1)) {
2225
 
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2226
 
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
2227
 
                        + PAGE_HEAP_NO_USER_LOW,
2228
 
                        (ulong) (count + 1));
2229
 
 
2230
 
                goto func_exit;
2231
 
        }
2232
 
 
2233
 
        /* Check then the free list */
2234
 
        rec = page_header_get_ptr(page, PAGE_FREE);
2235
 
 
2236
 
        while (rec != NULL) {
2237
 
                if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
2238
 
                                  || rec >= page + UNIV_PAGE_SIZE)) {
2239
 
                        fprintf(stderr,
2240
 
                                "InnoDB: Free list record has"
2241
 
                                " a nonsensical offset %lu\n",
2242
 
                                (ulong) page_offset(rec));
2243
 
 
2244
 
                        goto func_exit;
2245
 
                }
2246
 
 
2247
 
                if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2248
 
                        fprintf(stderr,
2249
 
                                "InnoDB: Free list record %lu"
2250
 
                                " is above rec heap top %lu\n",
2251
 
                                (ulong) page_offset(rec),
2252
 
                                (ulong) page_offset(rec_heap_top));
2253
 
 
2254
 
                        goto func_exit;
2255
 
                }
2256
 
 
2257
 
                count++;
2258
 
 
2259
 
                if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2260
 
                        fprintf(stderr,
2261
 
                                "InnoDB: Page free list appears"
2262
 
                                " to be circular %lu\n",
2263
 
                                (ulong) count);
2264
 
                        goto func_exit;
2265
 
                }
2266
 
 
2267
 
                rec = page_rec_get_next(rec);
2268
 
        }
2269
 
 
2270
 
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2271
 
 
2272
 
                fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
2273
 
                        (ulong) page_dir_get_n_heap(page),
2274
 
                        (ulong) (count + 1));
2275
 
 
2276
 
                goto func_exit;
2277
 
        }
2278
 
 
2279
 
        ret = TRUE;
2280
 
 
2281
 
func_exit:
2282
 
        return(ret);
2283
 
}
2284
 
 
2285
 
/***************************************************************//**
2286
 
This function checks the consistency of an index page.
2287
 
@return TRUE if ok */
2288
 
UNIV_INTERN
2289
 
ibool
2290
 
page_validate(
2291
 
/*==========*/
2292
 
        page_t*         page,   /*!< in: index page */
2293
 
        dict_index_t*   index)  /*!< in: data dictionary index containing
2294
 
                                the page record type definition */
2295
 
{
2296
 
        page_dir_slot_t*slot;
2297
 
        mem_heap_t*     heap;
2298
 
        byte*           buf;
2299
 
        ulint           count;
2300
 
        ulint           own_count;
2301
 
        ulint           rec_own_count;
2302
 
        ulint           slot_no;
2303
 
        ulint           data_size;
2304
 
        rec_t*          rec;
2305
 
        rec_t*          old_rec         = NULL;
2306
 
        ulint           offs;
2307
 
        ulint           n_slots;
2308
 
        ibool           ret             = FALSE;
2309
 
        ulint           i;
2310
 
        ulint*          offsets         = NULL;
2311
 
        ulint*          old_offsets     = NULL;
2312
 
 
2313
 
        if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
2314
 
                          != dict_table_is_comp(index->table))) {
2315
 
                fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
2316
 
                goto func_exit2;
2317
 
        }
2318
 
        if (page_is_comp(page)) {
2319
 
                if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
2320
 
                        goto func_exit2;
2321
 
                }
2322
 
        } else {
2323
 
                if (UNIV_UNLIKELY(!page_simple_validate_old(page))) {
2324
 
                        goto func_exit2;
2325
 
                }
2326
 
        }
2327
 
 
2328
 
        heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
2329
 
 
2330
 
        /* The following buffer is used to check that the
2331
 
        records in the page record heap do not overlap */
2332
 
 
2333
 
        buf = mem_heap_zalloc(heap, UNIV_PAGE_SIZE);
2334
 
 
2335
 
        /* Check first that the record heap and the directory do not
2336
 
        overlap. */
2337
 
 
2338
 
        n_slots = page_dir_get_n_slots(page);
2339
 
 
2340
 
        if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
2341
 
                            <= page_dir_get_nth_slot(page, n_slots - 1)))) {
2342
 
 
2343
 
                fprintf(stderr, 
2344
 
                        "InnoDB: Record heap and dir overlap"
2345
 
                        " on space %lu page %lu index %s, %p, %p\n",
2346
 
                        (ulong) page_get_space_id(page),
2347
 
                        (ulong) page_get_page_no(page), index->name,
2348
 
                        page_header_get_ptr(page, PAGE_HEAP_TOP),
2349
 
                        page_dir_get_nth_slot(page, n_slots - 1));
2350
 
 
2351
 
                goto func_exit;
2352
 
        }
2353
 
 
2354
 
        /* Validate the record list in a loop checking also that
2355
 
        it is consistent with the directory. */
2356
 
        count = 0;
2357
 
        data_size = 0;
2358
 
        own_count = 1;
2359
 
        slot_no = 0;
2360
 
        slot = page_dir_get_nth_slot(page, slot_no);
2361
 
 
2362
 
        rec = page_get_infimum_rec(page);
2363
 
 
2364
 
        for (;;) {
2365
 
                offsets = rec_get_offsets(rec, index, offsets,
2366
 
                                          ULINT_UNDEFINED, &heap);
2367
 
 
2368
 
                if (page_is_comp(page) && page_rec_is_user_rec(rec)
2369
 
                    && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
2370
 
                                     == page_is_leaf(page))) {
2371
 
                        fputs("InnoDB: node_ptr flag mismatch\n", stderr);
2372
 
                        goto func_exit;
2373
 
                }
2374
 
 
2375
 
                if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2376
 
                        goto func_exit;
2377
 
                }
2378
 
 
2379
 
#ifndef UNIV_HOTBACKUP
2380
 
                /* Check that the records are in the ascending order */
2381
 
                if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW)
2382
 
                    && !page_rec_is_supremum(rec)) {
2383
 
                        if (UNIV_UNLIKELY
2384
 
                            (1 != cmp_rec_rec(rec, old_rec,
2385
 
                                              offsets, old_offsets, index))) {
2386
 
                                fprintf(stderr, 
2387
 
                                        "InnoDB: Records in wrong order"
2388
 
                                        " on space %lu page %lu index %s\n",
2389
 
                                        (ulong) page_get_space_id(page),
2390
 
                                        (ulong) page_get_page_no(page),
2391
 
                                        index->name);
2392
 
                                fputs("\nInnoDB: previous record ", stderr);
2393
 
                                rec_print_new(stderr, old_rec, old_offsets);
2394
 
                                fputs("\nInnoDB: record ", stderr);
2395
 
                                rec_print_new(stderr, rec, offsets);
2396
 
                                putc('\n', stderr);
2397
 
 
2398
 
                                goto func_exit;
2399
 
                        }
2400
 
                }
2401
 
#endif /* !UNIV_HOTBACKUP */
2402
 
 
2403
 
                if (page_rec_is_user_rec(rec)) {
2404
 
 
2405
 
                        data_size += rec_offs_size(offsets);
2406
 
                }
2407
 
 
2408
 
                offs = page_offset(rec_get_start(rec, offsets));
2409
 
 
2410
 
                for (i = rec_offs_size(offsets); i--; ) {
2411
 
                        if (UNIV_UNLIKELY(buf[offs + i])) {
2412
 
                                /* No other record may overlap this */
2413
 
 
2414
 
                                fputs("InnoDB: Record overlaps another\n",
2415
 
                                      stderr);
2416
 
                                goto func_exit;
2417
 
                        }
2418
 
 
2419
 
                        buf[offs + i] = 1;
2420
 
                }
2421
 
 
2422
 
                if (page_is_comp(page)) {
2423
 
                        rec_own_count = rec_get_n_owned_new(rec);
2424
 
                } else {
2425
 
                        rec_own_count = rec_get_n_owned_old(rec);
2426
 
                }
2427
 
 
2428
 
                if (UNIV_UNLIKELY(rec_own_count)) {
2429
 
                        /* This is a record pointed to by a dir slot */
2430
 
                        if (UNIV_UNLIKELY(rec_own_count != own_count)) {
2431
 
                                fprintf(stderr,
2432
 
                                        "InnoDB: Wrong owned count %lu, %lu\n",
2433
 
                                        (ulong) rec_own_count,
2434
 
                                        (ulong) own_count);
2435
 
                                goto func_exit;
2436
 
                        }
2437
 
 
2438
 
                        if (page_dir_slot_get_rec(slot) != rec) {
2439
 
                                fputs("InnoDB: Dir slot does not"
2440
 
                                      " point to right rec\n",
2441
 
                                      stderr);
2442
 
                                goto func_exit;
2443
 
                        }
2444
 
 
2445
 
                        page_dir_slot_check(slot);
2446
 
 
2447
 
                        own_count = 0;
2448
 
                        if (!page_rec_is_supremum(rec)) {
2449
 
                                slot_no++;
2450
 
                                slot = page_dir_get_nth_slot(page, slot_no);
2451
 
                        }
2452
 
                }
2453
 
 
2454
 
                if (page_rec_is_supremum(rec)) {
2455
 
                        break;
2456
 
                }
2457
 
 
2458
 
                count++;
2459
 
                own_count++;
2460
 
                old_rec = rec;
2461
 
                rec = page_rec_get_next(rec);
2462
 
 
2463
 
                /* set old_offsets to offsets; recycle offsets */
2464
 
                {
2465
 
                        ulint* offs = old_offsets;
2466
 
                        old_offsets = offsets;
2467
 
                        offsets = offs;
2468
 
                }
2469
 
        }
2470
 
 
2471
 
        if (page_is_comp(page)) {
2472
 
                if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2473
 
 
2474
 
                        goto n_owned_zero;
2475
 
                }
2476
 
        } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
2477
 
n_owned_zero:
2478
 
                fputs("InnoDB: n owned is zero\n", stderr);
2479
 
                goto func_exit;
2480
 
        }
2481
 
 
2482
 
        if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2483
 
                fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
2484
 
                        (ulong) slot_no, (ulong) (n_slots - 1));
2485
 
                goto func_exit;
2486
 
        }
2487
 
 
2488
 
        if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2489
 
                          + PAGE_HEAP_NO_USER_LOW
2490
 
                          != count + 1)) {
2491
 
                fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2492
 
                        (ulong) page_header_get_field(page, PAGE_N_RECS)
2493
 
                        + PAGE_HEAP_NO_USER_LOW,
2494
 
                        (ulong) (count + 1));
2495
 
                goto func_exit;
2496
 
        }
2497
 
 
2498
 
        if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
2499
 
                fprintf(stderr,
2500
 
                        "InnoDB: Summed data size %lu, returned by func %lu\n",
2501
 
                        (ulong) data_size, (ulong) page_get_data_size(page));
2502
 
                goto func_exit;
2503
 
        }
2504
 
 
2505
 
        /* Check then the free list */
2506
 
        rec = page_header_get_ptr(page, PAGE_FREE);
2507
 
 
2508
 
        while (rec != NULL) {
2509
 
                offsets = rec_get_offsets(rec, index, offsets,
2510
 
                                          ULINT_UNDEFINED, &heap);
2511
 
                if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2512
 
 
2513
 
                        goto func_exit;
2514
 
                }
2515
 
 
2516
 
                count++;
2517
 
                offs = page_offset(rec_get_start(rec, offsets));
2518
 
 
2519
 
                for (i = rec_offs_size(offsets); i--; ) {
2520
 
 
2521
 
                        if (UNIV_UNLIKELY(buf[offs + i])) {
2522
 
                                fputs("InnoDB: Record overlaps another"
2523
 
                                      " in free list\n", stderr);
2524
 
                                goto func_exit;
2525
 
                        }
2526
 
 
2527
 
                        buf[offs + i] = 1;
2528
 
                }
2529
 
 
2530
 
                rec = page_rec_get_next(rec);
2531
 
        }
2532
 
 
2533
 
        if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2534
 
                fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
2535
 
                        (ulong) page_dir_get_n_heap(page),
2536
 
                        (ulong) count + 1);
2537
 
                goto func_exit;
2538
 
        }
2539
 
 
2540
 
        ret = TRUE;
2541
 
 
2542
 
func_exit:
2543
 
        mem_heap_free(heap);
2544
 
 
2545
 
        if (UNIV_UNLIKELY(ret == FALSE)) {
2546
 
func_exit2:
2547
 
                fprintf(stderr, 
2548
 
                        "InnoDB: Apparent corruption"
2549
 
                        " in space %lu page %lu index %s\n",
2550
 
                        (ulong) page_get_space_id(page),
2551
 
                        (ulong) page_get_page_no(page),
2552
 
                        index->name);
2553
 
                buf_page_print(page, 0);
2554
 
        }
2555
 
 
2556
 
        return(ret);
2557
 
}
2558
 
 
2559
 
#ifndef UNIV_HOTBACKUP
2560
 
/***************************************************************//**
2561
 
Looks in the page record list for a record with the given heap number.
2562
 
@return record, NULL if not found */
2563
 
UNIV_INTERN
2564
 
const rec_t*
2565
 
page_find_rec_with_heap_no(
2566
 
/*=======================*/
2567
 
        const page_t*   page,   /*!< in: index page */
2568
 
        ulint           heap_no)/*!< in: heap number */
2569
 
{
2570
 
        const rec_t*    rec;
2571
 
 
2572
 
        if (page_is_comp(page)) {
2573
 
                rec = page + PAGE_NEW_INFIMUM;
2574
 
 
2575
 
                for(;;) {
2576
 
                        ulint   rec_heap_no = rec_get_heap_no_new(rec);
2577
 
 
2578
 
                        if (rec_heap_no == heap_no) {
2579
 
 
2580
 
                                return(rec);
2581
 
                        } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2582
 
 
2583
 
                                return(NULL);
2584
 
                        }
2585
 
 
2586
 
                        rec = page + rec_get_next_offs(rec, TRUE);
2587
 
                }
2588
 
        } else {
2589
 
                rec = page + PAGE_OLD_INFIMUM;
2590
 
 
2591
 
                for (;;) {
2592
 
                        ulint   rec_heap_no = rec_get_heap_no_old(rec);
2593
 
 
2594
 
                        if (rec_heap_no == heap_no) {
2595
 
 
2596
 
                                return(rec);
2597
 
                        } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2598
 
 
2599
 
                                return(NULL);
2600
 
                        }
2601
 
 
2602
 
                        rec = page + rec_get_next_offs(rec, FALSE);
2603
 
                }
2604
 
        }
2605
 
}
2606
 
#endif /* !UNIV_HOTBACKUP */