summaryrefslogtreecommitdiff
path: root/innobase
diff options
context:
space:
mode:
authorunknown <jan@hundin.mysql.fi>2004-11-30 12:11:02 +0200
committerunknown <jan@hundin.mysql.fi>2004-11-30 12:11:02 +0200
commit739b630ed7c1a832ff4e8075cd8739c93286e61f (patch)
tree84e8ec4376e25d1f3cb8c8ba19936ac217f93266 /innobase
parent857bd559ec956c4be44422f3f185d310029fb765 (diff)
parentdf0e057a52db9f085c42ec593f3000da4afdbbd7 (diff)
downloadmariadb-git-739b630ed7c1a832ff4e8075cd8739c93286e61f.tar.gz
Auto merged.
innobase/row/row0ins.c: Auto merged innobase/trx/trx0undo.c: Auto merged sql/ha_innodb.h: Auto merged sql/handler.h: Auto merged
Diffstat (limited to 'innobase')
-rw-r--r--innobase/include/trx0roll.h9
-rw-r--r--innobase/include/trx0trx.h41
-rw-r--r--innobase/include/trx0undo.h48
-rw-r--r--innobase/include/xa.h182
-rw-r--r--innobase/log/log0recv.c14
-rw-r--r--innobase/trx/trx0roll.c24
-rw-r--r--innobase/trx/trx0sys.c6
-rw-r--r--innobase/trx/trx0trx.c272
-rw-r--r--innobase/trx/trx0undo.c202
9 files changed, 753 insertions, 45 deletions
diff --git a/innobase/include/trx0roll.h b/innobase/include/trx0roll.h
index 6004551f456..893e5af6c01 100644
--- a/innobase/include/trx0roll.h
+++ b/innobase/include/trx0roll.h
@@ -104,11 +104,12 @@ trx_rollback(
/***********************************************************************
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);
-/*========================================*/
+void *
+trx_rollback_or_clean_all_without_sess(void *);
+/*============================================*/
/********************************************************************
Finishes a transaction rollback. */
diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h
index 602291f946a..8a16fa82057 100644
--- a/innobase/include/trx0trx.h
+++ b/innobase/include/trx0trx.h
@@ -16,6 +16,7 @@ Created 3/26/1996 Heikki Tuuri
#include "que0types.h"
#include "mem0mem.h"
#include "read0types.h"
+#include "xa.h"
extern ulint trx_n_mysql_transactions;
@@ -156,6 +157,36 @@ trx_commit_for_mysql(
/*=================*/
/* out: 0 or error number */
trx_t* trx); /* in: trx handle */
+
+/**************************************************************************
+Does the transaction prepare for MySQL. */
+
+ulint
+trx_prepare_for_mysql(
+/*=================*/
+ /* out: 0 or error number */
+ trx_t* trx); /* in: trx handle */
+
+/**************************************************************************
+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 */
+ XID* xid_list, /* in/out: prepared transactions */
+ uint len); /* in: number of slots in xid_list */
+
+/***********************************************************************
+This function is used to commit 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 Idenfication */
+
/**************************************************************************
If required, flushes the log to disk if we called trx_commit_for_mysql()
with trx->flush_log_later == TRUE. */
@@ -339,6 +370,9 @@ struct trx_struct{
if we can use the insert buffer for
them, we set this FALSE */
dulint id; /* transaction id */
+ XID xid; /* X/Open XA transaction
+ identification to identify a
+ transaction branch */
dulint no; /* transaction serialization number ==
max trx id when the transaction is
moved to COMMITTED_IN_MEMORY state */
@@ -353,8 +387,10 @@ struct trx_struct{
dulint table_id; /* table id if the preceding field is
TRUE */
/*------------------------------*/
- void* mysql_thd; /* MySQL thread handle corresponding
- to this trx, or NULL */
+ int active_trans; /* whether a transaction in MySQL
+ is active */
+ void* mysql_thd; /* MySQL thread handle corresponding
+ to this trx, or NULL */
char** mysql_query_str;/* pointer to the field in mysqld_thd
which contains the pointer to the
current SQL query string */
@@ -543,6 +579,7 @@ struct trx_struct{
#define TRX_NOT_STARTED 1
#define TRX_ACTIVE 2
#define TRX_COMMITTED_IN_MEMORY 3
+#define TRX_PREPARED 4 /* Support for 2PC/XA */
/* Transaction execution states when trx state is TRX_ACTIVE */
#define TRX_QUE_RUNNING 1 /* transaction is running */
diff --git a/innobase/include/trx0undo.h b/innobase/include/trx0undo.h
index 20002076cc3..5aecada05d8 100644
--- a/innobase/include/trx0undo.h
+++ b/innobase/include/trx0undo.h
@@ -14,6 +14,7 @@ Created 3/26/1996 Heikki Tuuri
#include "mtr0mtr.h"
#include "trx0sys.h"
#include "page0types.h"
+#include "xa.h"
/***************************************************************************
Builds a roll pointer dulint. */
@@ -36,7 +37,7 @@ trx_undo_decode_roll_ptr(
ibool* is_insert, /* out: TRUE if insert undo log */
ulint* rseg_id, /* out: rollback segment id */
ulint* page_no, /* out: page number */
- ulint* offset); /* out: offset of the undo entry within page */
+ ulint* offset); /* out: offset of the undo entry within page */
/***************************************************************************
Returns TRUE if the roll pointer is of the insert type. */
UNIV_INLINE
@@ -239,6 +240,18 @@ trx_undo_set_state_at_finish(
trx_t* trx, /* in: transaction */
trx_undo_t* undo, /* in: undo log memory copy */
mtr_t* mtr); /* in: mtr */
+/**********************************************************************
+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 */
+
/**************************************************************************
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
@@ -294,7 +307,23 @@ trx_undo_parse_discard_latest(
byte* end_ptr,/* in: buffer end */
page_t* page, /* in: page or NULL */
mtr_t* mtr); /* in: mtr or NULL */
+/************************************************************************
+Write X/Open XA Transaction Identification (XID) to undo log header */
+void
+trx_undo_write_xid(
+/*===============*/
+ trx_ulogf_t* log_hdr,/* in: undo log header */
+ XID* xid); /* in: X/Open XA Transaction Identification */
+
+/************************************************************************
+Read X/Open XA Transaction Identification (XID) from undo log header */
+
+void
+trx_undo_read_xid(
+/*==============*/
+ trx_ulogf_t* log_hdr,/* in: undo log header */
+ XID* xid); /* out: X/Open XA Transaction Identification */
/* Types of an undo log segment */
#define TRX_UNDO_INSERT 1 /* contains undo entries for inserts */
@@ -310,6 +339,8 @@ trx_undo_parse_discard_latest(
#define TRX_UNDO_TO_PURGE 4 /* update undo segment will not be
reused: it can be freed in purge when
all undo data in it is removed */
+#define TRX_UNDO_PREPARED 5 /* contains an undo log of an
+ prepared transaction */
/* Transaction undo log memory object; this is protected by the undo_mutex
in the corresponding transaction object */
@@ -332,6 +363,8 @@ struct trx_undo_struct{
field */
dulint trx_id; /* id of the trx assigned to the undo
log */
+ XID xid; /* X/Open XA transaction
+ identification */
ibool dict_operation; /* TRUE if a dict operation trx */
dulint table_id; /* if a dict operation, then the table
id */
@@ -452,7 +485,18 @@ page of an update undo log segment. */
#define TRX_UNDO_HISTORY_NODE 34 /* If the log is put to the history
list, the file list node is here */
/*-------------------------------------------------------------*/
-#define TRX_UNDO_LOG_HDR_SIZE (34 + FLST_NODE_SIZE)
+/* X/Open XA Transaction Identification (XID) */
+
+#define TRX_UNDO_XA_FORMAT (34 + FLST_NODE_SIZE)
+#define TRX_UNDO_XA_TRID_LEN (TRX_UNDO_XA_FORMAT + 4)
+#define TRX_UNDO_XA_BQUAL_LEN (TRX_UNDO_XA_TRID_LEN + 4)
+#define TRX_UNDO_XA_XID (TRX_UNDO_XA_BQUAL_LEN + 4)
+#define TRX_UNDO_XA_LEN (TRX_UNDO_XA_XID + XIDDATASIZE)
+#define TRX_UNDO_XA_EXISTS 256
+
+/*-------------------------------------------------------------*/
+#define TRX_UNDO_LOG_HDR_SIZE (TRX_UNDO_XA_LEN)
+/*-------------------------------------------------------------*/
#ifndef UNIV_NONINL
#include "trx0undo.ic"
diff --git a/innobase/include/xa.h b/innobase/include/xa.h
new file mode 100644
index 00000000000..c1ecd3cc1e7
--- /dev/null
+++ b/innobase/include/xa.h
@@ -0,0 +1,182 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1998-2002
+ * Sleepycat Software. All rights reserved.
+ *
+ * $Id: xa.h,v 11.5 2002/01/11 15:52:30 bostic Exp $
+ */
+/*
+ * Start of xa.h header
+ *
+ * Define a symbol to prevent multiple inclusions of this header file
+ */
+#ifndef XA_H
+#define XA_H
+
+/*
+ * Transaction branch identification: XID and NULLXID:
+ */
+#ifndef XIDDATASIZE
+
+#define XIDDATASIZE 128 /* size in bytes */
+#define MAXGTRIDSIZE 64 /* maximum size in bytes of gtrid */
+#define MAXBQUALSIZE 64 /* maximum size in bytes of bqual */
+
+struct xid_t {
+ long formatID; /* format identifier */
+ long gtrid_length; /* value from 1 through 64 */
+ long bqual_length; /* value from 1 through 64 */
+ char data[XIDDATASIZE];
+};
+typedef struct xid_t XID;
+#endif
+/*
+ * A value of -1 in formatID means that the XID is null.
+ */
+
+/*
+ * Declarations of routines by which RMs call TMs:
+ */
+extern int ax_reg __P((int, XID *, long));
+extern int ax_unreg __P((int, long));
+
+/*
+ * XA Switch Data Structure
+ */
+#define RMNAMESZ 32 /* length of resource manager name, */
+ /* including the null terminator */
+#define MAXINFOSIZE 256 /* maximum size in bytes of xa_info */
+ /* strings, including the null
+ terminator */
+struct xa_switch_t {
+ char name[RMNAMESZ]; /* name of resource manager */
+ long flags; /* resource manager specific options */
+ long version; /* must be 0 */
+ int (*xa_open_entry) /* xa_open function pointer */
+ __P((char *, int, long));
+ int (*xa_close_entry) /* xa_close function pointer */
+ __P((char *, int, long));
+ int (*xa_start_entry) /* xa_start function pointer */
+ __P((XID *, int, long));
+ int (*xa_end_entry) /* xa_end function pointer */
+ __P((XID *, int, long));
+ int (*xa_rollback_entry) /* xa_rollback function pointer */
+ __P((XID *, int, long));
+ int (*xa_prepare_entry) /* xa_prepare function pointer */
+ __P((XID *, int, long));
+ int (*xa_commit_entry) /* xa_commit function pointer */
+ __P((XID *, int, long));
+ int (*xa_recover_entry) /* xa_recover function pointer */
+ __P((XID *, long, int, long));
+ int (*xa_forget_entry) /* xa_forget function pointer */
+ __P((XID *, int, long));
+ int (*xa_complete_entry) /* xa_complete function pointer */
+ __P((int *, int *, int, long));
+};
+
+/*
+ * Flag definitions for the RM switch
+ */
+#define TMNOFLAGS 0x00000000L /* no resource manager features
+ selected */
+#define TMREGISTER 0x00000001L /* resource manager dynamically
+ registers */
+#define TMNOMIGRATE 0x00000002L /* resource manager does not support
+ association migration */
+#define TMUSEASYNC 0x00000004L /* resource manager supports
+ asynchronous operations */
+/*
+ * Flag definitions for xa_ and ax_ routines
+ */
+/* use TMNOFLAGGS, defined above, when not specifying other flags */
+#define TMASYNC 0x80000000L /* perform routine asynchronously */
+#define TMONEPHASE 0x40000000L /* caller is using one-phase commit
+ optimisation */
+#define TMFAIL 0x20000000L /* dissociates caller and marks
+ transaction branch rollback-only */
+#define TMNOWAIT 0x10000000L /* return if blocking condition
+ exists */
+#define TMRESUME 0x08000000L /* caller is resuming association with
+ suspended transaction branch */
+#define TMSUCCESS 0x04000000L /* dissociate caller from transaction
+ branch */
+#define TMSUSPEND 0x02000000L /* caller is suspending, not ending,
+ association */
+#define TMSTARTRSCAN 0x01000000L /* start a recovery scan */
+#define TMENDRSCAN 0x00800000L /* end a recovery scan */
+#define TMMULTIPLE 0x00400000L /* wait for any asynchronous
+ operation */
+#define TMJOIN 0x00200000L /* caller is joining existing
+ transaction branch */
+#define TMMIGRATE 0x00100000L /* caller intends to perform
+ migration */
+
+/*
+ * ax_() return codes (transaction manager reports to resource manager)
+ */
+#define TM_JOIN 2 /* caller is joining existing
+ transaction branch */
+#define TM_RESUME 1 /* caller is resuming association with
+ suspended transaction branch */
+#define TM_OK 0 /* normal execution */
+#define TMER_TMERR -1 /* an error occurred in the transaction
+ manager */
+#define TMER_INVAL -2 /* invalid arguments were given */
+#define TMER_PROTO -3 /* routine invoked in an improper
+ context */
+
+/*
+ * xa_() return codes (resource manager reports to transaction manager)
+ */
+#define XA_RBBASE 100 /* The inclusive lower bound of the
+ rollback codes */
+#define XA_RBROLLBACK XA_RBBASE /* The rollback was caused by an
+ unspecified reason */
+#define XA_RBCOMMFAIL XA_RBBASE+1 /* The rollback was caused by a
+ communication failure */
+#define XA_RBDEADLOCK XA_RBBASE+2 /* A deadlock was detected */
+#define XA_RBINTEGRITY XA_RBBASE+3 /* A condition that violates the
+ integrity of the resources was
+ detected */
+#define XA_RBOTHER XA_RBBASE+4 /* The resource manager rolled back the
+ transaction branch for a reason not
+ on this list */
+#define XA_RBPROTO XA_RBBASE+5 /* A protocol error occurred in the
+ resource manager */
+#define XA_RBTIMEOUT XA_RBBASE+6 /* A transaction branch took too long */
+#define XA_RBTRANSIENT XA_RBBASE+7 /* May retry the transaction branch */
+#define XA_RBEND XA_RBTRANSIENT /* The inclusive upper bound of the
+ rollback codes */
+#define XA_NOMIGRATE 9 /* resumption must occur where
+ suspension occurred */
+#define XA_HEURHAZ 8 /* the transaction branch may have
+ been heuristically completed */
+#define XA_HEURCOM 7 /* the transaction branch has been
+ heuristically committed */
+#define XA_HEURRB 6 /* the transaction branch has been
+ heuristically rolled back */
+#define XA_HEURMIX 5 /* the transaction branch has been
+ heuristically committed and rolled
+ back */
+#define XA_RETRY 4 /* routine returned with no effect and
+ may be re-issued */
+#define XA_RDONLY 3 /* the transaction branch was read-only
+ and has been committed */
+#define XA_OK 0 /* normal execution */
+#define XAER_ASYNC -2 /* asynchronous operation already
+ outstanding */
+#define XAER_RMERR -3 /* a resource manager error occurred in
+ the transaction branch */
+#define XAER_NOTA -4 /* the XID is not valid */
+#define XAER_INVAL -5 /* invalid arguments were given */
+#define XAER_PROTO -6 /* routine invoked in an improper
+ context */
+#define XAER_RMFAIL -7 /* resource manager unavailable */
+#define XAER_DUPID -8 /* the XID already exists */
+#define XAER_OUTSIDE -9 /* resource manager doing work outside
+ transaction */
+#endif /* ifndef XA_H */
+/*
+ * End of xa.h header
+ */
diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c
index 10f921bb1f0..28b9dec8aa0 100644
--- a/innobase/log/log0recv.c
+++ b/innobase/log/log0recv.c
@@ -2851,11 +2851,13 @@ void
recv_recovery_from_checkpoint_finish(void)
/*======================================*/
{
+ int i;
+ os_thread_id_t recovery_thread_id;
+
/* Rollback the uncommitted transactions which have no user session */
- if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) {
- trx_rollback_or_clean_all_without_sess();
- }
+ fprintf(stderr,
+ "InnoDB: Starting to apply log records to the database...\n");
/* Apply the hashed log records to the respective file pages */
@@ -2888,9 +2890,15 @@ recv_recovery_from_checkpoint_finish(void)
/* Free the resources of the recovery system */
recv_recovery_on = FALSE;
+
#ifndef UNIV_LOG_DEBUG
recv_sys_free();
#endif
+
+ if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) {
+ os_thread_create(trx_rollback_or_clean_all_without_sess,
+ (void *)&i, &recovery_thread_id);
+ }
}
/**********************************************************
diff --git a/innobase/trx/trx0roll.c b/innobase/trx/trx0roll.c
index eb7c7f43f03..db5e16c7778 100644
--- a/innobase/trx/trx0roll.c
+++ b/innobase/trx/trx0roll.c
@@ -331,10 +331,11 @@ 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)
+void *
+trx_rollback_or_clean_all_without_sess(void *i)
/*========================================*/
{
mem_heap_t* heap;
@@ -362,7 +363,7 @@ trx_rollback_or_clean_all_without_sess(void)
fprintf(stderr,
"InnoDB: Starting rollback of uncommitted transactions\n");
} else {
- return;
+ os_thread_exit(i);
}
loop:
heap = mem_heap_create(512);
@@ -371,9 +372,15 @@ loop:
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
- while (trx && (trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
+ while (trx) {
- trx = UT_LIST_GET_NEXT(trx_list, 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) {
+ trx->sess = trx_dummy_sess;
+ } else {
+ break;
+ }
}
mutex_exit(&kernel_mutex);
@@ -384,10 +391,11 @@ loop:
mem_heap_free(heap);
- return;
+ os_thread_exit(i);
}
trx->sess = trx_dummy_sess;
+
if (trx->conc_state == TRX_COMMITTED_IN_MEMORY) {
fprintf(stderr, "InnoDB: Cleaning up trx with id %lu %lu\n",
@@ -486,6 +494,8 @@ loop:
mem_heap_free(heap);
goto loop;
+
+ os_thread_exit(i); /* not reached */
}
/***********************************************************************
diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c
index 54bd5be01a1..35e18064329 100644
--- a/innobase/trx/trx0sys.c
+++ b/innobase/trx/trx0sys.c
@@ -887,8 +887,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 f7497ac4090..d7daecdb5b7 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 "xa.h"
/* Copy of the prototype for innobase_mysql_print_thd: this
copy MUST be equal to the one in mysql/sql/ha_innodb.cc ! */
@@ -156,6 +157,10 @@ trx_create(
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);
}
@@ -408,13 +413,22 @@ 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) {
+ 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
@@ -457,10 +471,22 @@ 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) {
+ trx->conc_state =
+ TRX_PREPARED;
+ } else {
+ trx->conc_state =
+ TRX_COMMITTED_IN_MEMORY;
+ }
+
/* We give a dummy value for the trx
number */
@@ -726,7 +752,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 */
@@ -1667,3 +1694,238 @@ 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;
+ dulint lsn;
+ trx_rseg_t* rseg;
+ trx_undo_t* undo;
+ ibool must_flush_log = FALSE;
+ 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 some other state: these modifications to the file data
+ structure define the transaction as prepared in the file
+ based world, at the serialization point of the log sequence
+ number lsn obtained below. */
+
+ 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) {
+ mutex_enter(&kernel_mutex);
+ trx->no = trx_sys_get_new_trx_no();
+
+ mutex_exit(&kernel_mutex);
+
+ /* 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);
+ /*--------------*/
+ 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 (trx->read_view) {
+ read_view_close(trx->read_view);
+
+ mem_heap_empty(trx->read_view_heap);
+ trx->read_view = NULL;
+ }
+
+ if (must_flush_log) {
+
+ mutex_exit(&kernel_mutex);
+
+ if (trx->insert_undo != NULL) {
+
+ trx_undo_insert_cleanup(trx);
+ }
+
+ /* 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 */
+ uint len) /* in: number of slots in xid_list */
+{
+ trx_t* trx;
+ int num_of_transactions = 0;
+
+ ut_ad(xid_list);
+ ut_ad(len);
+
+ 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[num_of_transactions] = trx->xid;
+ num_of_transactions++;
+
+ if ( (uint)num_of_transactions == len ) {
+ break;
+ }
+ }
+
+ trx = UT_LIST_GET_NEXT(trx_list, trx);
+ }
+
+ mutex_exit(&kernel_mutex);
+
+ fprintf(stderr,
+ "InnoDB: %d transactions in prepare state after recovery\n",
+ num_of_transactions);
+
+ return (num_of_transactions);
+}
+
+/***********************************************************************
+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 Idenfication */
+{
+ 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, &trx->xid,
+ 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..4e216808775 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 "xa.h"
/* How should the old versions in the history list be managed?
----------------------------------------------------------
@@ -97,6 +98,7 @@ 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 */
/*******************************************************************
@@ -109,6 +111,7 @@ trx_undo_insert_header_reuse(
page_t* undo_page, /* in: insert undo log segment header page,
x-latched */
dulint trx_id, /* in: transaction id */
+ XID* xid, /* in: X/Open XA transaction identification*/
mtr_t* mtr); /* in: mtr */
/**************************************************************************
If an update undo log can be discarded immediately, this function frees the
@@ -484,6 +487,7 @@ trx_undo_header_create(
TRX_UNDO_LOG_HDR_SIZE bytes free space
on it */
dulint trx_id, /* in: transaction id */
+ XID* xid, /* in: X/Open XA XID */
mtr_t* mtr) /* in: mtr */
{
trx_upagef_t* page_hdr;
@@ -530,11 +534,24 @@ 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);
-
+ /* If X/Open XID exits in the log header we store a
+ flag of it in upper byte of dict operation flag. */
+
+ if ( xid == NULL || xid->formatID == -1) {
+ mach_write_to_2(log_hdr + TRX_UNDO_DICT_OPERATION, FALSE);
+ } else {
+ mach_write_to_2(log_hdr + TRX_UNDO_DICT_OPERATION,
+ TRX_UNDO_XA_EXISTS|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 X/Open XA transaction identification if exists */
+ if ( xid && xid->formatID != -1) {
+ trx_undo_write_xid(log_hdr, xid);
+ }
+
trx_undo_header_create_log(undo_page, trx_id, mtr);
return(free);
@@ -569,6 +586,11 @@ trx_undo_parse_page_header(
mtr_t* mtr) /* in: mtr or NULL */
{
dulint trx_id;
+ XID xid;
+
+ /* Set X/Open XA transaction identification to NULL */
+ memset(&xid, 0, sizeof(xid));
+ xid.formatID = -1;
ptr = mach_dulint_parse_compressed(ptr, end_ptr, &trx_id);
@@ -579,10 +601,10 @@ trx_undo_parse_page_header(
if (page) {
if (type == MLOG_UNDO_HDR_CREATE) {
- trx_undo_header_create(page, trx_id, mtr);
+ trx_undo_header_create(page, trx_id, &xid, mtr);
} else {
ut_ad(type == MLOG_UNDO_HDR_REUSE);
- trx_undo_insert_header_reuse(page, trx_id, mtr);
+ trx_undo_insert_header_reuse(page, trx_id, &xid, mtr);
}
}
@@ -599,6 +621,7 @@ trx_undo_insert_header_reuse(
page_t* undo_page, /* in: insert undo log segment header page,
x-latched */
dulint trx_id, /* in: transaction id */
+ XID* xid, /* in: X/Open XA transaction identification */
mtr_t* mtr) /* in: mtr */
{
trx_upagef_t* page_hdr;
@@ -636,7 +659,17 @@ 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);
+ /* If X/Open XID exits in the log header we store a
+ flag of it in upper byte of dict operation flag and
+ then write the xid. */
+
+ if ( xid && xid->formatID != -1) {
+ mach_write_to_2(log_hdr + TRX_UNDO_DICT_OPERATION,
+ TRX_UNDO_XA_EXISTS|FALSE);
+ trx_undo_write_xid(log_hdr, xid);
+ } else {
+ mach_write_to_2(log_hdr + TRX_UNDO_DICT_OPERATION, FALSE);
+ }
trx_undo_insert_header_reuse_log(undo_page, trx_id, mtr);
@@ -718,6 +751,52 @@ trx_undo_discard_latest_update_undo(
}
/************************************************************************
+Write X/Open XA Transaction Identification (XID) to undo log header */
+
+void
+trx_undo_write_xid(
+/*===============*/
+ trx_ulogf_t* log_hdr,/* in: undo log header */
+ XID* xid) /* in: X/Open XA Transaction Identification */
+{
+ ulint i;
+
+ mach_write_to_4(log_hdr + TRX_UNDO_XA_FORMAT, xid->formatID);
+
+ mach_write_to_4(log_hdr + TRX_UNDO_XA_TRID_LEN, xid->gtrid_length);
+
+ mach_write_to_4(log_hdr + TRX_UNDO_XA_BQUAL_LEN, xid->bqual_length);
+
+ for(i=0; i < XIDDATASIZE; i++) {
+ mach_write_to_1(log_hdr + TRX_UNDO_XA_XID + i,
+ (ulint)(xid->data[i]));
+ }
+}
+
+/************************************************************************
+Read X/Open XA Transaction Identification (XID) from undo log header */
+
+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);
+ }
+}
+
+/************************************************************************
Tries to add a page to the undo log segment where the undo log is placed. */
ulint
@@ -1123,6 +1202,8 @@ trx_undo_mem_create_at_db_start(
fil_addr_t last_addr;
page_t* last_page;
trx_undo_rec_t* rec;
+ XID xid;
+ ulint dict_op;
if (id >= TRX_RSEG_N_SLOTS) {
fprintf(stderr,
@@ -1145,15 +1226,29 @@ trx_undo_mem_create_at_db_start(
undo_header = undo_page + offset;
trx_id = mtr_read_dulint(undo_header + TRX_UNDO_TRX_ID, mtr);
+
+ dict_op = mtr_read_ulint(
+ undo_header + TRX_UNDO_DICT_OPERATION,
+ MLOG_2BYTES, mtr);
+
+ /* Read X/Open XA transaction identification if exists or
+ set it to NULL. */
+
+ memset(&xid, 0, sizeof(xid));
+ xid.formatID = -1;
+
+ if (dict_op & TRX_UNDO_XA_EXISTS) {
+ 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 = (dict_op & 1);
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 +1367,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 +1391,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 +1419,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 +1437,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 +1475,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,9 +1510,10 @@ 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, xid, mtr);
- 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);
return(undo);
}
@@ -1432,6 +1533,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;
@@ -1475,16 +1577,17 @@ trx_undo_reuse_cached(
undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
if (type == TRX_UNDO_INSERT) {
- offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr);
+ offset = trx_undo_insert_header_reuse(undo_page, trx_id,
+ xid, 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);
+ offset = trx_undo_header_create(undo_page, trx_id, xid, 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 +1609,19 @@ 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);
-
+ /* Mark X/Open XA XID if it is not NULL to dict operation bit */
+
+ if ( trx->xid.formatID == -1) {
+ mlog_write_ulint(hdr_page + undo->hdr_offset +
+ TRX_UNDO_DICT_OPERATION,
+ trx->dict_operation, MLOG_2BYTES, mtr);
+ } else {
+ mlog_write_ulint(hdr_page + undo->hdr_offset +
+ TRX_UNDO_DICT_OPERATION,
+ trx->dict_operation | TRX_UNDO_XA_EXISTS,
+ MLOG_2BYTES, mtr);
+ }
+
mlog_write_dulint(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID,
trx->table_id, mtr);
@@ -1548,10 +1661,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 +1745,53 @@ 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;
+
+ trx_undo_write_xid(undo_header, &undo->xid);
+ 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