page0page.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:44k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************
  2. Index page routines
  3. (c) 1994-1996 Innobase Oy
  4. Created 2/2/1994 Heikki Tuuri
  5. *******************************************************/
  6. #define THIS_MODULE
  7. #include "page0page.h"
  8. #ifdef UNIV_NONINL
  9. #include "page0page.ic"
  10. #endif
  11. #undef THIS_MODULE
  12. #include "page0cur.h"
  13. #include "lock0lock.h"
  14. #include "fut0lst.h"
  15. #include "btr0sea.h"
  16. #include "buf0buf.h"
  17. /* THE INDEX PAGE
  18. ==============
  19. The index page consists of a page header which contains the page's
  20. id and other information. On top of it are the the index records
  21. in a heap linked into a one way linear list according to alphabetic order.
  22. Just below page end is an array of pointers which we call page directory,
  23. to about every sixth record in the list. The pointers are placed in
  24. the directory in the alphabetical order of the records pointed to,
  25. enabling us to make binary search using the array. Each slot n:o I
  26. in the directory points to a record, where a 4-bit field contains a count
  27. of those records which are in the linear list between pointer I and 
  28. the pointer I - 1 in the directory, including the record
  29. pointed to by pointer I and not including the record pointed to by I - 1.
  30. We say that the record pointed to by slot I, or that slot I, owns
  31. these records. The count is always kept in the range 4 to 8, with
  32. the exception that it is 1 for the first slot, and 1--8 for the second slot.  
  33. An essentially binary search can be performed in the list of index
  34. records, like we could do if we had pointer to every record in the
  35. page directory. The data structure is, however, more efficient when
  36. we are doing inserts, because most inserts are just pushed on a heap.
  37. Only every 8th insert requires block move in the directory pointer
  38. table, which itself is quite small. A record is deleted from the page
  39. by just taking it off the linear list and updating the number of owned
  40. records-field of the record which owns it, and updating the page directory,
  41. if necessary. A special case is the one when the record owns itself.
  42. Because the overhead of inserts is so small, we may also increase the
  43. page size from the projected default of 8 kB to 64 kB without too
  44. much loss of efficiency in inserts. Bigger page becomes actual
  45. when the disk transfer rate compared to seek and latency time rises.
  46. On the present system, the page size is set so that the page transfer
  47. time (3 ms) is 20 % of the disk random access time (15 ms).
  48. When the page is split, merged, or becomes full but contains deleted
  49. records, we have to reorganize the page.
  50. Assuming a page size of 8 kB, a typical index page of a secondary
  51. index contains 300 index entries, and the size of the page directory
  52. is 50 x 4 bytes = 200 bytes. */
  53. /*******************************************************************
  54. Looks for the directory slot which owns the given record. */
  55. ulint
  56. page_dir_find_owner_slot(
  57. /*=====================*/
  58. /* out: the directory slot number */
  59. rec_t* rec) /* in: the physical record */
  60. {
  61. ulint i;
  62. ulint steps = 0;
  63. page_t* page;
  64. page_dir_slot_t* slot;
  65. rec_t* original_rec = rec;
  66. ut_ad(page_rec_check(rec));
  67. while (rec_get_n_owned(rec) == 0) {
  68. steps++;
  69. rec = page_rec_get_next(rec);
  70. }
  71. page = buf_frame_align(rec);
  72. i = page_dir_get_n_slots(page) - 1;
  73. slot = page_dir_get_nth_slot(page, i); 
  74. while (page_dir_slot_get_rec(slot) != rec) {
  75.   if (i == 0) {
  76. fprintf(stderr,
  77. "InnoDB: Probable data corruption on page %lun"
  78. "InnoDB: Original record ",
  79. (ulong) buf_frame_get_page_no(page));
  80. rec_print(stderr, original_rec);
  81. fprintf(stderr, "n"
  82. "InnoDB: on that page. Steps %lu.n", (ulong) steps);
  83. fputs(
  84. "InnoDB: Cannot find the dir slot for record ",
  85. stderr);
  86. rec_print(stderr, rec);
  87. fputs("n"
  88. "InnoDB: on that page!n", stderr);
  89. buf_page_print(page);
  90.    ut_error;
  91.    }
  92. i--;
  93. slot = page_dir_get_nth_slot(page, i); 
  94. }
  95. return(i);
  96. }
  97. /******************************************************************
  98. Used to check the consistency of a directory slot. */
  99. static
  100. ibool
  101. page_dir_slot_check(
  102. /*================*/
  103. /* out: TRUE if succeed */
  104. page_dir_slot_t* slot) /* in: slot */
  105. {
  106. page_t* page;
  107. ulint n_slots;
  108. ulint n_owned;
  109. ut_a(slot);
  110. page = buf_frame_align(slot);
  111. n_slots = page_header_get_field(page, PAGE_N_DIR_SLOTS);
  112. ut_a(slot <= page_dir_get_nth_slot(page, 0));
  113. ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1));
  114. ut_a(page_rec_check(page + mach_read_from_2(slot)));
  115. n_owned = rec_get_n_owned(page + mach_read_from_2(slot));
  116. if (slot == page_dir_get_nth_slot(page, 0)) {
  117. ut_a(n_owned == 1);
  118. } else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) {
  119. ut_a(n_owned >= 1);
  120. ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
  121. } else {
  122. ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED);
  123. ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
  124. }
  125. return(TRUE);
  126. }
  127. /*****************************************************************
  128. Sets the max trx id field value. */
  129. void
  130. page_set_max_trx_id(
  131. /*================*/
  132. page_t* page, /* in: page */
  133. dulint trx_id) /* in: transaction id */
  134. {
  135. buf_block_t* block;
  136. ut_ad(page);
  137. block = buf_block_align(page);
  138. if (block->is_hashed) {
  139. rw_lock_x_lock(&btr_search_latch);
  140. }
  141. /* It is not necessary to write this change to the redo log, as
  142. during a database recovery we assume that the max trx id of every
  143. page is the maximum trx id assigned before the crash. */
  144. mach_write_to_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID, trx_id);
  145. if (block->is_hashed) {
  146. rw_lock_x_unlock(&btr_search_latch);
  147. }
  148. }
  149. /****************************************************************
  150. Allocates a block of memory from an index page. */
  151. byte*
  152. page_mem_alloc(
  153. /*===========*/
  154. /* out: pointer to start of allocated 
  155. buffer, or NULL if allocation fails */
  156. page_t* page, /* in: index page */
  157. ulint need, /* in: number of bytes needed */
  158. ulint* heap_no)/* out: this contains the heap number
  159. of the allocated record if allocation succeeds */
  160. {
  161. rec_t* rec;
  162. byte* block;
  163. ulint avl_space;
  164. ulint garbage;
  165. ut_ad(page && heap_no);
  166. /* If there are records in the free list, look if the first is
  167. big enough */
  168. rec = page_header_get_ptr(page, PAGE_FREE);
  169. if (rec && (rec_get_size(rec) >= need)) {
  170. page_header_set_ptr(page, PAGE_FREE, page_rec_get_next(rec));
  171. garbage = page_header_get_field(page, PAGE_GARBAGE);
  172. ut_ad(garbage >= need);
  173. page_header_set_field(page, PAGE_GARBAGE, garbage - need);
  174. *heap_no = rec_get_heap_no(rec);
  175. return(rec_get_start(rec));
  176. }
  177. /* Could not find space from the free list, try top of heap */
  178. avl_space = page_get_max_insert_size(page, 1);
  179. if (avl_space >= need) {
  180. block = page_header_get_ptr(page, PAGE_HEAP_TOP);
  181. page_header_set_ptr(page, PAGE_HEAP_TOP, block + need);
  182. *heap_no = page_header_get_field(page, PAGE_N_HEAP);
  183. page_header_set_field(page, PAGE_N_HEAP, 1 + *heap_no);
  184. return(block);
  185. }
  186. return(NULL);
  187. }
  188. /**************************************************************
  189. Writes a log record of page creation. */
  190. UNIV_INLINE
  191. void
  192. page_create_write_log(
  193. /*==================*/
  194. buf_frame_t* frame, /* in: a buffer frame where the page is
  195. created */
  196. mtr_t* mtr) /* in: mini-transaction handle */
  197. {
  198. mlog_write_initial_log_record(frame, MLOG_PAGE_CREATE, mtr);
  199. }
  200. /***************************************************************
  201. Parses a redo log record of creating a page. */
  202. byte*
  203. page_parse_create(
  204. /*==============*/
  205. /* out: end of log record or NULL */
  206. byte* ptr, /* in: buffer */
  207. byte* end_ptr __attribute__((unused)), /* in: buffer end */
  208. page_t* page, /* in: page or NULL */
  209. mtr_t* mtr) /* in: mtr or NULL */
  210. {
  211. ut_ad(ptr && end_ptr);
  212. /* The record is empty, except for the record initial part */
  213. if (page) {
  214. page_create(page, mtr);
  215. }
  216. return(ptr);
  217. }
  218. /**************************************************************
  219. The index page creation function. */
  220. page_t* 
  221. page_create(
  222. /*========*/
  223. /* out: pointer to the page */
  224. buf_frame_t* frame, /* in: a buffer frame where the page is
  225. created */
  226. mtr_t* mtr) /* in: mini-transaction handle */
  227. {
  228. page_dir_slot_t* slot;
  229. mem_heap_t* heap;
  230. dtuple_t* tuple;
  231. dfield_t* field;
  232. byte* heap_top;
  233. rec_t* infimum_rec;
  234. rec_t* supremum_rec;
  235. page_t* page;
  236. ut_ad(frame && mtr);
  237. ut_ad(PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE
  238.       <= PAGE_DATA);
  239. ut_ad(PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE
  240.       <= PAGE_DATA);
  241. /* 1. INCREMENT MODIFY CLOCK */
  242. buf_frame_modify_clock_inc(frame);
  243. /* 2. WRITE LOG INFORMATION */
  244. page_create_write_log(frame, mtr);
  245. page = frame;
  246. fil_page_set_type(page, FIL_PAGE_INDEX);
  247. heap = mem_heap_create(200);
  248. /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
  249. /* Create first a data tuple for infimum record */
  250. tuple = dtuple_create(heap, 1);
  251. field = dtuple_get_nth_field(tuple, 0);
  252. dfield_set_data(field, "infimum", sizeof "infimum");
  253. dtype_set(dfield_get_type(field), DATA_VARCHAR, DATA_ENGLISH, 20, 0);
  254. /* Set the corresponding physical record to its place in the page
  255. record heap */
  256. heap_top = page + PAGE_DATA;
  257. infimum_rec = rec_convert_dtuple_to_rec(heap_top, tuple);
  258. ut_a(infimum_rec == page + PAGE_INFIMUM);
  259. rec_set_n_owned(infimum_rec, 1);
  260. rec_set_heap_no(infimum_rec, 0);
  261. heap_top = rec_get_end(infimum_rec);
  262. /* Create then a tuple for supremum */
  263. tuple = dtuple_create(heap, 1);
  264. field = dtuple_get_nth_field(tuple, 0);
  265. dfield_set_data(field, "supremum", sizeof "supremum");
  266. dtype_set(dfield_get_type(field), DATA_VARCHAR, DATA_ENGLISH, 20, 0);
  267. supremum_rec = rec_convert_dtuple_to_rec(heap_top, tuple);
  268. ut_a(supremum_rec == page + PAGE_SUPREMUM);
  269. rec_set_n_owned(supremum_rec, 1);
  270. rec_set_heap_no(supremum_rec, 1);
  271. heap_top = rec_get_end(supremum_rec);
  272. ut_ad(heap_top == page + PAGE_SUPREMUM_END);
  273. mem_heap_free(heap);
  274. /* 4. INITIALIZE THE PAGE HEADER */
  275. page_header_set_field(page, PAGE_N_DIR_SLOTS, 2);
  276. page_header_set_ptr(page, PAGE_HEAP_TOP, heap_top);
  277. page_header_set_field(page, PAGE_N_HEAP, 2);
  278. page_header_set_ptr(page, PAGE_FREE, NULL);
  279. page_header_set_field(page, PAGE_GARBAGE, 0);
  280. page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
  281. page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
  282. page_header_set_field(page, PAGE_N_DIRECTION, 0);
  283. page_header_set_field(page, PAGE_N_RECS, 0);
  284. page_set_max_trx_id(page, ut_dulint_zero);
  285. /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
  286. /* Set the slots to point to infimum and supremum. */
  287. slot = page_dir_get_nth_slot(page, 0);
  288. page_dir_slot_set_rec(slot, infimum_rec);
  289. slot = page_dir_get_nth_slot(page, 1);
  290. page_dir_slot_set_rec(slot, supremum_rec);
  291. /* Set the next pointers in infimum and supremum */
  292. rec_set_next_offs(infimum_rec, (ulint)(supremum_rec - page)); 
  293. rec_set_next_offs(supremum_rec, 0);
  294. return(page);
  295. }
  296. /*****************************************************************
  297. Differs from page_copy_rec_list_end, because this function does not
  298. touch the lock table and max trx id on page. */
  299. void
  300. page_copy_rec_list_end_no_locks(
  301. /*============================*/
  302. page_t* new_page, /* in: index page to copy to */
  303. page_t* page, /* in: index page */
  304. rec_t* rec, /* in: record on page */
  305. mtr_t* mtr) /* in: mtr */
  306. {
  307. page_cur_t cur1;
  308. page_cur_t cur2;
  309. rec_t* sup;
  310. page_cur_position(rec, &cur1);
  311. if (page_cur_is_before_first(&cur1)) {
  312. page_cur_move_to_next(&cur1);
  313. }
  314. ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == PAGE_INFIMUM);
  315. page_cur_set_before_first(new_page, &cur2);
  316. /* Copy records from the original page to the new page */
  317. sup = page_get_supremum_rec(page);
  318. while (sup != page_cur_get_rec(&cur1)) {
  319. if (!page_cur_rec_insert(&cur2,
  320. page_cur_get_rec(&cur1), mtr)) {
  321. /* Track an assertion failure reported on the mailing
  322. list on June 18th, 2003 */
  323.         buf_page_print(new_page);
  324.         buf_page_print(page);
  325. ut_print_timestamp(stderr);
  326. fprintf(stderr,
  327. "InnoDB: rec offset %lu, cur1 offset %lu, cur2 offset %lun",
  328.       (ulong)(rec - page),
  329.       (ulong)(page_cur_get_rec(&cur1) - page),
  330.       (ulong)(page_cur_get_rec(&cur2) - new_page));
  331. ut_error;
  332. }
  333. page_cur_move_to_next(&cur1);
  334. page_cur_move_to_next(&cur2);
  335. }
  336. }
  337. /*****************************************************************
  338. Copies records from page to new_page, from a given record onward,
  339. including that record. Infimum and supremum records are not copied.
  340. The records are copied to the start of the record list on new_page. */
  341. void
  342. page_copy_rec_list_end(
  343. /*===================*/
  344. page_t* new_page, /* in: index page to copy to */
  345. page_t* page, /* in: index page */
  346. rec_t* rec, /* in: record on page */
  347. mtr_t* mtr) /* in: mtr */
  348. {
  349. if (page_header_get_field(new_page, PAGE_N_HEAP) == 2) {
  350. page_copy_rec_list_end_to_created_page(new_page, page, rec,
  351. mtr);
  352. } else {
  353. page_copy_rec_list_end_no_locks(new_page, page, rec, mtr);
  354. }
  355. /* Update the lock table, MAX_TRX_ID, and possible hash index */
  356. lock_move_rec_list_end(new_page, page, rec);
  357. page_update_max_trx_id(new_page, page_get_max_trx_id(page));
  358. btr_search_move_or_delete_hash_entries(new_page, page);
  359. }
  360. /*****************************************************************
  361. Copies records from page to new_page, up to the given record,
  362. NOT including that record. Infimum and supremum records are not copied.
  363. The records are copied to the end of the record list on new_page. */
  364. void
  365. page_copy_rec_list_start(
  366. /*=====================*/
  367. page_t* new_page, /* in: index page to copy to */
  368. page_t* page, /* in: index page */
  369. rec_t* rec, /* in: record on page */
  370. mtr_t* mtr) /* in: mtr */
  371. {
  372. page_cur_t cur1;
  373. page_cur_t cur2;
  374. rec_t* old_end;
  375. page_cur_set_before_first(page, &cur1);
  376. if (rec == page_cur_get_rec(&cur1)) {
  377. return;
  378. }
  379. page_cur_move_to_next(&cur1);
  380. page_cur_set_after_last(new_page, &cur2);
  381. page_cur_move_to_prev(&cur2);
  382. old_end = page_cur_get_rec(&cur2);
  383. /* Copy records from the original page to the new page */
  384. while (page_cur_get_rec(&cur1) != rec) {
  385. ut_a(
  386. page_cur_rec_insert(&cur2, page_cur_get_rec(&cur1), mtr));
  387. page_cur_move_to_next(&cur1);
  388. page_cur_move_to_next(&cur2);
  389. }
  390. /* Update the lock table, MAX_TRX_ID, and possible hash index */
  391. lock_move_rec_list_start(new_page, page, rec, old_end);
  392. page_update_max_trx_id(new_page, page_get_max_trx_id(page));
  393. btr_search_move_or_delete_hash_entries(new_page, page);
  394. }
  395. /**************************************************************
  396. Writes a log record of a record list end or start deletion. */
  397. UNIV_INLINE
  398. void
  399. page_delete_rec_list_write_log(
  400. /*===========================*/
  401. page_t* page, /* in: index page */
  402. rec_t* rec, /* in: record on page */
  403. byte type, /* in: operation type: MLOG_LIST_END_DELETE, ... */
  404. mtr_t* mtr) /* in: mtr */
  405. {
  406. ut_ad((type == MLOG_LIST_END_DELETE)
  407. || (type == MLOG_LIST_START_DELETE));
  408. mlog_write_initial_log_record(page, type, mtr);
  409. /* Write the parameter as a 2-byte ulint */
  410. mlog_catenate_ulint(mtr, rec - page, MLOG_2BYTES);
  411. }
  412. /**************************************************************
  413. Parses a log record of a record list end or start deletion. */
  414. byte*
  415. page_parse_delete_rec_list(
  416. /*=======================*/
  417. /* out: end of log record or NULL */
  418. byte type, /* in: MLOG_LIST_END_DELETE or
  419. MLOG_LIST_START_DELETE */
  420. byte* ptr, /* in: buffer */
  421. byte* end_ptr,/* in: buffer end */
  422. page_t* page, /* in: page or NULL */
  423. mtr_t* mtr) /* in: mtr or NULL */
  424. {
  425. ulint offset;
  426. ut_ad((type == MLOG_LIST_END_DELETE)
  427.       || (type == MLOG_LIST_START_DELETE)); 
  428.       
  429. /* Read the record offset as a 2-byte ulint */
  430. if (end_ptr < ptr + 2) {
  431. return(NULL);
  432. }
  433. offset = mach_read_from_2(ptr);
  434. ptr += 2;
  435. if (!page) {
  436. return(ptr);
  437. }
  438. if (type == MLOG_LIST_END_DELETE) {
  439. page_delete_rec_list_end(page, page + offset, ULINT_UNDEFINED,
  440. ULINT_UNDEFINED, mtr);
  441. } else {
  442. page_delete_rec_list_start(page, page + offset, mtr);
  443. }
  444. return(ptr);
  445. }
  446. /*****************************************************************
  447. Deletes records from a page from a given record onward, including that record.
  448. The infimum and supremum records are not deleted. */
  449. void
  450. page_delete_rec_list_end(
  451. /*=====================*/
  452. page_t* page, /* in: index page */
  453. rec_t* rec, /* in: record on page */
  454. ulint n_recs, /* in: number of records to delete, or ULINT_UNDEFINED
  455. if not known */
  456. ulint size, /* in: the sum of the sizes of the records in the end
  457. of the chain to delete, or ULINT_UNDEFINED if not
  458. known */
  459. mtr_t* mtr) /* in: mtr */
  460. {
  461. page_dir_slot_t* slot;
  462. ulint slot_index;
  463. rec_t* last_rec;
  464. rec_t* prev_rec;
  465. rec_t* free;
  466. rec_t* rec2;
  467. ulint count;
  468. ulint n_owned;
  469. rec_t* sup;
  470. /* Reset the last insert info in the page header and increment
  471. the modify clock for the frame */
  472. page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
  473. /* The page gets invalid for optimistic searches: increment the
  474. frame modify clock */
  475. buf_frame_modify_clock_inc(page);
  476. sup = page_get_supremum_rec(page);
  477. if (rec == page_get_infimum_rec(page)) {
  478. rec = page_rec_get_next(rec);
  479. }
  480. page_delete_rec_list_write_log(page, rec, MLOG_LIST_END_DELETE, mtr);
  481. if (rec == sup) {
  482. return;
  483. }
  484. prev_rec = page_rec_get_prev(rec);
  485. last_rec = page_rec_get_prev(sup);
  486. if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
  487. /* Calculate the sum of sizes and the number of records */
  488. size = 0;
  489. n_recs = 0;
  490. rec2 = rec;
  491. while (rec2 != sup) {
  492. size += rec_get_size(rec2);
  493. n_recs++;
  494. rec2 = page_rec_get_next(rec2);
  495. }
  496. }
  497. /* Update the page directory; there is no need to balance the number
  498. of the records owned by the supremum record, as it is allowed to be
  499. less than PAGE_DIR_SLOT_MIN_N_OWNED */
  500. rec2 = rec;
  501. count = 0;
  502. while (rec_get_n_owned(rec2) == 0) {
  503. count++;
  504. rec2 = page_rec_get_next(rec2);
  505. }
  506. ut_ad(rec_get_n_owned(rec2) - count > 0);
  507. n_owned = rec_get_n_owned(rec2) - count;
  508. slot_index = page_dir_find_owner_slot(rec2);
  509. slot = page_dir_get_nth_slot(page, slot_index);
  510. page_dir_slot_set_rec(slot, sup);
  511. page_dir_slot_set_n_owned(slot, n_owned);
  512. page_header_set_field(page, PAGE_N_DIR_SLOTS, slot_index + 1);
  513. /* Remove the record chain segment from the record chain */
  514. page_rec_set_next(prev_rec, page_get_supremum_rec(page));
  515. /* Catenate the deleted chain segment to the page free list */
  516. free = page_header_get_ptr(page, PAGE_FREE);
  517. page_rec_set_next(last_rec, free);
  518. page_header_set_ptr(page, PAGE_FREE, rec);
  519. page_header_set_field(page, PAGE_GARBAGE,
  520. size + page_header_get_field(page, PAGE_GARBAGE));
  521. page_header_set_field(page, PAGE_N_RECS,
  522. (ulint)(page_get_n_recs(page) - n_recs));
  523. }
  524. /*****************************************************************
  525. Deletes records from page, up to the given record, NOT including
  526. that record. Infimum and supremum records are not deleted. */
  527. void
  528. page_delete_rec_list_start(
  529. /*=======================*/
  530. page_t* page, /* in: index page */
  531. rec_t* rec, /* in: record on page */
  532. mtr_t* mtr) /* in: mtr */
  533. {
  534. page_cur_t cur1;
  535. ulint log_mode;
  536. page_delete_rec_list_write_log(page, rec, MLOG_LIST_START_DELETE, mtr);
  537. page_cur_set_before_first(page, &cur1);
  538. if (rec == page_cur_get_rec(&cur1)) {
  539. return;
  540. }
  541. page_cur_move_to_next(&cur1);
  542. /* Individual deletes are not logged */
  543. log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
  544. while (page_cur_get_rec(&cur1) != rec) {
  545. page_cur_delete_rec(&cur1, mtr);
  546. }
  547. /* Restore log mode */
  548. mtr_set_log_mode(mtr, log_mode);
  549. }
  550. /*****************************************************************
  551. Moves record list end to another page. Moved records include
  552. split_rec. */
  553. void
  554. page_move_rec_list_end(
  555. /*===================*/
  556. page_t* new_page, /* in: index page where to move */
  557. page_t* page, /* in: index page */
  558. rec_t* split_rec, /* in: first record to move */
  559. mtr_t* mtr) /* in: mtr */
  560. {
  561. ulint old_data_size;
  562. ulint new_data_size;
  563. ulint old_n_recs;
  564. ulint new_n_recs;
  565. old_data_size = page_get_data_size(new_page);
  566. old_n_recs = page_get_n_recs(new_page);
  567. page_copy_rec_list_end(new_page, page, split_rec, mtr);
  568. new_data_size = page_get_data_size(new_page);
  569. new_n_recs = page_get_n_recs(new_page);
  570. ut_ad(new_data_size >= old_data_size);
  571. page_delete_rec_list_end(page, split_rec, new_n_recs - old_n_recs,
  572. new_data_size - old_data_size, mtr);
  573. }
  574. /*****************************************************************
  575. Moves record list start to another page. Moved records do not include
  576. split_rec. */
  577. void
  578. page_move_rec_list_start(
  579. /*=====================*/
  580. page_t* new_page, /* in: index page where to move */
  581. page_t* page, /* in: index page */
  582. rec_t* split_rec, /* in: first record not to move */
  583. mtr_t* mtr) /* in: mtr */
  584. {
  585. page_copy_rec_list_start(new_page, page, split_rec, mtr);
  586. page_delete_rec_list_start(page, split_rec, mtr);
  587. }
  588. /***************************************************************************
  589. This is a low-level operation which is used in a database index creation
  590. to update the page number of a created B-tree to a data dictionary record. */
  591. void
  592. page_rec_write_index_page_no(
  593. /*=========================*/
  594. rec_t* rec, /* in: record to update */
  595. ulint i, /* in: index of the field to update */
  596. ulint page_no,/* in: value to write */
  597. mtr_t* mtr) /* in: mtr */
  598. {
  599. byte* data;
  600. ulint len;
  601. data = rec_get_nth_field(rec, i, &len);
  602. ut_ad(len == 4);
  603. mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr);
  604. }
  605. /******************************************************************
  606. Used to delete n slots from the directory. This function updates
  607. also n_owned fields in the records, so that the first slot after
  608. the deleted ones inherits the records of the deleted slots. */
  609. UNIV_INLINE
  610. void
  611. page_dir_delete_slots(
  612. /*==================*/
  613. page_t* page, /* in: the index page */
  614. ulint start, /* in: first slot to be deleted */
  615. ulint n) /* in: number of slots to delete (currently 
  616. only n == 1 allowed) */
  617. {
  618. page_dir_slot_t* slot;
  619. ulint i;
  620. ulint sum_owned = 0;
  621. ulint n_slots;
  622. rec_t* rec;
  623. ut_ad(n == 1);
  624. ut_ad(start > 0);
  625. ut_ad(start + n < page_dir_get_n_slots(page));
  626. n_slots = page_dir_get_n_slots(page);
  627. /* 1. Reset the n_owned fields of the slots to be
  628. deleted */
  629. for (i = start; i < start + n; i++) {
  630. slot = page_dir_get_nth_slot(page, i);
  631. sum_owned += page_dir_slot_get_n_owned(slot);
  632. page_dir_slot_set_n_owned(slot, 0);
  633. }
  634. /* 2. Update the n_owned value of the first non-deleted slot */
  635. slot = page_dir_get_nth_slot(page, start + n);
  636. page_dir_slot_set_n_owned(slot,
  637. sum_owned + page_dir_slot_get_n_owned(slot));
  638. /* 3. Destroy start and other slots by copying slots */
  639. for (i = start + n; i < n_slots; i++) {
  640. slot = page_dir_get_nth_slot(page, i);
  641. rec = page_dir_slot_get_rec(slot);
  642. slot = page_dir_get_nth_slot(page, i - n);
  643. page_dir_slot_set_rec(slot, rec);
  644. }
  645. /* 4. Update the page header */
  646. page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots - n);
  647. }
  648. /******************************************************************
  649. Used to add n slots to the directory. Does not set the record pointers
  650. in the added slots or update n_owned values: this is the responsibility
  651. of the caller. */
  652. UNIV_INLINE
  653. void
  654. page_dir_add_slots(
  655. /*===============*/
  656. page_t* page, /* in: the index page */
  657. ulint start, /* in: the slot above which the new slots are added */
  658. ulint n) /* in: number of slots to add (currently only n == 1 
  659. allowed) */
  660. {
  661. page_dir_slot_t* slot;
  662. ulint n_slots;
  663. ulint i;
  664. rec_t* rec;
  665. ut_ad(n == 1);
  666. n_slots = page_dir_get_n_slots(page);
  667. ut_ad(start < n_slots - 1);
  668. /* Update the page header */
  669. page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots + n);
  670. /* Move slots up */
  671. for (i = n_slots - 1; i > start; i--) {
  672. slot = page_dir_get_nth_slot(page, i);
  673. rec = page_dir_slot_get_rec(slot);
  674. slot = page_dir_get_nth_slot(page, i + n);
  675. page_dir_slot_set_rec(slot, rec);
  676. }
  677. }
  678. /********************************************************************
  679. Splits a directory slot which owns too many records. */
  680. void
  681. page_dir_split_slot(
  682. /*================*/
  683. page_t* page, /* in: the index page in question */
  684. ulint slot_no) /* in: the directory slot */
  685. {
  686. rec_t* rec;
  687. page_dir_slot_t* new_slot;
  688. page_dir_slot_t* prev_slot;
  689. page_dir_slot_t* slot;
  690. ulint i;
  691. ulint n_owned;
  692. ut_ad(page);
  693. ut_ad(slot_no > 0);
  694. slot = page_dir_get_nth_slot(page, slot_no);
  695. n_owned = page_dir_slot_get_n_owned(slot);
  696. ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
  697. /* 1. We loop to find a record approximately in the middle of the 
  698. records owned by the slot. */
  699. prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
  700. rec = page_dir_slot_get_rec(prev_slot);
  701. for (i = 0; i < n_owned / 2; i++) {
  702. rec = page_rec_get_next(rec);
  703. }
  704. ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
  705. /* 2. We add one directory slot immediately below the slot to be
  706. split. */
  707. page_dir_add_slots(page, slot_no - 1, 1);
  708. /* The added slot is now number slot_no, and the old slot is
  709. now number slot_no + 1 */
  710. new_slot = page_dir_get_nth_slot(page, slot_no);
  711. slot = page_dir_get_nth_slot(page, slot_no + 1);
  712. /* 3. We store the appropriate values to the new slot. */
  713. page_dir_slot_set_rec(new_slot, rec);
  714. page_dir_slot_set_n_owned(new_slot, n_owned / 2);
  715. /* 4. Finally, we update the number of records field of the 
  716. original slot */
  717. page_dir_slot_set_n_owned(slot, n_owned - (n_owned / 2));
  718. }
  719. /*****************************************************************
  720. Tries to balance the given directory slot with too few records with the upper
  721. neighbor, so that there are at least the minimum number of records owned by
  722. the slot; this may result in the merging of two slots. */
  723. void
  724. page_dir_balance_slot(
  725. /*==================*/
  726. page_t* page, /* in: index page */
  727. ulint slot_no)  /* in: the directory slot */
  728. {
  729. page_dir_slot_t* slot;
  730. page_dir_slot_t* up_slot;
  731. ulint n_owned;
  732. ulint up_n_owned;
  733. rec_t* old_rec;
  734. rec_t* new_rec;
  735. ut_ad(page);
  736. ut_ad(slot_no > 0);
  737. slot = page_dir_get_nth_slot(page, slot_no);
  738. /* The last directory slot cannot be balanced with the upper
  739. neighbor, as there is none. */
  740. if (slot_no == page_dir_get_n_slots(page) - 1) {
  741. return;
  742. }
  743. up_slot = page_dir_get_nth_slot(page, slot_no + 1);
  744. n_owned = page_dir_slot_get_n_owned(slot);
  745. up_n_owned = page_dir_slot_get_n_owned(up_slot);
  746. ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
  747. /* If the upper slot has the minimum value of n_owned, we will merge
  748. the two slots, therefore we assert: */ 
  749. ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
  750. if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
  751. /* In this case we can just transfer one record owned
  752. by the upper slot to the property of the lower slot */
  753. old_rec = page_dir_slot_get_rec(slot);
  754. new_rec = page_rec_get_next(old_rec);
  755. rec_set_n_owned(old_rec, 0);
  756. rec_set_n_owned(new_rec, n_owned + 1);
  757. page_dir_slot_set_rec(slot, new_rec);
  758. page_dir_slot_set_n_owned(up_slot, up_n_owned -1);
  759. } else {
  760. /* In this case we may merge the two slots */
  761. page_dir_delete_slots(page, slot_no, 1);
  762. }
  763. }
  764. /****************************************************************
  765. Returns the middle record of the record list. If there are an even number
  766. of records in the list, returns the first record of the upper half-list. */
  767. rec_t*
  768. page_get_middle_rec(
  769. /*================*/
  770. /* out: middle record */
  771. page_t* page) /* in: page */
  772. {
  773. page_dir_slot_t* slot;
  774. ulint middle;
  775. ulint i;
  776. ulint n_owned;
  777. ulint count;
  778. rec_t* rec;
  779. /* This many records we must leave behind */
  780. middle = (page_get_n_recs(page) + 2) / 2;
  781. count = 0;
  782. for (i = 0;; i++) {
  783. slot = page_dir_get_nth_slot(page, i);
  784. n_owned = page_dir_slot_get_n_owned(slot);
  785. if (count + n_owned > middle) {
  786. break;
  787. } else {
  788. count += n_owned;
  789. }
  790. }
  791. ut_ad(i > 0);
  792. slot = page_dir_get_nth_slot(page, i - 1);
  793. rec = page_dir_slot_get_rec(slot);
  794. rec = page_rec_get_next(rec);
  795. /* There are now count records behind rec */
  796. for (i = 0; i < middle - count; i++) {
  797. rec = page_rec_get_next(rec);
  798. }
  799. return(rec);
  800. }
  801. /*******************************************************************
  802. Returns the number of records before the given record in chain.
  803. The number includes infimum and supremum records. */
  804. ulint
  805. page_rec_get_n_recs_before(
  806. /*=======================*/
  807. /* out: number of records */
  808. rec_t* rec) /* in: the physical record */
  809. {
  810. page_dir_slot_t* slot;
  811. rec_t* slot_rec;
  812. page_t* page;
  813. ulint i;
  814. lint n = 0;
  815. ut_ad(page_rec_check(rec));
  816. page = buf_frame_align(rec);
  817. while (rec_get_n_owned(rec) == 0) {
  818. rec = page_rec_get_next(rec);
  819. n--;
  820. }
  821. for (i = 0; ; i++) {
  822. slot = page_dir_get_nth_slot(page, i);
  823. slot_rec = page_dir_slot_get_rec(slot);
  824. n += rec_get_n_owned(slot_rec);
  825. if (rec == slot_rec) {
  826. break;
  827. }
  828. }
  829. n--;
  830. ut_ad(n >= 0);
  831. return((ulint) n);
  832. }
  833. /****************************************************************
  834. Prints record contents including the data relevant only in
  835. the index page context. */
  836.  
  837. void
  838. page_rec_print(
  839. /*===========*/
  840. rec_t* rec)
  841. {
  842. rec_print(stderr, rec);
  843. fprintf(stderr,
  844.       "            n_owned: %lu; heap_no: %lu; next rec: %lun",
  845. (ulong) rec_get_n_owned(rec),
  846. (ulong) rec_get_heap_no(rec),
  847. (ulong) rec_get_next_offs(rec));
  848. page_rec_check(rec);
  849. rec_validate(rec);
  850. }
  851. /*******************************************************************
  852. This is used to print the contents of the directory for
  853. debugging purposes. */
  854. void
  855. page_dir_print(
  856. /*===========*/
  857. page_t* page, /* in: index page */
  858. ulint pr_n) /* in: print n first and n last entries */
  859. {
  860. ulint n;
  861. ulint i;
  862. page_dir_slot_t* slot;
  863. n = page_dir_get_n_slots(page);
  864. fprintf(stderr, "--------------------------------n"
  865. "PAGE DIRECTORYn"
  866. "Page address %pn"
  867. "Directory stack top at offs: %lu; number of slots: %lun",
  868. page, (ulong)(page_dir_get_nth_slot(page, n - 1) - page), (ulong) n);
  869. for (i = 0; i < n; i++) {
  870. slot = page_dir_get_nth_slot(page, i);
  871. if ((i == pr_n) && (i < n - pr_n)) {
  872. fputs("    ...   n", stderr);
  873. }
  874.      if ((i < pr_n) || (i >= n - pr_n)) {
  875. fprintf(stderr,
  876.        "Contents of slot: %lu: n_owned: %lu, rec offs: %lun",
  877. (ulong) i, (ulong) page_dir_slot_get_n_owned(slot),
  878. (ulong)(page_dir_slot_get_rec(slot) - page));
  879.      }
  880. }
  881. fprintf(stderr, "Total of %lu recordsn"
  882. "--------------------------------n",
  883. (ulong) (2 + page_get_n_recs(page)));
  884. }
  885. /*******************************************************************
  886. This is used to print the contents of the page record list for
  887. debugging purposes. */
  888. void
  889. page_print_list(
  890. /*============*/
  891. page_t* page, /* in: index page */
  892. ulint pr_n) /* in: print n first and n last entries */
  893. {
  894. page_cur_t cur;
  895. ulint count;
  896. ulint n_recs;
  897. fprintf(stderr,
  898. "--------------------------------n"
  899. "PAGE RECORD LISTn"
  900. "Page address %pn", page);
  901. n_recs = page_get_n_recs(page);
  902. page_cur_set_before_first(page, &cur);
  903. count = 0;
  904. for (;;) {
  905. page_rec_print(cur.rec);
  906. if (count == pr_n) {
  907. break;
  908. }
  909. if (page_cur_is_after_last(&cur)) {
  910. break;
  911. }
  912. page_cur_move_to_next(&cur);
  913. count++;
  914. }
  915. if (n_recs > 2 * pr_n) {
  916. fputs(" ... n", stderr);
  917. }
  918. while (!page_cur_is_after_last(&cur)) {
  919. page_cur_move_to_next(&cur);
  920. if (count + pr_n >= n_recs) {
  921. page_rec_print(cur.rec);
  922. }
  923. count++;
  924. }
  925. fprintf(stderr,
  926. "Total of %lu records n"
  927. "--------------------------------n",
  928. (ulong) (count + 1));
  929. }
  930. /*******************************************************************
  931. Prints the info in a page header. */
  932. void
  933. page_header_print(
  934. /*==============*/
  935. page_t* page)
  936. {
  937. fprintf(stderr,
  938. "--------------------------------n"
  939. "PAGE HEADER INFOn"
  940. "Page address %p, n records %lun"
  941. "n dir slots %lu, heap top %lun"
  942. "Page n heap %lu, free %lu, garbage %lun"
  943. "Page last insert %lu, direction %lu, n direction %lun",
  944. page, (ulong) page_header_get_field(page, PAGE_N_RECS),
  945. (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
  946. (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
  947. (ulong) page_header_get_field(page, PAGE_N_HEAP),
  948. (ulong) page_header_get_field(page, PAGE_FREE),
  949. (ulong) page_header_get_field(page, PAGE_GARBAGE),
  950. (ulong) page_header_get_field(page, PAGE_LAST_INSERT),
  951. (ulong) page_header_get_field(page, PAGE_DIRECTION),
  952. (ulong) page_header_get_field(page, PAGE_N_DIRECTION));
  953. }
  954. /*******************************************************************
  955. This is used to print the contents of the page for
  956. debugging purposes. */
  957. void
  958. page_print(
  959. /*======*/
  960. page_t* page, /* in: index page */
  961. ulint dn, /* in: print dn first and last entries in directory */
  962. ulint rn) /* in: print rn first and last records on page */
  963. {
  964. page_header_print(page);
  965. page_dir_print(page, dn);
  966. page_print_list(page, rn);
  967. }
  968. /*******************************************************************
  969. The following is used to validate a record on a page. This function
  970. differs from rec_validate as it can also check the n_owned field and
  971. the heap_no field. */
  972. ibool
  973. page_rec_validate(
  974. /*==============*/
  975. /* out: TRUE if ok */
  976. rec_t*  rec) /* in: record on the page */
  977. {
  978. ulint n_owned;
  979. ulint heap_no;
  980. page_t* page;
  981. page = buf_frame_align(rec);
  982. page_rec_check(rec);
  983. rec_validate(rec);
  984. n_owned = rec_get_n_owned(rec);
  985. heap_no = rec_get_heap_no(rec);
  986. if (!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED)) {
  987. fprintf(stderr,
  988. "InnoDB: Dir slot of rec %lu, n owned too big %lun",
  989. (ulong)(rec - page), (ulong) n_owned);
  990. return(FALSE);
  991. }
  992. if (!(heap_no < page_header_get_field(page, PAGE_N_HEAP))) {
  993. fprintf(stderr,
  994. "InnoDB: Heap no of rec %lu too big %lu %lun",
  995. (ulong)(rec - page), (ulong) heap_no,
  996. (ulong) page_header_get_field(page, PAGE_N_HEAP));
  997. return(FALSE);
  998. }
  999. return(TRUE);
  1000. }
  1001. /*******************************************************************
  1002. Checks that the first directory slot points to the infimum record and
  1003. the last to the supremum. This function is intended to track if the
  1004. bug fixed in 4.0.14 has caused corruption to users' databases. */
  1005. void
  1006. page_check_dir(
  1007. /*===========*/
  1008. page_t* page) /* in: index page */
  1009. {
  1010. ulint n_slots;
  1011. n_slots = page_dir_get_n_slots(page);
  1012. if (page_dir_slot_get_rec(page_dir_get_nth_slot(page, 0))
  1013.     != page_get_infimum_rec(page)) {
  1014.         fprintf(stderr,
  1015. "InnoDB: Page directory corruption: supremum not pointed ton");
  1016. buf_page_print(page);
  1017.         }
  1018. if (page_dir_slot_get_rec(page_dir_get_nth_slot(page, n_slots - 1))
  1019.     != page_get_supremum_rec(page)) {
  1020.         fprintf(stderr,
  1021. "InnoDB: Page directory corruption: supremum not pointed ton");
  1022. buf_page_print(page);
  1023.         }
  1024. }
  1025. /*******************************************************************
  1026. This function checks the consistency of an index page when we do not
  1027. know the index. This is also resilient so that this should never crash
  1028. even if the page is total garbage. */
  1029. ibool
  1030. page_simple_validate(
  1031. /*=================*/
  1032. /* out: TRUE if ok */
  1033. page_t* page) /* in: index page */
  1034. {
  1035. page_cur_t  cur;
  1036. page_dir_slot_t* slot;
  1037. ulint slot_no;
  1038. ulint n_slots;
  1039. rec_t* rec;
  1040. byte* rec_heap_top;
  1041. ulint count;
  1042. ulint own_count;
  1043. ibool ret = FALSE;
  1044. /* Check first that the record heap and the directory do not
  1045. overlap. */
  1046. n_slots = page_dir_get_n_slots(page);
  1047. if (n_slots > UNIV_PAGE_SIZE / 4) {
  1048. fprintf(stderr,
  1049. "InnoDB: Nonsensical number %lu of page dir slotsn", (ulong) n_slots);
  1050. goto func_exit;
  1051. }
  1052. rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
  1053. if (rec_heap_top > page_dir_get_nth_slot(page, n_slots - 1)) {
  1054. fprintf(stderr,
  1055.     "InnoDB: Record heap and dir overlap on a page, heap top %lu, dir %lun",
  1056.         (ulong)(page_header_get_ptr(page, PAGE_HEAP_TOP) - page),
  1057.         (ulong)(page_dir_get_nth_slot(page, n_slots - 1) - page));
  1058.         goto func_exit;
  1059.         }
  1060. /* Validate the record list in a loop checking also that it is
  1061. consistent with the page record directory. */
  1062. count = 0;
  1063. own_count = 1;
  1064. slot_no = 0;
  1065. slot = page_dir_get_nth_slot(page, slot_no);
  1066. page_cur_set_before_first(page, &cur);
  1067. for (;;) {
  1068. rec = (&cur)->rec;
  1069. if (rec > rec_heap_top) {
  1070. fprintf(stderr,
  1071. "InnoDB: Record %lu is above rec heap top %lun",
  1072. (ulong)(rec - page), (ulong)(rec_heap_top - page));
  1073. goto func_exit;
  1074. }
  1075. if (rec_get_n_owned(rec) != 0) {
  1076. /* This is a record pointed to by a dir slot */
  1077. if (rec_get_n_owned(rec) != own_count) {
  1078. fprintf(stderr,
  1079. "InnoDB: Wrong owned count %lu, %lu, rec %lun",
  1080. (ulong) rec_get_n_owned(rec),
  1081. (ulong) own_count,
  1082. (ulong)(rec - page));
  1083. goto func_exit;
  1084. }
  1085. if (page_dir_slot_get_rec(slot) != rec) {
  1086. fprintf(stderr,
  1087. "InnoDB: Dir slot does not point to right rec %lun",
  1088. (ulong)(rec - page));
  1089. goto func_exit;
  1090. }
  1091. own_count = 0;
  1092. if (!page_cur_is_after_last(&cur)) {
  1093. slot_no++;
  1094. slot = page_dir_get_nth_slot(page, slot_no);
  1095. }
  1096. }
  1097. if (page_cur_is_after_last(&cur)) {
  1098. break;
  1099. }
  1100. if (rec_get_next_offs(rec) < FIL_PAGE_DATA
  1101. || rec_get_next_offs(rec) >= UNIV_PAGE_SIZE) {
  1102. fprintf(stderr,
  1103. "InnoDB: Next record offset nonsensical %lu for rec %lun",
  1104.   (ulong) rec_get_next_offs(rec),
  1105.   (ulong)(rec - page));
  1106. goto func_exit;
  1107. }
  1108. count++;
  1109. if (count > UNIV_PAGE_SIZE) {
  1110. fprintf(stderr,
  1111. "InnoDB: Page record list appears to be circular %lun",
  1112. (ulong) count);
  1113. goto func_exit;
  1114. }
  1115. page_cur_move_to_next(&cur);
  1116. own_count++;
  1117. }
  1118. if (rec_get_n_owned(rec) == 0) {
  1119. fprintf(stderr, "InnoDB: n owned is zero in a supremum recn");
  1120. goto func_exit;
  1121. }
  1122. if (slot_no != n_slots - 1) {
  1123. fprintf(stderr, "InnoDB: n slots wrong %lu, %lun",
  1124. (ulong) slot_no, (ulong) (n_slots - 1));
  1125. goto func_exit;
  1126. }
  1127. if (page_header_get_field(page, PAGE_N_RECS) + 2 != count + 1) {
  1128. fprintf(stderr, "InnoDB: n recs wrong %lu %lun",
  1129. (ulong) page_header_get_field(page, PAGE_N_RECS) + 2,
  1130. (ulong) (count + 1));
  1131. goto func_exit;
  1132. }
  1133. /* Check then the free list */
  1134. rec = page_header_get_ptr(page, PAGE_FREE);
  1135. while (rec != NULL) {
  1136. if (rec < page + FIL_PAGE_DATA
  1137. || rec >= page + UNIV_PAGE_SIZE) {
  1138. fprintf(stderr,
  1139. "InnoDB: Free list record has a nonsensical offset %lun",
  1140. (ulong)(rec - page));
  1141. goto func_exit;
  1142. }
  1143. if (rec > rec_heap_top) {
  1144. fprintf(stderr,
  1145. "InnoDB: Free list record %lu is above rec heap top %lun",
  1146. (ulong)(rec - page), (ulong)(rec_heap_top - page));
  1147. goto func_exit;
  1148. }
  1149. count++;
  1150. if (count > UNIV_PAGE_SIZE) {
  1151. fprintf(stderr,
  1152. "InnoDB: Page free list appears to be circular %lun",
  1153.     (ulong) count);
  1154. goto func_exit;
  1155. }
  1156. rec = page_rec_get_next(rec);
  1157. }
  1158. if (page_header_get_field(page, PAGE_N_HEAP) != count + 1) {
  1159. fprintf(stderr, "InnoDB: N heap is wrong %lu, %lun",
  1160. (ulong) page_header_get_field(page, PAGE_N_HEAP),
  1161. (ulong) (count + 1));
  1162. goto func_exit;
  1163. }
  1164. ret = TRUE;
  1165. func_exit:
  1166. return(ret);   
  1167. }
  1168. /*******************************************************************
  1169. This function checks the consistency of an index page. */
  1170. ibool
  1171. page_validate(
  1172. /*==========*/
  1173. /* out: TRUE if ok */
  1174. page_t* page, /* in: index page */
  1175. dict_index_t* index) /* in: data dictionary index containing
  1176. the page record type definition */
  1177. {
  1178. page_dir_slot_t* slot;
  1179. mem_heap_t* heap;
  1180. page_cur_t  cur;
  1181. byte* buf;
  1182. ulint count;
  1183. ulint own_count;
  1184. ulint slot_no;
  1185. ulint data_size;
  1186. rec_t* rec;
  1187. rec_t* old_rec = NULL;
  1188. ulint offs;
  1189. ulint n_slots;
  1190. ibool ret = FALSE;
  1191. ulint i;
  1192. if (!page_simple_validate(page)) {
  1193. goto func_exit2;
  1194. }
  1195. heap = mem_heap_create(UNIV_PAGE_SIZE);
  1196. /* The following buffer is used to check that the
  1197. records in the page record heap do not overlap */
  1198. buf = mem_heap_alloc(heap, UNIV_PAGE_SIZE);
  1199. for (i = 0; i < UNIV_PAGE_SIZE; i++) {
  1200. buf[i] = 0;
  1201. }
  1202. /* Check first that the record heap and the directory do not
  1203. overlap. */
  1204. n_slots = page_dir_get_n_slots(page);
  1205. if (!(page_header_get_ptr(page, PAGE_HEAP_TOP) <=
  1206. page_dir_get_nth_slot(page, n_slots - 1))) {
  1207. fputs("InnoDB: Record heap and dir overlap on a page ",
  1208. stderr);
  1209. dict_index_name_print(stderr, NULL, index);
  1210. fprintf(stderr, ", %p, %pn",
  1211. page_header_get_ptr(page, PAGE_HEAP_TOP),
  1212. page_dir_get_nth_slot(page, n_slots - 1));
  1213.         goto func_exit;
  1214.         }
  1215. /* Validate the record list in a loop checking also that
  1216. it is consistent with the directory. */
  1217. count = 0;
  1218. data_size = 0;
  1219. own_count = 1;
  1220. slot_no = 0;
  1221. slot = page_dir_get_nth_slot(page, slot_no);
  1222. page_cur_set_before_first(page, &cur);
  1223. for (;;) {
  1224. rec = cur.rec;
  1225. if (!page_rec_validate(rec)) {
  1226. goto func_exit;
  1227. }
  1228. /* Check that the records are in the ascending order */
  1229. if ((count >= 2) && (!page_cur_is_after_last(&cur))) {
  1230. if (!(1 == cmp_rec_rec(rec, old_rec, index))) {
  1231. fprintf(stderr,
  1232. "InnoDB: Records in wrong order on page %lu",
  1233. (ulong) buf_frame_get_page_no(page));
  1234. dict_index_name_print(stderr, NULL, index);
  1235. fputs("nInnoDB: previous record ", stderr);
  1236. rec_print(stderr, old_rec);
  1237. fputs("nInnoDB: record ", stderr);
  1238. rec_print(stderr, rec);
  1239. putc('n', stderr);
  1240. goto func_exit;
  1241. }
  1242. }
  1243. if ((rec != page_get_supremum_rec(page))
  1244.     && (rec != page_get_infimum_rec(page))) {
  1245. data_size += rec_get_size(rec);
  1246. }
  1247. offs = rec_get_start(rec) - page;
  1248. for (i = 0; i < rec_get_size(rec); i++) {
  1249. if (!buf[offs + i] == 0) {
  1250. /* No other record may overlap this */
  1251. fputs("InnoDB: Record overlaps anothern",
  1252. stderr);
  1253. goto func_exit;
  1254. }
  1255. buf[offs + i] = 1;
  1256. }
  1257. if (rec_get_n_owned(rec) != 0) {
  1258. /* This is a record pointed to by a dir slot */
  1259. if (rec_get_n_owned(rec) != own_count) {
  1260. fprintf(stderr,
  1261. "InnoDB: Wrong owned count %lu, %lun",
  1262. (ulong) rec_get_n_owned(rec),
  1263. (ulong) own_count);
  1264. goto func_exit;
  1265. }
  1266. if (page_dir_slot_get_rec(slot) != rec) {
  1267. fputs(
  1268. "InnoDB: Dir slot does not point to right recn",
  1269. stderr);
  1270. goto func_exit;
  1271. }
  1272. page_dir_slot_check(slot);
  1273. own_count = 0;
  1274. if (!page_cur_is_after_last(&cur)) {
  1275. slot_no++;
  1276. slot = page_dir_get_nth_slot(page, slot_no);
  1277. }
  1278. }
  1279. if (page_cur_is_after_last(&cur)) {
  1280. break;
  1281. }
  1282. if (rec_get_next_offs(rec) < FIL_PAGE_DATA
  1283. || rec_get_next_offs(rec) >= UNIV_PAGE_SIZE) {
  1284. fprintf(stderr,
  1285. "InnoDB: Next record offset wrong %lun",
  1286. (ulong) rec_get_next_offs(rec));
  1287. goto func_exit;
  1288. }
  1289. count++;
  1290. page_cur_move_to_next(&cur);
  1291. own_count++;
  1292. old_rec = rec;
  1293. }
  1294. if (rec_get_n_owned(rec) == 0) {
  1295. fputs("InnoDB: n owned is zeron", stderr);
  1296. goto func_exit;
  1297. }
  1298. if (slot_no != n_slots - 1) {
  1299. fprintf(stderr, "InnoDB: n slots wrong %lu %lun",
  1300. (ulong) slot_no, (ulong) (n_slots - 1));
  1301. goto func_exit;
  1302. }
  1303. if (page_header_get_field(page, PAGE_N_RECS) + 2 != count + 1) {
  1304. fprintf(stderr, "InnoDB: n recs wrong %lu %lun",
  1305. (ulong) page_header_get_field(page, PAGE_N_RECS) + 2,
  1306. (ulong) (count + 1));
  1307. goto func_exit;
  1308. }
  1309. if (data_size != page_get_data_size(page)) {
  1310. fprintf(stderr,
  1311. "InnoDB: Summed data size %lu, returned by func %lun",
  1312. (ulong) data_size, (ulong) page_get_data_size(page));
  1313. goto func_exit;
  1314. }
  1315. /* Check then the free list */
  1316. rec = page_header_get_ptr(page, PAGE_FREE);
  1317. while (rec != NULL) {
  1318. if (!page_rec_validate(rec)) {
  1319. goto func_exit;
  1320. }
  1321. count++;
  1322. offs = rec_get_start(rec) - page;
  1323. for (i = 0; i < rec_get_size(rec); i++) {
  1324. if (buf[offs + i] != 0) {
  1325. fputs(
  1326. "InnoDB: Record overlaps another in free listn", stderr);
  1327. goto func_exit;
  1328. }
  1329. buf[offs + i] = 1;
  1330. }
  1331. rec = page_rec_get_next(rec);
  1332. }
  1333. if (page_header_get_field(page, PAGE_N_HEAP) != count + 1) {
  1334. fprintf(stderr, "InnoDB: N heap is wrong %lu %lun",
  1335. (ulong) page_header_get_field(page, PAGE_N_HEAP),
  1336. (ulong) count + 1);
  1337. goto func_exit;
  1338. }
  1339. ret = TRUE;
  1340. func_exit:
  1341. mem_heap_free(heap);
  1342. if (ret == FALSE) {
  1343. func_exit2:
  1344. fprintf(stderr, "InnoDB: Apparent corruption in page %lu in ",
  1345. (ulong) buf_frame_get_page_no(page));
  1346. dict_index_name_print(stderr, NULL, index);
  1347. putc('n', stderr);
  1348. buf_page_print(page);
  1349. }
  1350. return(ret);   
  1351. }
  1352. /*******************************************************************
  1353. Looks in the page record list for a record with the given heap number. */
  1354. rec_t*
  1355. page_find_rec_with_heap_no(
  1356. /*=======================*/
  1357. /* out: record, NULL if not found */
  1358. page_t* page, /* in: index page */
  1359. ulint heap_no)/* in: heap number */
  1360. {
  1361. page_cur_t cur;
  1362. page_cur_set_before_first(page, &cur);
  1363. for (;;) {
  1364. if (rec_get_heap_no(cur.rec) == heap_no) {
  1365. return(cur.rec);
  1366. }
  1367. if (page_cur_is_after_last(&cur)) {
  1368. return(NULL);
  1369. }
  1370. page_cur_move_to_next(&cur);
  1371. }
  1372. }