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