summaryrefslogtreecommitdiff
path: root/innobase/trx
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/trx')
-rw-r--r--innobase/trx/trx0rec.c105
-rw-r--r--innobase/trx/trx0roll.c141
-rw-r--r--innobase/trx/trx0sys.c25
-rw-r--r--innobase/trx/trx0trx.c309
-rw-r--r--innobase/trx/trx0undo.c247
5 files changed, 718 insertions, 109 deletions
diff --git a/innobase/trx/trx0rec.c b/innobase/trx/trx0rec.c
index fe429d1cc62..90ecb217c1d 100644
--- a/innobase/trx/trx0rec.c
+++ b/innobase/trx/trx0rec.c
@@ -38,16 +38,18 @@ trx_undof_page_add_undo_rec_log(
ulint new_free, /* in: end offset of the entry */
mtr_t* mtr) /* in: mtr */
{
- byte* log_ptr;
- ulint len;
+ byte* log_ptr;
+ const byte* log_end;
+ ulint len;
- log_ptr = mlog_open(mtr, 30 + MLOG_BUF_MARGIN);
+ log_ptr = mlog_open(mtr, 11 + 13 + MLOG_BUF_MARGIN);
if (log_ptr == NULL) {
return;
}
+ log_end = &log_ptr[11 + 13 + MLOG_BUF_MARGIN];
log_ptr = mlog_write_initial_log_record_fast(undo_page,
MLOG_UNDO_INSERT, log_ptr, mtr);
len = new_free - old_free - 4;
@@ -55,14 +57,11 @@ trx_undof_page_add_undo_rec_log(
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) {
+ if (log_ptr + len <= log_end) {
+ memcpy(log_ptr, undo_page + old_free + 2, len);
+ mlog_close(mtr, log_ptr + len);
+ } else {
+ mlog_close(mtr, log_ptr);
mlog_catenate_string(mtr, undo_page + old_free + 2, len);
}
}
@@ -404,6 +403,7 @@ trx_undo_page_report_modify(
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 */
@@ -430,6 +430,7 @@ trx_undo_page_report_modify(
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;
@@ -454,7 +455,7 @@ trx_undo_page_report_modify(
/* Store first some general parameters to the undo log */
if (update) {
- if (rec_get_deleted_flag(rec)) {
+ if (rec_get_deleted_flag(rec, table->comp)) {
type_cmpl = TRX_UNDO_UPD_DEL_REC;
} else {
type_cmpl = TRX_UNDO_UPD_EXIST_REC;
@@ -479,14 +480,20 @@ trx_undo_page_report_modify(
/*----------------------------------------*/
/* Store the state of the info bits */
- bits = rec_get_info_bits(rec);
+ bits = rec_get_info_bits(rec, table->comp);
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);
+ 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);
- roll_ptr = dict_index_rec_get_sys_col(index, DATA_ROLL_PTR, rec);
len = mach_dulint_write_compressed(ptr, trx_id);
ptr += len;
@@ -499,7 +506,7 @@ trx_undo_page_report_modify(
for (i = 0; i < dict_index_get_n_unique(index); i++) {
- field = rec_get_nth_field(rec, i, &flen);
+ field = rec_get_nth_field(rec, offsets, i, &flen);
if (trx_undo_left(undo_page, ptr) < 4) {
@@ -547,14 +554,14 @@ trx_undo_page_report_modify(
ptr += len;
/* Save the old value of field */
- field = rec_get_nth_field(rec, pos, &flen);
+ field = rec_get_nth_field(rec, offsets, pos, &flen);
if (trx_undo_left(undo_page, ptr) < 5) {
return(0);
}
- if (rec_get_nth_field_extern_bit(rec, pos)) {
+ if (rec_offs_nth_extern(offsets, pos)) {
/* If a field has external storage, we add to
flen the flag */
@@ -631,7 +638,7 @@ trx_undo_page_report_modify(
ptr += len;
/* Save the old value of field */
- field = rec_get_nth_field(rec, pos, &flen);
+ field = rec_get_nth_field(rec, offsets, pos, &flen);
if (trx_undo_left(undo_page, ptr) < 5) {
@@ -1008,7 +1015,10 @@ trx_undo_report_row_operation(
ibool is_insert;
trx_rseg_t* rseg;
mtr_t mtr;
-
+ mem_heap_t* heap = NULL;
+ ulint offsets_[100] = { 100, };
+ ulint* offsets = offsets_;
+
ut_a(index->type & DICT_CLUSTERED);
if (flags & BTR_NO_UNDO_LOG_FLAG) {
@@ -1019,7 +1029,6 @@ trx_undo_report_row_operation(
}
ut_ad(thr);
- ut_a(index->type & DICT_CLUSTERED);
ut_ad((op_type != TRX_UNDO_INSERT_OP)
|| (clust_entry && !update && !rec));
@@ -1079,9 +1088,10 @@ trx_undo_report_row_operation(
index, clust_entry,
&mtr);
} else {
+ offsets = rec_get_offsets(rec, index, offsets,
+ ULINT_UNDEFINED, &heap);
offset = trx_undo_page_report_modify(undo_page, trx,
- index, rec, update,
- cmpl_info, &mtr);
+ index, rec, offsets, update, cmpl_info, &mtr);
}
if (offset == 0) {
@@ -1123,7 +1133,9 @@ trx_undo_report_row_operation(
mutex_exit(&(trx->undo_mutex));
mtr_commit(&mtr);
-
+ if (heap) {
+ mem_heap_free(heap);
+ }
return(DB_OUT_OF_FILE_SPACE);
}
}
@@ -1140,6 +1152,9 @@ trx_undo_report_row_operation(
*roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
offset);
+ if (heap) {
+ mem_heap_free(heap);
+ }
return(DB_SUCCESS);
}
@@ -1236,6 +1251,7 @@ trx_undo_prev_version_build(
index_rec page and purge_view */
rec_t* rec, /* in: version of a clustered index record */
dict_index_t* index, /* in: clustered index */
+ ulint* offsets,/* in: rec_get_offsets(rec, 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
@@ -1258,7 +1274,6 @@ trx_undo_prev_version_build(
ibool dummy_extern;
byte* buf;
ulint err;
-
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
#endif /* UNIV_SYNC_DEBUG */
@@ -1266,21 +1281,23 @@ trx_undo_prev_version_build(
MTR_MEMO_PAGE_S_FIX) ||
mtr_memo_contains(index_mtr, buf_block_align(index_rec),
MTR_MEMO_PAGE_X_FIX));
+ ut_ad(rec_offs_validate(rec, index, offsets));
+
if (!(index->type & DICT_CLUSTERED)) {
fprintf(stderr, "InnoDB: Error: trying to access"
" update undo rec for non-clustered index %s\n"
"InnoDB: Submit a detailed bug report to"
" http://bugs.mysql.com\n"
"InnoDB: index record ", index->name);
- rec_print(stderr, index_rec);
+ rec_print(stderr, index_rec, index);
fputs("\n"
"InnoDB: record version ", stderr);
- rec_print(stderr, rec);
+ rec_print_new(stderr, rec, offsets);
putc('\n', stderr);
return(DB_ERROR);
}
- roll_ptr = row_get_rec_roll_ptr(rec, index);
+ roll_ptr = row_get_rec_roll_ptr(rec, index, offsets);
old_roll_ptr = roll_ptr;
*old_vers = NULL;
@@ -1292,7 +1309,7 @@ trx_undo_prev_version_build(
return(DB_SUCCESS);
}
- rec_trx_id = row_get_rec_trx_id(rec, index);
+ rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
@@ -1341,10 +1358,10 @@ trx_undo_prev_version_build(
ut_print_buf(stderr, undo_rec, 150);
fputs("\n"
"InnoDB: index record ", stderr);
- rec_print(stderr, index_rec);
+ rec_print(stderr, index_rec, index);
fputs("\n"
"InnoDB: record version ", stderr);
- rec_print(stderr, rec);
+ rec_print_new(stderr, rec, offsets);
fprintf(stderr, "\n"
"InnoDB: Record trx id %lu %lu, update rec trx id %lu %lu\n"
"InnoDB: Roll ptr in rec %lu %lu, in update rec %lu %lu\n",
@@ -1358,11 +1375,10 @@ trx_undo_prev_version_build(
(ulong) ut_dulint_get_low(roll_ptr));
trx_purge_sys_print();
-
return(DB_ERROR);
}
- if (row_upd_changes_field_size_or_external(rec, index, update)) {
+ if (row_upd_changes_field_size_or_external(index, offsets, update)) {
ulint* ext_vect;
ulint n_ext_vect;
@@ -1372,27 +1388,28 @@ trx_undo_prev_version_build(
those fields that update updates to become externally stored
fields. Store the info to ext_vect: */
- ext_vect = mem_alloc(sizeof(ulint) * rec_get_n_fields(rec));
- n_ext_vect = btr_push_update_extern_fields(ext_vect, rec,
+ ext_vect = mem_alloc(sizeof(ulint)
+ * rec_offs_n_fields(offsets));
+ n_ext_vect = btr_push_update_extern_fields(ext_vect, offsets,
update);
entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec,
heap);
row_upd_index_replace_new_col_vals(entry, index, update, heap);
- buf = mem_heap_alloc(heap, rec_get_converted_size(entry));
+ buf = mem_heap_alloc(heap,
+ rec_get_converted_size(index, entry));
- *old_vers = rec_convert_dtuple_to_rec(buf, entry);
+ *old_vers = rec_convert_dtuple_to_rec(buf, index, entry);
/* Now set the extern bits in the old version of the record */
- rec_set_field_extern_bits(*old_vers, ext_vect, n_ext_vect,
- NULL);
+ rec_set_field_extern_bits(*old_vers, index,
+ ext_vect, n_ext_vect, NULL);
mem_free(ext_vect);
} else {
- buf = mem_heap_alloc(heap, rec_get_size(rec));
-
- *old_vers = rec_copy(buf, rec);
-
- row_upd_rec_in_place(*old_vers, update);
+ buf = mem_heap_alloc(heap, rec_offs_size(offsets));
+ *old_vers = rec_copy(buf, rec, offsets);
+ rec_offs_make_valid(*old_vers, index, offsets);
+ row_upd_rec_in_place(*old_vers, offsets, update);
}
return(DB_SUCCESS);
diff --git a/innobase/trx/trx0roll.c b/innobase/trx/trx0roll.c
index eb7c7f43f03..4c68e0a0dd3 100644
--- a/innobase/trx/trx0roll.c
+++ b/innobase/trx/trx0roll.c
@@ -30,9 +30,13 @@ Created 3/26/1996 Heikki Tuuri
/* This many pages must be undone before a truncate is tried within rollback */
#define TRX_ROLL_TRUNC_THRESHOLD 1
+/* In crash recovery, the current trx to be rolled back */
+trx_t* trx_roll_crash_recv_trx = NULL;
+
/* In crash recovery we set this to the undo n:o of the current trx to be
rolled back. Then we can print how many % the rollback has progressed. */
ib_longlong trx_roll_max_undo_no;
+
/* Auxiliary variable which tells the previous progress % we printed */
ulint trx_roll_progress_printed_pct;
@@ -48,6 +52,7 @@ trx_general_rollback_for_mysql(
trx_savept_t* savept) /* in: pointer to savepoint undo number, if
partial rollback requested */
{
+#ifndef UNIV_HOTBACKUP
mem_heap_t* heap;
que_thr_t* thr;
roll_node_t* roll_node;
@@ -99,6 +104,12 @@ trx_general_rollback_for_mysql(
srv_active_wake_master_thread();
return((int) trx->error_state);
+#else /* UNIV_HOTBACKUP */
+ /* This function depends on MySQL code that is not included in
+ InnoDB Hot Backup builds. Besides, this function should never
+ be called in InnoDB Hot Backup. */
+ ut_error;
+#endif /* UNIV_HOTBACKUP */
}
/***********************************************************************
@@ -313,6 +324,51 @@ trx_savepoint_for_mysql(
}
/***********************************************************************
+Releases a named savepoint. Savepoints which
+were set after this savepoint are deleted. */
+
+ulint
+trx_release_savepoint_for_mysql(
+/*============================*/
+ /* out: if no savepoint
+ of the name found then
+ DB_NO_SAVEPOINT,
+ otherwise DB_SUCCESS */
+ trx_t* trx, /* in: transaction handle */
+ const char* savepoint_name) /* in: savepoint name */
+{
+ trx_named_savept_t* savep;
+
+ savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
+
+ while (savep != NULL) {
+ if (0 == ut_strcmp(savep->name, savepoint_name)) {
+ /* Found */
+ break;
+ }
+ savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
+ }
+
+ if (savep == NULL) {
+
+ return(DB_NO_SAVEPOINT);
+ }
+
+ /* We can now free all savepoints strictly later than this one */
+
+ trx_roll_savepoints_free(trx, savep);
+
+ /* Now we can free this savepoint too */
+
+ UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep);
+
+ mem_free(savep->name);
+ mem_free(savep);
+
+ return(DB_SUCCESS);
+}
+
+/***********************************************************************
Returns a transaction savepoint taken at this point in time. */
trx_savept_t
@@ -331,11 +387,20 @@ trx_savept_take(
/***********************************************************************
Rollback or clean up transactions which have no user session. If the
transaction already was committed, then we clean up a possible insert
-undo log. If the transaction was not yet committed, then we roll it back. */
+undo log. If the transaction was not yet committed, then we roll it back.
+Note: this is done in a background thread. */
-void
-trx_rollback_or_clean_all_without_sess(void)
-/*========================================*/
+#ifndef __WIN__
+void*
+#else
+ulint
+#endif
+trx_rollback_or_clean_all_without_sess(
+/*===================================*/
+ /* out: a dummy parameter */
+ void* arg __attribute__((unused)))
+ /* in: a dummy parameter required by
+ os_thread_create */
{
mem_heap_t* heap;
que_fork_t* fork;
@@ -360,9 +425,9 @@ trx_rollback_or_clean_all_without_sess(void)
if (UT_LIST_GET_FIRST(trx_sys->trx_list)) {
fprintf(stderr,
- "InnoDB: Starting rollback of uncommitted transactions\n");
+"InnoDB: Starting in background the rollback of uncommitted transactions\n");
} else {
- return;
+ goto leave_function;
}
loop:
heap = mem_heap_create(512);
@@ -371,24 +436,40 @@ loop:
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
- while (trx && (trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
+ while (trx) {
+ if ((trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
+ trx = UT_LIST_GET_NEXT(trx_list, trx);
+ } else if (trx->conc_state == TRX_PREPARED) {
+
+ /* Roll back all prepared transactions if
+ innobase_force_recovery > 0 in my.cnf */
- trx = UT_LIST_GET_NEXT(trx_list, trx);
+ if (srv_force_recovery > 0) {
+ trx->conc_state = TRX_ACTIVE;
+ break;
+ } else {
+ trx->sess = trx_dummy_sess;
+ trx = UT_LIST_GET_NEXT(trx_list, trx);
+ }
+ } else {
+ break;
+ }
}
mutex_exit(&kernel_mutex);
if (trx == NULL) {
+ ut_print_timestamp(stderr);
fprintf(stderr,
- "InnoDB: Rollback of uncommitted transactions completed\n");
+ " InnoDB: Rollback of uncommitted transactions completed\n");
mem_heap_free(heap);
-
- return;
+
+ goto leave_function;
}
trx->sess = trx_dummy_sess;
-
+
if (trx->conc_state == TRX_COMMITTED_IN_MEMORY) {
fprintf(stderr, "InnoDB: Cleaning up trx with id %lu %lu\n",
(ulong) ut_dulint_get_high(trx->id),
@@ -417,21 +498,28 @@ loop:
ut_a(thr == que_fork_start_command(fork));
+ trx_roll_crash_recv_trx = trx;
trx_roll_max_undo_no = ut_conv_dulint_to_longlong(trx->undo_no);
trx_roll_progress_printed_pct = 0;
rows_to_undo = trx_roll_max_undo_no;
+
if (rows_to_undo > 1000000000) {
rows_to_undo = rows_to_undo / 1000000;
unit = "M";
}
+ ut_print_timestamp(stderr);
fprintf(stderr,
-"InnoDB: Rolling back trx with id %lu %lu, %lu%s rows to undo",
+" InnoDB: Rolling back trx with id %lu %lu, %lu%s rows to undo\n",
(ulong) ut_dulint_get_high(trx->id),
(ulong) ut_dulint_get_low(trx->id),
(ulong) rows_to_undo, unit);
mutex_exit(&kernel_mutex);
+ trx->mysql_thread_id = os_thread_get_curr_id();
+
+ trx->mysql_process_no = os_proc_get_number();
+
if (trx->dict_operation) {
row_mysql_lock_data_dictionary(trx);
}
@@ -446,7 +534,7 @@ loop:
fprintf(stderr,
"InnoDB: Waiting for rollback of trx id %lu to end\n",
- (ulong) ut_dulint_get_low(trx->id));
+ (ulong) ut_dulint_get_low(trx->id));
os_thread_sleep(100000);
mutex_enter(&kernel_mutex);
@@ -485,7 +573,23 @@ loop:
(ulong) ut_dulint_get_low(trx->id));
mem_heap_free(heap);
+ trx_roll_crash_recv_trx = NULL;
+
goto loop;
+
+leave_function:
+ /* We count the number of threads in os_thread_exit(). A created
+ thread should always use that to exit and not use return() to exit. */
+
+ os_thread_exit(NULL);
+
+ /* The following is dummy code to keep the compiler happy: */
+
+#ifndef __WIN__
+ return(NULL);
+#else
+ return(0);
+#endif
}
/***********************************************************************
@@ -846,16 +950,17 @@ try_again:
ut_ad(ut_dulint_cmp(ut_dulint_add(undo_no, 1), trx->undo_no) == 0);
/* We print rollback progress info if we are in a crash recovery
- and the transaction has at least 1000 row operations to undo */
+ and the transaction has at least 1000 row operations to undo. */
+
+ if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) {
- if (srv_is_being_started && trx_roll_max_undo_no > 1000) {
- progress_pct = 100 - (ulint)
+ progress_pct = 100 - (ulint)
((ut_conv_dulint_to_longlong(undo_no) * 100)
/ trx_roll_max_undo_no);
if (progress_pct != trx_roll_progress_printed_pct) {
if (trx_roll_progress_printed_pct == 0) {
fprintf(stderr,
- "\nInnoDB: Progress in percents: %lu", (ulong) progress_pct);
+"\nInnoDB: Progress in percents: %lu", (ulong) progress_pct);
} else {
fprintf(stderr,
" %lu", (ulong) progress_pct);
diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c
index 54bd5be01a1..57166e98f45 100644
--- a/innobase/trx/trx0sys.c
+++ b/innobase/trx/trx0sys.c
@@ -125,6 +125,22 @@ trx_doublewrite_init(
}
/********************************************************************
+Frees the doublewrite buffer. */
+static
+void
+trx_doublewrite_free(void)
+/*======================*/
+{
+ mutex_free(&(trx_doublewrite->mutex));
+
+ mem_free(trx_doublewrite->buf_block_arr);
+ ut_free(trx_doublewrite->write_buf_unaligned);
+
+ mem_free(trx_doublewrite);
+ trx_doublewrite = NULL;
+}
+
+/********************************************************************
Marks the trx sys header when we have successfully upgraded to the >= 4.1.x
multiple tablespace format. */
@@ -512,6 +528,9 @@ trx_sys_doublewrite_init_or_restore_pages(
fil_flush_file_spaces(FIL_TABLESPACE);
+ if (!srv_use_doublewrite_buf)
+ trx_doublewrite_free();
+
leave_func:
ut_free(unaligned_read_buf);
}
@@ -887,8 +906,12 @@ trx_sys_init_at_db_start(void)
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
for (;;) {
- rows_to_undo +=
+
+ if ( trx->conc_state != TRX_PREPARED) {
+ rows_to_undo +=
ut_conv_dulint_to_longlong(trx->undo_no);
+ }
+
trx = UT_LIST_GET_NEXT(trx_list, trx);
if (!trx) {
diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c
index 2fdee94b089..dd4da33abb5 100644
--- a/innobase/trx/trx0trx.c
+++ b/innobase/trx/trx0trx.c
@@ -24,6 +24,7 @@ Created 3/26/1996 Heikki Tuuri
#include "thr0loc.h"
#include "btr0sea.h"
#include "os0proc.h"
+#include "trx0xa.h"
/* Copy of the prototype for innobase_mysql_print_thd: this
copy MUST be equal to the one in mysql/sql/ha_innodb.cc ! */
@@ -155,10 +156,15 @@ trx_create(
trx->auto_inc_lock = NULL;
trx->n_lock_table_exp = 0;
+ trx->n_lock_table_transactional = 0;
trx->read_view_heap = mem_heap_create(256);
trx->read_view = NULL;
+ /* Set X/Open XA transaction identification to NULL */
+ memset(&trx->xid, 0, sizeof(trx->xid));
+ trx->xid.formatID = -1;
+
return(trx);
}
@@ -299,6 +305,7 @@ trx_free(
ut_a(!trx->has_search_latch);
ut_a(!trx->auto_inc_lock);
ut_a(!trx->n_lock_table_exp);
+ ut_a(!trx->n_lock_table_transactional);
ut_a(trx->dict_operation_lock_mode == 0);
@@ -427,13 +434,30 @@ trx_lists_init_at_db_start(void)
trx = trx_create(NULL);
trx->id = undo->trx_id;
-
+ trx->xid = undo->xid;
trx->insert_undo = undo;
trx->rseg = rseg;
if (undo->state != TRX_UNDO_ACTIVE) {
- trx->conc_state = TRX_COMMITTED_IN_MEMORY;
+ /* Prepared transactions are left in
+ the prepared state waiting for a
+ commit or abort decision from MySQL */
+
+ if (undo->state == TRX_UNDO_PREPARED) {
+
+ fprintf(stderr,
+"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
+ ut_dulint_get_high(trx->id),
+ ut_dulint_get_low(trx->id));
+
+ trx->conc_state = TRX_ACTIVE;
+
+ /* trx->conc_state = TRX_PREPARED;*/
+ } else {
+ trx->conc_state =
+ TRX_COMMITTED_IN_MEMORY;
+ }
/* We give a dummy value for the trx no;
this should have no relevance since purge
@@ -476,10 +500,29 @@ trx_lists_init_at_db_start(void)
trx = trx_create(NULL);
trx->id = undo->trx_id;
+ trx->xid = undo->xid;
if (undo->state != TRX_UNDO_ACTIVE) {
- trx->conc_state =
- TRX_COMMITTED_IN_MEMORY;
+
+ /* Prepared transactions are left in
+ the prepared state waiting for a
+ commit or abort decision from MySQL */
+
+ if (undo->state == TRX_UNDO_PREPARED) {
+ fprintf(stderr,
+"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
+ ut_dulint_get_high(trx->id),
+ ut_dulint_get_low(trx->id));
+
+ trx->conc_state = TRX_ACTIVE;
+
+ /* trx->conc_state =
+ TRX_PREPARED; */
+ } else {
+ trx->conc_state =
+ TRX_COMMITTED_IN_MEMORY;
+ }
+
/* We give a dummy value for the trx
number */
@@ -745,7 +788,8 @@ trx_commit_off_kernel(
mutex_enter(&kernel_mutex);
}
- ut_ad(trx->conc_state == TRX_ACTIVE);
+ ut_ad(trx->conc_state == TRX_ACTIVE
+ || trx->conc_state == TRX_PREPARED);
#ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */
@@ -1599,20 +1643,24 @@ trx_print(
(ulong) ut_dulint_get_high(trx->id),
(ulong) ut_dulint_get_low(trx->id));
- switch (trx->conc_state) {
+ switch (trx->conc_state) {
case TRX_NOT_STARTED:
fputs(", not started", f);
break;
case TRX_ACTIVE:
fprintf(f, ", ACTIVE %lu sec",
(ulong)difftime(time(NULL), trx->start_time));
- break;
+ break;
+ case TRX_PREPARED:
+ fprintf(f, ", ACTIVE (PREPARED) %lu sec",
+ (ulong)difftime(time(NULL), trx->start_time));
+ break;
case TRX_COMMITTED_IN_MEMORY:
fputs(", COMMITTED IN MEMORY", f);
break;
default:
fprintf(f, " state %lu", (ulong) trx->conc_state);
- }
+ }
#ifdef UNIV_LINUX
fprintf(f, ", process no %lu", trx->mysql_process_no);
@@ -1637,10 +1685,15 @@ trx_print(
putc('\n', f);
if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) {
+ fprintf(f, "mysql tables in use %lu, locked %lu\n",
+ (ulong) trx->n_mysql_tables_in_use,
+ (ulong) trx->mysql_n_tables_locked);
+ }
- fprintf(f, "mysql tables in use %lu, locked %lu\n",
- (ulong) trx->n_mysql_tables_in_use,
- (ulong) trx->mysql_n_tables_locked);
+ if (trx->n_lock_table_transactional > 0 || trx->n_lock_table_exp > 0) {
+fprintf(f, "mysql explicit table locks %lu, transactional table locks %lu\n",
+ (ulong) trx->n_lock_table_exp,
+ (ulong) trx->n_lock_table_transactional);
}
newline = TRUE;
@@ -1686,3 +1739,237 @@ trx_print(
innobase_mysql_print_thd(f, trx->mysql_thd);
}
}
+
+/********************************************************************
+Prepares a transaction. */
+
+void
+trx_prepare_off_kernel(
+/*==================*/
+ trx_t* trx) /* in: transaction */
+{
+ page_t* update_hdr_page;
+ trx_rseg_t* rseg;
+ trx_undo_t* undo;
+ ibool must_flush_log = FALSE;
+ dulint lsn;
+ mtr_t mtr;
+
+#ifdef UNIV_SYNC_DEBUG
+ ut_ad(mutex_own(&kernel_mutex));
+#endif /* UNIV_SYNC_DEBUG */
+
+ rseg = trx->rseg;
+
+ if (trx->insert_undo != NULL || trx->update_undo != NULL) {
+
+ mutex_exit(&kernel_mutex);
+
+ mtr_start(&mtr);
+
+ must_flush_log = TRUE;
+
+ /* Change the undo log segment states from TRX_UNDO_ACTIVE
+ to TRX_UNDO_PREPARED: these modifications to the file data
+ structure define the transaction as prepared in the
+ file-based world, at the serialization point of lsn. */
+
+ mutex_enter(&(rseg->mutex));
+
+ if (trx->insert_undo != NULL) {
+ trx_undo_set_state_at_prepare(trx, trx->insert_undo,
+ &mtr);
+ }
+
+ undo = trx->update_undo;
+
+ if (undo) {
+ /* It is not necessary to obtain trx->undo_mutex here
+ because only a single OS thread is allowed to do the
+ transaction prepare for this transaction. */
+
+ update_hdr_page = trx_undo_set_state_at_prepare(trx,
+ undo, &mtr);
+ }
+
+ mutex_exit(&(rseg->mutex));
+
+ /*--------------*/
+ mtr_commit(&mtr); /* This mtr commit makes the
+ transaction prepared in the file-based
+ world */
+ /*--------------*/
+ lsn = mtr.end_lsn;
+
+ mutex_enter(&kernel_mutex);
+ }
+
+#ifdef UNIV_SYNC_DEBUG
+ ut_ad(mutex_own(&kernel_mutex));
+#endif /* UNIV_SYNC_DEBUG */
+
+ /*--------------------------------------*/
+ trx->conc_state = TRX_PREPARED;
+ /*--------------------------------------*/
+
+ if (must_flush_log) {
+
+ mutex_exit(&kernel_mutex);
+
+ /* Write the log to the log files AND flush them to disk */
+
+ /*-------------------------------------*/
+
+ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
+
+ /*-------------------------------------*/
+
+ mutex_enter(&kernel_mutex);
+ }
+}
+
+/**************************************************************************
+Does the transaction prepare for MySQL. */
+
+ulint
+trx_prepare_for_mysql(
+/*====-=============*/
+ /* out: 0 or error number */
+ trx_t* trx) /* in: trx handle */
+{
+ /* Because we do not do the prepare by sending an Innobase
+ sig to the transaction, we must here make sure that trx has been
+ started. */
+
+ ut_a(trx);
+
+ trx->op_info = "preparing";
+
+ trx_start_if_not_started(trx);
+
+ mutex_enter(&kernel_mutex);
+
+ trx_prepare_off_kernel(trx);
+
+ mutex_exit(&kernel_mutex);
+
+ trx->op_info = "";
+
+ return(0);
+}
+
+/**************************************************************************
+This function is used to find number of prepared transactions and
+their transaction objects for a recovery. */
+
+int
+trx_recover_for_mysql(
+/*==================*/
+ /* out: number of prepared transactions
+ stored in xid_list */
+ XID* xid_list, /* in/out: prepared transactions */
+ ulint len) /* in: number of slots in xid_list */
+{
+ trx_t* trx;
+ int count = 0;
+
+ ut_ad(xid_list);
+ ut_ad(len);
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Starting recovery for XA transactions...\n");
+
+ /* We should set those transactions which are in the prepared state
+ to the xid_list */
+
+ mutex_enter(&kernel_mutex);
+
+ trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
+
+ while (trx) {
+ if (trx->conc_state == TRX_PREPARED) {
+ xid_list[count] = trx->xid;
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+" InnoDB: Transaction %lu %lu in prepared state after recovery\n",
+ (ulong) ut_dulint_get_high(trx->id),
+ (ulong) ut_dulint_get_low(trx->id));
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+" InnoDB: Transaction contains changes to %lu rows\n",
+ (ulong)ut_conv_dulint_to_longlong(trx->undo_no));
+
+ count++;
+
+ if ((uint)count == len ) {
+ break;
+ }
+ }
+
+ trx = UT_LIST_GET_NEXT(trx_list, trx);
+ }
+
+ mutex_exit(&kernel_mutex);
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+" InnoDB: %d transactions in prepare state after recovery\n",
+ count);
+
+ return (count);
+}
+
+/***********************************************************************
+This function is used to find one X/Open XA distributed transaction
+which is in the prepared state */
+
+trx_t*
+trx_get_trx_by_xid(
+/*===============*/
+ /* out: trx or NULL */
+ XID* xid) /* in: X/Open XA transaction identification */
+{
+ trx_t* trx;
+
+ if (xid == NULL) {
+
+ return (NULL);
+ }
+
+ mutex_enter(&kernel_mutex);
+
+ trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
+
+ while (trx) {
+ /* Compare two X/Open XA transaction id's: their
+ length should be the same and binary comparison
+ of gtrid_lenght+bqual_length bytes should be
+ the same */
+
+ if (xid->gtrid_length == trx->xid.gtrid_length &&
+ xid->bqual_length == trx->xid.bqual_length &&
+ memcmp(xid->data, trx->xid.data,
+ xid->gtrid_length +
+ xid->bqual_length) == 0) {
+ break;
+ }
+
+ trx = UT_LIST_GET_NEXT(trx_list, trx);
+ }
+
+ mutex_exit(&kernel_mutex);
+
+ if (trx) {
+ if (trx->conc_state != TRX_PREPARED) {
+
+ return(NULL);
+ }
+
+ return(trx);
+ } else {
+ return(NULL);
+ }
+}
diff --git a/innobase/trx/trx0undo.c b/innobase/trx/trx0undo.c
index 8d1518753dd..88185973dfc 100644
--- a/innobase/trx/trx0undo.c
+++ b/innobase/trx/trx0undo.c
@@ -19,6 +19,7 @@ Created 3/26/1996 Heikki Tuuri
#include "srv0srv.h"
#include "trx0rec.h"
#include "trx0purge.h"
+#include "trx0xa.h"
/* How should the old versions in the history list be managed?
----------------------------------------------------------
@@ -97,10 +98,13 @@ trx_undo_mem_create(
TRX_UNDO_UPDATE */
dulint trx_id, /* in: id of the trx for which the undo log
is created */
+ XID* xid, /* in: X/Open XA transaction identification*/
ulint page_no,/* in: undo log header page number */
- ulint offset); /* in: undo log header byte offset on page */
+ ulint offset);/* in: undo log header byte offset on page */
/*******************************************************************
-Initializes a cached insert undo log header page for new use. */
+Initializes a cached insert undo log header page for new use. NOTE that this
+function has its own log record type MLOG_UNDO_HDR_REUSE. You must NOT change
+the operation of this function! */
static
ulint
trx_undo_insert_header_reuse(
@@ -181,7 +185,8 @@ trx_undo_get_prev_rec(
/* We have to go to the previous undo log page to look for the
previous record */
- return(trx_undo_get_prev_rec_from_prev_page(rec, page_no, offset, mtr));
+ return(trx_undo_get_prev_rec_from_prev_page(rec, page_no, offset,
+ mtr));
}
/***************************************************************************
@@ -473,15 +478,17 @@ trx_undo_header_create_log(
}
/*******************************************************************
-Creates a new undo log header in file. */
+Creates a new undo log header in file. NOTE that this function has its own
+log record type MLOG_UNDO_HDR_CREATE. You must NOT change the operation of
+this function! */
static
ulint
trx_undo_header_create(
/*===================*/
/* out: header byte offset on page */
page_t* undo_page, /* in: undo log segment header page,
- x-latched; it is assumed that there is
- TRX_UNDO_LOG_HDR_SIZE bytes free space
+ x-latched; it is assumed that there are
+ TRX_UNDO_LOG_XA_HDR_SIZE bytes free space
on it */
dulint trx_id, /* in: transaction id */
mtr_t* mtr) /* in: mtr */
@@ -503,9 +510,9 @@ trx_undo_header_create(
log_hdr = undo_page + free;
- new_free = free + TRX_UNDO_LOG_HDR_SIZE;
+ new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE;
- ut_ad(new_free <= UNIV_PAGE_SIZE);
+ ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100);
mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free);
@@ -530,16 +537,102 @@ trx_undo_header_create(
mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id);
mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free);
- mach_write_to_2(log_hdr + TRX_UNDO_DICT_OPERATION, FALSE);
-
+ mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE);
+ mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE);
+
mach_write_to_2(log_hdr + TRX_UNDO_NEXT_LOG, 0);
mach_write_to_2(log_hdr + TRX_UNDO_PREV_LOG, prev_log);
-
+
+ /* Write the log record about the header creation */
trx_undo_header_create_log(undo_page, trx_id, mtr);
return(free);
}
+/************************************************************************
+Write X/Open XA Transaction Identification (XID) to undo log header */
+static
+void
+trx_undo_write_xid(
+/*===============*/
+ trx_ulogf_t* log_hdr,/* in: undo log header */
+ XID* xid, /* in: X/Open XA Transaction Identification */
+ mtr_t* mtr) /* in: mtr */
+{
+ mlog_write_ulint(log_hdr + TRX_UNDO_XA_FORMAT, xid->formatID,
+ MLOG_4BYTES, mtr);
+
+ mlog_write_ulint(log_hdr + TRX_UNDO_XA_TRID_LEN, xid->gtrid_length,
+ MLOG_4BYTES, mtr);
+
+ mlog_write_ulint(log_hdr + TRX_UNDO_XA_BQUAL_LEN, xid->bqual_length,
+ MLOG_4BYTES, mtr);
+
+ mlog_write_string(log_hdr + TRX_UNDO_XA_XID, xid->data,
+ XIDDATASIZE, mtr);
+}
+
+/************************************************************************
+Read X/Open XA Transaction Identification (XID) from undo log header */
+static
+void
+trx_undo_read_xid(
+/*==============*/
+ trx_ulogf_t* log_hdr,/* in: undo log header */
+ XID* xid) /* out: X/Open XA Transaction Identification */
+{
+ ulint i;
+
+ xid->formatID = mach_read_from_4(log_hdr + TRX_UNDO_XA_FORMAT);
+
+ xid->gtrid_length = mach_read_from_4(log_hdr + TRX_UNDO_XA_TRID_LEN);
+
+ xid->bqual_length = mach_read_from_4(log_hdr + TRX_UNDO_XA_BQUAL_LEN);
+
+ for (i = 0; i < XIDDATASIZE; i++) {
+ xid->data[i] = (char)mach_read_from_1(log_hdr +
+ TRX_UNDO_XA_XID + i);
+ }
+}
+
+/*******************************************************************
+Adds the XA XID after an undo log old-style header. */
+static
+void
+trx_undo_header_add_space_for_xid(
+/*==============================*/
+ page_t* undo_page,/* in: undo log segment header page */
+ trx_ulogf_t* log_hdr,/* in: undo log header */
+ mtr_t* mtr) /* in: mtr */
+{
+ trx_upagef_t* page_hdr;
+ ulint free;
+ ulint new_free;
+
+ page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
+
+ free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE);
+
+ /* free is now the end offset of the old style undo log header */
+
+ ut_a(free == (ulint)(log_hdr - undo_page) + TRX_UNDO_LOG_OLD_HDR_SIZE);
+
+ new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE
+ - TRX_UNDO_LOG_OLD_HDR_SIZE);
+
+ /* Add space for a XID after the header, update the free offset
+ fields on the undo log page and in the undo log header */
+
+ mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free,
+ MLOG_2BYTES, mtr);
+
+ mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, new_free,
+ MLOG_2BYTES, mtr);
+
+ mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, new_free,
+ MLOG_2BYTES, mtr);
+}
+
/**************************************************************************
Writes the mtr log entry of an undo log header reuse. */
UNIV_INLINE
@@ -590,7 +683,9 @@ trx_undo_parse_page_header(
}
/*******************************************************************
-Initializes a cached insert undo log header page for new use. */
+Initializes a cached insert undo log header page for new use. NOTE that this
+function has its own log record type MLOG_UNDO_HDR_REUSE. You must NOT change
+the operation of this function! */
static
ulint
trx_undo_insert_header_reuse(
@@ -614,9 +709,11 @@ trx_undo_insert_header_reuse(
free = TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE;
+ ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100);
+
log_hdr = undo_page + free;
- new_free = free + TRX_UNDO_LOG_HDR_SIZE;
+ new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE;
/* Insert undo data is not needed after commit: we may free all
the space on the page */
@@ -636,8 +733,10 @@ trx_undo_insert_header_reuse(
mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id);
mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free);
- mach_write_to_2(log_hdr + TRX_UNDO_DICT_OPERATION, FALSE);
+ mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE);
+ mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE);
+ /* Write the log record MLOG_UNDO_HDR_REUSE */
trx_undo_insert_header_reuse_log(undo_page, trx_id, mtr);
return(free);
@@ -800,7 +899,6 @@ trx_undo_free_page(
list */
ulint space, /* in: space */
ulint hdr_page_no, /* in: header page number */
- ulint hdr_offset, /* in: header offset */
ulint page_no, /* in: page number to free: must not be the
header page */
mtr_t* mtr) /* in: mtr which does not have a latch to any
@@ -813,7 +911,6 @@ trx_undo_free_page(
trx_rsegf_t* rseg_header;
ulint hist_size;
- UT_NOT_USED(hdr_offset);
ut_a(hdr_page_no != page_no);
#ifdef UNIV_SYNC_DEBUG
ut_ad(!mutex_own(&kernel_mutex));
@@ -870,8 +967,7 @@ trx_undo_free_page_in_rollback(
#endif /* UNIV_SYNC_DEBUG */
last_page_no = trx_undo_free_page(undo->rseg, FALSE, undo->space,
- undo->hdr_page_no, undo->hdr_offset,
- page_no, mtr);
+ undo->hdr_page_no, page_no, mtr);
undo->last_page_no = last_page_no;
undo->size--;
@@ -1039,7 +1135,7 @@ loop:
trx_undo_empty_header_page(space, hdr_page_no, hdr_offset,
&mtr);
} else {
- trx_undo_free_page(rseg, TRUE, space, hdr_page_no, hdr_offset,
+ trx_undo_free_page(rseg, TRUE, space, hdr_page_no,
page_no, &mtr);
}
@@ -1123,7 +1219,9 @@ trx_undo_mem_create_at_db_start(
fil_addr_t last_addr;
page_t* last_page;
trx_undo_rec_t* rec;
-
+ XID xid;
+ ibool xid_exists = FALSE;
+
if (id >= TRX_RSEG_N_SLOTS) {
fprintf(stderr,
"InnoDB: Error: undo->id is %lu\n", (ulong) id);
@@ -1145,15 +1243,30 @@ trx_undo_mem_create_at_db_start(
undo_header = undo_page + offset;
trx_id = mtr_read_dulint(undo_header + TRX_UNDO_TRX_ID, mtr);
+
+ xid_exists = mtr_read_ulint(undo_header + TRX_UNDO_XID_EXISTS,
+ MLOG_1BYTE, mtr);
+
+ /* Read X/Open XA transaction identification if it exists, or
+ set it to NULL. */
+
+ memset(&xid, 0, sizeof(xid));
+ xid.formatID = -1;
+
+ if (xid_exists == TRUE) {
+ trx_undo_read_xid(undo_header, &xid);
+ }
+
mutex_enter(&(rseg->mutex));
- undo = trx_undo_mem_create(rseg, id, type, trx_id, page_no, offset);
-
+ undo = trx_undo_mem_create(rseg, id, type, trx_id, &xid,
+ page_no, offset);
mutex_exit(&(rseg->mutex));
- undo->dict_operation = mtr_read_ulint(
- undo_header + TRX_UNDO_DICT_OPERATION,
- MLOG_2BYTES, mtr);
+ undo->dict_operation = mtr_read_ulint(
+ undo_header + TRX_UNDO_DICT_TRANS,
+ MLOG_1BYTE, mtr);
+
undo->table_id = mtr_read_dulint(undo_header + TRX_UNDO_TABLE_ID, mtr);
undo->state = state;
undo->size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr);
@@ -1272,7 +1385,8 @@ trx_undo_mem_create(
ulint type, /* in: type of the log: TRX_UNDO_INSERT or
TRX_UNDO_UPDATE */
dulint trx_id, /* in: id of the trx for which the undo log
- is created */
+ is created */
+ XID* xid, /* in: X/Open transaction identification */
ulint page_no,/* in: undo log header page number */
ulint offset) /* in: undo log header byte offset on page */
{
@@ -1295,6 +1409,7 @@ trx_undo_mem_create(
undo->state = TRX_UNDO_ACTIVE;
undo->del_marks = FALSE;
undo->trx_id = trx_id;
+ undo->xid = *xid;
undo->dict_operation = FALSE;
@@ -1322,6 +1437,7 @@ trx_undo_mem_init_for_reuse(
trx_undo_t* undo, /* in: undo log to init */
dulint trx_id, /* in: id of the trx for which the undo log
is created */
+ XID* xid, /* in: X/Open XA transaction identification*/
ulint offset) /* in: undo log header byte offset on page */
{
#ifdef UNIV_SYNC_DEBUG
@@ -1339,6 +1455,7 @@ trx_undo_mem_init_for_reuse(
undo->state = TRX_UNDO_ACTIVE;
undo->del_marks = FALSE;
undo->trx_id = trx_id;
+ undo->xid = *xid;
undo->dict_operation = FALSE;
@@ -1376,6 +1493,7 @@ trx_undo_create(
TRX_UNDO_UPDATE */
dulint trx_id, /* in: id of the trx for which the undo log
is created */
+ XID* xid, /* in: X/Open transaction identification*/
mtr_t* mtr) /* in: mtr */
{
trx_rsegf_t* rseg_header;
@@ -1410,10 +1528,12 @@ trx_undo_create(
page_no = buf_frame_get_page_no(undo_page);
- offset = trx_undo_header_create(undo_page, trx_id, mtr);
+ offset = trx_undo_header_create(undo_page, trx_id, mtr);
- undo = trx_undo_mem_create(rseg, id, type, trx_id, page_no, offset);
-
+ trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr);
+
+ undo = trx_undo_mem_create(rseg, id, type, trx_id, xid,
+ page_no, offset);
return(undo);
}
@@ -1432,6 +1552,7 @@ trx_undo_reuse_cached(
TRX_UNDO_UPDATE */
dulint trx_id, /* in: id of the trx for which the undo log
is used */
+ XID* xid, /* in: X/Open XA transaction identification */
mtr_t* mtr) /* in: mtr */
{
trx_undo_t* undo;
@@ -1476,15 +1597,19 @@ trx_undo_reuse_cached(
if (type == TRX_UNDO_INSERT) {
offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr);
+ trx_undo_header_add_space_for_xid(undo_page, undo_page + offset,
+ mtr);
} else {
ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
+ TRX_UNDO_PAGE_TYPE)
== TRX_UNDO_UPDATE);
offset = trx_undo_header_create(undo_page, trx_id, mtr);
+ trx_undo_header_add_space_for_xid(undo_page, undo_page + offset,
+ mtr);
}
- trx_undo_mem_init_for_reuse(undo, trx_id, offset);
+ trx_undo_mem_init_for_reuse(undo, trx_id, xid, offset);
return(undo);
}
@@ -1506,9 +1631,10 @@ trx_undo_mark_as_dict_operation(
hdr_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
- mlog_write_ulint(hdr_page + undo->hdr_offset + TRX_UNDO_DICT_OPERATION,
- trx->dict_operation, MLOG_2BYTES, mtr);
-
+ mlog_write_ulint(hdr_page + undo->hdr_offset +
+ TRX_UNDO_DICT_TRANS,
+ trx->dict_operation, MLOG_1BYTE, mtr);
+
mlog_write_dulint(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID,
trx->table_id, mtr);
@@ -1548,10 +1674,10 @@ trx_undo_assign_undo(
#endif /* UNIV_SYNC_DEBUG */
mutex_enter(&(rseg->mutex));
- undo = trx_undo_reuse_cached(rseg, type, trx->id, &mtr);
+ undo = trx_undo_reuse_cached(rseg, type, trx->id, &trx->xid, &mtr);
if (undo == NULL) {
- undo = trx_undo_create(rseg, type, trx->id, &mtr);
+ undo = trx_undo_create(rseg, type, trx->id, &trx->xid, &mtr);
if (undo == NULL) {
/* Did not succeed */
@@ -1632,6 +1758,57 @@ trx_undo_set_state_at_finish(
return(undo_page);
}
+/**********************************************************************
+Sets the state of the undo log segment at a transaction prepare. */
+
+page_t*
+trx_undo_set_state_at_prepare(
+/*==========================*/
+ /* out: undo log segment header page,
+ x-latched */
+ trx_t* trx, /* in: transaction */
+ trx_undo_t* undo, /* in: undo log memory copy */
+ mtr_t* mtr) /* in: mtr */
+{
+ trx_usegf_t* seg_hdr;
+ trx_upagef_t* page_hdr;
+ trx_ulogf_t* undo_header;
+ page_t* undo_page;
+ ulint offset;
+
+ ut_ad(trx && undo && mtr);
+
+ if (undo->id >= TRX_RSEG_N_SLOTS) {
+ fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
+ (ulong) undo->id);
+ mem_analyze_corruption((byte*)undo);
+ ut_error;
+ }
+
+ undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
+
+ seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
+ page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
+
+ /*------------------------------*/
+ undo->state = TRX_UNDO_PREPARED;
+ undo->xid = trx->xid;
+ /*------------------------------*/
+
+ mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, undo->state,
+ MLOG_2BYTES, mtr);
+
+ offset = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
+ undo_header = undo_page + offset;
+
+ mlog_write_ulint(undo_header + TRX_UNDO_XID_EXISTS,
+ TRUE, MLOG_1BYTE, mtr);
+
+ trx_undo_write_xid(undo_header, &undo->xid, mtr);
+
+ return(undo_page);
+}
+
/**************************************************************************
Adds the update undo log header as the first in the history list, and
frees the memory object, or puts it to the list of cached update undo log