/***************************************************************************** Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0sys.ic Transaction system Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0trx.h" #include "data0type.h" #ifndef UNIV_HOTBACKUP # include "srv0srv.h" # include "mtr0log.h" /* The typedef for rseg slot in the file copy */ typedef byte trx_sysf_rseg_t; /* Rollback segment specification slot offsets */ /*-------------------------------------------------------------*/ #define TRX_SYS_RSEG_SPACE 0 /* space where the segment header is placed; starting with MySQL/InnoDB 5.1.7, this is UNIV_UNDEFINED if the slot is unused */ #define TRX_SYS_RSEG_PAGE_NO 4 /* page number where the segment header is placed; this is FIL_NULL if the slot is unused */ /*-------------------------------------------------------------*/ /* Size of a rollback segment specification slot */ #define TRX_SYS_RSEG_SLOT_SIZE 8 /*****************************************************************//** Writes the value of max_trx_id to the file based trx system header. */ UNIV_INTERN void trx_sys_flush_max_trx_id(void); /*==========================*/ /***************************************************************//** Checks if a page address is the trx sys header page. @return TRUE if trx sys header page */ UNIV_INLINE ibool trx_sys_hdr_page( /*=============*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { if ((space == TRX_SYS_SPACE) && (page_no == TRX_SYS_PAGE_NO)) { return(TRUE); } return(FALSE); } /***************************************************************//** Checks if a space is the system tablespaces. @return TRUE if system tablespace */ UNIV_INLINE ibool trx_sys_sys_space( /*==============*/ ulint space) /*!< in: space */ { if (srv_doublewrite_file) { /* several spaces are reserved */ return((ibool)(space == TRX_SYS_SPACE || space == TRX_DOUBLEWRITE_SPACE)); } else { return((ibool)(space == TRX_SYS_SPACE)); } } /***************************************************************//** Checks if a space is the doublewrite tablespace. @return TRUE if doublewrite tablespace */ UNIV_INLINE ibool trx_sys_doublewrite_space( /*======================*/ ulint space) /*!< in: space */ { if (srv_doublewrite_file) { /* doublewrite buffer is separated */ return((ibool)(space == TRX_DOUBLEWRITE_SPACE)); } else { return((ibool)(space == TRX_SYS_SPACE)); } } /***************************************************************//** Gets the pointer in the nth slot of the rseg array. @return pointer to rseg object, NULL if slot not in use */ UNIV_INLINE trx_rseg_t* trx_sys_get_nth_rseg( /*=================*/ trx_sys_t* sys, /*!< in: trx system */ ulint n) /*!< in: index of slot */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(n < TRX_SYS_N_RSEGS); return(sys->rseg_array[n]); } /***************************************************************//** Sets the pointer in the nth slot of the rseg array. */ UNIV_INLINE void trx_sys_set_nth_rseg( /*=================*/ trx_sys_t* sys, /*!< in: trx system */ ulint n, /*!< in: index of slot */ trx_rseg_t* rseg) /*!< in: pointer to rseg object, NULL if slot not in use */ { ut_ad(n < TRX_SYS_N_RSEGS); sys->rseg_array[n] = rseg; } /**********************************************************************//** Gets a pointer to the transaction system header and x-latches its page. @return pointer to system header, page x-latched. */ UNIV_INLINE trx_sysf_t* trx_sysf_get( /*=========*/ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; trx_sysf_t* header; ut_ad(mtr); block = buf_page_get(TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER); header = TRX_SYS + buf_block_get_frame(block); return(header); } /*****************************************************************//** Gets the space of the nth rollback segment slot in the trx system file copy. @return space id */ UNIV_INLINE ulint trx_sysf_rseg_get_space( /*====================*/ trx_sysf_t* sys_header, /*!< in: trx sys header */ ulint i, /*!< in: slot index == rseg id */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(sys_header); ut_ad(i < TRX_SYS_N_RSEGS); return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_SPACE, MLOG_4BYTES, mtr)); } /*****************************************************************//** Gets the page number of the nth rollback segment slot in the trx system header. @return page number, FIL_NULL if slot unused */ UNIV_INLINE ulint trx_sysf_rseg_get_page_no( /*======================*/ trx_sysf_t* sys_header, /*!< in: trx system header */ ulint i, /*!< in: slot index == rseg id */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(sys_header); ut_ad(mutex_own(&(kernel_mutex))); ut_ad(i < TRX_SYS_N_RSEGS); return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_PAGE_NO, MLOG_4BYTES, mtr)); } /*****************************************************************//** Sets the space id of the nth rollback segment slot in the trx system file copy. */ UNIV_INLINE void trx_sysf_rseg_set_space( /*====================*/ trx_sysf_t* sys_header, /*!< in: trx sys file copy */ ulint i, /*!< in: slot index == rseg id */ ulint space, /*!< in: space id */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(sys_header); ut_ad(i < TRX_SYS_N_RSEGS); mlog_write_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_SPACE, space, MLOG_4BYTES, mtr); } /*****************************************************************//** Sets the page number of the nth rollback segment slot in the trx system header. */ UNIV_INLINE void trx_sysf_rseg_set_page_no( /*======================*/ trx_sysf_t* sys_header, /*!< in: trx sys header */ ulint i, /*!< in: slot index == rseg id */ ulint page_no, /*!< in: page number, FIL_NULL if the slot is reset to unused */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(sys_header); ut_ad(i < TRX_SYS_N_RSEGS); mlog_write_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_PAGE_NO, page_no, MLOG_4BYTES, mtr); } #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Writes a trx id to an index page. In case that the id size changes in some future version, this function should be used instead of mach_write_... */ UNIV_INLINE void trx_write_trx_id( /*=============*/ byte* ptr, /*!< in: pointer to memory where written */ trx_id_t id) /*!< in: id */ { #if DATA_TRX_ID_LEN != 6 # error "DATA_TRX_ID_LEN != 6" #endif mach_write_to_6(ptr, id); } #ifndef UNIV_HOTBACKUP /*****************************************************************//** Reads a trx id from an index page. In case that the id size changes in some future version, this function should be used instead of mach_read_... @return id */ UNIV_INLINE trx_id_t trx_read_trx_id( /*============*/ const byte* ptr) /*!< in: pointer to memory from where to read */ { #if DATA_TRX_ID_LEN != 6 # error "DATA_TRX_ID_LEN != 6" #endif return(mach_read_from_6(ptr)); } /****************************************************************//** Looks for the trx handle with the given id in trx_list. @return the trx handle or NULL if not found */ UNIV_INLINE trx_t* trx_get_on_id( /*==========*/ trx_id_t trx_id) /*!< in: trx id to search for */ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx != NULL) { if (0 == ut_dulint_cmp(trx_id, trx->id)) { return(trx); } trx = UT_LIST_GET_NEXT(trx_list, trx); } return(NULL); } #if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG /***********************************************************//** Assert that a transaction has been recovered. @return TRUE */ UNIV_INLINE ibool trx_assert_recovered( /*=================*/ trx_id_t trx_id) /*!< in: transaction identifier */ { trx_t* trx; mutex_enter(&kernel_mutex); trx = trx_get_on_id(trx_id); ut_a(trx); ut_a(trx->is_recovered); mutex_exit(&kernel_mutex); return(TRUE); } #endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ /****************************************************************//** Returns the minumum trx id in trx list. This is the smallest id for which the trx can possibly be active. (But, you must look at the trx->conc_state to find out if the minimum trx id transaction itself is active, or already committed.) @return the minimum trx id, or trx_sys->max_trx_id if the trx list is empty */ UNIV_INLINE trx_id_t trx_list_get_min_trx_id(void) /*=========================*/ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); trx = UT_LIST_GET_LAST(trx_sys->trx_list); if (trx == NULL) { return(trx_sys->max_trx_id); } return(trx->id); } /****************************************************************//** Checks if a transaction with the given id is active. @return TRUE if active */ UNIV_INLINE ibool trx_is_active( /*==========*/ trx_id_t trx_id) /*!< in: trx id of the transaction */ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); if (ut_dulint_cmp(trx_id, trx_list_get_min_trx_id()) < 0) { return(FALSE); } if (ut_dulint_cmp(trx_id, trx_sys->max_trx_id) >= 0) { /* There must be corruption: we return TRUE because this function is only called by lock_clust_rec_some_has_impl() and row_vers_impl_x_locked_off_kernel() and they have diagnostic prints in this case */ return(TRUE); } trx = trx_get_on_id(trx_id); if (trx && (trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED)) { return(TRUE); } return(FALSE); } /*****************************************************************//** Allocates a new transaction id. @return new, allocated trx id */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_id(void) /*========================*/ { trx_id_t id; ut_ad(mutex_own(&kernel_mutex)); /* VERY important: after the database is started, max_trx_id value is divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if will evaluate to TRUE when this function is first time called, and the value for trx id will be written to disk-based header! Thus trx id values will not overlap when the database is repeatedly started! */ if (ut_dulint_get_low(trx_sys->max_trx_id) % TRX_SYS_TRX_ID_WRITE_MARGIN == 0) { trx_sys_flush_max_trx_id(); } id = trx_sys->max_trx_id; UT_DULINT_INC(trx_sys->max_trx_id); return(id); } /*****************************************************************//** Allocates a new transaction number. @return new, allocated trx number */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_no(void) /*========================*/ { ut_ad(mutex_own(&kernel_mutex)); return(trx_sys_get_new_trx_id()); } #endif /* !UNIV_HOTBACKUP */