diff options
author | unknown <jan@hundin.mysql.fi> | 2004-11-30 12:11:02 +0200 |
---|---|---|
committer | unknown <jan@hundin.mysql.fi> | 2004-11-30 12:11:02 +0200 |
commit | 739b630ed7c1a832ff4e8075cd8739c93286e61f (patch) | |
tree | 84e8ec4376e25d1f3cb8c8ba19936ac217f93266 /innobase | |
parent | 857bd559ec956c4be44422f3f185d310029fb765 (diff) | |
parent | df0e057a52db9f085c42ec593f3000da4afdbbd7 (diff) | |
download | mariadb-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.h | 9 | ||||
-rw-r--r-- | innobase/include/trx0trx.h | 41 | ||||
-rw-r--r-- | innobase/include/trx0undo.h | 48 | ||||
-rw-r--r-- | innobase/include/xa.h | 182 | ||||
-rw-r--r-- | innobase/log/log0recv.c | 14 | ||||
-rw-r--r-- | innobase/trx/trx0roll.c | 24 | ||||
-rw-r--r-- | innobase/trx/trx0sys.c | 6 | ||||
-rw-r--r-- | innobase/trx/trx0trx.c | 272 | ||||
-rw-r--r-- | innobase/trx/trx0undo.c | 202 |
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 |