📄 btr0btr.c
字号:
/***************************************************************Parses a redo log record of reorganizing a page. */byte*btr_parse_page_reorganize(/*======================*/ /* out: end of log record or NULL */ byte* ptr, /* in: buffer */ byte* end_ptr __attribute__((unused)), /* in: buffer end */ dict_index_t* index, /* in: record descriptor */ page_t* page, /* in: page or NULL */ mtr_t* mtr) /* in: mtr or NULL */{ ut_ad(ptr && end_ptr); /* The record is empty, except for the record initial part */ if (page) { btr_page_reorganize_low(TRUE, page, index, mtr); } return(ptr);}/*****************************************************************Empties an index page. */staticvoidbtr_page_empty(/*===========*/ page_t* page, /* in: page to be emptied */ mtr_t* mtr) /* in: mtr */{ ut_ad(mtr_memo_contains(mtr, buf_block_align(page), MTR_MEMO_PAGE_X_FIX)); btr_search_drop_page_hash_index(page); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ page_create(page, mtr, page_is_comp(page)); buf_block_align(page)->check_index_page_at_flush = TRUE;}/*****************************************************************Makes tree one level higher by splitting the root, and insertsthe tuple. It is assumed that mtr contains an x-latch on the tree.NOTE that the operation of this function must always succeed,we cannot reverse it: therefore enough free disk space must beguaranteed to be available before this function is called. */rec_t*btr_root_raise_and_insert(/*======================*/ /* out: inserted record */ btr_cur_t* cursor, /* in: cursor at which to insert: must be on the root page; when the function returns, the cursor is positioned on the predecessor of the inserted record */ dtuple_t* tuple, /* in: tuple to insert */ mtr_t* mtr) /* in: mtr */{ dict_tree_t* tree; page_t* root; page_t* new_page; ulint new_page_no; rec_t* rec; mem_heap_t* heap; dtuple_t* node_ptr; ulint level; rec_t* node_ptr_rec; page_cur_t* page_cursor; root = btr_cur_get_page(cursor); tree = btr_cur_get_tree(cursor); ut_ad(dict_tree_get_page(tree) == buf_frame_get_page_no(root)); ut_ad(mtr_memo_contains(mtr, dict_tree_get_lock(tree), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, buf_block_align(root), MTR_MEMO_PAGE_X_FIX)); btr_search_drop_page_hash_index(root); /* Allocate a new page to the tree. Root splitting is done by first moving the root records to the new page, emptying the root, putting a node pointer to the new page, and then splitting the new page. */ new_page = btr_page_alloc(tree, 0, FSP_NO_DIR, btr_page_get_level(root, mtr), mtr); btr_page_create(new_page, tree, mtr); level = btr_page_get_level(root, mtr); /* Set the levels of the new index page and root page */ btr_page_set_level(new_page, level, mtr); btr_page_set_level(root, level + 1, mtr); /* Set the next node and previous node fields of new page */ btr_page_set_next(new_page, FIL_NULL, mtr); btr_page_set_prev(new_page, FIL_NULL, mtr); /* Move the records from root to the new page */ page_move_rec_list_end(new_page, root, page_get_infimum_rec(root), cursor->index, mtr); /* If this is a pessimistic insert which is actually done to perform a pessimistic update then we have stored the lock information of the record to be inserted on the infimum of the root page: we cannot discard the lock structs on the root page */ lock_update_root_raise(new_page, root); /* Create a memory heap where the node pointer is stored */ heap = mem_heap_create(100); rec = page_rec_get_next(page_get_infimum_rec(new_page)); new_page_no = buf_frame_get_page_no(new_page); /* Build the node pointer (= node key and page address) for the child */ node_ptr = dict_tree_build_node_ptr(tree, rec, new_page_no, heap, level); /* Reorganize the root to get free space */ btr_page_reorganize(root, cursor->index, mtr); page_cursor = btr_cur_get_page_cur(cursor); /* Insert node pointer to the root */ page_cur_set_before_first(root, page_cursor); node_ptr_rec = page_cur_tuple_insert(page_cursor, node_ptr, cursor->index, mtr); ut_ad(node_ptr_rec); /* The node pointer must be marked as the predefined minimum record, as there is no lower alphabetical limit to records in the leftmost node of a level: */ btr_set_min_rec_mark(node_ptr_rec, page_is_comp(root), mtr); /* Free the memory heap */ mem_heap_free(heap); /* We play safe and reset the free bits for the new page *//* fprintf(stderr, "Root raise new page no %lu\n", buf_frame_get_page_no(new_page)); */ ibuf_reset_free_bits(UT_LIST_GET_FIRST(tree->tree_indexes), new_page); /* Reposition the cursor to the child node */ page_cur_search(new_page, cursor->index, tuple, PAGE_CUR_LE, page_cursor); /* Split the child and insert tuple */ return(btr_page_split_and_insert(cursor, tuple, mtr));} /*****************************************************************Decides if the page should be split at the convergence point of insertsconverging to the left. */iboolbtr_page_get_split_rec_to_left(/*===========================*/ /* out: TRUE if split recommended */ btr_cur_t* cursor, /* in: cursor at which to insert */ rec_t** split_rec) /* out: if split recommended, the first record on upper half page, or NULL if tuple to be inserted should be first */{ page_t* page; rec_t* insert_point; rec_t* infimum; page = btr_cur_get_page(cursor); insert_point = btr_cur_get_rec(cursor); if (page_header_get_ptr(page, PAGE_LAST_INSERT) == page_rec_get_next(insert_point)) { infimum = page_get_infimum_rec(page); /* If the convergence is in the middle of a page, include also the record immediately before the new insert to the upper page. Otherwise, we could repeatedly move from page to page lots of records smaller than the convergence point. */ if (infimum != insert_point && page_rec_get_next(infimum) != insert_point) { *split_rec = insert_point; } else { *split_rec = page_rec_get_next(insert_point); } return(TRUE); } return(FALSE);}/*****************************************************************Decides if the page should be split at the convergence point of insertsconverging to the right. */iboolbtr_page_get_split_rec_to_right(/*============================*/ /* out: TRUE if split recommended */ btr_cur_t* cursor, /* in: cursor at which to insert */ rec_t** split_rec) /* out: if split recommended, the first record on upper half page, or NULL if tuple to be inserted should be first */{ page_t* page; rec_t* insert_point; page = btr_cur_get_page(cursor); insert_point = btr_cur_get_rec(cursor); /* We use eager heuristics: if the new insert would be right after the previous insert on the same page, we assume that there is a pattern of sequential inserts here. */ if (UNIV_LIKELY(page_header_get_ptr(page, PAGE_LAST_INSERT) == insert_point)) { rec_t* next_rec; next_rec = page_rec_get_next(insert_point); if (page_rec_is_supremum(next_rec)) {split_at_new: /* Split at the new record to insert */ *split_rec = NULL; } else { rec_t* next_next_rec = page_rec_get_next(next_rec); if (page_rec_is_supremum(next_next_rec)) { goto split_at_new; } /* If there are >= 2 user records up from the insert point, split all but 1 off. We want to keep one because then sequential inserts can use the adaptive hash index, as they can do the necessary checks of the right search position just by looking at the records on this page. */ *split_rec = next_next_rec; } return(TRUE); } return(FALSE);}/*****************************************************************Calculates a split record such that the tuple will certainly fit onits half-page when the split is performed. We assume in this functiononly that the cursor page has at least one user record. */staticrec_t*btr_page_get_sure_split_rec(/*========================*/ /* out: split record, or NULL if tuple will be the first record on upper half-page */ btr_cur_t* cursor, /* in: cursor at which insert should be made */ dtuple_t* tuple) /* in: tuple to insert */ { page_t* page; ulint insert_size; ulint free_space; ulint total_data; ulint total_n_recs; ulint total_space; ulint incl_data; rec_t* ins_rec; rec_t* rec; rec_t* next_rec; ulint n; mem_heap_t* heap; ulint* offsets; page = btr_cur_get_page(cursor); insert_size = rec_get_converted_size(cursor->index, tuple); free_space = page_get_free_space_of_empty(page_is_comp(page)); /* free_space is now the free space of a created new page */ total_data = page_get_data_size(page) + insert_size; total_n_recs = page_get_n_recs(page) + 1; ut_ad(total_n_recs >= 2); total_space = total_data + page_dir_calc_reserved_space(total_n_recs); n = 0; incl_data = 0; ins_rec = btr_cur_get_rec(cursor); rec = page_get_infimum_rec(page); heap = NULL; offsets = NULL; /* We start to include records to the left half, and when the space reserved by them exceeds half of total_space, then if the included records fit on the left page, they will be put there if something was left over also for the right page, otherwise the last included record will be the first on the right half page */ for (;;) { /* Decide the next record to include */ if (rec == ins_rec) { rec = NULL; /* NULL denotes that tuple is now included */ } else if (rec == NULL) { rec = page_rec_get_next(ins_rec); } else { rec = page_rec_get_next(rec); } if (rec == NULL) { /* Include tuple */ incl_data += insert_size; } else { offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); incl_data += rec_offs_size(offsets); } n++; if (incl_data + page_dir_calc_reserved_space(n) >= total_space / 2) { if (incl_data + page_dir_calc_reserved_space(n) <= free_space) { /* The next record will be the first on the right half page if it is not the supremum record of page */ if (rec == ins_rec) { rec = NULL; goto func_exit; } else if (rec == NULL) { next_rec = page_rec_get_next(ins_rec); } else { next_rec = page_rec_get_next(rec); } ut_ad(next_rec); if (!page_rec_is_supremum(next_rec)) { rec = next_rec; } }func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(rec); } }} /*****************************************************************Returns TRUE if the insert fits on the appropriate half-page with thechosen split_rec. */staticiboolbtr_page_insert_fits(/*=================*/ /* out: TRUE if fits */ btr_cur_t* cursor, /* in: cursor at which insert should be made */ rec_t* split_rec, /* in: suggestion for first record on upper half-page, or NULL if tuple to be inserted should be first */ const ulint* offsets, /* in: rec_get_offsets( split_rec, cursor->index) */ dtuple_t* tuple, /* in: tuple to insert */ mem_heap_t* heap) /* in: temporary memory heap */{ page_t* page; ulint insert_size; ulint free_space; ulint total_data; ulint total_n_recs; rec_t* rec; rec_t* end_rec; ulint* offs; page = btr_cur_get_page(cursor); ut_ad(!split_rec == !offsets); ut_ad(!offsets || !page_is_comp(page) == !rec_offs_comp(offsets)); ut_ad(!offsets || rec_offs_validate(split_rec, cursor->index, offsets)); insert_size = rec_get_converted_size(cursor->index, tuple); free_space = page_get_free_space_of_empty(page_is_comp(page)); /* free_space is now the free space of a created new page */ total_data = page_get_data_size(page) + insert_size; total_n_recs = page_get_n_recs(page) + 1; /* We determine which records (from rec to end_rec, not including end_rec) will end up on the other half page from tuple when it is inserted. */ if (split_rec == NULL) { rec = page_rec_get_next(page_get_infimum_rec(page)); end_rec = page_rec_get_next(btr_cur_get_rec(cursor)); } else if (cmp_dtuple_rec(tuple, split_rec, offsets) >= 0) { rec = page_rec_get_next(page_get_infimum_rec(page)); end_rec = split_rec; } else { rec = split_rec; end_rec = page_get_supremum_rec(page); } if (total_data + page_dir_calc_reserved_space(total_n_recs) <= free_space) { /* Ok, there will be enough available space on the half page where the tuple is inserted */ return(TRUE); } offs = NULL; while (rec != end_rec) { /* In this loop we calculate the amount of reserved space after rec is removed from page. */ offs = rec_get_offsets(rec, cursor->index, offs, ULINT_UNDEFINED, &heap); total_data -= rec_offs_size(offs); total_n_recs--; if (total_data + page_dir_calc_reserved_space(total_n_recs) <= free_space) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -