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); }
/********************************************************//** Writes 1 - 4 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN 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; switch (type) { case MLOG_1BYTE: mach_write_to_1(ptr, val); break; case MLOG_2BYTES: mach_write_to_2(ptr, val); break; case MLOG_4BYTES: mach_write_to_4(ptr, val); break; default: ut_error; } 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, page_offset(ptr)); log_ptr += 2; log_ptr += mach_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); }
/********************************************************//** Parses a log record written by mlog_write_ulint or mlog_write_dulint. @return parsed record end, NULL if not a complete record or a corrupt record */ UNIV_INTERN byte* mlog_parse_nbytes( /*==============*/ ulint type, /*!< in: log record type: MLOG_1BYTE, ... */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* page, /*!< in: page where to apply the log record, or NULL */ void* page_zip)/*!< in/out: compressed page, or NULL */ { ulint offset; ulint val; dulint dval; ut_a(type <= MLOG_8BYTES); ut_a(!page || !page_zip || fil_page_get_type(page) != FIL_PAGE_INDEX); if (end_ptr < ptr + 2) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; if (offset >= UNIV_PAGE_SIZE) { recv_sys->found_corrupt_log = TRUE; return(NULL); } if (type == MLOG_8BYTES) { ptr = mach_dulint_parse_compressed(ptr, end_ptr, &dval); if (ptr == NULL) { return(NULL); } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_8 (((page_zip_des_t*) page_zip)->data + offset, dval); } mach_write_to_8(page + offset, dval); } return(ptr); } ptr = mach_parse_compressed(ptr, end_ptr, &val); if (ptr == NULL) { return(NULL); } switch (type) { case MLOG_1BYTE: if (UNIV_UNLIKELY(val > 0xFFUL)) { goto corrupt; } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_1 (((page_zip_des_t*) page_zip)->data + offset, val); } mach_write_to_1(page + offset, val); } break; case MLOG_2BYTES: if (UNIV_UNLIKELY(val > 0xFFFFUL)) { goto corrupt; } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_2 (((page_zip_des_t*) page_zip)->data + offset, val); } mach_write_to_2(page + offset, val); } break; case MLOG_4BYTES: if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_4 (((page_zip_des_t*) page_zip)->data + offset, val); } mach_write_to_4(page + offset, val); } break; default: corrupt: recv_sys->found_corrupt_log = TRUE; ptr = NULL; } return(ptr); }
void row_upd_index_write_log( /*====================*/ upd_t* update, /* in: update vector */ byte* log_ptr,/* in: pointer to mlog buffer: must contain at least MLOG_BUF_MARGIN bytes of free space; the buffer is closed within this function */ mtr_t* mtr) /* in: mtr into whose log to write */ { upd_field_t* upd_field; dfield_t* new_val; ulint len; ulint n_fields; byte* buf_end; ulint i; n_fields = upd_get_n_fields(update); buf_end = log_ptr + MLOG_BUF_MARGIN; mach_write_to_1(log_ptr, update->info_bits); log_ptr++; log_ptr += mach_write_compressed(log_ptr, n_fields); for (i = 0; i < n_fields; i++) { ut_ad(MLOG_BUF_MARGIN > 30); if (log_ptr + 30 > buf_end) { mlog_close(mtr, log_ptr); log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN); buf_end = log_ptr + MLOG_BUF_MARGIN; } upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); len = new_val->len; log_ptr += mach_write_compressed(log_ptr, upd_field->field_no); log_ptr += mach_write_compressed(log_ptr, len); if (len != UNIV_SQL_NULL) { if (log_ptr + len < buf_end) { ut_memcpy(log_ptr, new_val->data, len); log_ptr += len; } else { mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, new_val->data, len); log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN); buf_end = log_ptr + MLOG_BUF_MARGIN; } } } mlog_close(mtr, log_ptr); }
byte* mlog_parse_nbytes( /*==============*/ /* out: parsed record end, NULL if not a complete record or a corrupt record */ ulint type, /* in: log record type: MLOG_1BYTE, ... */ byte* ptr, /* in: buffer */ byte* end_ptr,/* in: buffer end */ byte* page) /* in: page where to apply the log record, or NULL */ { ulint offset; ulint val; dulint dval; ut_a(type <= MLOG_8BYTES); if (end_ptr < ptr + 2) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; if (offset >= UNIV_PAGE_SIZE) { recv_sys->found_corrupt_log = TRUE; return(NULL); } if (type == MLOG_8BYTES) { ptr = mach_dulint_parse_compressed(ptr, end_ptr, &dval); if (ptr == NULL) { return(NULL); } if (page) { mach_write_to_8(page + offset, dval); } return(ptr); } ptr = mach_parse_compressed(ptr, end_ptr, &val); if (ptr == NULL) { return(NULL); } if (type == MLOG_1BYTE) { if (val > 0xFFUL) { recv_sys->found_corrupt_log = TRUE; return(NULL); } } else if (type == MLOG_2BYTES) { if (val > 0xFFFFUL) { recv_sys->found_corrupt_log = TRUE; return(NULL); } } else { if (type != MLOG_4BYTES) { recv_sys->found_corrupt_log = TRUE; return(NULL); } } if (page) { if (type == MLOG_1BYTE) { mach_write_to_1(page + offset, val); } else if (type == MLOG_2BYTES) { mach_write_to_2(page + offset, val); } else { ut_a(type == MLOG_4BYTES); mach_write_to_4(page + offset, val); } } return(ptr); }
/************************************************************************** Reports in the undo log of an update or delete marking of a clustered index record. */ static ulint trx_undo_page_report_modify( /*========================*/ /* out: byte offset of the inserted undo log entry on the page if succeed, 0 if fail */ page_t* undo_page, /* in: undo log page */ trx_t* trx, /* in: transaction */ dict_index_t* index, /* in: clustered index where update or delete marking is done */ rec_t* rec, /* in: clustered index record which has NOT yet been modified */ const ulint* offsets, /* in: rec_get_offsets(rec, index) */ upd_t* update, /* in: update vector which tells the columns to be updated; in the case of a delete, this should be set to NULL */ ulint cmpl_info, /* in: compiler info on secondary index updates */ mtr_t* mtr) /* in: mtr */ { dict_table_t* table; upd_field_t* upd_field; ulint first_free; byte* ptr; ulint len; byte* field; ulint flen; ulint pos; dulint roll_ptr; dulint trx_id; ulint bits; ulint col_no; byte* old_ptr; ulint type_cmpl; byte* type_cmpl_ptr; ulint i; ut_a(index->type & DICT_CLUSTERED); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE); table = index->table; first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); ptr = undo_page + first_free; ut_ad(first_free <= UNIV_PAGE_SIZE); if (trx_undo_left(undo_page, ptr) < 50) { /* NOTE: the value 50 must be big enough so that the general fields written below fit on the undo log page */ return(0); } /* Reserve 2 bytes for the pointer to the next undo log record */ ptr += 2; /* Store first some general parameters to the undo log */ if (update) { if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) { type_cmpl = TRX_UNDO_UPD_DEL_REC; } else { type_cmpl = TRX_UNDO_UPD_EXIST_REC; } } else { type_cmpl = TRX_UNDO_DEL_MARK_REC; } type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT); mach_write_to_1(ptr, type_cmpl); type_cmpl_ptr = ptr; ptr++; len = mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += len; len = mach_dulint_write_much_compressed(ptr, table->id); ptr += len; /*----------------------------------------*/ /* Store the state of the info bits */ bits = rec_get_info_bits(rec, dict_table_is_comp(table)); mach_write_to_1(ptr, bits); ptr += 1; /* Store the values of the system columns */ field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_TRX_ID), &len); ut_ad(len == DATA_TRX_ID_LEN); trx_id = trx_read_trx_id(field); field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_ROLL_PTR), &len); ut_ad(len == DATA_ROLL_PTR_LEN); roll_ptr = trx_read_roll_ptr(field); len = mach_dulint_write_compressed(ptr, trx_id); ptr += len; len = mach_dulint_write_compressed(ptr, roll_ptr); ptr += len; /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record which will be modified in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { field = rec_get_nth_field(rec, offsets, i, &flen); if (trx_undo_left(undo_page, ptr) < 4) { return(0); } len = mach_write_compressed(ptr, flen); ptr += len; if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } /*----------------------------------------*/ /* Save to the undo log the old values of the columns to be updated. */ if (update) { if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, upd_get_n_fields(update)); ptr += len; for (i = 0; i < upd_get_n_fields(update); i++) { upd_field = upd_get_nth_field(update, i); pos = upd_field->field_no; /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, pos); ptr += len; /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } if (rec_offs_nth_extern(offsets, pos)) { /* If a field has external storage, we add to flen the flag */ len = mach_write_compressed( ptr, UNIV_EXTERN_STORAGE_FIELD + flen); /* Notify purge that it eventually has to free the old externally stored field */ trx->update_undo->del_marks = TRUE; *type_cmpl_ptr = *type_cmpl_ptr | TRX_UNDO_UPD_EXTERN; } else { len = mach_write_compressed(ptr, flen); } ptr += len; if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } } /*----------------------------------------*/ /* In the case of a delete marking, and also in the case of an update where any ordering field of any index changes, store the values of all columns which occur as ordering fields in any index. This info is used in the purge of old versions where we use it to build and search the delete marked index records, to look if we can remove them from the index tree. Note that starting from 4.0.14 also externally stored fields can be ordering in some index. But we always store at least 384 first bytes locally to the clustered index record, which means we can construct the column prefix fields in the index from the stored data. */ if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { trx->update_undo->del_marks = TRUE; if (trx_undo_left(undo_page, ptr) < 5) { return(0); } old_ptr = ptr; /* Reserve 2 bytes to write the number of bytes the stored fields take in this undo record */ ptr += 2; for (col_no = 0; col_no < dict_table_get_n_cols(table); col_no++) { const dict_col_t* col = dict_table_get_nth_col(table, col_no); if (col->ord_part > 0) { pos = dict_index_get_nth_col_pos(index, col_no); /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, pos); ptr += len; /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, flen); ptr += len; if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } } mach_write_to_2(old_ptr, ptr - old_ptr); } /*----------------------------------------*/ /* Write pointers to the previous and the next undo log records */ if (trx_undo_left(undo_page, ptr) < 2) { return(0); } mach_write_to_2(ptr, first_free); ptr += 2; mach_write_to_2(undo_page + first_free, ptr - undo_page); mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, ptr - undo_page); /* Write to the REDO log about this change in the UNDO log */ trx_undof_page_add_undo_rec_log(undo_page, first_free, ptr - undo_page, mtr); return(first_free); }
/************************************************************************** Reports in the undo log of an insert of a clustered index record. */ static ulint trx_undo_page_report_insert( /*========================*/ /* out: offset of the inserted entry on the page if succeed, 0 if fail */ page_t* undo_page, /* in: undo log page */ trx_t* trx, /* in: transaction */ dict_index_t* index, /* in: clustered index */ dtuple_t* clust_entry, /* in: index entry which will be inserted to the clustered index */ mtr_t* mtr) /* in: mtr */ { ulint first_free; byte* ptr; ulint len; dfield_t* field; ulint flen; ulint i; ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT); first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); ptr = undo_page + first_free; ut_ad(first_free <= UNIV_PAGE_SIZE); if (trx_undo_left(undo_page, ptr) < 30) { /* NOTE: the value 30 must be big enough such that the general fields written below fit on the undo log page */ return(0); } /* Reserve 2 bytes for the pointer to the next undo log record */ ptr += 2; /* Store first some general parameters to the undo log */ mach_write_to_1(ptr, TRX_UNDO_INSERT_REC); ptr++; len = mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += len; len = mach_dulint_write_much_compressed(ptr, (index->table)->id); ptr += len; /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record to be inserted in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { field = dtuple_get_nth_field(clust_entry, i); flen = dfield_get_len(field); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, flen); ptr += len; if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, dfield_get_data(field), flen); ptr += flen; } } if (trx_undo_left(undo_page, ptr) < 2) { return(0); } /*----------------------------------------*/ /* Write pointers to the previous and the next undo log records */ if (trx_undo_left(undo_page, ptr) < 2) { return(0); } mach_write_to_2(ptr, first_free); ptr += 2; mach_write_to_2(undo_page + first_free, ptr - undo_page); mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, ptr - undo_page); /* Write the log entry to the REDO log of this change in the UNDO log */ trx_undof_page_add_undo_rec_log(undo_page, first_free, ptr - undo_page, mtr); return(first_free); }