page0page.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:34k
源码类别:

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. /* A cached template page used in page_create */
  17. page_t* page_template = NULL;
  18. /* THE INDEX PAGE
  19. ==============
  20. The index page consists of a page header which contains the page's
  21. id and other information. On top of it are the the index records
  22. in a heap linked into a one way linear list according to alphabetic order.
  23. Just below page end is an array of pointers which we call page directory,
  24. to about every sixth record in the list. The pointers are placed in
  25. the directory in the alphabetical order of the records pointed to,
  26. enabling us to make binary search using the array. Each slot n:o I
  27. in the directory points to a record, where a 4-bit field contains a count
  28. of those records which are in the linear list between pointer I and 
  29. the pointer I - 1 in the directory, including the record
  30. pointed to by pointer I and not including the record pointed to by I - 1.
  31. We say that the record pointed to by slot I, or that slot I, owns
  32. these records. The count is always kept in the range 4 to 8, with
  33. the exception that it is 1 for the first slot, and 1--8 for the second slot.  
  34. An essentially binary search can be performed in the list of index
  35. records, like we could do if we had pointer to every record in the
  36. page directory. The data structure is, however, more efficient when
  37. we are doing inserts, because most inserts are just pushed on a heap.
  38. Only every 8th insert requires block move in the directory pointer
  39. table, which itself is quite small. A record is deleted from the page
  40. by just taking it off the linear list and updating the number of owned
  41. records-field of the record which owns it, and updating the page directory,
  42. if necessary. A special case is the one when the record owns itself.
  43. Because the overhead of inserts is so small, we may also increase the
  44. page size from the projected default of 8 kB to 64 kB without too
  45. much loss of efficiency in inserts. Bigger page becomes actual
  46. when the disk transfer rate compared to seek and latency time rises.
  47. On the present system, the page size is set so that the page transfer
  48. time (3 ms) is 20 % of the disk random access time (15 ms).
  49. When the page is split, merged, or becomes full but contains deleted
  50. records, we have to reorganize the page.
  51. Assuming a page size of 8 kB, a typical index page of a secondary
  52. index contains 300 index entries, and the size of the page directory
  53. is 50 x 4 bytes = 200 bytes. */
  54. /*****************************************************************
  55. Sets the max trx id field value. */
  56. void
  57. page_set_max_trx_id(
  58. /*================*/
  59. page_t* page, /* in: page */
  60. dulint trx_id) /* in: transaction id */
  61. {
  62. buf_block_t* block;
  63. ut_ad(page);
  64. block = buf_block_align(page);
  65. if (block->is_hashed) {
  66. rw_lock_x_lock(&btr_search_latch);
  67. }
  68. /* It is not necessary to write this change to the redo log, as
  69. during a database recovery we assume that the max trx id of every
  70. page is the maximum trx id assigned before the crash. */
  71. mach_write_to_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID, trx_id);
  72. if (block->is_hashed) {
  73. rw_lock_x_unlock(&btr_search_latch);
  74. }
  75. }
  76. /****************************************************************
  77. Allocates a block of memory from an index page. */
  78. byte*
  79. page_mem_alloc(
  80. /*===========*/
  81. /* out: pointer to start of allocated 
  82. buffer, or NULL if allocation fails */
  83. page_t* page, /* in: index page */
  84. ulint need, /* in: number of bytes needed */
  85. ulint* heap_no)/* out: this contains the heap number
  86. of the allocated record if allocation succeeds */
  87. {
  88. rec_t* rec;
  89. byte* block;
  90. ulint avl_space;
  91. ulint garbage;
  92. ut_ad(page && heap_no);
  93. /* If there are records in the free list, look if the first is
  94. big enough */
  95. rec = page_header_get_ptr(page, PAGE_FREE);
  96. if (rec && (rec_get_size(rec) >= need)) {
  97. page_header_set_ptr(page, PAGE_FREE, page_rec_get_next(rec));
  98. garbage = page_header_get_field(page, PAGE_GARBAGE);
  99. ut_ad(garbage >= need);
  100. page_header_set_field(page, PAGE_GARBAGE, garbage - need);
  101. *heap_no = rec_get_heap_no(rec);
  102. return(rec_get_start(rec));
  103. }
  104. /* Could not find space from the free list, try top of heap */
  105. avl_space = page_get_max_insert_size(page, 1);
  106. if (avl_space >= need) {
  107. block = page_header_get_ptr(page, PAGE_HEAP_TOP);
  108. page_header_set_ptr(page, PAGE_HEAP_TOP, block + need);
  109. *heap_no = page_header_get_field(page, PAGE_N_HEAP);
  110. page_header_set_field(page, PAGE_N_HEAP, 1 + *heap_no);
  111. return(block);
  112. }
  113. return(NULL);
  114. }
  115. /**************************************************************
  116. Writes a log record of page creation. */
  117. UNIV_INLINE
  118. void
  119. page_create_write_log(
  120. /*==================*/
  121. buf_frame_t* frame, /* in: a buffer frame where the page is
  122. created */
  123. mtr_t* mtr) /* in: mini-transaction handle */
  124. {
  125. mlog_write_initial_log_record(frame, MLOG_PAGE_CREATE, mtr);
  126. }
  127. /***************************************************************
  128. Parses a redo log record of creating a page. */
  129. byte*
  130. page_parse_create(
  131. /*==============*/
  132. /* out: end of log record or NULL */
  133. byte* ptr, /* in: buffer */
  134. byte* end_ptr,/* in: buffer end */
  135. page_t* page, /* in: page or NULL */
  136. mtr_t* mtr) /* in: mtr or NULL */
  137. {
  138. ut_ad(ptr && end_ptr);
  139. /* The record is empty, except for the record initial part */
  140. if (page) {
  141. page_create(page, mtr);
  142. }
  143. return(ptr);
  144. }
  145. /**************************************************************
  146. The index page creation function. */
  147. page_t* 
  148. page_create(
  149. /*========*/
  150. /* out: pointer to the page */
  151. buf_frame_t* frame, /* in: a buffer frame where the page is
  152. created */
  153. mtr_t* mtr) /* in: mini-transaction handle */
  154. {
  155. page_dir_slot_t* slot;
  156. mem_heap_t* heap;
  157. dtuple_t* tuple;
  158. dfield_t* field;
  159. byte* heap_top;
  160. rec_t* infimum_rec;
  161. rec_t* supremum_rec;
  162. page_t* page;
  163. ut_ad(frame && mtr);
  164. ut_ad(PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE
  165.       <= PAGE_DATA);
  166. ut_ad(PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE
  167.       <= PAGE_DATA);
  168. /* 1. INCREMENT MODIFY CLOCK */
  169. buf_frame_modify_clock_inc(frame);
  170. /* 2. WRITE LOG INFORMATION */
  171. page_create_write_log(frame, mtr);
  172. page = frame;
  173. fil_page_set_type(page, FIL_PAGE_INDEX);
  174. /* If we have a page template, copy the page structure from there */
  175. if (page_template) {
  176. ut_memcpy(page + PAGE_HEADER,
  177.   page_template + PAGE_HEADER, PAGE_HEADER_PRIV_END);
  178. ut_memcpy(page + PAGE_DATA,
  179.   page_template + PAGE_DATA,
  180.   PAGE_SUPREMUM_END - PAGE_DATA);
  181. ut_memcpy(page + UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START,
  182. page_template + UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START,
  183.    PAGE_EMPTY_DIR_START - PAGE_DIR);
  184. return(frame);
  185. }
  186. heap = mem_heap_create(200);
  187. /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
  188. /* Create first a data tuple for infimum record */
  189. tuple = dtuple_create(heap, 1);
  190. field = dtuple_get_nth_field(tuple, 0);
  191. dfield_set_data(field, "infimum", strlen("infimum") + 1);
  192. dtype_set(dfield_get_type(field), DATA_VARCHAR, DATA_ENGLISH, 20, 0);
  193. /* Set the corresponding physical record to its place in the page
  194. record heap */
  195. heap_top = page + PAGE_DATA;
  196. infimum_rec = rec_convert_dtuple_to_rec(heap_top, tuple);
  197. ut_ad(infimum_rec == page + PAGE_INFIMUM);
  198. rec_set_n_owned(infimum_rec, 1);
  199. rec_set_heap_no(infimum_rec, 0);
  200. heap_top = rec_get_end(infimum_rec);
  201. /* Create then a tuple for supremum */
  202. tuple = dtuple_create(heap, 1);
  203. field = dtuple_get_nth_field(tuple, 0);
  204. dfield_set_data(field, "supremum", strlen("supremum") + 1);
  205. dtype_set(dfield_get_type(field), DATA_VARCHAR, DATA_ENGLISH, 20, 0);
  206. supremum_rec = rec_convert_dtuple_to_rec(heap_top, tuple);
  207. ut_ad(supremum_rec == page + PAGE_SUPREMUM);
  208. rec_set_n_owned(supremum_rec, 1);
  209. rec_set_heap_no(supremum_rec, 1);
  210. heap_top = rec_get_end(supremum_rec);
  211. ut_ad(heap_top == page + PAGE_SUPREMUM_END);
  212. mem_heap_free(heap);
  213. /* 4. INITIALIZE THE PAGE HEADER */
  214. page_header_set_field(page, PAGE_N_DIR_SLOTS, 2);
  215. page_header_set_ptr(page, PAGE_HEAP_TOP, heap_top);
  216. page_header_set_field(page, PAGE_N_HEAP, 2);
  217. page_header_set_ptr(page, PAGE_FREE, NULL);
  218. page_header_set_field(page, PAGE_GARBAGE, 0);
  219. page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
  220. page_header_set_field(page, PAGE_N_RECS, 0);
  221. page_set_max_trx_id(page, ut_dulint_zero);
  222. /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
  223. /* Set the slots to point to infimum and supremum. */
  224. slot = page_dir_get_nth_slot(page, 0);
  225. page_dir_slot_set_rec(slot, infimum_rec);
  226. slot = page_dir_get_nth_slot(page, 1);
  227. page_dir_slot_set_rec(slot, supremum_rec);
  228. /* Set next pointers in infimum and supremum */
  229. rec_set_next_offs(infimum_rec, (ulint)(supremum_rec - page)); 
  230. rec_set_next_offs(supremum_rec, 0);
  231. if (page_template == NULL) {
  232. page_template = mem_alloc(UNIV_PAGE_SIZE);
  233. ut_memcpy(page_template, page, UNIV_PAGE_SIZE);
  234. }
  235. return(page);
  236. }
  237. /*****************************************************************
  238. Differs from page_copy_rec_list_end, because this function does not
  239. touch the lock table and max trx id on page. */
  240. void
  241. page_copy_rec_list_end_no_locks(
  242. /*============================*/
  243. page_t* new_page, /* in: index page to copy to */
  244. page_t* page, /* in: index page */
  245. rec_t* rec, /* in: record on page */
  246. mtr_t* mtr) /* in: mtr */
  247. {
  248. page_cur_t cur1;
  249. page_cur_t cur2;
  250. rec_t* sup;
  251. page_cur_position(rec, &cur1);
  252. if (page_cur_is_before_first(&cur1)) {
  253. page_cur_move_to_next(&cur1);
  254. }
  255. page_cur_set_before_first(new_page, &cur2);
  256. /* Copy records from the original page to the new page */
  257. sup = page_get_supremum_rec(page);
  258. while (sup != page_cur_get_rec(&cur1)) {
  259. ut_a(
  260. page_cur_rec_insert(&cur2, page_cur_get_rec(&cur1), mtr));
  261. page_cur_move_to_next(&cur1);
  262. page_cur_move_to_next(&cur2);
  263. }
  264. }
  265. /*****************************************************************
  266. Copies records from page to new_page, from a given record onward,
  267. including that record. Infimum and supremum records are not copied.
  268. The records are copied to the start of the record list on new_page. */
  269. void
  270. page_copy_rec_list_end(
  271. /*===================*/
  272. page_t* new_page, /* in: index page to copy to */
  273. page_t* page, /* in: index page */
  274. rec_t* rec, /* in: record on page */
  275. mtr_t* mtr) /* in: mtr */
  276. {
  277. if (page_header_get_field(new_page, PAGE_N_HEAP) == 2) {
  278. page_copy_rec_list_end_to_created_page(new_page, page, rec,
  279. mtr);
  280. } else {
  281. page_copy_rec_list_end_no_locks(new_page, page, rec, mtr);
  282. }
  283. /* Update the lock table, MAX_TRX_ID, and possible hash index */
  284. lock_move_rec_list_end(new_page, page, rec);
  285. page_update_max_trx_id(new_page, page_get_max_trx_id(page));
  286. btr_search_move_or_delete_hash_entries(new_page, page);
  287. }
  288. /*****************************************************************
  289. Copies records from page to new_page, up to the given record,
  290. NOT including that record. Infimum and supremum records are not copied.
  291. The records are copied to the end of the record list on new_page. */
  292. void
  293. page_copy_rec_list_start(
  294. /*=====================*/
  295. page_t* new_page, /* in: index page to copy to */
  296. page_t* page, /* in: index page */
  297. rec_t* rec, /* in: record on page */
  298. mtr_t* mtr) /* in: mtr */
  299. {
  300. page_cur_t cur1;
  301. page_cur_t cur2;
  302. rec_t* old_end;
  303. page_cur_set_before_first(page, &cur1);
  304. if (rec == page_cur_get_rec(&cur1)) {
  305. return;
  306. }
  307. page_cur_move_to_next(&cur1);
  308. page_cur_set_after_last(new_page, &cur2);
  309. page_cur_move_to_prev(&cur2);
  310. old_end = page_cur_get_rec(&cur2);
  311. /* Copy records from the original page to the new page */
  312. while (page_cur_get_rec(&cur1) != rec) {
  313. ut_a(
  314. page_cur_rec_insert(&cur2, page_cur_get_rec(&cur1), mtr));
  315. page_cur_move_to_next(&cur1);
  316. page_cur_move_to_next(&cur2);
  317. }
  318. /* Update the lock table, MAX_TRX_ID, and possible hash index */
  319. lock_move_rec_list_start(new_page, page, rec, old_end);
  320. page_update_max_trx_id(new_page, page_get_max_trx_id(page));
  321. btr_search_move_or_delete_hash_entries(new_page, page);
  322. }
  323. /**************************************************************
  324. Writes a log record of a record list end or start deletion. */
  325. UNIV_INLINE
  326. void
  327. page_delete_rec_list_write_log(
  328. /*===========================*/
  329. page_t* page, /* in: index page */
  330. rec_t* rec, /* in: record on page */
  331. byte type, /* in: operation type: MLOG_LIST_END_DELETE, ... */
  332. mtr_t* mtr) /* in: mtr */
  333. {
  334. ut_ad((type == MLOG_LIST_END_DELETE)
  335. || (type == MLOG_LIST_START_DELETE));
  336. mlog_write_initial_log_record(page, type, mtr);
  337. /* Write the parameter as a 2-byte ulint */
  338. mlog_catenate_ulint(mtr, rec - page, MLOG_2BYTES);
  339. }
  340. /**************************************************************
  341. Parses a log record of a record list end or start deletion. */
  342. byte*
  343. page_parse_delete_rec_list(
  344. /*=======================*/
  345. /* out: end of log record or NULL */
  346. byte type, /* in: MLOG_LIST_END_DELETE or MLOG_LIST_START_DELETE */
  347. byte* ptr, /* in: buffer */
  348. byte* end_ptr,/* in: buffer end */
  349. page_t* page, /* in: page or NULL */
  350. mtr_t* mtr) /* in: mtr or NULL */
  351. {
  352. ulint offset;
  353. ut_ad((type == MLOG_LIST_END_DELETE)
  354.       || (type == MLOG_LIST_START_DELETE)); 
  355.       
  356. /* Read the record offset as a 2-byte ulint */
  357. if (end_ptr < ptr + 2) {
  358. return(NULL);
  359. }
  360. offset = mach_read_from_2(ptr);
  361. ptr += 2;
  362. if (!page) {
  363. return(ptr);
  364. }
  365. if (type == MLOG_LIST_END_DELETE) {
  366. page_delete_rec_list_end(page, page + offset, ULINT_UNDEFINED,
  367. ULINT_UNDEFINED, mtr);
  368. } else {
  369. page_delete_rec_list_start(page, page + offset, mtr);
  370. }
  371. return(ptr);
  372. }
  373. /*****************************************************************
  374. Deletes records from a page from a given record onward, including that record.
  375. The infimum and supremum records are not deleted. */
  376. void
  377. page_delete_rec_list_end(
  378. /*=====================*/
  379. page_t* page, /* in: index page */
  380. rec_t* rec, /* in: record on page */
  381. ulint n_recs, /* in: number of records to delete, or ULINT_UNDEFINED
  382. if not known */
  383. ulint size, /* in: the sum of the sizes of the records in the end
  384. of the chain to delete, or ULINT_UNDEFINED if not
  385. known */
  386. mtr_t* mtr) /* in: mtr */
  387. {
  388. page_dir_slot_t* slot;
  389. ulint slot_index;
  390. rec_t* last_rec;
  391. rec_t* prev_rec;
  392. rec_t* free;
  393. rec_t* rec2;
  394. ulint count;
  395. ulint n_owned;
  396. rec_t* sup;
  397. /* Reset the last insert info in the page header and increment
  398. the modify clock for the frame */
  399. page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
  400. /* The page gets invalid for optimistic searches: increment the
  401. frame modify clock */
  402. buf_frame_modify_clock_inc(page);
  403. sup = page_get_supremum_rec(page);
  404. if (rec == page_get_infimum_rec(page)) {
  405. rec = page_rec_get_next(rec);
  406. }
  407. page_delete_rec_list_write_log(page, rec, MLOG_LIST_END_DELETE, mtr);
  408. if (rec == sup) {
  409. return;
  410. }
  411. prev_rec = page_rec_get_prev(rec);
  412. last_rec = page_rec_get_prev(sup);
  413. if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
  414. /* Calculate the sum of sizes and the number of records */
  415. size = 0;
  416. n_recs = 0;
  417. rec2 = rec;
  418. while (rec2 != sup) {
  419. size += rec_get_size(rec2);
  420. n_recs++;
  421. rec2 = page_rec_get_next(rec2);
  422. }
  423. }
  424. /* Update the page directory; there is no need to balance the number
  425. of the records owned by the supremum record, as it is allowed to be
  426. less than PAGE_DIR_SLOT_MIN_N_OWNED */
  427. rec2 = rec;
  428. count = 0;
  429. while (rec_get_n_owned(rec2) == 0) {
  430. count++;
  431. rec2 = page_rec_get_next(rec2);
  432. }
  433. ut_ad(rec_get_n_owned(rec2) - count > 0);
  434. n_owned = rec_get_n_owned(rec2) - count;
  435. slot_index = page_dir_find_owner_slot(rec2);
  436. slot = page_dir_get_nth_slot(page, slot_index);
  437. page_dir_slot_set_rec(slot, sup);
  438. page_dir_slot_set_n_owned(slot, n_owned);
  439. page_header_set_field(page, PAGE_N_DIR_SLOTS, slot_index + 1);
  440. /* Remove the record chain segment from the record chain */
  441. page_rec_set_next(prev_rec, page_get_supremum_rec(page));
  442. /* Catenate the deleted chain segment to the page free list */
  443. free = page_header_get_ptr(page, PAGE_FREE);
  444. page_rec_set_next(last_rec, free);
  445. page_header_set_ptr(page, PAGE_FREE, rec);
  446. page_header_set_field(page, PAGE_GARBAGE,
  447. size + page_header_get_field(page, PAGE_GARBAGE));
  448. page_header_set_field(page, PAGE_N_RECS,
  449. (ulint)(page_get_n_recs(page) - n_recs));
  450. }
  451. /*****************************************************************
  452. Deletes records from page, up to the given record, NOT including
  453. that record. Infimum and supremum records are not deleted. */
  454. void
  455. page_delete_rec_list_start(
  456. /*=======================*/
  457. page_t* page, /* in: index page */
  458. rec_t* rec, /* in: record on page */
  459. mtr_t* mtr) /* in: mtr */
  460. {
  461. page_cur_t cur1;
  462. ulint log_mode;
  463. page_delete_rec_list_write_log(page, rec, MLOG_LIST_START_DELETE, mtr);
  464. page_cur_set_before_first(page, &cur1);
  465. if (rec == page_cur_get_rec(&cur1)) {
  466. return;
  467. }
  468. page_cur_move_to_next(&cur1);
  469. /* Individual deletes are not logged */
  470. log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
  471. while (page_cur_get_rec(&cur1) != rec) {
  472. page_cur_delete_rec(&cur1, mtr);
  473. }
  474. /* Restore log mode */
  475. mtr_set_log_mode(mtr, log_mode);
  476. }
  477. /*****************************************************************
  478. Moves record list end to another page. Moved records include
  479. split_rec. */
  480. void
  481. page_move_rec_list_end(
  482. /*===================*/
  483. page_t* new_page, /* in: index page where to move */
  484. page_t* page, /* in: index page */
  485. rec_t* split_rec, /* in: first record to move */
  486. mtr_t* mtr) /* in: mtr */
  487. {
  488. ulint old_data_size;
  489. ulint new_data_size;
  490. ulint old_n_recs;
  491. ulint new_n_recs;
  492. old_data_size = page_get_data_size(new_page);
  493. old_n_recs = page_get_n_recs(new_page);
  494. page_copy_rec_list_end(new_page, page, split_rec, mtr);
  495. new_data_size = page_get_data_size(new_page);
  496. new_n_recs = page_get_n_recs(new_page);
  497. ut_ad(new_data_size >= old_data_size);
  498. page_delete_rec_list_end(page, split_rec, new_n_recs - old_n_recs,
  499. new_data_size - old_data_size, mtr);
  500. }
  501. /*****************************************************************
  502. Moves record list start to another page. Moved records do not include
  503. split_rec. */
  504. void
  505. page_move_rec_list_start(
  506. /*=====================*/
  507. page_t* new_page, /* in: index page where to move */
  508. page_t* page, /* in: index page */
  509. rec_t* split_rec, /* in: first record not to move */
  510. mtr_t* mtr) /* in: mtr */
  511. {
  512. page_copy_rec_list_start(new_page, page, split_rec, mtr);
  513. page_delete_rec_list_start(page, split_rec, mtr);
  514. }
  515. /***************************************************************************
  516. This is a low-level operation which is used in a database index creation
  517. to update the page number of a created B-tree to a data dictionary record. */
  518. void
  519. page_rec_write_index_page_no(
  520. /*=========================*/
  521. rec_t* rec, /* in: record to update */
  522. ulint i, /* in: index of the field to update */
  523. ulint page_no,/* in: value to write */
  524. mtr_t* mtr) /* in: mtr */
  525. {
  526. byte* data;
  527. ulint len;
  528. data = rec_get_nth_field(rec, i, &len);
  529. ut_ad(len == 4);
  530. mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr);
  531. }
  532. /******************************************************************
  533. Used to delete n slots from the directory. This function updates
  534. also n_owned fields in the records, so that the first slot after
  535. the deleted ones inherits the records of the deleted slots. */
  536. UNIV_INLINE
  537. void
  538. page_dir_delete_slots(
  539. /*==================*/
  540. page_t* page, /* in: the index page */
  541. ulint start, /* in: first slot to be deleted */
  542. ulint n) /* in: number of slots to delete (currently 
  543. only n == 1 allowed) */
  544. {
  545. page_dir_slot_t* slot;
  546. ulint i;
  547. ulint sum_owned = 0;
  548. ulint n_slots;
  549. rec_t* rec;
  550. ut_ad(n == 1);
  551. ut_ad(start > 0);
  552. ut_ad(start + n < page_dir_get_n_slots(page));
  553. n_slots = page_dir_get_n_slots(page);
  554. /* 1. Reset the n_owned fields of the slots to be
  555. deleted */
  556. for (i = start; i < start + n; i++) {
  557. slot = page_dir_get_nth_slot(page, i);
  558. sum_owned += page_dir_slot_get_n_owned(slot);
  559. page_dir_slot_set_n_owned(slot, 0);
  560. }
  561. /* 2. Update the n_owned value of the first non-deleted slot */
  562. slot = page_dir_get_nth_slot(page, start + n);
  563. page_dir_slot_set_n_owned(slot,
  564. sum_owned + page_dir_slot_get_n_owned(slot));
  565. /* 3. Destroy start and other slots by copying slots */
  566. for (i = start + n; i < n_slots; i++) {
  567. slot = page_dir_get_nth_slot(page, i);
  568. rec = page_dir_slot_get_rec(slot);
  569. slot = page_dir_get_nth_slot(page, i - n);
  570. page_dir_slot_set_rec(slot, rec);
  571. }
  572. /* 4. Update the page header */
  573. page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots - n);
  574. }
  575. /******************************************************************
  576. Used to add n slots to the directory. Does not set the record pointers
  577. in the added slots or update n_owned values: this is the responsibility
  578. of the caller. */
  579. UNIV_INLINE
  580. void
  581. page_dir_add_slots(
  582. /*===============*/
  583. page_t* page, /* in: the index page */
  584. ulint start, /* in: the slot above which the new slots are added */
  585. ulint n) /* in: number of slots to add (currently only n == 1 
  586. allowed) */
  587. {
  588. page_dir_slot_t* slot;
  589. ulint n_slots;
  590. ulint i;
  591. rec_t* rec;
  592. ut_ad(n == 1);
  593. n_slots = page_dir_get_n_slots(page);
  594. ut_ad(start < n_slots - 1);
  595. /* Update the page header */
  596. page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots + n);
  597. /* Move slots up */
  598. for (i = n_slots - 1; i > start; i--) {
  599. slot = page_dir_get_nth_slot(page, i);
  600. rec = page_dir_slot_get_rec(slot);
  601. slot = page_dir_get_nth_slot(page, i + n);
  602. page_dir_slot_set_rec(slot, rec);
  603. }
  604. }
  605. /********************************************************************
  606. Splits a directory slot which owns too many records. */
  607. void
  608. page_dir_split_slot(
  609. /*================*/
  610. page_t* page, /* in: the index page in question */
  611. ulint slot_no) /* in: the directory slot */
  612. {
  613. rec_t* rec;
  614. page_dir_slot_t* new_slot;
  615. page_dir_slot_t* prev_slot;
  616. page_dir_slot_t* slot;
  617. ulint i;
  618. ulint n_owned;
  619. ut_ad(page);
  620. ut_ad(slot_no > 0);
  621. slot = page_dir_get_nth_slot(page, slot_no);
  622. n_owned = page_dir_slot_get_n_owned(slot);
  623. ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
  624. /* 1. We loop to find a record approximately in the middle of the 
  625. records owned by the slot. */
  626. prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
  627. rec = page_dir_slot_get_rec(prev_slot);
  628. for (i = 0; i < n_owned / 2; i++) {
  629. rec = page_rec_get_next(rec);
  630. }
  631. ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
  632. /* 2. We add one directory slot immediately below the slot to be
  633. split. */
  634. page_dir_add_slots(page, slot_no - 1, 1);
  635. /* The added slot is now number slot_no, and the old slot is
  636. now number slot_no + 1 */
  637. new_slot = page_dir_get_nth_slot(page, slot_no);
  638. slot = page_dir_get_nth_slot(page, slot_no + 1);
  639. /* 3. We store the appropriate values to the new slot. */
  640. page_dir_slot_set_rec(new_slot, rec);
  641. page_dir_slot_set_n_owned(new_slot, n_owned / 2);
  642. /* 4. Finally, we update the number of records field of the 
  643. original slot */
  644. page_dir_slot_set_n_owned(slot, n_owned - (n_owned / 2));
  645. }
  646. /*****************************************************************
  647. Tries to balance the given directory slot with too few records with the upper
  648. neighbor, so that there are at least the minimum number of records owned by
  649. the slot; this may result in the merging of two slots. */
  650. void
  651. page_dir_balance_slot(
  652. /*==================*/
  653. page_t* page, /* in: index page */
  654. ulint slot_no)  /* in: the directory slot */
  655. {
  656. page_dir_slot_t* slot;
  657. page_dir_slot_t* up_slot;
  658. ulint n_owned;
  659. ulint up_n_owned;
  660. rec_t* old_rec;
  661. rec_t* new_rec;
  662. ut_ad(page);
  663. ut_ad(slot_no > 0);
  664. slot = page_dir_get_nth_slot(page, slot_no);
  665. /* The last directory slot cannot be balanced with the upper
  666. neighbor, as there is none. */
  667. if (slot_no == page_dir_get_n_slots(page) - 1) {
  668. return;
  669. }
  670. up_slot = page_dir_get_nth_slot(page, slot_no + 1);
  671. n_owned = page_dir_slot_get_n_owned(slot);
  672. up_n_owned = page_dir_slot_get_n_owned(up_slot);
  673. ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
  674. /* If the upper slot has the minimum value of n_owned, we will merge
  675. the two slots, therefore we assert: */ 
  676. ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
  677. if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
  678. /* In this case we can just transfer one record owned
  679. by the upper slot to the property of the lower slot */
  680. old_rec = page_dir_slot_get_rec(slot);
  681. new_rec = page_rec_get_next(old_rec);
  682. rec_set_n_owned(old_rec, 0);
  683. rec_set_n_owned(new_rec, n_owned + 1);
  684. page_dir_slot_set_rec(slot, new_rec);
  685. page_dir_slot_set_n_owned(up_slot, up_n_owned -1);
  686. } else {
  687. /* In this case we may merge the two slots */
  688. page_dir_delete_slots(page, slot_no, 1);
  689. }
  690. }
  691. /****************************************************************
  692. Returns the middle record of the record list. If there are an even number
  693. of records in the list, returns the first record of the upper half-list. */
  694. rec_t*
  695. page_get_middle_rec(
  696. /*================*/
  697. /* out: middle record */
  698. page_t* page) /* in: page */
  699. {
  700. page_dir_slot_t* slot;
  701. ulint middle;
  702. ulint i;
  703. ulint n_owned;
  704. ulint count;
  705. rec_t* rec;
  706. /* This many records we must leave behind */
  707. middle = (page_get_n_recs(page) + 2) / 2;
  708. count = 0;
  709. for (i = 0;; i++) {
  710. slot = page_dir_get_nth_slot(page, i);
  711. n_owned = page_dir_slot_get_n_owned(slot);
  712. if (count + n_owned > middle) {
  713. break;
  714. } else {
  715. count += n_owned;
  716. }
  717. }
  718. ut_ad(i > 0);
  719. slot = page_dir_get_nth_slot(page, i - 1);
  720. rec = page_dir_slot_get_rec(slot);
  721. rec = page_rec_get_next(rec);
  722. /* There are now count records behind rec */
  723. for (i = 0; i < middle - count; i++) {
  724. rec = page_rec_get_next(rec);
  725. }
  726. return(rec);
  727. }
  728. /*******************************************************************
  729. Returns the number of records before the given record in chain.
  730. The number includes infimum and supremum records. */
  731. ulint
  732. page_rec_get_n_recs_before(
  733. /*=======================*/
  734. /* out: number of records */
  735. rec_t* rec) /* in: the physical record */
  736. {
  737. page_dir_slot_t* slot;
  738. rec_t* slot_rec;
  739. page_t* page;
  740. ulint i;
  741. lint n = 0;
  742. ut_ad(page_rec_check(rec));
  743. page = buf_frame_align(rec);
  744. while (rec_get_n_owned(rec) == 0) {
  745. rec = page_rec_get_next(rec);
  746. n--;
  747. }
  748. for (i = 0; ; i++) {
  749. slot = page_dir_get_nth_slot(page, i);
  750. slot_rec = page_dir_slot_get_rec(slot);
  751. n += rec_get_n_owned(slot_rec);
  752. if (rec == slot_rec) {
  753. break;
  754. }
  755. }
  756. n--;
  757. ut_ad(n >= 0);
  758. return((ulint) n);
  759. }
  760. /****************************************************************
  761. Prints record contents including the data relevant only in
  762. the index page context. */
  763.  
  764. void
  765. page_rec_print(
  766. /*===========*/
  767. rec_t* rec)
  768. {
  769. rec_print(rec);
  770. printf(
  771.       "            n_owned: %lu; heap_no: %lu; next rec: %lun",
  772. rec_get_n_owned(rec),
  773. rec_get_heap_no(rec),
  774. rec_get_next_offs(rec));
  775. page_rec_check(rec);
  776. rec_validate(rec);
  777. }
  778. /*******************************************************************
  779. This is used to print the contents of the directory for
  780. debugging purposes. */
  781. void
  782. page_dir_print(
  783. /*===========*/
  784. page_t* page, /* in: index page */
  785. ulint pr_n) /* in: print n first and n last entries */
  786. {
  787. ulint n;
  788. ulint i;
  789. page_dir_slot_t* slot;
  790. n = page_dir_get_n_slots(page);
  791. printf("--------------------------------n");
  792. printf("PAGE DIRECTORYn");
  793. printf("Page address %lxn", (ulint)page);
  794. printf("Directory stack top at offs: %lu; number of slots: %lun", 
  795. (ulint)(page_dir_get_nth_slot(page, n - 1) - page), n);
  796. for (i = 0; i < n; i++) {
  797. slot = page_dir_get_nth_slot(page, i);
  798. if ((i == pr_n) && (i < n - pr_n)) {
  799. printf("    ...   n");
  800. }
  801.      if ((i < pr_n) || (i >= n - pr_n)) {
  802.     printf(
  803.        "Contents of slot: %lu: n_owned: %lu, rec offs: %lun",
  804. i, page_dir_slot_get_n_owned(slot),
  805. (ulint)(page_dir_slot_get_rec(slot) - page));
  806.      }
  807. }
  808. printf("Total of %lu recordsn", 2 + page_get_n_recs(page));
  809. printf("--------------------------------n");
  810. }
  811. /*******************************************************************
  812. This is used to print the contents of the page record list for
  813. debugging purposes. */
  814. void
  815. page_print_list(
  816. /*============*/
  817. page_t* page, /* in: index page */
  818. ulint pr_n) /* in: print n first and n last entries */
  819. {
  820. page_cur_t cur;
  821. rec_t* rec;
  822. ulint count;
  823. ulint n_recs;
  824. printf("--------------------------------n");
  825. printf("PAGE RECORD LISTn");
  826. printf("Page address %lun", (ulint)page);
  827. n_recs = page_get_n_recs(page);
  828. page_cur_set_before_first(page, &cur);
  829. count = 0;
  830. for (;;) {
  831. rec = (&cur)->rec;
  832. page_rec_print(rec);
  833. if (count == pr_n) {
  834. break;
  835. }
  836. if (page_cur_is_after_last(&cur)) {
  837. break;
  838. }
  839. page_cur_move_to_next(&cur);
  840. count++;
  841. }
  842. if (n_recs > 2 * pr_n) {
  843. printf(" ... n");
  844. }
  845. for (;;) {
  846. if (page_cur_is_after_last(&cur)) {
  847. break;
  848. }
  849. page_cur_move_to_next(&cur);
  850. if (count + pr_n >= n_recs) {
  851. rec = (&cur)->rec;
  852. page_rec_print(rec);
  853. }
  854. count++;
  855. }
  856. printf("Total of %lu records n", count + 1);
  857. printf("--------------------------------n");
  858. }
  859. /*******************************************************************
  860. Prints the info in a page header. */
  861. void
  862. page_header_print(
  863. /*==============*/
  864. page_t* page)
  865. {
  866. printf("--------------------------------n");
  867. printf("PAGE HEADER INFOn");
  868. printf("Page address %lx, n records %lun", (ulint)page,
  869. page_header_get_field(page, PAGE_N_RECS));
  870. printf("n dir slots %lu, heap top %lun",
  871. page_header_get_field(page, PAGE_N_DIR_SLOTS),
  872. page_header_get_field(page, PAGE_HEAP_TOP));
  873. printf("Page n heap %lu, free %lu, garbage %lun",
  874. page_header_get_field(page, PAGE_N_HEAP),
  875. page_header_get_field(page, PAGE_FREE),
  876. page_header_get_field(page, PAGE_GARBAGE));
  877. printf("Page last insert %lu, direction %lu, n direction %lun",
  878. page_header_get_field(page, PAGE_LAST_INSERT),
  879. page_header_get_field(page, PAGE_DIRECTION),
  880. page_header_get_field(page, PAGE_N_DIRECTION));
  881. }
  882. /*******************************************************************
  883. This is used to print the contents of the page for
  884. debugging purposes. */
  885. void
  886. page_print(
  887. /*======*/
  888. page_t* page, /* in: index page */
  889. ulint dn, /* in: print dn first and last entries in directory */
  890. ulint rn) /* in: print rn first and last records on page */
  891. {
  892. page_header_print(page);
  893. page_dir_print(page, dn);
  894. page_print_list(page, rn);
  895. }
  896. /*******************************************************************
  897. The following is used to validate a record on a page. This function
  898. differs from rec_validate as it can also check the n_owned field and
  899. the heap_no field. */
  900. ibool
  901. page_rec_validate(
  902. /*==============*/
  903. /* out: TRUE if ok */
  904. rec_t*  rec) /* in: record on the page */
  905. {
  906. ulint n_owned;
  907. ulint heap_no;
  908. page_t* page;
  909. page = buf_frame_align(rec);
  910. page_rec_check(rec);
  911. rec_validate(rec);
  912. n_owned = rec_get_n_owned(rec);
  913. heap_no = rec_get_heap_no(rec);
  914. ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
  915. ut_a(heap_no < page_header_get_field(page, PAGE_N_HEAP));
  916. return(TRUE);
  917. }
  918. /*******************************************************************
  919. This function checks the consistency of an index page. */
  920. ibool
  921. page_validate(
  922. /*==========*/
  923. /* out: TRUE if ok */
  924. page_t* page, /* in: index page */
  925. dict_index_t* index) /* in: data dictionary index containing
  926. the page record type definition */
  927. {
  928. mem_heap_t* heap;
  929. byte* buf;
  930. ulint i;
  931. ulint count;
  932. ulint own_count;
  933. ulint slot_no;
  934. ulint data_size;
  935. page_cur_t  cur;
  936. rec_t* rec;
  937. rec_t* old_rec = NULL;
  938. page_dir_slot_t* slot;
  939. ulint offs;
  940. ulint n_slots;
  941. heap = mem_heap_create(UNIV_PAGE_SIZE);
  942. /* The following buffer is used to check that the
  943. records in the page record heap do not overlap */
  944. buf = mem_heap_alloc(heap, UNIV_PAGE_SIZE);
  945. for (i = 0; i < UNIV_PAGE_SIZE; i++) {
  946. buf[i] = 0;
  947. }
  948. /* Check first that the record heap and the directory do not
  949. overlap. */
  950. n_slots = page_dir_get_n_slots(page);
  951. ut_ad(page_header_get_ptr(page, PAGE_HEAP_TOP) <=
  952. page_dir_get_nth_slot(page, n_slots - 1));
  953. /* Validate the record list in a loop checking also that
  954. it is consistent with the directory. */
  955. count = 0;
  956. data_size = 0;
  957. own_count = 1;
  958. slot_no = 0;
  959. slot = page_dir_get_nth_slot(page, slot_no);
  960. page_cur_set_before_first(page, &cur);
  961. for (;;) {
  962. rec = (&cur)->rec;
  963. page_rec_validate(rec);
  964. /* Check that the records are in the ascending order */
  965. if ((count >= 2) && (!page_cur_is_after_last(&cur))) {
  966. ut_a(1 == cmp_rec_rec(rec, old_rec, index));
  967. }
  968. if ((rec != page_get_supremum_rec(page))
  969.     && (rec != page_get_infimum_rec(page))) {
  970. data_size += rec_get_size(rec);
  971. }
  972. offs = rec_get_start(rec) - page;
  973. for (i = 0; i < rec_get_size(rec); i++) {
  974. ut_a(buf[offs + i] == 0); /* No other record may
  975.   overlap this */
  976. buf[offs + i] = 1;
  977. }
  978. if (rec_get_n_owned(rec) != 0) {
  979. /* This is a record pointed to by a dir slot */
  980. ut_a(rec_get_n_owned(rec) == own_count);
  981. ut_a(page_dir_slot_get_rec(slot) == rec);
  982. page_dir_slot_check(slot);
  983. own_count = 0;
  984. if (!page_cur_is_after_last(&cur)) {
  985. slot_no++;
  986. slot = page_dir_get_nth_slot(page, slot_no);
  987. }
  988. }
  989. if (page_cur_is_after_last(&cur)) {
  990. break;
  991. }
  992. count++;
  993. page_cur_move_to_next(&cur);
  994. own_count++;
  995. old_rec = rec;
  996. }
  997. ut_a(rec_get_n_owned(rec) != 0);
  998. ut_a(slot_no == n_slots - 1);
  999. ut_a(page_header_get_field(page, PAGE_N_RECS) + 2 == count + 1);
  1000. if (data_size != page_get_data_size(page)) {
  1001. printf("Summed data size %lu, returned by func %lun",
  1002. data_size, page_get_data_size(page));
  1003. ut_error;
  1004. }
  1005. /* Check then the free list */
  1006. rec = page_header_get_ptr(page, PAGE_FREE);
  1007. while (rec != NULL) {
  1008. page_rec_validate(rec);
  1009. count++;
  1010. offs = rec_get_start(rec) - page;
  1011. for (i = 0; i < rec_get_size(rec); i++) {
  1012. ut_a(buf[offs + i] == 0);
  1013. buf[offs + i] = 1;
  1014. }
  1015. rec = page_rec_get_next(rec);
  1016. }
  1017. ut_a(page_header_get_field(page, PAGE_N_HEAP) == count + 1);
  1018. mem_heap_free(heap);
  1019. return(TRUE);   
  1020. }
  1021. /*******************************************************************
  1022. Looks in the page record list for a record with the given heap number. */
  1023. rec_t*
  1024. page_find_rec_with_heap_no(
  1025. /*=======================*/
  1026. /* out: record, NULL if not found */
  1027. page_t* page, /* in: index page */
  1028. ulint heap_no)/* in: heap number */
  1029. {
  1030. page_cur_t cur;
  1031. rec_t* rec;
  1032. page_cur_set_before_first(page, &cur);
  1033. for (;;) {
  1034. rec = (&cur)->rec;
  1035. if (rec_get_heap_no(rec) == heap_no) {
  1036. return(rec);
  1037. }
  1038. if (page_cur_is_after_last(&cur)) {
  1039. return(NULL);
  1040. }
  1041. page_cur_move_to_next(&cur);
  1042. }
  1043. }