Drizzled Public API Documentation

page0page.cc
1 /*****************************************************************************
2 
3 Copyright (C) 1994, 2010, Innobase Oy. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15 St, Fifth Floor, Boston, MA 02110-1301 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************/
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 /***************************************************************/
87 UNIV_INTERN
88 ulint
90 /*=====================*/
91  const rec_t* rec)
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 /**************************************************************/
163 static
164 ibool
165 page_dir_slot_check(
166 /*================*/
167  page_dir_slot_t* 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 
183 
184  if (page_is_comp(page)) {
186  } else {
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 /*************************************************************/
205 UNIV_INTERN
206 void
208 /*================*/
209  buf_block_t* block,
210  page_zip_des_t* page_zip,
211  trx_id_t trx_id,
212  mtr_t* mtr)
213 {
214  page_t* page = buf_block_get_frame(block);
215 #ifndef UNIV_HOTBACKUP
216  const ibool is_hashed = block->is_hashed;
217 
218  if (is_hashed) {
219  rw_lock_x_lock(&btr_search_latch);
220  }
221 
222  ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
223 #endif /* !UNIV_HOTBACKUP */
224 
225  /* It is not necessary to write this change to the redo log, as
226  during a database recovery we assume that the max trx id of every
227  page is the maximum trx id assigned before the crash. */
228 
229  if (UNIV_LIKELY_NULL(page_zip)) {
230  mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
231  page_zip_write_header(page_zip,
232  page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
233  8, mtr);
234 #ifndef UNIV_HOTBACKUP
235  } else if (mtr) {
236  mlog_write_ull(page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
237  trx_id, mtr);
238 #endif /* !UNIV_HOTBACKUP */
239  } else {
240  mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
241  }
242 
243 #ifndef UNIV_HOTBACKUP
244  if (is_hashed) {
245  rw_lock_x_unlock(&btr_search_latch);
246  }
247 #endif /* !UNIV_HOTBACKUP */
248 }
249 
250 /************************************************************/
253 UNIV_INTERN
254 byte*
256 /*================*/
257  page_t* page,
258  page_zip_des_t* page_zip,
261  ulint need,
262  ulint* heap_no)
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 /**********************************************************/
291 UNIV_INLINE
292 void
293 page_create_write_log(
294 /*==================*/
295  buf_frame_t* frame,
297  mtr_t* mtr,
298  ibool comp)
299 {
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 /***********************************************************/
311 UNIV_INTERN
312 byte*
314 /*==============*/
315  byte* ptr,
316  byte* /*end_ptr __attribute__((unused))*/,
317  ulint comp,
318  buf_block_t* block,
319  mtr_t* mtr)
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 /**********************************************************/
335 static
336 page_t*
337 page_create_low(
338 /*============*/
339  buf_block_t* block,
341  ulint comp)
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 */
371 
372  page = buf_block_get_frame(block);
373 
374  fil_page_set_type(page, FIL_PAGE_INDEX);
375 
376  heap = mem_heap_create(200);
377 
378  /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
379 
380  /* Create first a data tuple for infimum record */
381  tuple = dtuple_create(heap, 1);
382  dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM);
383  field = dtuple_get_nth_field(tuple, 0);
384 
385  dfield_set_data(field, "infimum", 8);
386  dtype_set(dfield_get_type(field),
387  DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8);
388  /* Set the corresponding physical record to its place in the page
389  record heap */
390 
391  heap_top = page + PAGE_DATA;
392 
393  infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
394 
395  if (UNIV_LIKELY(comp)) {
396  ut_a(infimum_rec == page + PAGE_NEW_INFIMUM);
397 
398  rec_set_n_owned_new(infimum_rec, NULL, 1);
399  rec_set_heap_no_new(infimum_rec, 0);
400  } else {
401  ut_a(infimum_rec == page + PAGE_OLD_INFIMUM);
402 
403  rec_set_n_owned_old(infimum_rec, 1);
404  rec_set_heap_no_old(infimum_rec, 0);
405  }
406 
407  offsets = rec_get_offsets(infimum_rec, index, NULL,
408  ULINT_UNDEFINED, &heap);
409 
410  heap_top = rec_get_end(infimum_rec, offsets);
411 
412  /* Create then a tuple for supremum */
413 
414  tuple = dtuple_create(heap, 1);
415  dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM);
416  field = dtuple_get_nth_field(tuple, 0);
417 
418  dfield_set_data(field, "supremum", comp ? 8 : 9);
419  dtype_set(dfield_get_type(field),
420  DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9);
421 
422  supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
423 
424  if (UNIV_LIKELY(comp)) {
425  ut_a(supremum_rec == page + PAGE_NEW_SUPREMUM);
426 
427  rec_set_n_owned_new(supremum_rec, NULL, 1);
428  rec_set_heap_no_new(supremum_rec, 1);
429  } else {
430  ut_a(supremum_rec == page + PAGE_OLD_SUPREMUM);
431 
432  rec_set_n_owned_old(supremum_rec, 1);
433  rec_set_heap_no_old(supremum_rec, 1);
434  }
435 
436  offsets = rec_get_offsets(supremum_rec, index, offsets,
437  ULINT_UNDEFINED, &heap);
438  heap_top = rec_get_end(supremum_rec, offsets);
439 
440  ut_ad(heap_top == page
441  + (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END));
442 
443  mem_heap_free(heap);
444 
445  /* 4. INITIALIZE THE PAGE */
446 
447  page_header_set_field(page, NULL, PAGE_N_DIR_SLOTS, 2);
448  page_header_set_ptr(page, NULL, PAGE_HEAP_TOP, heap_top);
449  page_header_set_field(page, NULL, PAGE_N_HEAP, comp
450  ? 0x8000 | PAGE_HEAP_NO_USER_LOW
451  : PAGE_HEAP_NO_USER_LOW);
452  page_header_set_ptr(page, NULL, PAGE_FREE, NULL);
453  page_header_set_field(page, NULL, PAGE_GARBAGE, 0);
454  page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, NULL);
455  page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION);
456  page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0);
457  page_header_set_field(page, NULL, PAGE_N_RECS, 0);
458  page_set_max_trx_id(block, NULL, 0, NULL);
459  memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START
460  - page_offset(heap_top));
461 
462  /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
463 
464  /* Set the slots to point to infimum and supremum. */
465 
466  slot = page_dir_get_nth_slot(page, 0);
467  page_dir_slot_set_rec(slot, infimum_rec);
468 
469  slot = page_dir_get_nth_slot(page, 1);
470  page_dir_slot_set_rec(slot, supremum_rec);
471 
472  /* Set the next pointers in infimum and supremum */
473 
474  if (UNIV_LIKELY(comp)) {
475  rec_set_next_offs_new(infimum_rec, PAGE_NEW_SUPREMUM);
476  rec_set_next_offs_new(supremum_rec, 0);
477  } else {
478  rec_set_next_offs_old(infimum_rec, PAGE_OLD_SUPREMUM);
479  rec_set_next_offs_old(supremum_rec, 0);
480  }
481 
482  return(page);
483 }
484 
485 /**********************************************************/
488 UNIV_INTERN
489 page_t*
491 /*========*/
492  buf_block_t* block,
494  mtr_t* mtr,
495  ulint comp)
496 {
497  page_create_write_log(buf_block_get_frame(block), mtr, comp);
498  return(page_create_low(block, comp));
499 }
500 
501 /**********************************************************/
504 UNIV_INTERN
505 page_t*
507 /*============*/
508  buf_block_t* block,
510  dict_index_t* index,
511  ulint level,
512  mtr_t* mtr)
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 /*************************************************************/
537 UNIV_INTERN
538 void
540 /*============================*/
541  buf_block_t* new_block,
542  buf_block_t* block,
543  rec_t* rec,
544  dict_index_t* index,
545  mtr_t* 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 /*************************************************************/
612 UNIV_INTERN
613 rec_t*
615 /*===================*/
616  buf_block_t* new_block,
617  buf_block_t* block,
618  rec_t* rec,
619  dict_index_t* index,
620  mtr_t* 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) {
653  index, mtr);
654  } else {
655  page_copy_rec_list_end_no_locks(new_block, block, rec,
656  index, mtr);
657  }
658 
659  /* Update PAGE_MAX_TRX_ID on the uncompressed page.
660  Modifications will be redo logged and copied to the compressed
661  page in page_zip_compress() or page_zip_reorganize() below. */
662  if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
663  page_update_max_trx_id(new_block, NULL,
664  page_get_max_trx_id(page), mtr);
665  }
666 
667  if (UNIV_LIKELY_NULL(new_page_zip)) {
668  mtr_set_log_mode(mtr, log_mode);
669 
670  if (UNIV_UNLIKELY
671  (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
672  /* Before trying to reorganize the page,
673  store the number of preceding records on the page. */
674  ulint ret_pos
676  /* Before copying, "ret" was the successor of
677  the predefined infimum record. It must still
678  have at least one predecessor (the predefined
679  infimum record, or a freshly copied record
680  that is smaller than "ret"). */
681  ut_a(ret_pos > 0);
682 
683  if (UNIV_UNLIKELY
684  (!page_zip_reorganize(new_block, index, mtr))) {
685 
686  if (UNIV_UNLIKELY
687  (!page_zip_decompress(new_page_zip,
688  new_page, FALSE))) {
689  ut_error;
690  }
691  ut_ad(page_validate(new_page, index));
692  return(NULL);
693  } else {
694  /* The page was reorganized:
695  Seek to ret_pos. */
696  ret = new_page + PAGE_NEW_INFIMUM;
697 
698  do {
699  ret = rec_get_next_ptr(ret, TRUE);
700  } while (--ret_pos);
701  }
702  }
703  }
704 
705  /* Update the lock table and possible hash index */
706 
707  lock_move_rec_list_end(new_block, block, rec);
708 
709  btr_search_move_or_delete_hash_entries(new_block, block, index);
710 
711  return(ret);
712 }
713 
714 /*************************************************************/
720 UNIV_INTERN
721 rec_t*
723 /*=====================*/
724  buf_block_t* new_block,
725  buf_block_t* block,
726  rec_t* rec,
727  dict_index_t* index,
728  mtr_t* mtr)
729 {
730  page_t* new_page = buf_block_get_frame(new_block);
731  page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block);
732  page_cur_t cur1;
733  rec_t* cur2;
734  ulint log_mode = 0 /* remove warning */;
735  mem_heap_t* heap = NULL;
736  rec_t* ret
737  = page_rec_get_prev(page_get_supremum_rec(new_page));
738  ulint offsets_[REC_OFFS_NORMAL_SIZE];
739  ulint* offsets = offsets_;
740  rec_offs_init(offsets_);
741 
742  /* Here, "ret" may be pointing to a user record or the
743  predefined infimum record. */
744 
745  if (page_rec_is_infimum(rec)) {
746 
747  return(ret);
748  }
749 
750  if (UNIV_LIKELY_NULL(new_page_zip)) {
751  log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
752  }
753 
754  page_cur_set_before_first(block, &cur1);
755  page_cur_move_to_next(&cur1);
756 
757  cur2 = ret;
758 
759  /* Copy records from the original page to the new page */
760 
761  while (page_cur_get_rec(&cur1) != rec) {
762  rec_t* cur1_rec = page_cur_get_rec(&cur1);
763  offsets = rec_get_offsets(cur1_rec, index, offsets,
764  ULINT_UNDEFINED, &heap);
765  cur2 = page_cur_insert_rec_low(cur2, index,
766  cur1_rec, offsets, mtr);
767  ut_a(cur2);
768 
769  page_cur_move_to_next(&cur1);
770  }
771 
772  if (UNIV_LIKELY_NULL(heap)) {
773  mem_heap_free(heap);
774  }
775 
776  /* Update PAGE_MAX_TRX_ID on the uncompressed page.
777  Modifications will be redo logged and copied to the compressed
778  page in page_zip_compress() or page_zip_reorganize() below. */
779  if (dict_index_is_sec_or_ibuf(index)
780  && page_is_leaf(page_align(rec))) {
781  page_update_max_trx_id(new_block, NULL,
783  mtr);
784  }
785 
786  if (UNIV_LIKELY_NULL(new_page_zip)) {
787  mtr_set_log_mode(mtr, log_mode);
788 
789  if (UNIV_UNLIKELY
790  (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
791  /* Before trying to reorganize the page,
792  store the number of preceding records on the page. */
793  ulint ret_pos
795  /* Before copying, "ret" was the predecessor
796  of the predefined supremum record. If it was
797  the predefined infimum record, then it would
798  still be the infimum. Thus, the assertion
799  ut_a(ret_pos > 0) would fail here. */
800 
801  if (UNIV_UNLIKELY
802  (!page_zip_reorganize(new_block, index, mtr))) {
803 
804  if (UNIV_UNLIKELY
805  (!page_zip_decompress(new_page_zip,
806  new_page, FALSE))) {
807  ut_error;
808  }
809  ut_ad(page_validate(new_page, index));
810  return(NULL);
811  } else {
812  /* The page was reorganized:
813  Seek to ret_pos. */
814  ret = new_page + PAGE_NEW_INFIMUM;
815 
816  do {
817  ret = rec_get_next_ptr(ret, TRUE);
818  } while (--ret_pos);
819  }
820  }
821  }
822 
823  /* Update the lock table and possible hash index */
824 
825  lock_move_rec_list_start(new_block, block, rec, ret);
826 
827  btr_search_move_or_delete_hash_entries(new_block, block, index);
828 
829  return(ret);
830 }
831 
832 /**********************************************************/
834 UNIV_INLINE
835 void
836 page_delete_rec_list_write_log(
837 /*===========================*/
838  rec_t* rec,
839  dict_index_t* index,
840  byte type,
842  mtr_t* mtr)
843 {
844  byte* log_ptr;
846  || type == MLOG_LIST_START_DELETE
847  || type == MLOG_COMP_LIST_END_DELETE
848  || type == MLOG_COMP_LIST_START_DELETE);
849 
850  log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2);
851  if (log_ptr) {
852  /* Write the parameter as a 2-byte ulint */
853  mach_write_to_2(log_ptr, page_offset(rec));
854  mlog_close(mtr, log_ptr + 2);
855  }
856 }
857 #else /* !UNIV_HOTBACKUP */
858 # define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0)
859 #endif /* !UNIV_HOTBACKUP */
860 
861 /**********************************************************/
864 UNIV_INTERN
865 byte*
867 /*=======================*/
868  byte type,
872  byte* ptr,
873  byte* end_ptr,
874  buf_block_t* block,
875  dict_index_t* index,
876  mtr_t* mtr)
877 {
878  page_t* page;
879  ulint offset;
880 
882  || type == MLOG_LIST_START_DELETE
883  || type == MLOG_COMP_LIST_END_DELETE
884  || type == MLOG_COMP_LIST_START_DELETE);
885 
886  /* Read the record offset as a 2-byte ulint */
887 
888  if (end_ptr < ptr + 2) {
889 
890  return(NULL);
891  }
892 
893  offset = mach_read_from_2(ptr);
894  ptr += 2;
895 
896  if (!block) {
897 
898  return(ptr);
899  }
900 
901  page = buf_block_get_frame(block);
902 
903  ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
904 
905  if (type == MLOG_LIST_END_DELETE
906  || type == MLOG_COMP_LIST_END_DELETE) {
907  page_delete_rec_list_end(page + offset, block, index,
908  ULINT_UNDEFINED, ULINT_UNDEFINED,
909  mtr);
910  } else {
911  page_delete_rec_list_start(page + offset, block, index, mtr);
912  }
913 
914  return(ptr);
915 }
916 
917 /*************************************************************/
920 UNIV_INTERN
921 void
923 /*=====================*/
924  rec_t* rec,
925  buf_block_t* block,
926  dict_index_t* index,
927  ulint n_recs,
929  ulint size,
932  mtr_t* mtr)
933 {
934  page_dir_slot_t*slot;
935  ulint slot_index;
936  rec_t* last_rec;
937  rec_t* prev_rec;
938  ulint n_owned;
939  page_zip_des_t* page_zip = buf_block_get_page_zip(block);
940  page_t* page = page_align(rec);
941  mem_heap_t* heap = NULL;
942  ulint offsets_[REC_OFFS_NORMAL_SIZE];
943  ulint* offsets = offsets_;
944  rec_offs_init(offsets_);
945 
946  ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE);
947  ut_ad(!page_zip || page_rec_is_comp(rec));
948 #ifdef UNIV_ZIP_DEBUG
949  ut_a(!page_zip || page_zip_validate(page_zip, page));
950 #endif /* UNIV_ZIP_DEBUG */
951 
952  if (page_rec_is_infimum(rec)) {
953  rec = page_rec_get_next(rec);
954  }
955 
956  if (page_rec_is_supremum(rec)) {
957 
958  return;
959  }
960 
961  /* Reset the last insert info in the page header and increment
962  the modify clock for the frame */
963 
964  page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL);
965 
966  /* The page gets invalid for optimistic searches: increment the
967  frame modify clock */
968 
970 
971  page_delete_rec_list_write_log(rec, index, page_is_comp(page)
973  : MLOG_LIST_END_DELETE, mtr);
974 
975  if (UNIV_LIKELY_NULL(page_zip)) {
976  ulint log_mode;
977 
978  ut_a(page_is_comp(page));
979  /* Individual deletes are not logged */
980 
981  log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
982 
983  do {
984  page_cur_t cur;
985  page_cur_position(rec, block, &cur);
986 
987  offsets = rec_get_offsets(rec, index, offsets,
988  ULINT_UNDEFINED, &heap);
989  rec = rec_get_next_ptr(rec, TRUE);
990 #ifdef UNIV_ZIP_DEBUG
991  ut_a(page_zip_validate(page_zip, page));
992 #endif /* UNIV_ZIP_DEBUG */
993  page_cur_delete_rec(&cur, index, offsets, mtr);
994  } while (page_offset(rec) != PAGE_NEW_SUPREMUM);
995 
996  if (UNIV_LIKELY_NULL(heap)) {
997  mem_heap_free(heap);
998  }
999 
1000  /* Restore log mode */
1001 
1002  mtr_set_log_mode(mtr, log_mode);
1003  return;
1004  }
1005 
1006  prev_rec = page_rec_get_prev(rec);
1007 
1008  last_rec = page_rec_get_prev(page_get_supremum_rec(page));
1009 
1010  if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
1011  rec_t* rec2 = rec;
1012  /* Calculate the sum of sizes and the number of records */
1013  size = 0;
1014  n_recs = 0;
1015 
1016  do {
1017  ulint s;
1018  offsets = rec_get_offsets(rec2, index, offsets,
1019  ULINT_UNDEFINED, &heap);
1020  s = rec_offs_size(offsets);
1021  ut_ad(rec2 - page + s - rec_offs_extra_size(offsets)
1022  < UNIV_PAGE_SIZE);
1023  ut_ad(size + s < UNIV_PAGE_SIZE);
1024  size += s;
1025  n_recs++;
1026 
1027  rec2 = page_rec_get_next(rec2);
1028  } while (!page_rec_is_supremum(rec2));
1029 
1030  if (UNIV_LIKELY_NULL(heap)) {
1031  mem_heap_free(heap);
1032  }
1033  }
1034 
1035  ut_ad(size < UNIV_PAGE_SIZE);
1036 
1037  /* Update the page directory; there is no need to balance the number
1038  of the records owned by the supremum record, as it is allowed to be
1039  less than PAGE_DIR_SLOT_MIN_N_OWNED */
1040 
1041  if (page_is_comp(page)) {
1042  rec_t* rec2 = rec;
1043  ulint count = 0;
1044 
1045  while (rec_get_n_owned_new(rec2) == 0) {
1046  count++;
1047 
1048  rec2 = rec_get_next_ptr(rec2, TRUE);
1049  }
1050 
1051  ut_ad(rec_get_n_owned_new(rec2) > count);
1052 
1053  n_owned = rec_get_n_owned_new(rec2) - count;
1054  slot_index = page_dir_find_owner_slot(rec2);
1055  slot = page_dir_get_nth_slot(page, slot_index);
1056  } else {
1057  rec_t* rec2 = rec;
1058  ulint count = 0;
1059 
1060  while (rec_get_n_owned_old(rec2) == 0) {
1061  count++;
1062 
1063  rec2 = rec_get_next_ptr(rec2, FALSE);
1064  }
1065 
1066  ut_ad(rec_get_n_owned_old(rec2) > count);
1067 
1068  n_owned = rec_get_n_owned_old(rec2) - count;
1069  slot_index = page_dir_find_owner_slot(rec2);
1070  slot = page_dir_get_nth_slot(page, slot_index);
1071  }
1072 
1073  page_dir_slot_set_rec(slot, page_get_supremum_rec(page));
1074  page_dir_slot_set_n_owned(slot, NULL, n_owned);
1075 
1076  page_dir_set_n_slots(page, NULL, slot_index + 1);
1077 
1078  /* Remove the record chain segment from the record chain */
1079  page_rec_set_next(prev_rec, page_get_supremum_rec(page));
1080 
1081  /* Catenate the deleted chain segment to the page free list */
1082 
1083  page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE));
1084  page_header_set_ptr(page, NULL, PAGE_FREE, rec);
1085 
1086  page_header_set_field(page, NULL, PAGE_GARBAGE, size
1087  + page_header_get_field(page, PAGE_GARBAGE));
1088 
1089  page_header_set_field(page, NULL, PAGE_N_RECS,
1090  (ulint)(page_get_n_recs(page) - n_recs));
1091 }
1092 
1093 /*************************************************************/
1096 UNIV_INTERN
1097 void
1099 /*=======================*/
1100  rec_t* rec,
1101  buf_block_t* block,
1102  dict_index_t* index,
1103  mtr_t* mtr)
1104 {
1105  page_cur_t cur1;
1106  ulint log_mode;
1107  ulint offsets_[REC_OFFS_NORMAL_SIZE];
1108  ulint* offsets = offsets_;
1109  mem_heap_t* heap = NULL;
1110  byte type;
1111 
1112  rec_offs_init(offsets_);
1113 
1114  ut_ad((ibool) !!page_rec_is_comp(rec)
1115  == dict_table_is_comp(index->table));
1116 #ifdef UNIV_ZIP_DEBUG
1117  {
1118  page_zip_des_t* page_zip= buf_block_get_page_zip(block);
1119  page_t* page = buf_block_get_frame(block);
1120 
1121  /* page_zip_validate() would detect a min_rec_mark mismatch
1122  in btr_page_split_and_insert()
1123  between btr_attach_half_pages() and insert_page = ...
1124  when btr_page_get_split_rec_to_left() holds
1125  (direction == FSP_DOWN). */
1126  ut_a(!page_zip || page_zip_validate_low(page_zip, page, TRUE));
1127  }
1128 #endif /* UNIV_ZIP_DEBUG */
1129 
1130  if (page_rec_is_infimum(rec)) {
1131 
1132  return;
1133  }
1134 
1135  if (page_rec_is_comp(rec)) {
1137  } else {
1138  type = MLOG_LIST_START_DELETE;
1139  }
1140 
1141  page_delete_rec_list_write_log(rec, index, type, mtr);
1142 
1143  page_cur_set_before_first(block, &cur1);
1144  page_cur_move_to_next(&cur1);
1145 
1146  /* Individual deletes are not logged */
1147 
1148  log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
1149 
1150  while (page_cur_get_rec(&cur1) != rec) {
1151  offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
1152  offsets, ULINT_UNDEFINED, &heap);
1153  page_cur_delete_rec(&cur1, index, offsets, mtr);
1154  }
1155 
1156  if (UNIV_LIKELY_NULL(heap)) {
1157  mem_heap_free(heap);
1158  }
1159 
1160  /* Restore log mode */
1161 
1162  mtr_set_log_mode(mtr, log_mode);
1163 }
1164 
1165 #ifndef UNIV_HOTBACKUP
1166 /*************************************************************/
1171 UNIV_INTERN
1172 ibool
1174 /*===================*/
1175  buf_block_t* new_block,
1176  buf_block_t* block,
1177  rec_t* split_rec,
1178  dict_index_t* index,
1179  mtr_t* mtr)
1180 {
1181  page_t* new_page = buf_block_get_frame(new_block);
1182  ulint old_data_size;
1183  ulint new_data_size;
1184  ulint old_n_recs;
1185  ulint new_n_recs;
1186 
1187  old_data_size = page_get_data_size(new_page);
1188  old_n_recs = page_get_n_recs(new_page);
1189 #ifdef UNIV_ZIP_DEBUG
1190  {
1191  page_zip_des_t* new_page_zip
1192  = buf_block_get_page_zip(new_block);
1193  page_zip_des_t* page_zip
1194  = buf_block_get_page_zip(block);
1195  ut_a(!new_page_zip == !page_zip);
1196  ut_a(!new_page_zip
1197  || page_zip_validate(new_page_zip, new_page));
1198  ut_a(!page_zip
1199  || page_zip_validate(page_zip, page_align(split_rec)));
1200  }
1201 #endif /* UNIV_ZIP_DEBUG */
1202 
1203  if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block,
1204  split_rec, index, mtr))) {
1205  return(FALSE);
1206  }
1207 
1208  new_data_size = page_get_data_size(new_page);
1209  new_n_recs = page_get_n_recs(new_page);
1210 
1211  ut_ad(new_data_size >= old_data_size);
1212 
1213  page_delete_rec_list_end(split_rec, block, index,
1214  new_n_recs - old_n_recs,
1215  new_data_size - old_data_size, mtr);
1216 
1217  return(TRUE);
1218 }
1219 
1220 /*************************************************************/
1224 UNIV_INTERN
1225 ibool
1227 /*=====================*/
1228  buf_block_t* new_block,
1229  buf_block_t* block,
1230  rec_t* split_rec,
1231  dict_index_t* index,
1232  mtr_t* mtr)
1233 {
1234  if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block,
1235  split_rec, index, mtr))) {
1236  return(FALSE);
1237  }
1238 
1239  page_delete_rec_list_start(split_rec, block, index, mtr);
1240 
1241  return(TRUE);
1242 }
1243 
1244 /***********************************************************************/
1247 UNIV_INTERN
1248 void
1250 /*=========================*/
1251  rec_t* rec,
1252  ulint i,
1253  ulint page_no,
1254  mtr_t* mtr)
1255 {
1256  byte* data;
1257  ulint len;
1258 
1259  data = rec_get_nth_field_old(rec, i, &len);
1260 
1261  ut_ad(len == 4);
1262 
1263  mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr);
1264 }
1265 #endif /* !UNIV_HOTBACKUP */
1266 
1267 /**************************************************************/
1271 UNIV_INLINE
1272 void
1273 page_dir_delete_slot(
1274 /*=================*/
1275  page_t* page,
1276  page_zip_des_t* page_zip,
1277  ulint slot_no)
1278 {
1279  page_dir_slot_t* slot;
1280  ulint n_owned;
1281  ulint i;
1282  ulint n_slots;
1283 
1284  ut_ad(!page_zip || page_is_comp(page));
1285  ut_ad(slot_no > 0);
1286  ut_ad(slot_no + 1 < page_dir_get_n_slots(page));
1287 
1288  n_slots = page_dir_get_n_slots(page);
1289 
1290  /* 1. Reset the n_owned fields of the slots to be
1291  deleted */
1292  slot = page_dir_get_nth_slot(page, slot_no);
1293  n_owned = page_dir_slot_get_n_owned(slot);
1294  page_dir_slot_set_n_owned(slot, page_zip, 0);
1295 
1296  /* 2. Update the n_owned value of the first non-deleted slot */
1297 
1298  slot = page_dir_get_nth_slot(page, slot_no + 1);
1299  page_dir_slot_set_n_owned(slot, page_zip,
1300  n_owned + page_dir_slot_get_n_owned(slot));
1301 
1302  /* 3. Destroy the slot by copying slots */
1303  for (i = slot_no + 1; i < n_slots; i++) {
1304  rec_t* rec = (rec_t*)
1305  page_dir_slot_get_rec(page_dir_get_nth_slot(page, i));
1306  page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec);
1307  }
1308 
1309  /* 4. Zero out the last slot, which will be removed */
1310  mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0);
1311 
1312  /* 5. Update the page header */
1313  page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1);
1314 }
1315 
1316 /**************************************************************/
1320 UNIV_INLINE
1321 void
1322 page_dir_add_slot(
1323 /*==============*/
1324  page_t* page,
1325  page_zip_des_t* page_zip,
1326  ulint start)
1328 {
1329  page_dir_slot_t* slot;
1330  ulint n_slots;
1331 
1332  n_slots = page_dir_get_n_slots(page);
1333 
1334  ut_ad(start < n_slots - 1);
1335 
1336  /* Update the page header */
1337  page_dir_set_n_slots(page, page_zip, n_slots + 1);
1338 
1339  /* Move slots up */
1340  slot = page_dir_get_nth_slot(page, n_slots);
1341  memmove(slot, slot + PAGE_DIR_SLOT_SIZE,
1342  (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE);
1343 }
1344 
1345 /****************************************************************/
1347 UNIV_INTERN
1348 void
1350 /*================*/
1351  page_t* page,
1352  page_zip_des_t* page_zip,
1354  ulint slot_no)
1355 {
1356  rec_t* rec;
1357  page_dir_slot_t* new_slot;
1358  page_dir_slot_t* prev_slot;
1359  page_dir_slot_t* slot;
1360  ulint i;
1361  ulint n_owned;
1362 
1363  ut_ad(page);
1364  ut_ad(!page_zip || page_is_comp(page));
1365  ut_ad(slot_no > 0);
1366 
1367  slot = page_dir_get_nth_slot(page, slot_no);
1368 
1369  n_owned = page_dir_slot_get_n_owned(slot);
1370  ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
1371 
1372  /* 1. We loop to find a record approximately in the middle of the
1373  records owned by the slot. */
1374 
1375  prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
1376  rec = (rec_t*) page_dir_slot_get_rec(prev_slot);
1377 
1378  for (i = 0; i < n_owned / 2; i++) {
1379  rec = page_rec_get_next(rec);
1380  }
1381 
1382  ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
1383 
1384  /* 2. We add one directory slot immediately below the slot to be
1385  split. */
1386 
1387  page_dir_add_slot(page, page_zip, slot_no - 1);
1388 
1389  /* The added slot is now number slot_no, and the old slot is
1390  now number slot_no + 1 */
1391 
1392  new_slot = page_dir_get_nth_slot(page, slot_no);
1393  slot = page_dir_get_nth_slot(page, slot_no + 1);
1394 
1395  /* 3. We store the appropriate values to the new slot. */
1396 
1397  page_dir_slot_set_rec(new_slot, rec);
1398  page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2);
1399 
1400  /* 4. Finally, we update the number of records field of the
1401  original slot */
1402 
1403  page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2));
1404 }
1405 
1406 /*************************************************************/
1410 UNIV_INTERN
1411 void
1413 /*==================*/
1414  page_t* page,
1415  page_zip_des_t* page_zip,
1416  ulint slot_no)
1417 {
1418  page_dir_slot_t* slot;
1419  page_dir_slot_t* up_slot;
1420  ulint n_owned;
1421  ulint up_n_owned;
1422  rec_t* old_rec;
1423  rec_t* new_rec;
1424 
1425  ut_ad(page);
1426  ut_ad(!page_zip || page_is_comp(page));
1427  ut_ad(slot_no > 0);
1428 
1429  slot = page_dir_get_nth_slot(page, slot_no);
1430 
1431  /* The last directory slot cannot be balanced with the upper
1432  neighbor, as there is none. */
1433 
1434  if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) {
1435 
1436  return;
1437  }
1438 
1439  up_slot = page_dir_get_nth_slot(page, slot_no + 1);
1440 
1441  n_owned = page_dir_slot_get_n_owned(slot);
1442  up_n_owned = page_dir_slot_get_n_owned(up_slot);
1443 
1444  ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
1445 
1446  /* If the upper slot has the minimum value of n_owned, we will merge
1447  the two slots, therefore we assert: */
1448  ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
1449 
1450  if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
1451 
1452  /* In this case we can just transfer one record owned
1453  by the upper slot to the property of the lower slot */
1454  old_rec = (rec_t*) page_dir_slot_get_rec(slot);
1455 
1456  if (page_is_comp(page)) {
1457  new_rec = rec_get_next_ptr(old_rec, TRUE);
1458 
1459  rec_set_n_owned_new(old_rec, page_zip, 0);
1460  rec_set_n_owned_new(new_rec, page_zip, n_owned + 1);
1461  } else {
1462  new_rec = rec_get_next_ptr(old_rec, FALSE);
1463 
1464  rec_set_n_owned_old(old_rec, 0);
1465  rec_set_n_owned_old(new_rec, n_owned + 1);
1466  }
1467 
1468  page_dir_slot_set_rec(slot, new_rec);
1469 
1470  page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1);
1471  } else {
1472  /* In this case we may merge the two slots */
1473  page_dir_delete_slot(page, page_zip, slot_no);
1474  }
1475 }
1476 
1477 #ifndef UNIV_HOTBACKUP
1478 /************************************************************/
1482 UNIV_INTERN
1483 rec_t*
1485 /*================*/
1486  page_t* page)
1487 {
1488  page_dir_slot_t* slot;
1489  ulint middle;
1490  ulint i;
1491  ulint n_owned;
1492  ulint count;
1493  rec_t* rec;
1494 
1495  /* This many records we must leave behind */
1496  middle = (page_get_n_recs(page) + PAGE_HEAP_NO_USER_LOW) / 2;
1497 
1498  count = 0;
1499 
1500  for (i = 0;; i++) {
1501 
1502  slot = page_dir_get_nth_slot(page, i);
1503  n_owned = page_dir_slot_get_n_owned(slot);
1504 
1505  if (count + n_owned > middle) {
1506  break;
1507  } else {
1508  count += n_owned;
1509  }
1510  }
1511 
1512  ut_ad(i > 0);
1513  slot = page_dir_get_nth_slot(page, i - 1);
1514  rec = (rec_t*) page_dir_slot_get_rec(slot);
1515  rec = page_rec_get_next(rec);
1516 
1517  /* There are now count records behind rec */
1518 
1519  for (i = 0; i < middle - count; i++) {
1520  rec = page_rec_get_next(rec);
1521  }
1522 
1523  return(rec);
1524 }
1525 #endif /* !UNIV_HOTBACKUP */
1526 
1527 /***************************************************************/
1531 UNIV_INTERN
1532 ulint
1534 /*=======================*/
1535  const rec_t* rec)
1536 {
1537  const page_dir_slot_t* slot;
1538  const rec_t* slot_rec;
1539  const page_t* page;
1540  ulint i;
1541  lint n = 0;
1542 
1543  ut_ad(page_rec_check(rec));
1544 
1545  page = page_align(rec);
1546  if (page_is_comp(page)) {
1547  while (rec_get_n_owned_new(rec) == 0) {
1548 
1549  rec = rec_get_next_ptr_const(rec, TRUE);
1550  n--;
1551  }
1552 
1553  for (i = 0; ; i++) {
1554  slot = page_dir_get_nth_slot(page, i);
1555  slot_rec = page_dir_slot_get_rec(slot);
1556 
1557  n += rec_get_n_owned_new(slot_rec);
1558 
1559  if (rec == slot_rec) {
1560 
1561  break;
1562  }
1563  }
1564  } else {
1565  while (rec_get_n_owned_old(rec) == 0) {
1566 
1567  rec = rec_get_next_ptr_const(rec, FALSE);
1568  n--;
1569  }
1570 
1571  for (i = 0; ; i++) {
1572  slot = page_dir_get_nth_slot(page, i);
1573  slot_rec = page_dir_slot_get_rec(slot);
1574 
1575  n += rec_get_n_owned_old(slot_rec);
1576 
1577  if (rec == slot_rec) {
1578 
1579  break;
1580  }
1581  }
1582  }
1583 
1584  n--;
1585 
1586  ut_ad(n >= 0);
1587 
1588  return((ulint) n);
1589 }
1590 
1591 #ifndef UNIV_HOTBACKUP
1592 /************************************************************/
1595 UNIV_INTERN
1596 void
1598 /*===========*/
1599  const rec_t* rec,
1600  const ulint* offsets)
1601 {
1602  ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
1603  rec_print_new(stderr, rec, offsets);
1604  if (page_rec_is_comp(rec)) {
1605  fprintf(stderr,
1606  " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1607  (ulong) rec_get_n_owned_new(rec),
1608  (ulong) rec_get_heap_no_new(rec),
1609  (ulong) rec_get_next_offs(rec, TRUE));
1610  } else {
1611  fprintf(stderr,
1612  " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
1613  (ulong) rec_get_n_owned_old(rec),
1614  (ulong) rec_get_heap_no_old(rec),
1615  (ulong) rec_get_next_offs(rec, TRUE));
1616  }
1617 
1618  page_rec_check(rec);
1619  rec_validate(rec, offsets);
1620 }
1621 
1622 /***************************************************************/
1625 UNIV_INTERN
1626 void
1628 /*===========*/
1629  page_t* page,
1630  ulint pr_n)
1631 {
1632  ulint n;
1633  ulint i;
1634  page_dir_slot_t* slot;
1635 
1636  n = page_dir_get_n_slots(page);
1637 
1638  fprintf(stderr, "--------------------------------\n"
1639  "PAGE DIRECTORY\n"
1640  "Page address %p\n"
1641  "Directory stack top at offs: %lu; number of slots: %lu\n",
1642  page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)),
1643  (ulong) n);
1644  for (i = 0; i < n; i++) {
1645  slot = page_dir_get_nth_slot(page, i);
1646  if ((i == pr_n) && (i < n - pr_n)) {
1647  fputs(" ... \n", stderr);
1648  }
1649  if ((i < pr_n) || (i >= n - pr_n)) {
1650  fprintf(stderr,
1651  "Contents of slot: %lu: n_owned: %lu,"
1652  " rec offs: %lu\n",
1653  (ulong) i,
1654  (ulong) page_dir_slot_get_n_owned(slot),
1655  (ulong)
1657  }
1658  }
1659  fprintf(stderr, "Total of %lu records\n"
1660  "--------------------------------\n",
1661  (ulong) (PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page)));
1662 }
1663 
1664 /***************************************************************/
1667 UNIV_INTERN
1668 void
1670 /*============*/
1671  buf_block_t* block,
1672  dict_index_t* index,
1673  ulint pr_n)
1674 {
1675  page_t* page = block->frame;
1676  page_cur_t cur;
1677  ulint count;
1678  ulint n_recs;
1679  mem_heap_t* heap = NULL;
1680  ulint offsets_[REC_OFFS_NORMAL_SIZE];
1681  ulint* offsets = offsets_;
1682  rec_offs_init(offsets_);
1683 
1684  ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table));
1685 
1686  fprintf(stderr,
1687  "--------------------------------\n"
1688  "PAGE RECORD LIST\n"
1689  "Page address %p\n", page);
1690 
1691  n_recs = page_get_n_recs(page);
1692 
1693  page_cur_set_before_first(block, &cur);
1694  count = 0;
1695  for (;;) {
1696  offsets = rec_get_offsets(cur.rec, index, offsets,
1697  ULINT_UNDEFINED, &heap);
1698  page_rec_print(cur.rec, offsets);
1699 
1700  if (count == pr_n) {
1701  break;
1702  }
1703  if (page_cur_is_after_last(&cur)) {
1704  break;
1705  }
1706  page_cur_move_to_next(&cur);
1707  count++;
1708  }
1709 
1710  if (n_recs > 2 * pr_n) {
1711  fputs(" ... \n", stderr);
1712  }
1713 
1714  while (!page_cur_is_after_last(&cur)) {
1715  page_cur_move_to_next(&cur);
1716 
1717  if (count + pr_n >= n_recs) {
1718  offsets = rec_get_offsets(cur.rec, index, offsets,
1719  ULINT_UNDEFINED, &heap);
1720  page_rec_print(cur.rec, offsets);
1721  }
1722  count++;
1723  }
1724 
1725  fprintf(stderr,
1726  "Total of %lu records \n"
1727  "--------------------------------\n",
1728  (ulong) (count + 1));
1729 
1730  if (UNIV_LIKELY_NULL(heap)) {
1731  mem_heap_free(heap);
1732  }
1733 }
1734 
1735 /***************************************************************/
1737 UNIV_INTERN
1738 void
1740 /*==============*/
1741  const page_t* page)
1742 {
1743  fprintf(stderr,
1744  "--------------------------------\n"
1745  "PAGE HEADER INFO\n"
1746  "Page address %p, n records %lu (%s)\n"
1747  "n dir slots %lu, heap top %lu\n"
1748  "Page n heap %lu, free %lu, garbage %lu\n"
1749  "Page last insert %lu, direction %lu, n direction %lu\n",
1750  page, (ulong) page_header_get_field(page, PAGE_N_RECS),
1751  page_is_comp(page) ? "compact format" : "original format",
1752  (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
1753  (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1754  (ulong) page_dir_get_n_heap(page),
1755  (ulong) page_header_get_field(page, PAGE_FREE),
1756  (ulong) page_header_get_field(page, PAGE_GARBAGE),
1757  (ulong) page_header_get_field(page, PAGE_LAST_INSERT),
1758  (ulong) page_header_get_field(page, PAGE_DIRECTION),
1759  (ulong) page_header_get_field(page, PAGE_N_DIRECTION));
1760 }
1761 
1762 /***************************************************************/
1765 UNIV_INTERN
1766 void
1768 /*=======*/
1769  buf_block_t* block,
1770  dict_index_t* index,
1771  ulint dn,
1773  ulint rn)
1775 {
1776  page_t* page = block->frame;
1777 
1778  page_header_print(page);
1779  page_dir_print(page, dn);
1780  page_print_list(block, index, rn);
1781 }
1782 #endif /* !UNIV_HOTBACKUP */
1783 
1784 /***************************************************************/
1789 UNIV_INTERN
1790 ibool
1792 /*==============*/
1793  rec_t* rec,
1794  const ulint* offsets)
1795 {
1796  ulint n_owned;
1797  ulint heap_no;
1798  page_t* page;
1799 
1800  page = page_align(rec);
1801  ut_a(!page_is_comp(page) == !rec_offs_comp(offsets));
1802 
1803  page_rec_check(rec);
1804  rec_validate(rec, offsets);
1805 
1806  if (page_rec_is_comp(rec)) {
1807  n_owned = rec_get_n_owned_new(rec);
1808  heap_no = rec_get_heap_no_new(rec);
1809  } else {
1810  n_owned = rec_get_n_owned_old(rec);
1811  heap_no = rec_get_heap_no_old(rec);
1812  }
1813 
1814  if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) {
1815  fprintf(stderr,
1816  "InnoDB: Dir slot of rec %lu, n owned too big %lu\n",
1817  (ulong) page_offset(rec), (ulong) n_owned);
1818  return(FALSE);
1819  }
1820 
1821  if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) {
1822  fprintf(stderr,
1823  "InnoDB: Heap no of rec %lu too big %lu %lu\n",
1824  (ulong) page_offset(rec), (ulong) heap_no,
1825  (ulong) page_dir_get_n_heap(page));
1826  return(FALSE);
1827  }
1828 
1829  return(TRUE);
1830 }
1831 
1832 #ifndef UNIV_HOTBACKUP
1833 /***************************************************************/
1837 UNIV_INTERN
1838 void
1840 /*===========*/
1841  const page_t* page)
1842 {
1843  ulint n_slots;
1844  ulint infimum_offs;
1845  ulint supremum_offs;
1846 
1847  n_slots = page_dir_get_n_slots(page);
1848  infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0));
1849  supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page,
1850  n_slots - 1));
1851 
1852  if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) {
1853 
1854  fprintf(stderr,
1855  "InnoDB: Page directory corruption:"
1856  " infimum not pointed to\n");
1857  buf_page_print(page, 0);
1858  }
1859 
1860  if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) {
1861 
1862  fprintf(stderr,
1863  "InnoDB: Page directory corruption:"
1864  " supremum not pointed to\n");
1865  buf_page_print(page, 0);
1866  }
1867 }
1868 #endif /* !UNIV_HOTBACKUP */
1869 
1870 /***************************************************************/
1875 UNIV_INTERN
1876 ibool
1878 /*=====================*/
1879  page_t* page)
1880 {
1881  page_dir_slot_t* slot;
1882  ulint slot_no;
1883  ulint n_slots;
1884  rec_t* rec;
1885  byte* rec_heap_top;
1886  ulint count;
1887  ulint own_count;
1888  ibool ret = FALSE;
1889 
1890  ut_a(!page_is_comp(page));
1891 
1892  /* Check first that the record heap and the directory do not
1893  overlap. */
1894 
1895  n_slots = page_dir_get_n_slots(page);
1896 
1897  if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
1898  fprintf(stderr,
1899  "InnoDB: Nonsensical number %lu of page dir slots\n",
1900  (ulong) n_slots);
1901 
1902  goto func_exit;
1903  }
1904 
1905  rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
1906 
1907  if (UNIV_UNLIKELY(rec_heap_top
1908  > page_dir_get_nth_slot(page, n_slots - 1))) {
1909 
1910  fprintf(stderr,
1911  "InnoDB: Record heap and dir overlap on a page,"
1912  " heap top %lu, dir %lu\n",
1913  (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
1914  (ulong)
1915  page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
1916 
1917  goto func_exit;
1918  }
1919 
1920  /* Validate the record list in a loop checking also that it is
1921  consistent with the page record directory. */
1922 
1923  count = 0;
1924  own_count = 1;
1925  slot_no = 0;
1926  slot = page_dir_get_nth_slot(page, slot_no);
1927 
1928  rec = page_get_infimum_rec(page);
1929 
1930  for (;;) {
1931  if (UNIV_UNLIKELY(rec > rec_heap_top)) {
1932  fprintf(stderr,
1933  "InnoDB: Record %lu is above"
1934  " rec heap top %lu\n",
1935  (ulong)(rec - page),
1936  (ulong)(rec_heap_top - page));
1937 
1938  goto func_exit;
1939  }
1940 
1941  if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) {
1942  /* This is a record pointed to by a dir slot */
1943  if (UNIV_UNLIKELY(rec_get_n_owned_old(rec)
1944  != own_count)) {
1945 
1946  fprintf(stderr,
1947  "InnoDB: Wrong owned count %lu, %lu,"
1948  " rec %lu\n",
1949  (ulong) rec_get_n_owned_old(rec),
1950  (ulong) own_count,
1951  (ulong)(rec - page));
1952 
1953  goto func_exit;
1954  }
1955 
1956  if (UNIV_UNLIKELY
1957  (page_dir_slot_get_rec(slot) != rec)) {
1958  fprintf(stderr,
1959  "InnoDB: Dir slot does not point"
1960  " to right rec %lu\n",
1961  (ulong)(rec - page));
1962 
1963  goto func_exit;
1964  }
1965 
1966  own_count = 0;
1967 
1968  if (!page_rec_is_supremum(rec)) {
1969  slot_no++;
1970  slot = page_dir_get_nth_slot(page, slot_no);
1971  }
1972  }
1973 
1974  if (page_rec_is_supremum(rec)) {
1975 
1976  break;
1977  }
1978 
1979  if (UNIV_UNLIKELY
1980  (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA
1981  || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) {
1982  fprintf(stderr,
1983  "InnoDB: Next record offset"
1984  " nonsensical %lu for rec %lu\n",
1985  (ulong) rec_get_next_offs(rec, FALSE),
1986  (ulong) (rec - page));
1987 
1988  goto func_exit;
1989  }
1990 
1991  count++;
1992 
1993  if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
1994  fprintf(stderr,
1995  "InnoDB: Page record list appears"
1996  " to be circular %lu\n",
1997  (ulong) count);
1998  goto func_exit;
1999  }
2000 
2001  rec = page_rec_get_next(rec);
2002  own_count++;
2003  }
2004 
2005  if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
2006  fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n");
2007 
2008  goto func_exit;
2009  }
2010 
2011  if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2012  fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
2013  (ulong) slot_no, (ulong) (n_slots - 1));
2014  goto func_exit;
2015  }
2016 
2017  if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2018  + PAGE_HEAP_NO_USER_LOW
2019  != count + 1)) {
2020  fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2021  (ulong) page_header_get_field(page, PAGE_N_RECS)
2022  + PAGE_HEAP_NO_USER_LOW,
2023  (ulong) (count + 1));
2024 
2025  goto func_exit;
2026  }
2027 
2028  /* Check then the free list */
2029  rec = page_header_get_ptr(page, PAGE_FREE);
2030 
2031  while (rec != NULL) {
2032  if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
2033  || rec >= page + UNIV_PAGE_SIZE)) {
2034  fprintf(stderr,
2035  "InnoDB: Free list record has"
2036  " a nonsensical offset %lu\n",
2037  (ulong) (rec - page));
2038 
2039  goto func_exit;
2040  }
2041 
2042  if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2043  fprintf(stderr,
2044  "InnoDB: Free list record %lu"
2045  " is above rec heap top %lu\n",
2046  (ulong) (rec - page),
2047  (ulong) (rec_heap_top - page));
2048 
2049  goto func_exit;
2050  }
2051 
2052  count++;
2053 
2054  if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2055  fprintf(stderr,
2056  "InnoDB: Page free list appears"
2057  " to be circular %lu\n",
2058  (ulong) count);
2059  goto func_exit;
2060  }
2061 
2062  rec = page_rec_get_next(rec);
2063  }
2064 
2065  if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2066 
2067  fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
2068  (ulong) page_dir_get_n_heap(page),
2069  (ulong) (count + 1));
2070 
2071  goto func_exit;
2072  }
2073 
2074  ret = TRUE;
2075 
2076 func_exit:
2077  return(ret);
2078 }
2079 
2080 /***************************************************************/
2085 UNIV_INTERN
2086 ibool
2088 /*=====================*/
2089  page_t* page)
2090 {
2091  page_dir_slot_t* slot;
2092  ulint slot_no;
2093  ulint n_slots;
2094  rec_t* rec;
2095  byte* rec_heap_top;
2096  ulint count;
2097  ulint own_count;
2098  ibool ret = FALSE;
2099 
2100  ut_a(page_is_comp(page));
2101 
2102  /* Check first that the record heap and the directory do not
2103  overlap. */
2104 
2105  n_slots = page_dir_get_n_slots(page);
2106 
2107  if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
2108  fprintf(stderr,
2109  "InnoDB: Nonsensical number %lu"
2110  " of page dir slots\n", (ulong) n_slots);
2111 
2112  goto func_exit;
2113  }
2114 
2115  rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
2116 
2117  if (UNIV_UNLIKELY(rec_heap_top
2118  > page_dir_get_nth_slot(page, n_slots - 1))) {
2119 
2120  fprintf(stderr,
2121  "InnoDB: Record heap and dir overlap on a page,"
2122  " heap top %lu, dir %lu\n",
2123  (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
2124  (ulong)
2125  page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
2126 
2127  goto func_exit;
2128  }
2129 
2130  /* Validate the record list in a loop checking also that it is
2131  consistent with the page record directory. */
2132 
2133  count = 0;
2134  own_count = 1;
2135  slot_no = 0;
2136  slot = page_dir_get_nth_slot(page, slot_no);
2137 
2138  rec = page_get_infimum_rec(page);
2139 
2140  for (;;) {
2141  if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2142  fprintf(stderr,
2143  "InnoDB: Record %lu is above rec"
2144  " heap top %lu\n",
2145  (ulong) page_offset(rec),
2146  (ulong) page_offset(rec_heap_top));
2147 
2148  goto func_exit;
2149  }
2150 
2151  if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) {
2152  /* This is a record pointed to by a dir slot */
2153  if (UNIV_UNLIKELY(rec_get_n_owned_new(rec)
2154  != own_count)) {
2155 
2156  fprintf(stderr,
2157  "InnoDB: Wrong owned count %lu, %lu,"
2158  " rec %lu\n",
2159  (ulong) rec_get_n_owned_new(rec),
2160  (ulong) own_count,
2161  (ulong) page_offset(rec));
2162 
2163  goto func_exit;
2164  }
2165 
2166  if (UNIV_UNLIKELY
2167  (page_dir_slot_get_rec(slot) != rec)) {
2168  fprintf(stderr,
2169  "InnoDB: Dir slot does not point"
2170  " to right rec %lu\n",
2171  (ulong) page_offset(rec));
2172 
2173  goto func_exit;
2174  }
2175 
2176  own_count = 0;
2177 
2178  if (!page_rec_is_supremum(rec)) {
2179  slot_no++;
2180  slot = page_dir_get_nth_slot(page, slot_no);
2181  }
2182  }
2183 
2184  if (page_rec_is_supremum(rec)) {
2185 
2186  break;
2187  }
2188 
2189  if (UNIV_UNLIKELY
2190  (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA
2191  || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) {
2192  fprintf(stderr,
2193  "InnoDB: Next record offset nonsensical %lu"
2194  " for rec %lu\n",
2195  (ulong) rec_get_next_offs(rec, TRUE),
2196  (ulong) page_offset(rec));
2197 
2198  goto func_exit;
2199  }
2200 
2201  count++;
2202 
2203  if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2204  fprintf(stderr,
2205  "InnoDB: Page record list appears"
2206  " to be circular %lu\n",
2207  (ulong) count);
2208  goto func_exit;
2209  }
2210 
2211  rec = page_rec_get_next(rec);
2212  own_count++;
2213  }
2214 
2215  if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2216  fprintf(stderr, "InnoDB: n owned is zero"
2217  " in a supremum rec\n");
2218 
2219  goto func_exit;
2220  }
2221 
2222  if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2223  fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
2224  (ulong) slot_no, (ulong) (n_slots - 1));
2225  goto func_exit;
2226  }
2227 
2228  if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2229  + PAGE_HEAP_NO_USER_LOW
2230  != count + 1)) {
2231  fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2232  (ulong) page_header_get_field(page, PAGE_N_RECS)
2233  + PAGE_HEAP_NO_USER_LOW,
2234  (ulong) (count + 1));
2235 
2236  goto func_exit;
2237  }
2238 
2239  /* Check then the free list */
2240  rec = page_header_get_ptr(page, PAGE_FREE);
2241 
2242  while (rec != NULL) {
2243  if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
2244  || rec >= page + UNIV_PAGE_SIZE)) {
2245  fprintf(stderr,
2246  "InnoDB: Free list record has"
2247  " a nonsensical offset %lu\n",
2248  (ulong) page_offset(rec));
2249 
2250  goto func_exit;
2251  }
2252 
2253  if (UNIV_UNLIKELY(rec > rec_heap_top)) {
2254  fprintf(stderr,
2255  "InnoDB: Free list record %lu"
2256  " is above rec heap top %lu\n",
2257  (ulong) page_offset(rec),
2258  (ulong) page_offset(rec_heap_top));
2259 
2260  goto func_exit;
2261  }
2262 
2263  count++;
2264 
2265  if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
2266  fprintf(stderr,
2267  "InnoDB: Page free list appears"
2268  " to be circular %lu\n",
2269  (ulong) count);
2270  goto func_exit;
2271  }
2272 
2273  rec = page_rec_get_next(rec);
2274  }
2275 
2276  if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2277 
2278  fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
2279  (ulong) page_dir_get_n_heap(page),
2280  (ulong) (count + 1));
2281 
2282  goto func_exit;
2283  }
2284 
2285  ret = TRUE;
2286 
2287 func_exit:
2288  return(ret);
2289 }
2290 
2291 /***************************************************************/
2294 UNIV_INTERN
2295 ibool
2297 /*==========*/
2298  page_t* page,
2299  dict_index_t* index)
2301 {
2302  page_dir_slot_t*slot= NULL;
2303  mem_heap_t* heap= NULL;
2304  byte* buf= NULL;
2305  ulint count= 0;
2306  ulint own_count= 0;
2307  ulint rec_own_count= 0;
2308  ulint slot_no= 0;
2309  ulint data_size= 0;
2310  rec_t* rec= NULL;
2311  rec_t* old_rec = NULL;
2312  ulint offs= 0;
2313  ulint n_slots= 0;
2314  ibool ret = FALSE;
2315  ulint i= 0;
2316  ulint* offsets = NULL;
2317  ulint* old_offsets = NULL;
2318  void* buf_ptr= NULL;
2319 
2320  if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
2321  != dict_table_is_comp(index->table))) {
2322  fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
2323  goto func_exit2;
2324  }
2325  if (page_is_comp(page)) {
2326  if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
2327  goto func_exit2;
2328  }
2329  } else {
2330  if (UNIV_UNLIKELY(!page_simple_validate_old(page))) {
2331  goto func_exit2;
2332  }
2333  }
2334 
2335  heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
2336 
2337  /* The following buffer is used to check that the
2338  records in the page record heap do not overlap */
2339 
2340  buf_ptr= mem_heap_zalloc(heap, UNIV_PAGE_SIZE);
2341  buf = static_cast<byte *>(buf_ptr);
2342 
2343  /* Check first that the record heap and the directory do not
2344  overlap. */
2345 
2346  n_slots = page_dir_get_n_slots(page);
2347 
2348  if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
2349  <= page_dir_get_nth_slot(page, n_slots - 1)))) {
2350 
2351  fprintf(stderr,
2352  "InnoDB: Record heap and dir overlap"
2353  " on space %lu page %lu index %s, %p, %p\n",
2354  (ulong) page_get_space_id(page),
2355  (ulong) page_get_page_no(page), index->name,
2356  page_header_get_ptr(page, PAGE_HEAP_TOP),
2357  page_dir_get_nth_slot(page, n_slots - 1));
2358 
2359  goto func_exit;
2360  }
2361 
2362  /* Validate the record list in a loop checking also that
2363  it is consistent with the directory. */
2364  count = 0;
2365  data_size = 0;
2366  own_count = 1;
2367  slot_no = 0;
2368  slot = page_dir_get_nth_slot(page, slot_no);
2369 
2370  rec = page_get_infimum_rec(page);
2371 
2372  for (;;) {
2373  offsets = rec_get_offsets(rec, index, offsets,
2374  ULINT_UNDEFINED, &heap);
2375 
2376  if (page_is_comp(page) && page_rec_is_user_rec(rec)
2377  && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
2378  == page_is_leaf(page))) {
2379  fputs("InnoDB: node_ptr flag mismatch\n", stderr);
2380  goto func_exit;
2381  }
2382 
2383  if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2384  goto func_exit;
2385  }
2386 
2387 #ifndef UNIV_HOTBACKUP
2388  /* Check that the records are in the ascending order */
2389  if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW)
2390  && !page_rec_is_supremum(rec)) {
2391  if (UNIV_UNLIKELY
2392  (1 != cmp_rec_rec(rec, old_rec,
2393  offsets, old_offsets, index))) {
2394  fprintf(stderr,
2395  "InnoDB: Records in wrong order"
2396  " on space %lu page %lu index %s\n",
2397  (ulong) page_get_space_id(page),
2398  (ulong) page_get_page_no(page),
2399  index->name);
2400  fputs("\nInnoDB: previous record ", stderr);
2401  rec_print_new(stderr, old_rec, old_offsets);
2402  fputs("\nInnoDB: record ", stderr);
2403  rec_print_new(stderr, rec, offsets);
2404  putc('\n', stderr);
2405 
2406  goto func_exit;
2407  }
2408  }
2409 #endif /* !UNIV_HOTBACKUP */
2410 
2411  if (page_rec_is_user_rec(rec)) {
2412 
2413  data_size += rec_offs_size(offsets);
2414  }
2415 
2416  offs = page_offset(rec_get_start(rec, offsets));
2417  i = rec_offs_size(offsets);
2418  if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
2419  fputs("InnoDB: record offset out of bounds\n", stderr);
2420  goto func_exit;
2421  }
2422 
2423  while (i--) {
2424  if (UNIV_UNLIKELY(buf[offs + i])) {
2425  /* No other record may overlap this */
2426 
2427  fputs("InnoDB: Record overlaps another\n",
2428  stderr);
2429  goto func_exit;
2430  }
2431 
2432  buf[offs + i] = 1;
2433  }
2434 
2435  if (page_is_comp(page)) {
2436  rec_own_count = rec_get_n_owned_new(rec);
2437  } else {
2438  rec_own_count = rec_get_n_owned_old(rec);
2439  }
2440 
2441  if (UNIV_UNLIKELY(rec_own_count)) {
2442  /* This is a record pointed to by a dir slot */
2443  if (UNIV_UNLIKELY(rec_own_count != own_count)) {
2444  fprintf(stderr,
2445  "InnoDB: Wrong owned count %lu, %lu\n",
2446  (ulong) rec_own_count,
2447  (ulong) own_count);
2448  goto func_exit;
2449  }
2450 
2451  if (page_dir_slot_get_rec(slot) != rec) {
2452  fputs("InnoDB: Dir slot does not"
2453  " point to right rec\n",
2454  stderr);
2455  goto func_exit;
2456  }
2457 
2458  page_dir_slot_check(slot);
2459 
2460  own_count = 0;
2461  if (!page_rec_is_supremum(rec)) {
2462  slot_no++;
2463  slot = page_dir_get_nth_slot(page, slot_no);
2464  }
2465  }
2466 
2467  if (page_rec_is_supremum(rec)) {
2468  break;
2469  }
2470 
2471  count++;
2472  own_count++;
2473  old_rec = rec;
2474  rec = page_rec_get_next(rec);
2475 
2476  /* set old_offsets to offsets; recycle offsets */
2477  {
2478  ulint* tmp_offs = old_offsets;
2479  old_offsets = offsets;
2480  offsets = tmp_offs;
2481  }
2482  }
2483 
2484  if (page_is_comp(page)) {
2485  if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
2486 
2487  goto n_owned_zero;
2488  }
2489  } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
2490 n_owned_zero:
2491  fputs("InnoDB: n owned is zero\n", stderr);
2492  goto func_exit;
2493  }
2494 
2495  if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
2496  fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
2497  (ulong) slot_no, (ulong) (n_slots - 1));
2498  goto func_exit;
2499  }
2500 
2501  if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
2502  + PAGE_HEAP_NO_USER_LOW
2503  != count + 1)) {
2504  fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
2505  (ulong) page_header_get_field(page, PAGE_N_RECS)
2506  + PAGE_HEAP_NO_USER_LOW,
2507  (ulong) (count + 1));
2508  goto func_exit;
2509  }
2510 
2511  if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
2512  fprintf(stderr,
2513  "InnoDB: Summed data size %lu, returned by func %lu\n",
2514  (ulong) data_size, (ulong) page_get_data_size(page));
2515  goto func_exit;
2516  }
2517 
2518  /* Check then the free list */
2519  rec = page_header_get_ptr(page, PAGE_FREE);
2520 
2521  while (rec != NULL) {
2522  offsets = rec_get_offsets(rec, index, offsets,
2523  ULINT_UNDEFINED, &heap);
2524  if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2525 
2526  goto func_exit;
2527  }
2528 
2529  count++;
2530  offs = page_offset(rec_get_start(rec, offsets));
2531  i = rec_offs_size(offsets);
2532  if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
2533  fputs("InnoDB: record offset out of bounds\n", stderr);
2534  goto func_exit;
2535  }
2536 
2537  while (i--) {
2538 
2539  if (UNIV_UNLIKELY(buf[offs + i])) {
2540  fputs("InnoDB: Record overlaps another"
2541  " in free list\n", stderr);
2542  goto func_exit;
2543  }
2544 
2545  buf[offs + i] = 1;
2546  }
2547 
2548  rec = page_rec_get_next(rec);
2549  }
2550 
2551  if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
2552  fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
2553  (ulong) page_dir_get_n_heap(page),
2554  (ulong) count + 1);
2555  goto func_exit;
2556  }
2557 
2558  ret = TRUE;
2559 
2560 func_exit:
2561  mem_heap_free(heap);
2562 
2563  if (UNIV_UNLIKELY(ret == FALSE)) {
2564 func_exit2:
2565  fprintf(stderr,
2566  "InnoDB: Apparent corruption"
2567  " in space %lu page %lu index %s\n",
2568  (ulong) page_get_space_id(page),
2569  (ulong) page_get_page_no(page),
2570  index->name);
2571  buf_page_print(page, 0);
2572  }
2573 
2574  return(ret);
2575 }
2576 
2577 #ifndef UNIV_HOTBACKUP
2578 /***************************************************************/
2581 UNIV_INTERN
2582 const rec_t*
2584 /*=======================*/
2585  const page_t* page,
2586  ulint heap_no)
2587 {
2588  const rec_t* rec;
2589 
2590  if (page_is_comp(page)) {
2591  rec = page + PAGE_NEW_INFIMUM;
2592 
2593  for(;;) {
2594  ulint rec_heap_no = rec_get_heap_no_new(rec);
2595 
2596  if (rec_heap_no == heap_no) {
2597 
2598  return(rec);
2599  } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2600 
2601  return(NULL);
2602  }
2603 
2604  rec = page + rec_get_next_offs(rec, TRUE);
2605  }
2606  } else {
2607  rec = page + PAGE_OLD_INFIMUM;
2608 
2609  for (;;) {
2610  ulint rec_heap_no = rec_get_heap_no_old(rec);
2611 
2612  if (rec_heap_no == heap_no) {
2613 
2614  return(rec);
2615  } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
2616 
2617  return(NULL);
2618  }
2619 
2620  rec = page + rec_get_next_offs(rec, FALSE);
2621  }
2622  }
2623 }
2624 #endif /* !UNIV_HOTBACKUP */