/*************************************************************************** Gets the previous record in an undo log from the previous page. */ static trx_undo_rec_t* trx_undo_get_prev_rec_from_prev_page( /*=================================*/ /* out: undo log record, the page s-latched, NULL if none */ trx_undo_rec_t* rec, /* in: undo record */ ulint page_no,/* in: undo log header page number */ ulint offset, /* in: undo log header offset on page */ mtr_t* mtr) /* in: mtr */ { ulint prev_page_no; page_t* prev_page; page_t* undo_page; undo_page = buf_frame_align(rec); prev_page_no = flst_get_prev_addr(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr) .page; if (prev_page_no == FIL_NULL) { return(NULL); } prev_page = trx_undo_page_get_s_latched( buf_frame_get_space_id(undo_page), prev_page_no, mtr); return(trx_undo_page_get_last_rec(prev_page, page_no, offset)); }
void flst_cut_end( /*=========*/ flst_base_node_t* base, /* in: pointer to base node of list */ flst_node_t* node2, /* in: first node to remove */ ulint n_nodes,/* in: number of nodes to remove, must be >= 1 */ mtr_t* mtr) /* in: mini-transaction handle */ { ulint space; flst_node_t* node1; fil_addr_t node1_addr; fil_addr_t node2_addr; ulint len; ut_ad(mtr && node2 && base); ut_ad(mtr_memo_contains(mtr, buf_block_align(base), MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains(mtr, buf_block_align(node2), MTR_MEMO_PAGE_X_FIX)); ut_ad(n_nodes > 0); buf_ptr_get_fsp_addr(node2, &space, &node2_addr); node1_addr = flst_get_prev_addr(node2, mtr); if (!fil_addr_is_null(node1_addr)) { /* Update next field of node1 */ if (node1_addr.page == node2_addr.page) { node1 = buf_frame_align(node2) + node1_addr.boffset; } else { node1 = fut_get_ptr(space, node1_addr, RW_X_LATCH, mtr); } flst_write_addr(node1 + FLST_NEXT, fil_addr_null, mtr); } else { /* node2 was first in list: update the field in base */ flst_write_addr(base + FLST_FIRST, fil_addr_null, mtr); } flst_write_addr(base + FLST_LAST, node1_addr, mtr); /* Update len of base node */ len = flst_get_len(base, mtr); ut_ad(len >= n_nodes); mlog_write_ulint(base + FLST_LEN, len - n_nodes, MLOG_4BYTES, mtr); }
void mlog_write_ulint( /*=============*/ byte* ptr, /* in: pointer where to write */ ulint val, /* in: value to write */ byte type, /* in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ mtr_t* mtr) /* in: mini-transaction handle */ { byte* log_ptr; if (ptr < buf_pool->frame_zero || ptr >= buf_pool->high_end) { fprintf(stderr, "InnoDB: Error: trying to write to" " a stray memory location %p\n", (void*) ptr); ut_error; } if (type == MLOG_1BYTE) { mach_write_to_1(ptr, val); } else if (type == MLOG_2BYTES) { mach_write_to_2(ptr, val); } else { ut_ad(type == MLOG_4BYTES); mach_write_to_4(ptr, val); } log_ptr = mlog_open(mtr, 11 + 2 + 5); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr); mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr)); log_ptr += 2; log_ptr += mach_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); }
void mlog_write_string( /*==============*/ byte* ptr, /* in: pointer where to write */ const byte* str, /* in: string to write */ ulint len, /* in: string length */ mtr_t* mtr) /* in: mini-transaction handle */ { byte* log_ptr; if (UNIV_UNLIKELY(ptr < buf_pool->frame_zero) || UNIV_UNLIKELY(ptr >= buf_pool->high_end)) { fprintf(stderr, "InnoDB: Error: trying to write to" " a stray memory location %p\n", (void*) ptr); ut_error; } ut_ad(ptr && mtr); ut_a(len < UNIV_PAGE_SIZE); ut_memcpy(ptr, str, len); log_ptr = mlog_open(mtr, 30); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_WRITE_STRING, log_ptr, mtr); mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr)); log_ptr += 2; mach_write_to_2(log_ptr, len); log_ptr += 2; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, str, len); }
ibool row_search_on_row_ref( /*==================*/ /* out: TRUE if found */ btr_pcur_t* pcur, /* in/out: persistent cursor, which must be closed by the caller */ ulint mode, /* in: BTR_MODIFY_LEAF, ... */ dict_table_t* table, /* in: table */ dtuple_t* ref, /* in: row reference */ mtr_t* mtr) /* in: mtr */ { ulint low_match; rec_t* rec; dict_index_t* index; page_t* page; ut_ad(dtuple_check_typed(ref)); index = dict_table_get_first_index(table); ut_a(dtuple_get_n_fields(ref) == dict_index_get_n_unique(index)); btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr); low_match = btr_pcur_get_low_match(pcur); rec = btr_pcur_get_rec(pcur); page = buf_frame_align(rec); if (rec == page_get_infimum_rec(page)) { return(FALSE); } if (low_match != dtuple_get_n_fields(ref)) { return(FALSE); } return(TRUE); }
void flst_print( /*=======*/ flst_base_node_t* base, /* in: pointer to base node of list */ mtr_t* mtr) /* in: mtr */ { buf_frame_t* frame; ulint len; ut_ad(base && mtr); ut_ad(mtr_memo_contains(mtr, buf_block_align(base), MTR_MEMO_PAGE_X_FIX)); frame = buf_frame_align(base); len = flst_get_len(base, mtr); printf("FILE-BASED LIST:\n"); printf("Base node in space %lu page %lu byte offset %lu; len %lu\n", buf_frame_get_space_id(frame), buf_frame_get_page_no(frame), (ulint) (base - frame), len); }
void flst_add_first( /*===========*/ flst_base_node_t* base, /* in: pointer to base node of list */ flst_node_t* node, /* in: node to add */ mtr_t* mtr) /* in: mini-transaction handle */ { ulint space; fil_addr_t node_addr; ulint len; fil_addr_t first_addr; flst_node_t* first_node; ut_ad(mtr && base && node); ut_ad(base != node); ut_ad(mtr_memo_contains(mtr, buf_block_align(base), MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains(mtr, buf_block_align(node), MTR_MEMO_PAGE_X_FIX)); len = flst_get_len(base, mtr); first_addr = flst_get_first(base, mtr); buf_ptr_get_fsp_addr(node, &space, &node_addr); /* If the list is not empty, call flst_insert_before */ if (len != 0) { if (first_addr.page == node_addr.page) { first_node = buf_frame_align(node) + first_addr.boffset; } else { first_node = fut_get_ptr(space, first_addr, RW_X_LATCH, mtr); } flst_insert_before(base, node, first_node, mtr); } else { /* else call flst_add_to_empty */ flst_add_to_empty(base, node, mtr); } }
trx_undo_rec_t* trx_undo_get_next_rec( /*==================*/ /* out: undo log record, the page s-latched, NULL if none */ trx_undo_rec_t* rec, /* in: undo record */ ulint page_no,/* in: undo log header page number */ ulint offset, /* in: undo log header offset on page */ mtr_t* mtr) /* in: mtr */ { trx_undo_rec_t* next_rec; next_rec = trx_undo_page_get_next_rec(rec, page_no, offset); if (next_rec) { return(next_rec); } return(trx_undo_get_next_rec_from_next_page(buf_frame_align(rec), page_no, offset, RW_S_LATCH, mtr)); }
void mlog_write_dulint( /*==============*/ byte* ptr, /* in: pointer where to write */ dulint val, /* in: value to write */ mtr_t* mtr) /* in: mini-transaction handle */ { byte* log_ptr; if (UNIV_UNLIKELY(ptr < buf_pool->frame_zero) || UNIV_UNLIKELY(ptr >= buf_pool->high_end)) { fprintf(stderr, "InnoDB: Error: trying to write to" " a stray memory location %p\n", (void*) ptr); ut_error; } ut_ad(ptr && mtr); mach_write_to_8(ptr, val); log_ptr = mlog_open(mtr, 11 + 2 + 9); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_8BYTES, log_ptr, mtr); mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr)); log_ptr += 2; log_ptr += mach_dulint_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); }
ibool row_search_index_entry( /*===================*/ /* out: TRUE if found */ dict_index_t* index, /* in: index */ dtuple_t* entry, /* in: index entry */ ulint mode, /* in: BTR_MODIFY_LEAF, ... */ btr_pcur_t* pcur, /* in/out: persistent cursor, which must be closed by the caller */ mtr_t* mtr) /* in: mtr */ { ulint n_fields; ulint low_match; page_t* page; rec_t* rec; ut_ad(dtuple_check_typed(entry)); btr_pcur_open(index, entry, PAGE_CUR_LE, mode, pcur, mtr); low_match = btr_pcur_get_low_match(pcur); rec = btr_pcur_get_rec(pcur); page = buf_frame_align(rec); n_fields = dtuple_get_n_fields(entry); if (rec == page_get_infimum_rec(page)) { return(FALSE); } if (low_match != n_fields) { /* Not found */ return(FALSE); } return(TRUE); }
void flst_remove( /*========*/ flst_base_node_t* base, /* in: pointer to base node of list */ flst_node_t* node2, /* in: node to remove */ mtr_t* mtr) /* in: mini-transaction handle */ { ulint space; flst_node_t* node1; fil_addr_t node1_addr; fil_addr_t node2_addr; flst_node_t* node3; fil_addr_t node3_addr; ulint len; ut_ad(mtr && node2 && base); ut_ad(mtr_memo_contains(mtr, buf_block_align(base), MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains(mtr, buf_block_align(node2), MTR_MEMO_PAGE_X_FIX)); buf_ptr_get_fsp_addr(node2, &space, &node2_addr); node1_addr = flst_get_prev_addr(node2, mtr); node3_addr = flst_get_next_addr(node2, mtr); if (!fil_addr_is_null(node1_addr)) { /* Update next field of node1 */ if (node1_addr.page == node2_addr.page) { node1 = buf_frame_align(node2) + node1_addr.boffset; } else { node1 = fut_get_ptr(space, node1_addr, RW_X_LATCH, mtr); } ut_ad(node1 != node2); flst_write_addr(node1 + FLST_NEXT, node3_addr, mtr); } else { /* node2 was first in list: update first field in base */ flst_write_addr(base + FLST_FIRST, node3_addr, mtr); } if (!fil_addr_is_null(node3_addr)) { /* Update prev field of node3 */ if (node3_addr.page == node2_addr.page) { node3 = buf_frame_align(node2) + node3_addr.boffset; } else { node3 = fut_get_ptr(space, node3_addr, RW_X_LATCH, mtr); } ut_ad(node2 != node3); flst_write_addr(node3 + FLST_PREV, node1_addr, mtr); } else { /* node2 was last in list: update last field in base */ flst_write_addr(base + FLST_LAST, node1_addr, mtr); } /* Update len of base node */ len = flst_get_len(base, mtr); ut_ad(len > 0); mlog_write_ulint(base + FLST_LEN, len - 1, MLOG_4BYTES, mtr); }
/*************************************************************************** Gets the next record to purge and updates the info in the purge system. */ static trx_undo_rec_t* trx_purge_get_next_rec( /*===================*/ /* out: copy of an undo log record or pointer to the dummy undo log record */ mem_heap_t* heap) /* in: memory heap where copied */ { trx_undo_rec_t* rec; trx_undo_rec_t* rec_copy; trx_undo_rec_t* rec2; trx_undo_rec_t* next_rec; page_t* undo_page; page_t* page; ulint offset; ulint page_no; ulint space; ulint type; ulint cmpl_info; mtr_t mtr; ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored); space = purge_sys->rseg->space; page_no = purge_sys->page_no; offset = purge_sys->offset; if (offset == 0) { /* It is the dummy undo log record, which means that there is no need to purge this undo log */ trx_purge_rseg_get_next_history_log(purge_sys->rseg); /* Look for the next undo log and record to purge */ trx_purge_choose_next_log(); return(&trx_purge_dummy_rec); } mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr); rec = undo_page + offset; rec2 = rec; for (;;) { /* Try first to find the next record which requires a purge operation from the same page of the same undo log */ next_rec = trx_undo_page_get_next_rec(rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset); if (next_rec == NULL) { rec2 = trx_undo_get_next_rec( rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset, &mtr); break; } rec2 = next_rec; type = trx_undo_rec_get_type(rec2); if (type == TRX_UNDO_DEL_MARK_REC) { break; } cmpl_info = trx_undo_rec_get_cmpl_info(rec2); if (trx_undo_rec_get_extern_storage(rec2)) { break; } if ((type == TRX_UNDO_UPD_EXIST_REC) && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { break; } } if (rec2 == NULL) { mtr_commit(&mtr); trx_purge_rseg_get_next_history_log(purge_sys->rseg); /* Look for the next undo log and record to purge */ trx_purge_choose_next_log(); mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr); rec = undo_page + offset; } else { page = buf_frame_align(rec2); purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2); purge_sys->page_no = buf_frame_get_page_no(page); purge_sys->offset = rec2 - page; if (undo_page != page) { /* We advance to a new page of the undo log: */ purge_sys->n_pages_handled++; } } rec_copy = trx_undo_rec_copy(rec, heap); mtr_commit(&mtr); return(rec_copy); }
/*************************************************************************** Chooses the next undo log to purge and updates the info in purge_sys. This function is used to initialize purge_sys when the next record to purge is not known, and also to update the purge system info on the next record when purge has handled the whole undo log for a transaction. */ static void trx_purge_choose_next_log(void) /*===========================*/ { trx_undo_rec_t* rec; trx_rseg_t* rseg; trx_rseg_t* min_rseg; dulint min_trx_no; ulint space = 0; /* remove warning (??? bug ???) */ ulint page_no = 0; /* remove warning (??? bug ???) */ ulint offset = 0; /* remove warning (??? bug ???) */ mtr_t mtr; ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored == FALSE); rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); min_trx_no = ut_dulint_max; min_rseg = NULL; while (rseg) { mutex_enter(&(rseg->mutex)); if (rseg->last_page_no != FIL_NULL) { if ((min_rseg == NULL) || (ut_dulint_cmp(min_trx_no, rseg->last_trx_no) > 0)) { min_rseg = rseg; min_trx_no = rseg->last_trx_no; space = rseg->space; ut_a(space == 0); /* We assume in purge of externally stored fields that space id == 0 */ page_no = rseg->last_page_no; offset = rseg->last_offset; } } mutex_exit(&(rseg->mutex)); rseg = UT_LIST_GET_NEXT(rseg_list, rseg); } if (min_rseg == NULL) { return; } mtr_start(&mtr); if (!min_rseg->last_del_marks) { /* No need to purge this log */ rec = &trx_purge_dummy_rec; } else { rec = trx_undo_get_first_rec(space, page_no, offset, RW_S_LATCH, &mtr); if (rec == NULL) { /* Undo log empty */ rec = &trx_purge_dummy_rec; } } purge_sys->next_stored = TRUE; purge_sys->rseg = min_rseg; purge_sys->hdr_page_no = page_no; purge_sys->hdr_offset = offset; purge_sys->purge_trx_no = min_trx_no; if (rec == &trx_purge_dummy_rec) { purge_sys->purge_undo_no = ut_dulint_zero; purge_sys->page_no = page_no; purge_sys->offset = 0; } else { purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec); purge_sys->page_no = buf_frame_get_page_no(rec); purge_sys->offset = rec - buf_frame_align(rec); } mtr_commit(&mtr); }
/*************************************************************** Purges an update of an existing record. Also purges an update of a delete marked record if that record contained an externally stored field. */ static void row_purge_upd_exist_or_extern( /*==========================*/ purge_node_t* node, /* in: row purge node */ que_thr_t* thr) /* in: query thread */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; upd_field_t* ufield; ibool is_insert; ulint rseg_id; ulint page_no; ulint offset; ulint internal_offset; byte* data_field; ulint data_field_len; ulint i; mtr_t mtr; ut_ad(node && thr); if (node->rec_type == TRX_UNDO_UPD_DEL_REC) { goto skip_secondaries; } heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; if (row_upd_changes_ord_field_binary(NULL, node->index, node->update)) { /* Build the older version of the index entry */ entry = row_build_index_entry(node->row, index, heap); row_purge_remove_sec_if_poss(node, thr, index, entry); } node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); skip_secondaries: /* Free possible externally stored fields */ for (i = 0; i < upd_get_n_fields(node->update); i++) { ufield = upd_get_nth_field(node->update, i); if (ufield->extern_storage) { /* We use the fact that new_val points to node->undo_rec and get thus the offset of dfield data inside the unod record. Then we can calculate from node->roll_ptr the file address of the new_val data */ internal_offset = ((byte*)ufield->new_val.data) - node->undo_rec; ut_a(internal_offset < UNIV_PAGE_SIZE); trx_undo_decode_roll_ptr(node->roll_ptr, &is_insert, &rseg_id, &page_no, &offset); mtr_start(&mtr); /* We have to acquire an X-latch to the clustered index tree */ index = dict_table_get_first_index(node->table); mtr_x_lock(dict_tree_get_lock(index->tree), &mtr); /* We assume in purge of externally stored fields that the space id of the undo log record is 0! */ data_field = buf_page_get(0, page_no, RW_X_LATCH, &mtr) + offset + internal_offset; buf_page_dbg_add_level(buf_frame_align(data_field), SYNC_TRX_UNDO_PAGE); data_field_len = ufield->new_val.len; btr_free_externally_stored_field(index, data_field, data_field_len, FALSE, &mtr); mtr_commit(&mtr); } } }
/*************************************************************** Purges an update of an existing record. Also purges an update of a delete marked record if that record contained an externally stored field. */ static void row_purge_upd_exist_or_extern( /*==========================*/ purge_node_t* node) /* in: row purge node */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; upd_field_t* ufield; ibool is_insert; ulint rseg_id; ulint page_no; ulint offset; ulint internal_offset; byte* data_field; ulint data_field_len; ulint i; mtr_t mtr; ut_ad(node); if (node->rec_type == TRX_UNDO_UPD_DEL_REC) { goto skip_secondaries; } heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; if (row_upd_changes_ord_field_binary(NULL, node->index, node->update)) { /* Build the older version of the index entry */ entry = row_build_index_entry(node->row, index, heap); row_purge_remove_sec_if_poss(node, index, entry); } node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); skip_secondaries: /* Free possible externally stored fields */ for (i = 0; i < upd_get_n_fields(node->update); i++) { ufield = upd_get_nth_field(node->update, i); if (ufield->extern_storage) { /* We use the fact that new_val points to node->undo_rec and get thus the offset of dfield data inside the unod record. Then we can calculate from node->roll_ptr the file address of the new_val data */ internal_offset = ((byte*)ufield->new_val.data) - node->undo_rec; ut_a(internal_offset < UNIV_PAGE_SIZE); trx_undo_decode_roll_ptr(node->roll_ptr, &is_insert, &rseg_id, &page_no, &offset); mtr_start(&mtr); /* We have to acquire an X-latch to the clustered index tree */ index = dict_table_get_first_index(node->table); mtr_x_lock(dict_index_get_lock(index), &mtr); /* NOTE: we must also acquire an X-latch to the root page of the tree. We will need it when we free pages from the tree. If the tree is of height 1, the tree X-latch does NOT protect the root page, because it is also a leaf page. Since we will have a latch on an undo log page, we would break the latching order if we would only later latch the root page of such a tree! */ btr_root_get(index, &mtr); /* We assume in purge of externally stored fields that the space id of the undo log record is 0! */ data_field = buf_page_get(0, page_no, RW_X_LATCH, &mtr) + offset + internal_offset; #ifdef UNIV_SYNC_DEBUG buf_page_dbg_add_level(buf_frame_align(data_field), SYNC_TRX_UNDO_PAGE); #endif /* UNIV_SYNC_DEBUG */ data_field_len = ufield->new_val.len; btr_free_externally_stored_field(index, data_field, data_field_len, FALSE, &mtr); mtr_commit(&mtr); } } }