summaryrefslogtreecommitdiff
path: root/innobase/trx/trx0rec.c
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/trx/trx0rec.c')
-rw-r--r--innobase/trx/trx0rec.c1282
1 files changed, 1282 insertions, 0 deletions
diff --git a/innobase/trx/trx0rec.c b/innobase/trx/trx0rec.c
new file mode 100644
index 00000000000..fa2e480ece0
--- /dev/null
+++ b/innobase/trx/trx0rec.c
@@ -0,0 +1,1282 @@
+/******************************************************
+Transaction undo log record
+
+(c) 1996 Innobase Oy
+
+Created 3/26/1996 Heikki Tuuri
+*******************************************************/
+
+#include "trx0rec.h"
+
+#ifdef UNIV_NONINL
+#include "trx0rec.ic"
+#endif
+
+#include "fsp0fsp.h"
+#include "mach0data.h"
+#include "trx0rseg.h"
+#include "trx0trx.h"
+#include "trx0undo.h"
+#include "dict0dict.h"
+#include "ut0mem.h"
+#include "row0upd.h"
+#include "que0que.h"
+#include "trx0purge.h"
+#include "row0row.h"
+
+/*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
+
+/**************************************************************************
+Writes the mtr log entry of the inserted undo log record on the undo log
+page. */
+UNIV_INLINE
+void
+trx_undof_page_add_undo_rec_log(
+/*============================*/
+ page_t* undo_page, /* in: undo log page */
+ ulint old_free, /* in: start offset of the inserted entry */
+ ulint new_free, /* in: end offset of the entry */
+ mtr_t* mtr) /* in: mtr */
+{
+ byte* log_ptr;
+ ulint len;
+
+#ifdef notdefined
+ ulint i;
+ byte* prev_rec_ptr;
+ byte* ptr;
+ ulint min_len;
+
+ ut_ad(new_free >= old_free + 4);
+
+ i = 0;
+ ptr = undo_page + old_free + 2;
+
+ if (old_free > mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
+ + TRX_UNDO_PAGE_START)) {
+ prev_rec_ptr = undo_page + mach_read_from_2(ptr - 4) + 2;
+
+ min_len = ut_min(new_free - old_free - 4,
+ (undo_page + old_free - 2) - prev_rec_ptr);
+ for (;;) {
+ if (i >= min_len) {
+
+ break;
+ } else if ((*ptr == *prev_rec_ptr)
+ || ((*ptr == *prev_rec_ptr + 1)
+ && (ptr + 1 == suffix))) {
+ i++;
+ ptr++;
+ prev_rec_ptr++;
+ } else {
+ break;
+ }
+ }
+ }
+
+ mlog_write_initial_log_record(undo_page, MLOG_UNDO_INSERT, mtr);
+
+ mlog_catenate_ulint(mtr, old_free, MLOG_2BYTES);
+
+ mlog_catenate_ulint_compressed(mtr, i);
+
+ mlog_catenate_string(mtr, ptr, new_free - old_free - 2 - i);
+#endif
+ log_ptr = mlog_open(mtr, 30 + MLOG_BUF_MARGIN);
+
+ if (log_ptr == NULL) {
+
+ return;
+ }
+
+ log_ptr = mlog_write_initial_log_record_fast(undo_page,
+ MLOG_UNDO_INSERT, log_ptr, mtr);
+ len = new_free - old_free - 4;
+
+ mach_write_to_2(log_ptr, len);
+ log_ptr += 2;
+
+ if (len < 256) {
+ ut_memcpy(log_ptr, undo_page + old_free + 2, len);
+ log_ptr += len;
+ }
+
+ mlog_close(mtr, log_ptr);
+
+ if (len >= MLOG_BUF_MARGIN) {
+ mlog_catenate_string(mtr, undo_page + old_free + 2, len);
+ }
+}
+
+/***************************************************************
+Parses a redo log record of adding an undo log record. */
+
+byte*
+trx_undo_parse_add_undo_rec(
+/*========================*/
+ /* out: end of log record or NULL */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ page_t* page) /* in: page or NULL */
+{
+ ulint len;
+ byte* rec;
+ ulint first_free;
+
+ if (end_ptr < ptr + 2) {
+
+ return(NULL);
+ }
+
+ len = mach_read_from_2(ptr);
+ ptr += 2;
+
+ if (end_ptr < ptr + len) {
+
+ return(NULL);
+ }
+
+ if (page == NULL) {
+
+ return(ptr + len);
+ }
+
+ first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
+ + TRX_UNDO_PAGE_FREE);
+ rec = page + first_free;
+
+ mach_write_to_2(rec, first_free + 4 + len);
+ mach_write_to_2(rec + 2 + len, first_free);
+
+ mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
+ first_free + 4 + len);
+ ut_memcpy(rec + 2, ptr, len);
+
+ return(ptr + len);
+}
+
+/**************************************************************************
+Calculates the free space left for extending an undo log record. */
+UNIV_INLINE
+ulint
+trx_undo_left(
+/*==========*/
+ /* out: bytes left */
+ page_t* page, /* in: undo log page */
+ byte* ptr) /* in: pointer to page */
+{
+ /* The '- 10' is a safety margin, in case we have some small
+ calculation error below */
+
+ return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
+}
+
+/**************************************************************************
+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);
+}
+
+/**************************************************************************
+Reads from an undo log record the general parameters. */
+
+byte*
+trx_undo_rec_get_pars(
+/*==================*/
+ /* out: remaining part of undo log
+ record after reading these values */
+ trx_undo_rec_t* undo_rec, /* in: undo log record */
+ ulint* type, /* out: undo record type:
+ TRX_UNDO_INSERT_REC, ... */
+ ulint* cmpl_info, /* out: compiler info, relevant only
+ for update type records */
+ dulint* undo_no, /* out: undo log record number */
+ dulint* table_id) /* out: table id */
+{
+ byte* ptr;
+ ulint len;
+ ulint type_cmpl;
+
+ ptr = undo_rec + 2;
+
+ type_cmpl = mach_read_from_1(ptr);
+ ptr++;
+
+ *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
+ *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
+
+ *undo_no = mach_dulint_read_much_compressed(ptr);
+ len = mach_dulint_get_much_compressed_size(*undo_no);
+ ptr += len;
+
+ *table_id = mach_dulint_read_much_compressed(ptr);
+ len = mach_dulint_get_much_compressed_size(*table_id);
+ ptr += len;
+
+ return(ptr);
+}
+
+/**************************************************************************
+Reads from an undo log record a stored column value. */
+UNIV_INLINE
+byte*
+trx_undo_rec_get_col_val(
+/*=====================*/
+ /* out: remaining part of undo log record after
+ reading these values */
+ byte* ptr, /* in: pointer to remaining part of undo log record */
+ byte** field, /* out: pointer to stored field */
+ ulint* len) /* out: length of the field, or UNIV_SQL_NULL */
+{
+ *len = mach_read_compressed(ptr);
+ ptr += mach_get_compressed_size(*len);
+
+ *field = ptr;
+
+ if (*len != UNIV_SQL_NULL) {
+ ptr += *len;
+ }
+
+ return(ptr);
+}
+
+/***********************************************************************
+Builds a row reference from an undo log record. */
+
+byte*
+trx_undo_rec_get_row_ref(
+/*=====================*/
+ /* out: pointer to remaining part of undo
+ record */
+ byte* ptr, /* in: remaining part of a copy of an undo log
+ record, at the start of the row reference;
+ NOTE that this copy of the undo log record must
+ be preserved as long as the row reference is
+ used, as we do NOT copy the data in the
+ record! */
+ dict_index_t* index, /* in: clustered index */
+ dtuple_t** ref, /* out, own: row reference */
+ mem_heap_t* heap) /* in: memory heap from which the memory
+ needed is allocated */
+{
+ ulint i;
+ dfield_t* dfield;
+ byte* field;
+ ulint len;
+ ulint ref_len;
+
+ ut_ad(index && ptr && ref && heap);
+
+ ref_len = dict_index_get_n_unique(index);
+
+ *ref = dtuple_create(heap, ref_len);
+
+ dict_index_copy_types(*ref, index, ref_len);
+
+ for (i = 0; i < ref_len; i++) {
+ dfield = dtuple_get_nth_field(*ref, i);
+
+ ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
+
+ dfield_set_data(dfield, field, len);
+ }
+
+ return(ptr);
+}
+
+/***********************************************************************
+Skips a row reference from an undo log record. */
+
+byte*
+trx_undo_rec_skip_row_ref(
+/*======================*/
+ /* out: pointer to remaining part of undo
+ record */
+ byte* ptr, /* in: remaining part in update undo log
+ record, at the start of the row reference */
+ dict_index_t* index) /* in: clustered index */
+{
+ ulint i;
+ byte* field;
+ ulint len;
+ ulint ref_len;
+
+ ut_ad(index && ptr);
+
+ ref_len = dict_index_get_n_unique(index);
+
+ for (i = 0; i < ref_len; i++) {
+ ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
+ }
+
+ 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 */
+ 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;
+ dict_col_t* col;
+ 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;
+ ulint i;
+
+ ut_ad(index->type & DICT_CLUSTERED);
+ 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)) {
+ 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);
+
+ 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);
+ mach_write_to_1(ptr, bits);
+ ptr += 1;
+
+ /* Store the values of the system columns */
+ trx_id = dict_index_rec_get_sys_col(index, DATA_TRX_ID, rec);
+
+ roll_ptr = dict_index_rec_get_sys_col(index, DATA_ROLL_PTR, rec);
+
+ 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, 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, 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;
+ }
+ }
+ }
+
+ /*----------------------------------------*/
+ /* 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. */
+
+ 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++) {
+
+ 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, 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);
+}
+
+/**************************************************************************
+Reads from an undo log update record the system field values of the old
+version. */
+
+byte*
+trx_undo_update_rec_get_sys_cols(
+/*=============================*/
+ /* out: remaining part of undo log
+ record after reading these values */
+ byte* ptr, /* in: remaining part of undo log
+ record after reading general
+ parameters */
+ dulint* trx_id, /* out: trx id */
+ dulint* roll_ptr, /* out: roll ptr */
+ ulint* info_bits) /* out: info bits state */
+{
+ ulint len;
+
+ /* Read the state of the info bits */
+ *info_bits = mach_read_from_1(ptr);
+ ptr += 1;
+
+ /* Read the values of the system columns */
+
+ *trx_id = mach_dulint_read_compressed(ptr);
+ len = mach_dulint_get_compressed_size(*trx_id);
+ ptr += len;
+
+ *roll_ptr = mach_dulint_read_compressed(ptr);
+ len = mach_dulint_get_compressed_size(*roll_ptr);
+ ptr += len;
+
+ return(ptr);
+}
+
+/**************************************************************************
+Reads from an update undo log record the number of updated fields. */
+UNIV_INLINE
+byte*
+trx_undo_update_rec_get_n_upd_fields(
+/*=================================*/
+ /* out: remaining part of undo log record after
+ reading this value */
+ byte* ptr, /* in: pointer to remaining part of undo log record */
+ ulint* n) /* out: number of fields */
+{
+ *n = mach_read_compressed(ptr);
+ ptr += mach_get_compressed_size(*n);
+
+ return(ptr);
+}
+
+/**************************************************************************
+Reads from an update undo log record a stored field number. */
+UNIV_INLINE
+byte*
+trx_undo_update_rec_get_field_no(
+/*=============================*/
+ /* out: remaining part of undo log record after
+ reading this value */
+ byte* ptr, /* in: pointer to remaining part of undo log record */
+ ulint* field_no)/* out: field number */
+{
+ *field_no = mach_read_compressed(ptr);
+ ptr += mach_get_compressed_size(*field_no);
+
+ return(ptr);
+}
+
+/***********************************************************************
+Builds an update vector based on a remaining part of an undo log record. */
+
+byte*
+trx_undo_update_rec_get_update(
+/*===========================*/
+ /* out: remaining part of the record */
+ byte* ptr, /* in: remaining part in update undo log
+ record, after reading the row reference
+ NOTE that this copy of the undo log record must
+ be preserved as long as the update vector is
+ used, as we do NOT copy the data in the
+ record! */
+ dict_index_t* index, /* in: clustered index */
+ ulint type, /* in: TRX_UNDO_UPD_EXIST_REC,
+ TRX_UNDO_UPD_DEL_REC, or
+ TRX_UNDO_DEL_MARK_REC; in the last case,
+ only trx id and roll ptr fields are added to
+ the update vector */
+ dulint trx_id, /* in: transaction id from this undorecord */
+ dulint roll_ptr,/* in: roll pointer from this undo record */
+ ulint info_bits,/* in: info bits from this undo record */
+ mem_heap_t* heap, /* in: memory heap from which the memory
+ needed is allocated */
+ upd_t** upd) /* out, own: update vector */
+{
+ upd_field_t* upd_field;
+ upd_t* update;
+ ulint n_fields;
+ byte* buf;
+ byte* field;
+ ulint len;
+ ulint field_no;
+ ulint i;
+
+ if (type != TRX_UNDO_DEL_MARK_REC) {
+ ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
+ } else {
+ n_fields = 0;
+ }
+
+ update = upd_create(n_fields + 2, heap);
+
+ update->info_bits = info_bits;
+
+ /* Store first trx id and roll ptr to update vector */
+
+ upd_field = upd_get_nth_field(update, n_fields);
+ buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN);
+ trx_write_trx_id(buf, trx_id);
+
+ upd_field_set_field_no(upd_field,
+ dict_index_get_sys_col_pos(index, DATA_TRX_ID),
+ index);
+ dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
+
+ upd_field = upd_get_nth_field(update, n_fields + 1);
+ buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN);
+ trx_write_roll_ptr(buf, roll_ptr);
+
+ upd_field_set_field_no(upd_field,
+ dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
+ index);
+ dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
+
+ /* Store then the updated ordinary columns to update vector */
+
+ for (i = 0; i < n_fields; i++) {
+
+ ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
+ ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
+
+ upd_field = upd_get_nth_field(update, i);
+
+ upd_field_set_field_no(upd_field, field_no, index);
+
+ dfield_set_data(&(upd_field->new_val), field, len);
+ }
+
+ *upd = update;
+
+ return(ptr);
+}
+
+/***********************************************************************
+Builds a partial row from an update undo log record. It contains the
+columns which occur as ordering in any index of the table. */
+
+byte*
+trx_undo_rec_get_partial_row(
+/*=========================*/
+ /* out: pointer to remaining part of undo
+ record */
+ byte* ptr, /* in: remaining part in update undo log
+ record of a suitable type, at the start of
+ the stored index columns;
+ NOTE that this copy of the undo log record must
+ be preserved as long as the partial row is
+ used, as we do NOT copy the data in the
+ record! */
+ dict_index_t* index, /* in: clustered index */
+ dtuple_t** row, /* out, own: partial row */
+ mem_heap_t* heap) /* in: memory heap from which the memory
+ needed is allocated */
+{
+ dfield_t* dfield;
+ byte* field;
+ ulint len;
+ ulint field_no;
+ ulint col_no;
+ ulint row_len;
+ ulint total_len;
+ byte* start_ptr;
+ ulint i;
+
+ ut_ad(index && ptr && row && heap);
+
+ row_len = dict_table_get_n_cols(index->table);
+
+ *row = dtuple_create(heap, row_len);
+
+ dict_table_copy_types(*row, index->table);
+
+ start_ptr = ptr;
+
+ total_len = mach_read_from_2(ptr);
+ ptr += 2;
+
+ for (i = 0;; i++) {
+
+ if (ptr == start_ptr + total_len) {
+
+ break;
+ }
+
+ ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
+
+ col_no = dict_index_get_nth_col_no(index, field_no);
+
+ ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
+
+ dfield = dtuple_get_nth_field(*row, col_no);
+
+ dfield_set_data(dfield, field, len);
+ }
+
+ return(ptr);
+}
+
+/***************************************************************************
+Erases the unused undo log page end. */
+static
+void
+trx_undo_erase_page_end(
+/*====================*/
+ page_t* undo_page, /* in: undo page whose end to erase */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint first_free;
+ ulint i;
+
+ first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
+ + TRX_UNDO_PAGE_FREE);
+ for (i = first_free; i < UNIV_PAGE_SIZE - FIL_PAGE_DATA_END; i++) {
+ undo_page[i] = 0xFF;
+ }
+
+ mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
+}
+
+/***************************************************************
+Parses a redo log record of erasing of an undo page end. */
+
+byte*
+trx_undo_parse_erase_page_end(
+/*==========================*/
+ /* out: end of log record or NULL */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ page_t* page, /* in: page or NULL */
+ mtr_t* mtr) /* in: mtr or NULL */
+{
+ ut_ad(ptr && end_ptr);
+
+ if (page == NULL) {
+
+ return(ptr);
+ }
+
+ trx_undo_erase_page_end(page, mtr);
+
+ return(ptr);
+}
+
+/***************************************************************************
+Writes information to an undo log about an insert, update, or a delete marking
+of a clustered index record. This information is used in a rollback of the
+transaction and in consistent reads that must look to the history of this
+transaction. */
+
+ulint
+trx_undo_report_row_operation(
+/*==========================*/
+ /* out: DB_SUCCESS or error code */
+ ulint flags, /* in: if BTR_NO_UNDO_LOG_FLAG bit is
+ set, does nothing */
+ ulint op_type, /* in: TRX_UNDO_INSERT_OP or
+ TRX_UNDO_MODIFY_OP */
+ que_thr_t* thr, /* in: query thread */
+ dict_index_t* index, /* in: clustered index */
+ dtuple_t* clust_entry, /* in: in the case of an insert,
+ index entry to insert into the
+ clustered index, otherwise NULL */
+ upd_t* update, /* in: in the case of an update,
+ the update vector, otherwise NULL */
+ ulint cmpl_info, /* in: compiler info on secondary
+ index updates */
+ rec_t* rec, /* in: case of an update or delete
+ marking, the record in the clustered
+ index, otherwise NULL */
+ dulint* roll_ptr) /* out: rollback pointer to the
+ inserted undo log record,
+ ut_dulint_zero if BTR_NO_UNDO_LOG
+ flag was specified */
+{
+ trx_t* trx;
+ trx_undo_t* undo;
+ page_t* undo_page;
+ ulint offset;
+ mtr_t mtr;
+ ulint page_no;
+ ibool is_insert;
+ trx_rseg_t* rseg;
+
+ if (flags & BTR_NO_UNDO_LOG_FLAG) {
+
+ *roll_ptr = ut_dulint_zero;
+
+ return(DB_SUCCESS);
+ }
+
+ ut_ad(thr);
+ ut_ad(index->type & DICT_CLUSTERED);
+ ut_ad((op_type != TRX_UNDO_INSERT_OP)
+ || (clust_entry && !update && !rec));
+
+ trx = thr_get_trx(thr);
+ rseg = trx->rseg;
+
+ mutex_enter(&(trx->undo_mutex));
+
+ /* If the undo log is not assigned yet, assign one */
+
+ if (op_type == TRX_UNDO_INSERT_OP) {
+
+ if (trx->insert_undo == NULL) {
+
+ trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
+ }
+
+ undo = trx->insert_undo;
+ is_insert = TRUE;
+ } else {
+ ut_ad(op_type == TRX_UNDO_MODIFY_OP);
+
+ if (trx->update_undo == NULL) {
+
+ trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
+
+ }
+
+ undo = trx->update_undo;
+ is_insert = FALSE;
+ }
+
+ if (undo == NULL) {
+ /* Did not succeed: out of space */
+ mutex_exit(&(trx->undo_mutex));
+
+ return(DB_OUT_OF_FILE_SPACE);
+ }
+
+ page_no = undo->last_page_no;
+
+ mtr_start(&mtr);
+
+ for (;;) {
+ undo_page = buf_page_get_gen(undo->space, page_no,
+ RW_X_LATCH, undo->guess_page,
+ BUF_GET,
+ #ifdef UNIV_SYNC_DEBUG
+ __FILE__, __LINE__,
+ #endif
+ &mtr);
+
+ buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE);
+
+ if (op_type == TRX_UNDO_INSERT_OP) {
+ offset = trx_undo_page_report_insert(undo_page, trx,
+ index, clust_entry,
+ &mtr);
+ } else {
+ offset = trx_undo_page_report_modify(undo_page, trx,
+ index, rec, update,
+ cmpl_info, &mtr);
+ }
+
+ if (offset == 0) {
+ /* The record did not fit on the page. We erase the
+ end segment of the undo log page and write a log
+ record of it: this is to ensure that in the debug
+ version the replicate page constructed using the log
+ records stays identical to the original page */
+
+ trx_undo_erase_page_end(undo_page, &mtr);
+ }
+
+ mtr_commit(&mtr);
+
+ if (offset != 0) {
+ /* Success */
+
+ break;
+ }
+
+ ut_ad(page_no == undo->last_page_no);
+
+ /* We have to extend the undo log by one page */
+
+ mtr_start(&mtr);
+
+ /* When we add a page to an undo log, this is analogous to
+ a pessimistic insert in a B-tree, and we must reserve the
+ counterpart of the tree latch, which is the rseg mutex. */
+
+ mutex_enter(&(rseg->mutex));
+
+ page_no = trx_undo_add_page(trx, undo, &mtr);
+
+ mutex_exit(&(rseg->mutex));
+
+ if (page_no == FIL_NULL) {
+ /* Did not succeed: out of space */
+
+ mutex_exit(&(trx->undo_mutex));
+ mtr_commit(&mtr);
+
+ return(DB_OUT_OF_FILE_SPACE);
+ }
+ }
+
+ undo->empty = FALSE;
+ undo->top_page_no = page_no;
+ undo->top_offset = offset;
+ undo->top_undo_no = trx->undo_no;
+ undo->guess_page = undo_page;
+
+ UT_DULINT_INC(trx->undo_no);
+
+ mutex_exit(&(trx->undo_mutex));
+
+ *roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
+ offset);
+ return(DB_SUCCESS);
+}
+
+/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
+
+/**********************************************************************
+Copies an undo record to heap. This function can be called if we know that
+the undo log record exists. */
+
+trx_undo_rec_t*
+trx_undo_get_undo_rec_low(
+/*======================*/
+ /* out, own: copy of the record */
+ dulint roll_ptr, /* in: roll pointer to record */
+ mem_heap_t* heap) /* in: memory heap where copied */
+{
+ ulint rseg_id;
+ ulint page_no;
+ ulint offset;
+ page_t* undo_page;
+ trx_rseg_t* rseg;
+ ibool is_insert;
+ mtr_t mtr;
+ trx_undo_rec_t* undo_rec;
+
+ trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
+ &offset);
+ rseg = trx_rseg_get_on_id(rseg_id);
+
+ mtr_start(&mtr);
+
+ undo_page = trx_undo_page_get_s_latched(rseg->space, page_no, &mtr);
+
+ undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
+
+ mtr_commit(&mtr);
+
+ return(undo_rec);
+}
+
+/**********************************************************************
+Copies an undo record to heap. */
+
+ulint
+trx_undo_get_undo_rec(
+/*==================*/
+ /* out: DB_SUCCESS, or
+ DB_MISSING_HISTORY if the undo log
+ has been truncated and we cannot
+ fetch the old version; NOTE: the
+ caller must have latches on the
+ clustered index page and purge_view */
+ dulint roll_ptr, /* in: roll pointer to record */
+ dulint trx_id, /* in: id of the trx that generated
+ the roll pointer: it points to an
+ undo log of this transaction */
+ trx_undo_rec_t** undo_rec, /* out, own: copy of the record */
+ mem_heap_t* heap) /* in: memory heap where copied */
+{
+ ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
+
+ if (!trx_purge_update_undo_must_exist(trx_id)) {
+
+ /* It may be that the necessary undo log has already been
+ deleted */
+
+ return(DB_MISSING_HISTORY);
+ }
+
+ *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
+
+ return(DB_SUCCESS);
+}
+
+/***********************************************************************
+Build a previous version of a clustered index record. This function checks
+that the caller has a latch on the index page of the clustered index record
+and an s-latch on the purge_view. This guarantees that the stack of versions
+is locked. */
+
+ulint
+trx_undo_prev_version_build(
+/*========================*/
+ /* out: DB_SUCCESS, or DB_MISSING_HISTORY if
+ the previous version is not >= purge_view,
+ which means that it may have been removed */
+ rec_t* index_rec,/* in: clustered index record in the
+ index tree */
+ mtr_t* index_mtr,/* in: mtr which contains the latch to
+ index_rec page and purge_view */
+ rec_t* rec, /* in: version of a clustered index record */
+ dict_index_t* index, /* in: clustered index */
+ mem_heap_t* heap, /* in: memory heap from which the memory
+ needed is allocated */
+ rec_t** old_vers)/* out, own: previous version, or NULL if
+ rec is the first inserted version, or if
+ history data has been deleted */
+{
+ trx_undo_rec_t* undo_rec;
+ dtuple_t* entry;
+ dulint rec_trx_id;
+ ulint type;
+ dulint undo_no;
+ dulint table_id;
+ dulint trx_id;
+ dulint roll_ptr;
+ upd_t* update;
+ byte* ptr;
+ ulint info_bits;
+ ulint cmpl_info;
+ byte* buf;
+ ulint err;
+
+ ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
+ ut_ad(mtr_memo_contains(index_mtr, buf_block_align(index_rec),
+ MTR_MEMO_PAGE_S_FIX) ||
+ mtr_memo_contains(index_mtr, buf_block_align(index_rec),
+ MTR_MEMO_PAGE_X_FIX));
+
+ roll_ptr = row_get_rec_roll_ptr(rec, index);
+
+ if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
+
+ /* The record rec is the first inserted version */
+ *old_vers = NULL;
+
+ return(DB_SUCCESS);
+ }
+
+ rec_trx_id = row_get_rec_trx_id(rec, index);
+
+ err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
+
+ if (err != DB_SUCCESS) {
+
+ *old_vers = NULL;
+
+ return(err);
+ }
+
+ ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info, &undo_no,
+ &table_id);
+ ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
+ &info_bits);
+ ptr = trx_undo_rec_skip_row_ref(ptr, index);
+
+ trx_undo_update_rec_get_update(ptr, index, type, trx_id, roll_ptr,
+ info_bits, heap, &update);
+
+ if (row_upd_changes_field_size(rec, index, update)) {
+
+ entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
+
+ row_upd_clust_index_replace_new_col_vals(entry, update);
+
+ buf = mem_heap_alloc(heap, rec_get_converted_size(entry));
+
+ *old_vers = rec_convert_dtuple_to_rec(buf, entry);
+ } else {
+ buf = mem_heap_alloc(heap, rec_get_size(rec));
+
+ *old_vers = rec_copy(buf, rec);
+
+ row_upd_rec_in_place(*old_vers, update);
+ }
+
+ return(DB_SUCCESS);
+}