mirror of
https://github.com/MariaDB/server.git
synced 2025-01-30 18:41:56 +01:00
1153 lines
39 KiB
C++
1153 lines
39 KiB
C++
/*****************************************************************************
|
|
|
|
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:
|
|
/**
|
|
Least significant 31 bits is 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".
|
|
|
|
If the most significant bit is set this transaction should stop inheriting
|
|
(GAP)locks. Generally set to true during transaction prepare for RC or lower
|
|
isolation, if requested. Needed for replication replay where
|
|
we don't want to get blocked on GAP locks taken for protecting
|
|
concurrent unique insert or replace operation.
|
|
*/
|
|
Atomic_relaxed<uint32_t> skip_lock_inheritance_and_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 (skip_lock_inheritance_and_n_ref & ~(1U << 31)) > 0;
|
|
}
|
|
|
|
void reference()
|
|
{
|
|
ut_d(auto old_n_ref =)
|
|
skip_lock_inheritance_and_n_ref.fetch_add(1);
|
|
ut_ad(int32_t(old_n_ref << 1) >= 0);
|
|
}
|
|
|
|
void release_reference()
|
|
{
|
|
ut_d(auto old_n_ref =)
|
|
skip_lock_inheritance_and_n_ref.fetch_sub(1);
|
|
ut_ad(int32_t(old_n_ref << 1) > 0);
|
|
}
|
|
|
|
bool is_not_inheriting_locks() const
|
|
{
|
|
return skip_lock_inheritance_and_n_ref >> 31;
|
|
}
|
|
|
|
void set_skip_lock_inheritance()
|
|
{
|
|
ut_d(auto old_n_ref=) skip_lock_inheritance_and_n_ref.fetch_add(1U << 31);
|
|
ut_ad(!(old_n_ref >> 31));
|
|
}
|
|
|
|
void reset_skip_lock_inheritance()
|
|
{
|
|
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
|
__asm__("lock btrl $31, %0" : : "m"(skip_lock_inheritance_and_n_ref));
|
|
#elif defined _MSC_VER && (defined _M_IX86 || defined _M_X64)
|
|
_interlockedbittestandreset(
|
|
reinterpret_cast<volatile long *>(&skip_lock_inheritance_and_n_ref),
|
|
31);
|
|
#else
|
|
skip_lock_inheritance_and_n_ref.fetch_and(~1U << 31);
|
|
#endif
|
|
}
|
|
|
|
/** @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(!*detailed_error);
|
|
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);
|
|
ut_ad(!is_not_inheriting_locks());
|
|
}
|
|
|
|
/** @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.inl"
|
|
|
|
#endif
|