diff options
Diffstat (limited to 'storage/innobase/include/trx0trx.h')
-rw-r--r-- | storage/innobase/include/trx0trx.h | 1126 |
1 files changed, 1126 insertions, 0 deletions
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h new file mode 100644 index 00000000..09132e7c --- /dev/null +++ b/storage/innobase/include/trx0trx.h @@ -0,0 +1,1126 @@ +/***************************************************************************** + +Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 2021, MariaDB Corporation. + +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., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file include/trx0trx.h +The transaction + +Created 3/26/1996 Heikki Tuuri +*******************************************************/ + +#ifndef trx0trx_h +#define trx0trx_h + +#include "trx0types.h" +#include "lock0types.h" +#include "que0types.h" +#include "mem0mem.h" +#include "trx0xa.h" +#include "ut0vec.h" +#include "fts0fts.h" +#include "read0types.h" +#include "ilist.h" + +#include <vector> +#include <set> + +// Forward declaration +struct mtr_t; +struct rw_trx_hash_element_t; + +/******************************************************************//** +Set detailed error message for the transaction. */ +void +trx_set_detailed_error( +/*===================*/ + trx_t* trx, /*!< in: transaction struct */ + const char* msg); /*!< in: detailed error message */ +/*************************************************************//** +Set detailed error message for the transaction from a file. Note that the +file is rewinded before reading from it. */ +void +trx_set_detailed_error_from_file( +/*=============================*/ + trx_t* trx, /*!< in: transaction struct */ + FILE* file); /*!< in: file to read message from */ +/****************************************************************//** +Retrieves the error_info field from a trx. +@return the error info */ +UNIV_INLINE +const dict_index_t* +trx_get_error_info( +/*===============*/ + const trx_t* trx); /*!< in: trx object */ + +/** @return an allocated transaction */ +trx_t *trx_create(); + +/** At shutdown, frees a transaction object. */ +void trx_free_at_shutdown(trx_t *trx); + +/** Disconnect a prepared transaction from MySQL. +@param[in,out] trx transaction */ +void trx_disconnect_prepared(trx_t *trx); + +/** Initialize (resurrect) transactions at startup. */ +dberr_t trx_lists_init_at_db_start(); + +/*************************************************************//** +Starts the transaction if it is not yet started. */ +void +trx_start_if_not_started_xa_low( +/*============================*/ + trx_t* trx, /*!< in/out: transaction */ + bool read_write); /*!< in: true if read write transaction */ +/*************************************************************//** +Starts the transaction if it is not yet started. */ +void +trx_start_if_not_started_low( +/*=========================*/ + trx_t* trx, /*!< in/out: transaction */ + bool read_write); /*!< in: true if read write transaction */ + +/*************************************************************//** +Starts a transaction for internal processing. */ +void +trx_start_internal_low( +/*===================*/ + trx_t* trx); /*!< in/out: transaction */ + +/** Starts a read-only transaction for internal processing. +@param[in,out] trx transaction to be started */ +void +trx_start_internal_read_only_low( + trx_t* trx); + +#ifdef UNIV_DEBUG +#define trx_start_if_not_started_xa(t, rw) \ + do { \ + (t)->start_line = __LINE__; \ + (t)->start_file = __FILE__; \ + trx_start_if_not_started_xa_low((t), rw); \ + } while (false) + +#define trx_start_if_not_started(t, rw) \ + do { \ + (t)->start_line = __LINE__; \ + (t)->start_file = __FILE__; \ + trx_start_if_not_started_low((t), rw); \ + } while (false) + +#define trx_start_internal(t) \ + do { \ + (t)->start_line = __LINE__; \ + (t)->start_file = __FILE__; \ + trx_start_internal_low((t)); \ + } while (false) + +#define trx_start_internal_read_only(t) \ + do { \ + (t)->start_line = __LINE__; \ + (t)->start_file = __FILE__; \ + trx_start_internal_read_only_low(t); \ + } while (false) +#else +#define trx_start_if_not_started(t, rw) \ + trx_start_if_not_started_low((t), rw) + +#define trx_start_internal(t) \ + trx_start_internal_low((t)) + +#define trx_start_internal_read_only(t) \ + trx_start_internal_read_only_low(t) + +#define trx_start_if_not_started_xa(t, rw) \ + trx_start_if_not_started_xa_low((t), (rw)) +#endif /* UNIV_DEBUG */ + +/*************************************************************//** +Starts the transaction for a DDL operation. */ +void +trx_start_for_ddl_low( +/*==================*/ + trx_t* trx, /*!< in/out: transaction */ + trx_dict_op_t op); /*!< in: dictionary operation type */ + +#ifdef UNIV_DEBUG +#define trx_start_for_ddl(t, o) \ + do { \ + ut_ad((t)->start_file == 0); \ + (t)->start_line = __LINE__; \ + (t)->start_file = __FILE__; \ + trx_start_for_ddl_low((t), (o)); \ + } while (0) +#else +#define trx_start_for_ddl(t, o) \ + trx_start_for_ddl_low((t), (o)) +#endif /* UNIV_DEBUG */ + +/**********************************************************************//** +Does the transaction commit for MySQL. +@return DB_SUCCESS or error number */ +dberr_t +trx_commit_for_mysql( +/*=================*/ + trx_t* trx); /*!< in/out: transaction */ +/** XA PREPARE a transaction. +@param[in,out] trx transaction to prepare */ +void trx_prepare_for_mysql(trx_t* trx); +/**********************************************************************//** +This function is used to find number of prepared transactions and +their transaction objects for a recovery. +@return number of prepared transactions */ +int +trx_recover_for_mysql( +/*==================*/ + XID* xid_list, /*!< in/out: prepared transactions */ + uint len); /*!< in: number of slots in xid_list */ +/** Look up an X/Open distributed transaction in XA PREPARE state. +@param[in] xid X/Open XA transaction identifier +@return transaction on match (the trx_t::xid will be invalidated); +note that the trx may have been committed before the caller acquires +trx_t::mutex +@retval NULL if no match */ +trx_t* trx_get_trx_by_xid(const XID* xid); +/**********************************************************************//** +If required, flushes the log to disk if we called trx_commit_for_mysql() +with trx->flush_log_later == TRUE. */ +void +trx_commit_complete_for_mysql( +/*==========================*/ + trx_t* trx); /*!< in/out: transaction */ +/**********************************************************************//** +Marks the latest SQL statement ended. */ +void +trx_mark_sql_stat_end( +/*==================*/ + trx_t* trx); /*!< in: trx handle */ +/****************************************************************//** +Prepares a transaction for commit/rollback. */ +void +trx_commit_or_rollback_prepare( +/*===========================*/ + trx_t* trx); /*!< in/out: transaction */ +/*********************************************************************//** +Creates a commit command node struct. +@return own: commit node struct */ +commit_node_t* +trx_commit_node_create( +/*===================*/ + mem_heap_t* heap); /*!< in: mem heap where created */ +/***********************************************************//** +Performs an execution step for a commit type node in a query graph. +@return query thread to run next, or NULL */ +que_thr_t* +trx_commit_step( +/*============*/ + que_thr_t* thr); /*!< in: query thread */ + +/**********************************************************************//** +Prints info about a transaction. */ +void +trx_print_low( +/*==========*/ + FILE* f, + /*!< in: output stream */ + const trx_t* trx, + /*!< in: transaction */ + ulint max_query_len, + /*!< in: max query length to print, + or 0 to use the default max length */ + ulint n_rec_locks, + /*!< in: lock_number_of_rows_locked(&trx->lock) */ + ulint n_trx_locks, + /*!< in: length of trx->lock.trx_locks */ + ulint heap_size); + /*!< in: mem_heap_get_size(trx->lock.lock_heap) */ + +/**********************************************************************//** +Prints info about a transaction. +When possible, use trx_print() instead. */ +void +trx_print_latched( +/*==============*/ + FILE* f, /*!< in: output stream */ + const trx_t* trx, /*!< in: transaction */ + ulint max_query_len); /*!< in: max query length to print, + or 0 to use the default max length */ + +/**********************************************************************//** +Prints info about a transaction. +Acquires and releases lock_sys.mutex. */ +void +trx_print( +/*======*/ + FILE* f, /*!< in: output stream */ + const trx_t* trx, /*!< in: transaction */ + ulint max_query_len); /*!< in: max query length to print, + or 0 to use the default max length */ + +/**********************************************************************//** +Determine if a transaction is a dictionary operation. +@return dictionary operation mode */ +UNIV_INLINE +enum trx_dict_op_t +trx_get_dict_operation( +/*===================*/ + const trx_t* trx) /*!< in: transaction */ + MY_ATTRIBUTE((warn_unused_result)); +/**********************************************************************//** +Flag a transaction a dictionary operation. */ +UNIV_INLINE +void +trx_set_dict_operation( +/*===================*/ + trx_t* trx, /*!< in/out: transaction */ + enum trx_dict_op_t op); /*!< in: operation, not + TRX_DICT_OP_NONE */ + +/**********************************************************************//** +Determines if a transaction is in the given state. +The caller must hold trx->mutex, or it must be the thread +that is serving a running transaction. +A running RW transaction must be in trx_sys.rw_trx_hash. +@return TRUE if trx->state == state */ +UNIV_INLINE +bool +trx_state_eq( +/*=========*/ + const trx_t* trx, /*!< in: transaction */ + trx_state_t state, /*!< in: state; + if state != TRX_STATE_NOT_STARTED + asserts that + trx->state != TRX_STATE_NOT_STARTED */ + bool relaxed = false) + /*!< in: whether to allow + trx->state == TRX_STATE_NOT_STARTED + after an error has been reported */ + MY_ATTRIBUTE((nonnull, warn_unused_result)); + +/**********************************************************************//** +Determines if the currently running transaction has been interrupted. +@return true if interrupted */ +bool +trx_is_interrupted( +/*===============*/ + const trx_t* trx); /*!< in: transaction */ + +/*******************************************************************//** +Calculates the "weight" of a transaction. The weight of one transaction +is estimated as the number of altered rows + the number of locked rows. +@param t transaction +@return transaction weight */ +#define TRX_WEIGHT(t) ((t)->undo_no + UT_LIST_GET_LEN((t)->lock.trx_locks)) + +/*******************************************************************//** +Compares the "weight" (or size) of two transactions. Transactions that +have edited non-transactional tables are considered heavier than ones +that have not. +@return true if weight(a) >= weight(b) */ +bool +trx_weight_ge( +/*==========*/ + const trx_t* a, /*!< in: the transaction to be compared */ + const trx_t* b); /*!< in: the transaction to be compared */ +/* Maximum length of a string that can be returned by +trx_get_que_state_str(). */ +#define TRX_QUE_STATE_STR_MAX_LEN 12 /* "ROLLING BACK" */ + +/*******************************************************************//** +Retrieves transaction's que state in a human readable string. The string +should not be free()'d or modified. +@return string in the data segment */ +UNIV_INLINE +const char* +trx_get_que_state_str( +/*==================*/ + const trx_t* trx); /*!< in: transaction */ + +/** Retreieves the transaction ID. +In a given point in time it is guaranteed that IDs of the running +transactions are unique. The values returned by this function for readonly +transactions may be reused, so a subsequent RO transaction may get the same ID +as a RO transaction that existed in the past. The values returned by this +function should be used for printing purposes only. +@param[in] trx transaction whose id to retrieve +@return transaction id */ +UNIV_INLINE +trx_id_t +trx_get_id_for_print( + const trx_t* trx); + +/** Create the trx_t pool */ +void +trx_pool_init(); + +/** Destroy the trx_t pool */ +void +trx_pool_close(); + +/** +Set the transaction as a read-write transaction if it is not already +tagged as such. +@param[in,out] trx Transaction that needs to be "upgraded" to RW from RO */ +void +trx_set_rw_mode( + trx_t* trx); + +/** +Transactions that aren't started by the MySQL server don't set +the trx_t::mysql_thd field. For such transactions we set the lock +wait timeout to 0 instead of the user configured value that comes +from innodb_lock_wait_timeout via trx_t::mysql_thd. +@param trx transaction +@return lock wait timeout in seconds */ +#define trx_lock_wait_timeout_get(t) \ + ((t)->mysql_thd != NULL \ + ? thd_lock_wait_timeout((t)->mysql_thd) \ + : 0) + +typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_list; + +/*******************************************************************//** +Latching protocol for trx_lock_t::que_state. trx_lock_t::que_state +captures the state of the query thread during the execution of a query. +This is different from a transaction state. The query state of a transaction +can be updated asynchronously by other threads. The other threads can be +system threads, like the timeout monitor thread or user threads executing +other queries. Another thing to be mindful of is that there is a delay between +when a query thread is put into LOCK_WAIT state and before it actually starts +waiting. Between these two events it is possible that the query thread is +granted the lock it was waiting for, which implies that the state can be changed +asynchronously. + +All these operations take place within the context of locking. Therefore state +changes within the locking code must acquire both the lock mutex and the +trx->mutex when changing trx->lock.que_state to TRX_QUE_LOCK_WAIT or +trx->lock.wait_lock to non-NULL but when the lock wait ends it is sufficient +to only acquire the trx->mutex. +To query the state either of the mutexes is sufficient within the locking +code and no mutex is required when the query thread is no longer waiting. */ + +/** The locks and state of an active transaction. Protected by +lock_sys.mutex, trx->mutex or both. */ +struct trx_lock_t { +#ifdef UNIV_DEBUG + /** number of active query threads; at most 1, except for the + dummy transaction in trx_purge() */ + ulint n_active_thrs; +#endif + trx_que_t que_state; /*!< valid when trx->state + == TRX_STATE_ACTIVE: TRX_QUE_RUNNING, + TRX_QUE_LOCK_WAIT, ... */ + + lock_t* wait_lock; /*!< if trx execution state is + TRX_QUE_LOCK_WAIT, this points to + the lock request, otherwise this is + NULL; set to non-NULL when holding + both trx->mutex and lock_sys.mutex; + set to NULL when holding + lock_sys.mutex; readers should + hold lock_sys.mutex, except when + they are holding trx->mutex and + wait_lock==NULL */ + ib_uint64_t deadlock_mark; /*!< A mark field that is initialized + to and checked against lock_mark_counter + by lock_deadlock_recursive(). */ + bool was_chosen_as_deadlock_victim; + /*!< when the transaction decides to + wait for a lock, it sets this to false; + if another transaction chooses this + transaction as a victim in deadlock + resolution, it sets this to true. + Protected by trx->mutex. */ + time_t wait_started; /*!< lock wait started at this time, + protected only by lock_sys.mutex */ + + que_thr_t* wait_thr; /*!< query thread belonging to this + trx that is in QUE_THR_LOCK_WAIT + state. For threads suspended in a + lock wait, this is protected by + lock_sys.mutex. Otherwise, this may + only be modified by the thread that is + serving the running transaction. */ +#ifdef WITH_WSREP + bool was_chosen_as_wsrep_victim; + /*!< high priority wsrep thread has + marked this trx to abort */ +#endif /* WITH_WSREP */ + + /** Pre-allocated record locks */ + struct { + ib_lock_t lock; byte pad[256]; + } rec_pool[8]; + + /** Pre-allocated table locks */ + ib_lock_t table_pool[8]; + + /** Next available rec_pool[] entry */ + unsigned rec_cached; + + /** Next available table_pool[] entry */ + unsigned table_cached; + + mem_heap_t* lock_heap; /*!< memory heap for trx_locks; + protected by lock_sys.mutex */ + + trx_lock_list_t trx_locks; /*!< locks requested by the transaction; + insertions are protected by trx->mutex + and lock_sys.mutex; removals are + protected by lock_sys.mutex */ + + lock_list table_locks; /*!< All table locks requested by this + transaction, including AUTOINC locks */ + + /** List of pending trx_t::evict_table() */ + UT_LIST_BASE_NODE_T(dict_table_t) evicted_tables; + + bool cancel; /*!< true if the transaction is being + rolled back either via deadlock + detection or due to lock timeout. The + caller has to acquire the trx_t::mutex + in order to cancel the locks. In + lock_trx_table_locks_remove() we + check for this cancel of a transaction's + locks and avoid reacquiring the trx + mutex to prevent recursive deadlocks. + Protected by both the lock sys mutex + and the trx_t::mutex. */ + ulint n_rec_locks; /*!< number of rec locks in this trx */ +}; + +/** Logical first modification time of a table in a transaction */ +class trx_mod_table_time_t +{ + /** First modification of the table */ + undo_no_t first; + /** First modification of a system versioned column */ + undo_no_t first_versioned; + + /** Magic value signifying that a system versioned column of a + table was never modified in a transaction. */ + static const undo_no_t UNVERSIONED = IB_ID_MAX; + +public: + /** Constructor + @param[in] rows number of modified rows so far */ + trx_mod_table_time_t(undo_no_t rows) + : first(rows), first_versioned(UNVERSIONED) {} + +#ifdef UNIV_DEBUG + /** Validation + @param[in] rows number of modified rows so far + @return whether the object is valid */ + bool valid(undo_no_t rows = UNVERSIONED) const + { + return first <= first_versioned && first <= rows; + } +#endif /* UNIV_DEBUG */ + /** @return if versioned columns were modified */ + bool is_versioned() const { return first_versioned != UNVERSIONED; } + + /** After writing an undo log record, set is_versioned() if needed + @param[in] rows number of modified rows so far */ + void set_versioned(undo_no_t rows) + { + ut_ad(!is_versioned()); + first_versioned = rows; + ut_ad(valid()); + } + + /** Invoked after partial rollback + @param[in] limit number of surviving modified rows + @return whether this should be erased from trx_t::mod_tables */ + bool rollback(undo_no_t limit) + { + ut_ad(valid()); + if (first >= limit) { + return true; + } + + if (first_versioned < limit && is_versioned()) { + first_versioned = UNVERSIONED; + } + + return false; + } +}; + +/** Collection of persistent tables and their first modification +in a transaction. +We store pointers to the table objects in memory because +we know that a table object will not be destroyed while a transaction +that modified it is running. */ +typedef std::map< + dict_table_t*, trx_mod_table_time_t, + std::less<dict_table_t*>, + ut_allocator<std::pair<dict_table_t* const, trx_mod_table_time_t> > > + trx_mod_tables_t; + +/** The transaction handle + +Normally, there is a 1:1 relationship between a transaction handle +(trx) and a session (client connection). One session is associated +with exactly one user transaction. There are some exceptions to this: + +* For DDL operations, a subtransaction is allocated that modifies the +data dictionary tables. Lock waits and deadlocks are prevented by +acquiring the dict_sys.latch before starting the subtransaction +and releasing it after committing the subtransaction. + +* The purge system uses a special transaction that is not associated +with any session. + +* If the system crashed or it was quickly shut down while there were +transactions in the ACTIVE or PREPARED state, these transactions would +no longer be associated with a session when the server is restarted. + +A session may be served by at most one thread at a time. The serving +thread of a session might change in some MySQL implementations. +Therefore we do not have os_thread_get_curr_id() assertions in the code. + +Normally, only the thread that is currently associated with a running +transaction may access (read and modify) the trx object, and it may do +so without holding any mutex. The following are exceptions to this: + +* trx_rollback_recovered() may access resurrected (connectionless) +transactions (state == TRX_STATE_ACTIVE && is_recovered) +while the system is already processing new user transactions (!is_recovered). + +* trx_print_low() may access transactions not associated with the current +thread. The caller must be holding lock_sys.mutex. + +* When a transaction handle is in the trx_sys.trx_list, some of its fields +must not be modified without holding trx->mutex. + +* The locking code (in particular, lock_deadlock_recursive() and +lock_rec_convert_impl_to_expl()) will access transactions associated +to other connections. The locks of transactions are protected by +lock_sys.mutex (insertions also by trx->mutex). */ + +/** Represents an instance of rollback segment along with its state variables.*/ +struct trx_undo_ptr_t { + trx_rseg_t* rseg; /*!< rollback segment assigned to the + transaction, or NULL if not assigned + yet */ + trx_undo_t* undo; /*!< pointer to the undo log, or + NULL if nothing logged yet */ +}; + +/** An instance of temporary rollback segment. */ +struct trx_temp_undo_t { + /** temporary rollback segment, or NULL if not assigned yet */ + trx_rseg_t* rseg; + /** pointer to the undo log, or NULL if nothing logged yet */ + trx_undo_t* undo; +}; + +/** Rollback segments assigned to a transaction for undo logging. */ +struct trx_rsegs_t { + /** undo log ptr holding reference to a rollback segment that resides in + system/undo tablespace used for undo logging of tables that needs + to be recovered on crash. */ + trx_undo_ptr_t m_redo; + + /** undo log for temporary tables; discarded immediately after + transaction commit/rollback */ + trx_temp_undo_t m_noredo; +}; + +struct trx_t : ilist_node<> { +private: + /** + Count of references. + + We can't release the locks nor commit the transaction until this reference + is 0. We can change the state to TRX_STATE_COMMITTED_IN_MEMORY to signify + that it is no longer "active". + */ + + Atomic_counter<int32_t> n_ref; + + +public: + TrxMutex mutex; /*!< Mutex protecting the fields + state and lock (except some fields + of lock, which are protected by + lock_sys.mutex) */ + + trx_id_t id; /*!< transaction id */ + + /** State of the trx from the point of view of concurrency control + and the valid state transitions. + + Possible states: + + TRX_STATE_NOT_STARTED + TRX_STATE_ACTIVE + TRX_STATE_PREPARED + TRX_STATE_PREPARED_RECOVERED (special case of TRX_STATE_PREPARED) + TRX_STATE_COMMITTED_IN_MEMORY (alias below COMMITTED) + + Valid state transitions are: + + Regular transactions: + * NOT_STARTED -> ACTIVE -> COMMITTED -> NOT_STARTED + + Auto-commit non-locking read-only: + * NOT_STARTED -> ACTIVE -> NOT_STARTED + + XA (2PC): + * NOT_STARTED -> ACTIVE -> PREPARED -> COMMITTED -> NOT_STARTED + + Recovered XA: + * NOT_STARTED -> PREPARED -> COMMITTED -> (freed) + + Recovered XA followed by XA ROLLBACK: + * NOT_STARTED -> PREPARED -> ACTIVE -> COMMITTED -> (freed) + + XA (2PC) (shutdown or disconnect before ROLLBACK or COMMIT): + * NOT_STARTED -> PREPARED -> (freed) + + Disconnected XA can become recovered: + * ... -> ACTIVE -> PREPARED (connected) -> PREPARED (disconnected) + Disconnected means from mysql e.g due to the mysql client disconnection. + Latching and various transaction lists membership rules: + + XA (2PC) transactions are always treated as non-autocommit. + + Transitions to ACTIVE or NOT_STARTED occur when transaction + is not in rw_trx_hash. + + Autocommit non-locking read-only transactions move between states + without holding any mutex. They are not in rw_trx_hash. + + All transactions, unless they are determined to be ac-nl-ro, + explicitly tagged as read-only or read-write, will first be put + on the read-only transaction list. Only when a !read-only transaction + in the read-only list tries to acquire an X or IX lock on a table + do we remove it from the read-only list and put it on the read-write + list. During this switch we assign it a rollback segment. + + When a transaction is NOT_STARTED, it can be in trx_list. It cannot be + in rw_trx_hash. + + ACTIVE->PREPARED->COMMITTED is only possible when trx is in rw_trx_hash. + The transition ACTIVE->PREPARED is protected by trx->mutex. + + ACTIVE->COMMITTED is possible when the transaction is in + rw_trx_hash. + + Transitions to COMMITTED are protected by trx_t::mutex. */ + trx_state_t state; +#ifdef WITH_WSREP + /** whether wsrep_on(mysql_thd) held at the start of transaction */ + bool wsrep; + bool is_wsrep() const { return UNIV_UNLIKELY(wsrep); } + /** true, if BF thread is performing unique secondary index scanning */ + bool wsrep_UK_scan; + bool is_wsrep_UK_scan() const { return UNIV_UNLIKELY(wsrep_UK_scan); } +#else /* WITH_WSREP */ + bool is_wsrep() const { return false; } +#endif /* WITH_WSREP */ + + ReadView read_view; /*!< consistent read view used in the + transaction, or NULL if not yet set */ + trx_lock_t lock; /*!< Information about the transaction + locks and state. Protected by + lock_sys.mutex (insertions also + by trx_t::mutex). */ + + /* These fields are not protected by any mutex. */ + + /** false=normal transaction, true=recovered (must be rolled back) + or disconnected transaction in XA PREPARE STATE. + + This field is accessed by the thread that owns the transaction, + without holding any mutex. + There is only one foreign-thread access in trx_print_low() + and a possible race condition with trx_disconnect_prepared(). */ + bool is_recovered; + const char* op_info; /*!< English text describing the + current operation, or an empty + string */ + uint isolation_level;/*!< TRX_ISO_REPEATABLE_READ, ... */ + bool check_foreigns; /*!< normally TRUE, but if the user + wants to suppress foreign key checks, + (in table imports, for example) we + set this FALSE */ + /*------------------------------*/ + /* MySQL has a transaction coordinator to coordinate two phase + commit between multiple storage engines and the binary log. When + an engine participates in a transaction, it's responsible for + registering itself using the trans_register_ha() API. */ + bool is_registered; /* This flag is set to true after the + transaction has been registered with + the coordinator using the XA API, and + is set to false after commit or + rollback. */ + /** whether this is holding the prepare mutex */ + bool active_commit_ordered; + /*------------------------------*/ + bool check_unique_secondary; + /*!< normally TRUE, but if the user + wants to speed up inserts by + suppressing unique key checks + for secondary indexes when we decide + if we can use the insert buffer for + them, we set this FALSE */ + bool flush_log_later;/* In 2PC, we hold the + prepare_commit mutex across + both phases. In that case, we + defer flush of the logs to disk + until after we release the + mutex. */ + bool must_flush_log_later;/*!< set in commit() + if flush_log_later was + set and redo log was written; + in that case we will + flush the log in + trx_commit_complete_for_mysql() */ + ulint duplicates; /*!< TRX_DUP_IGNORE | TRX_DUP_REPLACE */ + trx_dict_op_t dict_operation; /**< @see enum trx_dict_op_t */ + + ib_uint32_t dict_operation_lock_mode; + /*!< 0, RW_S_LATCH, or RW_X_LATCH: + the latch mode trx currently holds + on dict_sys.latch. Protected + by dict_sys.latch. */ + + /** wall-clock time of the latest transition to TRX_STATE_ACTIVE; + used for diagnostic purposes only */ + time_t start_time; + /** microsecond_interval_timer() of transaction start */ + ulonglong start_time_micro; + lsn_t commit_lsn; /*!< lsn at the time of the commit */ + table_id_t table_id; /*!< Table to drop iff dict_operation + == TRX_DICT_OP_TABLE, or 0. */ + /*------------------------------*/ + THD* mysql_thd; /*!< MySQL thread handle corresponding + to this trx, or NULL */ + + const char* mysql_log_file_name; + /*!< if MySQL binlog is used, this field + contains a pointer to the latest file + name; this is NULL if binlog is not + used */ + ulonglong mysql_log_offset; + /*!< if MySQL binlog is used, this + field contains the end offset of the + binlog entry */ + /*------------------------------*/ + ib_uint32_t n_mysql_tables_in_use; /*!< number of Innobase tables + used in the processing of the current + SQL statement in MySQL */ + ib_uint32_t mysql_n_tables_locked; + /*!< how many tables the current SQL + statement uses, except those + in consistent read */ + dberr_t error_state; /*!< 0 if no error, otherwise error + number; NOTE That ONLY the thread + doing the transaction is allowed to + set this field: this is NOT protected + by any mutex */ + const dict_index_t*error_info; /*!< if the error number indicates a + duplicate key error, a pointer to + the problematic index is stored here */ + ulint error_key_num; /*!< if the index creation fails to a + duplicate key error, a mysql key + number of that index is stored here */ + que_t* graph; /*!< query currently run in the session, + or NULL if none; NOTE that the query + belongs to the session, and it can + survive over a transaction commit, if + it is a stored procedure with a COMMIT + WORK statement, for instance */ + /*------------------------------*/ + UT_LIST_BASE_NODE_T(trx_named_savept_t) + trx_savepoints; /*!< savepoints set with SAVEPOINT ..., + oldest first */ + /*------------------------------*/ + undo_no_t undo_no; /*!< next undo log record number to + assign; since the undo log is + private for a transaction, this + is a simple ascending sequence + with no gaps; thus it represents + the number of modified/inserted + rows in a transaction */ + trx_savept_t last_sql_stat_start; + /*!< undo_no when the last sql statement + was started: in case of an error, trx + is rolled back down to this number */ + trx_rsegs_t rsegs; /* rollback segments for undo logging */ + undo_no_t roll_limit; /*!< least undo number to undo during + a partial rollback; 0 otherwise */ + bool in_rollback; /*!< true when the transaction is + executing a partial or full rollback */ + ulint pages_undone; /*!< number of undo log pages undone + since the last undo log truncation */ + /*------------------------------*/ + ulint n_autoinc_rows; /*!< no. of AUTO-INC rows required for + an SQL statement. This is useful for + multi-row INSERTs */ + ib_vector_t* autoinc_locks; /* AUTOINC locks held by this + transaction. Note that these are + also in the lock list trx_locks. This + vector needs to be freed explicitly + when the trx instance is destroyed. + Protected by lock_sys.mutex. */ + /*------------------------------*/ + bool read_only; /*!< true if transaction is flagged + as a READ-ONLY transaction. + if auto_commit && !will_lock + then it will be handled as a + AC-NL-RO-SELECT (Auto Commit Non-Locking + Read Only Select). A read only + transaction will not be assigned an + UNDO log. */ + bool auto_commit; /*!< true if it is an autocommit */ + bool will_lock; /*!< set to inform trx_start_low() that + the transaction may acquire locks */ + /*------------------------------*/ + fts_trx_t* fts_trx; /*!< FTS information, or NULL if + transaction hasn't modified tables + with FTS indexes (yet). */ + doc_id_t fts_next_doc_id;/* The document id used for updates */ + /*------------------------------*/ + ib_uint32_t flush_tables; /*!< if "covering" the FLUSH TABLES", + count of tables being flushed. */ + + /*------------------------------*/ + bool ddl; /*!< true if it is an internal + transaction for DDL */ + bool internal; /*!< true if it is a system/internal + transaction background task. This + includes DDL transactions too. Such + transactions are always treated as + read-write. */ + /*------------------------------*/ +#ifdef UNIV_DEBUG + unsigned start_line; /*!< Track where it was started from */ + const char* start_file; /*!< Filename where it was started */ +#endif /* UNIV_DEBUG */ + + XID* xid; /*!< X/Open XA transaction + identification to identify a + transaction branch */ + trx_mod_tables_t mod_tables; /*!< List of tables that were modified + by this transaction */ + /*------------------------------*/ + char* detailed_error; /*!< detailed error message for last + error, or empty. */ + rw_trx_hash_element_t *rw_trx_hash_element; + LF_PINS *rw_trx_hash_pins; + ulint magic_n; + + /** @return whether any persistent undo log has been generated */ + bool has_logged_persistent() const + { + return(rsegs.m_redo.undo); + } + + /** @return whether any undo log has been generated */ + bool has_logged() const + { + return(has_logged_persistent() || rsegs.m_noredo.undo); + } + + /** @return rollback segment for modifying temporary tables */ + trx_rseg_t* get_temp_rseg() + { + if (trx_rseg_t* rseg = rsegs.m_noredo.rseg) { + ut_ad(id != 0); + return(rseg); + } + + return(assign_temp_rseg()); + } + + /** Transition to committed state, to release implicit locks. */ + inline void commit_state(); + + /** Release any explicit locks of a committing transaction. */ + inline void release_locks(); + + /** Evict a table definition due to the rollback of ALTER TABLE. + @param[in] table_id table identifier */ + void evict_table(table_id_t table_id); + + /** Initiate rollback. + @param savept savepoint to which to roll back + @return error code or DB_SUCCESS */ + dberr_t rollback(trx_savept_t *savept= nullptr); + /** Roll back an active transaction. + @param savept savepoint to which to roll back */ + inline void rollback_low(trx_savept_t *savept= nullptr); + /** Finish rollback. + @return whether the rollback was completed normally + @retval false if the rollback was aborted by shutdown */ + inline bool rollback_finish(); +private: + /** Mark a transaction committed in the main memory data structures. */ + inline void commit_in_memory(const mtr_t *mtr); + /** Commit the transaction in a mini-transaction. + @param mtr mini-transaction (if there are any persistent modifications) */ + void commit_low(mtr_t *mtr= nullptr); +public: + /** Commit the transaction. */ + void commit(); + + + bool is_referenced() const { return n_ref > 0; } + + + void reference() + { +#ifdef UNIV_DEBUG + auto old_n_ref= +#endif + n_ref++; + ut_ad(old_n_ref >= 0); + } + + + void release_reference() + { +#ifdef UNIV_DEBUG + auto old_n_ref= +#endif + n_ref--; + ut_ad(old_n_ref > 0); + } + + /** @return whether the table has lock on + mysql.innodb_table_stats and mysql.innodb_index_stats */ + bool has_stats_table_lock() const; + + /** Free the memory to trx_pools */ + void free(); + + + void assert_freed() const + { + ut_ad(state == TRX_STATE_NOT_STARTED); + ut_ad(!id); + ut_ad(!has_logged()); + ut_ad(!is_referenced()); + ut_ad(!is_wsrep()); +#ifdef WITH_WSREP + ut_ad(!lock.was_chosen_as_wsrep_victim); +#endif + ut_ad(!read_view.is_open()); + ut_ad(!lock.wait_thr); + ut_ad(UT_LIST_GET_LEN(lock.trx_locks) == 0); + ut_ad(lock.table_locks.empty()); + ut_ad(!autoinc_locks || ib_vector_is_empty(autoinc_locks)); + ut_ad(UT_LIST_GET_LEN(lock.evicted_tables) == 0); + ut_ad(dict_operation == TRX_DICT_OP_NONE); + } + + /** @return whether this is a non-locking autocommit transaction */ + bool is_autocommit_non_locking() const { return auto_commit && !will_lock; } + +private: + /** Assign a rollback segment for modifying temporary tables. + @return the assigned rollback segment */ + trx_rseg_t *assign_temp_rseg(); +}; + +/** +Check if transaction is started. +@param[in] trx Transaction whose state we need to check +@reutrn true if transaction is in state started */ +inline bool trx_is_started(const trx_t* trx) +{ + return trx->state != TRX_STATE_NOT_STARTED; +} + +/* Transaction isolation levels (trx->isolation_level) */ +#define TRX_ISO_READ_UNCOMMITTED 0 /* dirty read: non-locking + SELECTs are performed so that + we do not look at a possible + earlier version of a record; + thus they are not 'consistent' + reads under this isolation + level; otherwise like level + 2 */ + +#define TRX_ISO_READ_COMMITTED 1 /* somewhat Oracle-like + isolation, except that in + range UPDATE and DELETE we + must block phantom rows + with next-key locks; + SELECT ... FOR UPDATE and ... + LOCK IN SHARE MODE only lock + the index records, NOT the + gaps before them, and thus + allow free inserting; + each consistent read reads its + own snapshot */ + +#define TRX_ISO_REPEATABLE_READ 2 /* this is the default; + all consistent reads in the + same trx read the same + snapshot; + full next-key locking used + in locking reads to block + insertions into gaps */ + +#define TRX_ISO_SERIALIZABLE 3 /* all plain SELECTs are + converted to LOCK IN SHARE + MODE reads */ + +/* Treatment of duplicate values (trx->duplicates; for example, in inserts). +Multiple flags can be combined with bitwise OR. */ +#define TRX_DUP_IGNORE 1U /* duplicate rows are to be updated */ +#define TRX_DUP_REPLACE 2U /* duplicate rows are to be replaced */ + + +/** Commit node states */ +enum commit_node_state { + COMMIT_NODE_SEND = 1, /*!< about to send a commit signal to + the transaction */ + COMMIT_NODE_WAIT /*!< commit signal sent to the transaction, + waiting for completion */ +}; + +/** Commit command node in a query graph */ +struct commit_node_t{ + que_common_t common; /*!< node type: QUE_NODE_COMMIT */ + enum commit_node_state + state; /*!< node execution state */ +}; + + +/** Test if trx->mutex is owned. */ +#define trx_mutex_own(t) mutex_own(&t->mutex) + +/** Acquire the trx->mutex. */ +#define trx_mutex_enter(t) do { \ + mutex_enter(&t->mutex); \ +} while (0) + +/** Release the trx->mutex. */ +#define trx_mutex_exit(t) do { \ + mutex_exit(&t->mutex); \ +} while (0) + +#include "trx0trx.ic" + +#endif |