summaryrefslogtreecommitdiff
path: root/storage/xtradb/trx/trx0trx.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/xtradb/trx/trx0trx.cc')
-rw-r--r--storage/xtradb/trx/trx0trx.cc2748
1 files changed, 0 insertions, 2748 deletions
diff --git a/storage/xtradb/trx/trx0trx.cc b/storage/xtradb/trx/trx0trx.cc
deleted file mode 100644
index 1d2f7ada54e..00000000000
--- a/storage/xtradb/trx/trx0trx.cc
+++ /dev/null
@@ -1,2748 +0,0 @@
-/*****************************************************************************
-
-Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2017, 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, Suite 500, Boston, MA 02110-1335 USA
-
-*****************************************************************************/
-
-/**************************************************//**
-@file trx/trx0trx.cc
-The transaction
-
-Created 3/26/1996 Heikki Tuuri
-*******************************************************/
-
-#include "btr0types.h"
-#include "trx0trx.h"
-
-#ifdef UNIV_NONINL
-#include "trx0trx.ic"
-#endif
-
-#include <mysql/service_wsrep.h>
-
-#include "trx0undo.h"
-#include "trx0rseg.h"
-#include "log0log.h"
-#include "que0que.h"
-#include "lock0lock.h"
-#include "trx0roll.h"
-#include "usr0sess.h"
-#include "read0read.h"
-#include "srv0srv.h"
-#include "srv0start.h"
-#include "btr0sea.h"
-#include "os0proc.h"
-#include "trx0xa.h"
-#include "trx0rec.h"
-#include "trx0purge.h"
-#include "ha_prototypes.h"
-#include "srv0mon.h"
-#include "ut0vec.h"
-
-#include<set>
-
-extern "C"
-int thd_deadlock_victim_preference(const MYSQL_THD thd1, const MYSQL_THD thd2);
-
-/** Set of table_id */
-typedef std::set<table_id_t> table_id_set;
-
-/** Dummy session used currently in MySQL interface */
-UNIV_INTERN sess_t* trx_dummy_sess = NULL;
-
-#ifdef UNIV_PFS_MUTEX
-/* Key to register the mutex with performance schema */
-UNIV_INTERN mysql_pfs_key_t trx_mutex_key;
-/* Key to register the mutex with performance schema */
-UNIV_INTERN mysql_pfs_key_t trx_undo_mutex_key;
-#endif /* UNIV_PFS_MUTEX */
-
-/*************************************************************//**
-Set detailed error message for the transaction. */
-UNIV_INTERN
-void
-trx_set_detailed_error(
-/*===================*/
- trx_t* trx, /*!< in: transaction struct */
- const char* msg) /*!< in: detailed error message */
-{
- ut_strlcpy(trx->detailed_error, msg, sizeof(trx->detailed_error));
-}
-
-/*************************************************************//**
-Set detailed error message for the transaction from a file. Note that the
-file is rewinded before reading from it. */
-UNIV_INTERN
-void
-trx_set_detailed_error_from_file(
-/*=============================*/
- trx_t* trx, /*!< in: transaction struct */
- FILE* file) /*!< in: file to read message from */
-{
- os_file_read_string(file, trx->detailed_error,
- sizeof(trx->detailed_error));
-}
-
-/*************************************************************//**
-Callback function for trx_find_descriptor() to compare trx IDs. */
-UNIV_INTERN
-int
-trx_descr_cmp(
-/*==========*/
- const void *a, /*!< in: pointer to first comparison argument */
- const void *b) /*!< in: pointer to second comparison argument */
-{
- const trx_id_t* da = (const trx_id_t*) a;
- const trx_id_t* db = (const trx_id_t*) b;
-
- if (*da < *db) {
- return -1;
- } else if (*da > *db) {
- return 1;
- }
-
- return 0;
-}
-
-/*************************************************************//**
-Reserve a slot for a given trx in the global descriptors array. */
-UNIV_INLINE
-void
-trx_reserve_descriptor(
-/*===================*/
- const trx_t* trx) /*!< in: trx pointer */
-{
- ulint n_used;
- ulint n_max;
- trx_id_t* descr;
-
- ut_ad(mutex_own(&trx_sys->mutex) || srv_is_being_started);
- ut_ad(srv_is_being_started ||
- !trx_find_descriptor(trx_sys->descriptors,
- trx_sys->descr_n_used,
- trx->id));
-
- n_used = trx_sys->descr_n_used + 1;
- n_max = trx_sys->descr_n_max;
-
- if (UNIV_UNLIKELY(n_used > n_max)) {
-
- n_max = n_max * 2;
-
- trx_sys->descriptors = static_cast<trx_id_t*>(
- ut_realloc(trx_sys->descriptors,
- n_max * sizeof(trx_id_t)));
-
- trx_sys->descr_n_max = n_max;
- srv_descriptors_memory = n_max * sizeof(trx_id_t);
- }
-
- descr = trx_sys->descriptors + n_used - 1;
-
- if (UNIV_UNLIKELY(n_used > 1 && trx->id < descr[-1])) {
-
- /* Find the slot where it should be inserted. We could use a
- binary search, but in reality linear search should be faster,
- because the slot we are looking for is near the array end. */
-
- trx_id_t* tdescr;
-
- for (tdescr = descr - 1;
- tdescr >= trx_sys->descriptors && *tdescr > trx->id;
- tdescr--) {
- }
-
- tdescr++;
-
- ut_memmove(tdescr + 1, tdescr, (descr - tdescr) *
- sizeof(trx_id_t));
-
- descr = tdescr;
- }
-
- *descr = trx->id;
-
- trx_sys->descr_n_used = n_used;
-}
-
-/*************************************************************//**
-Release a slot for a given trx in the global descriptors array. */
-UNIV_INTERN
-void
-trx_release_descriptor(
-/*===================*/
- trx_t* trx) /*!< in: trx pointer */
-{
- ulint size;
- trx_id_t* descr;
-
- ut_ad(mutex_own(&trx_sys->mutex));
-
- if (UNIV_LIKELY(trx->in_trx_serial_list)) {
-
- UT_LIST_REMOVE(trx_serial_list, trx_sys->trx_serial_list,
- trx);
- trx->in_trx_serial_list = false;
- }
-
- descr = trx_find_descriptor(trx_sys->descriptors,
- trx_sys->descr_n_used,
- trx->id);
-
- if (UNIV_UNLIKELY(descr == NULL)) {
-
- return;
- }
-
- size = (trx_sys->descriptors + trx_sys->descr_n_used - 1 - descr) *
- sizeof(trx_id_t);
-
- if (UNIV_LIKELY(size > 0)) {
-
- ut_memmove(descr, descr + 1, size);
- }
-
- trx_sys->descr_n_used--;
-}
-
-/****************************************************************//**
-Creates and initializes a transaction object. It must be explicitly
-started with trx_start_if_not_started() before using it. The default
-isolation level is TRX_ISO_REPEATABLE_READ.
-@return transaction instance, should never be NULL */
-static
-trx_t*
-trx_create(void)
-/*============*/
-{
- trx_t* trx;
- mem_heap_t* heap;
- ib_alloc_t* heap_alloc;
-
- trx = static_cast<trx_t*>(mem_zalloc(sizeof(*trx)));
-
- mutex_create(trx_mutex_key, &trx->mutex, SYNC_TRX);
-
- trx->magic_n = TRX_MAGIC_N;
-
- trx->active_commit_ordered = 0;
- trx->state = TRX_STATE_NOT_STARTED;
-
- trx->isolation_level = TRX_ISO_REPEATABLE_READ;
-
- trx->no = TRX_ID_MAX;
- trx->in_trx_serial_list = false;
-
- trx->support_xa = TRUE;
-
- trx->fake_changes = FALSE;
-
- trx->check_foreigns = TRUE;
- trx->check_unique_secondary = TRUE;
-
- trx->dict_operation = TRX_DICT_OP_NONE;
-
- trx->idle_start = 0;
- trx->last_stmt_start = 0;
-
- mutex_create(trx_undo_mutex_key, &trx->undo_mutex, SYNC_TRX_UNDO);
-
- trx->error_state = DB_SUCCESS;
-
- trx->lock.que_state = TRX_QUE_RUNNING;
-
- trx->lock.lock_heap = mem_heap_create_typed(
- 256, MEM_HEAP_FOR_LOCK_HEAP);
-
- trx->search_latch_timeout = BTR_SEA_TIMEOUT;
-
- trx->io_reads = 0;
- trx->io_read = 0;
- trx->io_reads_wait_timer = 0;
- trx->lock_que_wait_timer = 0;
- trx->innodb_que_wait_timer = 0;
- trx->distinct_page_access = 0;
- trx->distinct_page_access_hash = NULL;
- trx->take_stats = FALSE;
-
- trx->xid.formatID = -1;
-
- trx->op_info = "";
-
- trx->api_trx = false;
-
- trx->api_auto_commit = false;
-
- trx->read_write = true;
-
- heap = mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 8);
- heap_alloc = ib_heap_allocator_create(heap);
-
- /* Remember to free the vector explicitly in trx_free(). */
- trx->autoinc_locks = ib_vector_create(heap_alloc, sizeof(void**), 4);
-
- /* Remember to free the vector explicitly in trx_free(). */
- heap = mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 128);
- heap_alloc = ib_heap_allocator_create(heap);
-
- trx->lock.table_locks = ib_vector_create(
- heap_alloc, sizeof(void**), 32);
-#ifdef WITH_WSREP
- trx->wsrep_event = NULL;
-#endif /* WITH_WSREP */
-
- return(trx);
-}
-
-/********************************************************************//**
-Creates a transaction object for background operations by the master thread.
-@return own: transaction object */
-UNIV_INTERN
-trx_t*
-trx_allocate_for_background(void)
-/*=============================*/
-{
- trx_t* trx;
-
- trx = trx_create();
-
- trx->sess = trx_dummy_sess;
-
- return(trx);
-}
-
-/********************************************************************//**
-Creates a transaction object for MySQL.
-@return own: transaction object */
-UNIV_INTERN
-trx_t*
-trx_allocate_for_mysql(void)
-/*========================*/
-{
- trx_t* trx;
-
- trx = trx_allocate_for_background();
-
- mutex_enter(&trx_sys->mutex);
-
- ut_d(trx->in_mysql_trx_list = TRUE);
- UT_LIST_ADD_FIRST(mysql_trx_list, trx_sys->mysql_trx_list, trx);
-
- mutex_exit(&trx_sys->mutex);
-
- if (UNIV_UNLIKELY(trx->take_stats)) {
- trx->distinct_page_access_hash
- = static_cast<byte *>(mem_alloc(DPAH_SIZE));
- memset(trx->distinct_page_access_hash, 0, DPAH_SIZE);
- }
-
- return(trx);
-}
-
-/********************************************************************//**
-Frees a transaction object without releasing the corresponding descriptor.
-Should be used by callers that already own trx_sys->mutex. */
-static
-void
-trx_free_low(
-/*=========*/
- trx_t* trx) /*!< in, own: trx object */
-{
- ut_a(trx->magic_n == TRX_MAGIC_N);
- ut_ad(!trx->in_ro_trx_list);
- ut_ad(!trx->in_rw_trx_list);
- ut_ad(!trx->in_mysql_trx_list);
-
- mutex_free(&trx->undo_mutex);
-
- if (trx->undo_no_arr != NULL) {
- trx_undo_arr_free(trx->undo_no_arr);
- }
-
- ut_a(trx->lock.wait_lock == NULL);
- ut_a(trx->lock.wait_thr == NULL);
-
- ut_a(!trx->has_search_latch);
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(!btr_search_own_any());
-#endif
-
- ut_a(trx->dict_operation_lock_mode == 0);
-
- if (trx->lock.lock_heap) {
- mem_heap_free(trx->lock.lock_heap);
- }
-
- ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
-
- ut_a(ib_vector_is_empty(trx->autoinc_locks));
- /* We allocated a dedicated heap for the vector. */
- ib_vector_free(trx->autoinc_locks);
-
- if (trx->lock.table_locks != NULL) {
- /* We allocated a dedicated heap for the vector. */
- ib_vector_free(trx->lock.table_locks);
- }
-
- mutex_free(&trx->mutex);
-
- read_view_free(trx->prebuilt_view);
-
- mem_free(trx);
-}
-
-/********************************************************************//**
-Frees a transaction object. */
-static
-void
-trx_free(
-/*=========*/
- trx_t* trx) /*!< in, own: trx object */
-{
- mutex_enter(&trx_sys->mutex);
- trx_release_descriptor(trx);
- mutex_exit(&trx_sys->mutex);
-
- trx_free_low(trx);
-}
-
-/********************************************************************//**
-Frees a transaction object of a background operation of the master thread. */
-UNIV_INTERN
-void
-trx_free_for_background(
-/*====================*/
- trx_t* trx) /*!< in, own: trx object */
-{
-
- if (trx->distinct_page_access_hash)
- {
- mem_free(trx->distinct_page_access_hash);
- trx->distinct_page_access_hash= NULL;
- }
-
- if (trx->declared_to_be_inside_innodb) {
-
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Freeing a trx (%p, " TRX_ID_FMT ") which is declared "
- "to be processing inside InnoDB", trx, trx->id);
-
- trx_print(stderr, trx, 600);
- putc('\n', stderr);
-
- /* This is an error but not a fatal error. We must keep
- the counters like srv_conc_n_threads accurate. */
- srv_conc_force_exit_innodb(trx);
- }
-
- if (trx->n_mysql_tables_in_use != 0
- || trx->mysql_n_tables_locked != 0) {
-
- ib_logf(IB_LOG_LEVEL_ERROR,
- "MySQL is freeing a thd though "
- "trx->n_mysql_tables_in_use is %lu and "
- "trx->mysql_n_tables_locked is %lu.",
- (ulong) trx->n_mysql_tables_in_use,
- (ulong) trx->mysql_n_tables_locked);
-
- trx_print(stderr, trx, 600);
- ut_print_buf(stderr, trx, sizeof(trx_t));
- putc('\n', stderr);
- }
-
- ut_a(trx->state == TRX_STATE_NOT_STARTED);
- ut_a(trx->insert_undo == NULL);
- ut_a(trx->update_undo == NULL);
- ut_a(trx->read_view == NULL);
-
- trx_free(trx);
-}
-
-/********************************************************************//**
-At shutdown, frees a transaction object that is in the PREPARED state. */
-UNIV_INTERN
-void
-trx_free_prepared(
-/*==============*/
- trx_t* trx) /*!< in, own: trx object */
-{
- ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)
- || (trx_state_eq(trx, TRX_STATE_ACTIVE)
- && trx->is_recovered
- && (srv_read_only_mode
- || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO)));
- ut_a(trx->magic_n == TRX_MAGIC_N);
-
- lock_trx_release_locks(trx);
- trx_undo_free_prepared(trx);
-
- assert_trx_in_rw_list(trx);
-
- ut_a(!trx->read_only);
-
- UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx);
- ut_d(trx->in_rw_trx_list = FALSE);
-
- mutex_enter(&trx_sys->mutex);
- trx_release_descriptor(trx);
- mutex_exit(&trx_sys->mutex);
-
- /* Undo trx_resurrect_table_locks(). */
- UT_LIST_INIT(trx->lock.trx_locks);
-
- trx_free_low(trx);
-
- ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->rw_trx_list));
-}
-
-/********************************************************************//**
-Frees a transaction object for MySQL. */
-UNIV_INTERN
-void
-trx_free_for_mysql(
-/*===============*/
- trx_t* trx) /*!< in, own: trx object */
-{
- if (trx->distinct_page_access_hash)
- {
- mem_free(trx->distinct_page_access_hash);
- trx->distinct_page_access_hash= NULL;
- }
-
- mutex_enter(&trx_sys->mutex);
-
- ut_ad(trx->in_mysql_trx_list);
- ut_d(trx->in_mysql_trx_list = FALSE);
- UT_LIST_REMOVE(mysql_trx_list, trx_sys->mysql_trx_list, trx);
-
- ut_ad(trx_sys_validate_trx_list());
-
- mutex_exit(&trx_sys->mutex);
-
- trx_free_for_background(trx);
-}
-
-/****************************************************************//**
-Inserts the trx handle in the trx system trx list in the right position.
-The list is sorted on the trx id so that the biggest id is at the list
-start. This function is used at the database startup to insert incomplete
-transactions to the list. */
-static
-void
-trx_list_rw_insert_ordered(
-/*=======================*/
- trx_t* trx) /*!< in: trx handle */
-{
- trx_t* trx2;
-
- ut_ad(!trx->read_only);
-
- ut_d(trx->start_file = __FILE__);
- ut_d(trx->start_line = __LINE__);
-
- ut_a(srv_is_being_started);
- ut_ad(!trx->in_ro_trx_list);
- ut_ad(!trx->in_rw_trx_list);
- ut_ad(trx->state != TRX_STATE_NOT_STARTED);
- ut_ad(trx->is_recovered);
-
- for (trx2 = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
- trx2 != NULL;
- trx2 = UT_LIST_GET_NEXT(trx_list, trx2)) {
-
- assert_trx_in_rw_list(trx2);
-
- if (trx->id >= trx2->id) {
-
- ut_ad(trx->id > trx2->id);
- break;
- }
- }
-
- if (trx2 != NULL) {
- trx2 = UT_LIST_GET_PREV(trx_list, trx2);
-
- if (trx2 == NULL) {
- UT_LIST_ADD_FIRST(trx_list, trx_sys->rw_trx_list, trx);
- } else {
- UT_LIST_INSERT_AFTER(
- trx_list, trx_sys->rw_trx_list, trx2, trx);
- }
- } else {
- UT_LIST_ADD_LAST(trx_list, trx_sys->rw_trx_list, trx);
- }
-
-#ifdef UNIV_DEBUG
- if (trx->id > trx_sys->rw_max_trx_id) {
- trx_sys->rw_max_trx_id = trx->id;
- }
-#endif /* UNIV_DEBUG */
-
- ut_ad(!trx->in_rw_trx_list);
- ut_d(trx->in_rw_trx_list = TRUE);
-}
-
-/****************************************************************//**
-Resurrect the table locks for a resurrected transaction. */
-static
-void
-trx_resurrect_table_locks(
-/*======================*/
- trx_t* trx, /*!< in/out: transaction */
- const trx_undo_t* undo) /*!< in: undo log */
-{
- mtr_t mtr;
- page_t* undo_page;
- trx_undo_rec_t* undo_rec;
- table_id_set tables;
-
- ut_ad(undo == trx->insert_undo || undo == trx->update_undo);
-
- if (trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)
- || undo->empty) {
- return;
- }
-
- mtr_start(&mtr);
- /* trx_rseg_mem_create() may have acquired an X-latch on this
- page, so we cannot acquire an S-latch. */
- undo_page = trx_undo_page_get(
- undo->space, undo->zip_size, undo->top_page_no, &mtr);
- undo_rec = undo_page + undo->top_offset;
-
- do {
- ulint type;
- ulint cmpl_info;
- bool updated_extern;
- undo_no_t undo_no;
- table_id_t table_id;
-
- page_t* undo_rec_page = page_align(undo_rec);
-
- if (undo_rec_page != undo_page) {
- if (!mtr_memo_release(&mtr,
- buf_block_align(undo_page),
- MTR_MEMO_PAGE_X_FIX)) {
- /* The page of the previous undo_rec
- should have been latched by
- trx_undo_page_get() or
- trx_undo_get_prev_rec(). */
- ut_ad(0);
- }
-
- undo_page = undo_rec_page;
- }
-
- trx_undo_rec_get_pars(
- undo_rec, &type, &cmpl_info,
- &updated_extern, &undo_no, &table_id);
- tables.insert(table_id);
-
- undo_rec = trx_undo_get_prev_rec(
- undo_rec, undo->hdr_page_no,
- undo->hdr_offset, false, &mtr);
- } while (undo_rec);
-
- mtr_commit(&mtr);
-
- for (table_id_set::const_iterator i = tables.begin();
- i != tables.end(); i++) {
- if (dict_table_t* table = dict_table_open_on_id(
- *i, FALSE, DICT_TABLE_OP_LOAD_TABLESPACE)) {
- if (table->file_unreadable
- || dict_table_is_temporary(table)) {
- mutex_enter(&dict_sys->mutex);
- dict_table_close(table, TRUE, FALSE);
- dict_table_remove_from_cache(table);
- mutex_exit(&dict_sys->mutex);
- continue;
- }
-
- lock_table_ix_resurrect(table, trx);
-
- DBUG_PRINT("ib_trx",
- ("resurrect" TRX_ID_FMT
- " table '%s' IX lock from %s undo",
- trx->id, table->name,
- undo == trx->insert_undo
- ? "insert" : "update"));
-
- dict_table_close(table, FALSE, FALSE);
- }
- }
-}
-
-/****************************************************************//**
-Resurrect the transactions that were doing inserts the time of the
-crash, they need to be undone.
-@return trx_t instance */
-static
-trx_t*
-trx_resurrect_insert(
-/*=================*/
- trx_undo_t* undo, /*!< in: entry to UNDO */
- trx_rseg_t* rseg) /*!< in: rollback segment */
-{
- trx_t* trx;
-
- trx = trx_allocate_for_background();
-
- trx->rseg = rseg;
- trx->xid = undo->xid;
- trx->id = undo->trx_id;
- trx->insert_undo = undo;
- trx->is_recovered = TRUE;
-
- /* This is single-threaded startup code, we do not need the
- protection of trx->mutex or trx_sys->mutex here. */
-
- if (undo->state != TRX_UNDO_ACTIVE) {
-
- /* Prepared transactions are left in the prepared state
- waiting for a commit or abort decision from MySQL */
-
- if (undo->state == TRX_UNDO_PREPARED) {
-
- fprintf(stderr,
- "InnoDB: Transaction " TRX_ID_FMT " was in the"
- " XA prepared state.\n", trx->id);
-
- if (srv_force_recovery == 0) {
-
- /* XtraBackup should rollback prepared XA
- transactions */
- if (IS_XTRABACKUP()) {
- trx->state = TRX_STATE_ACTIVE;
- }
- else {
- trx->state = TRX_STATE_PREPARED;
- trx_sys->n_prepared_trx++;
- trx_sys->n_prepared_recovered_trx++;
- }
- } else {
- fprintf(stderr,
- "InnoDB: Since innodb_force_recovery"
- " > 0, we will rollback it anyway.\n");
-
- trx->state = TRX_STATE_ACTIVE;
- }
- } else {
- trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
- }
-
- /* We give a dummy value for the trx no; this should have no
- relevance since purge is not interested in committed
- transaction numbers, unless they are in the history
- list, in which case it looks the number from the disk based
- undo log structure */
-
- trx->no = trx->id;
- } else {
- trx->state = TRX_STATE_ACTIVE;
-
- /* A running transaction always has the number
- field inited to TRX_ID_MAX */
-
- trx->no = TRX_ID_MAX;
- }
-
- /* trx_start_low() is not called with resurrect, so need to initialize
- start time here.*/
- if (trx->state == TRX_STATE_ACTIVE
- || trx->state == TRX_STATE_PREPARED) {
- trx->start_time = ut_time();
- }
-
- if (undo->dict_operation) {
- trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
- trx->table_id = undo->table_id;
- }
-
- if (!undo->empty) {
- trx->undo_no = undo->top_undo_no + 1;
- }
-
- return(trx);
-}
-
-/****************************************************************//**
-Prepared transactions are left in the prepared state waiting for a
-commit or abort decision from MySQL */
-static
-void
-trx_resurrect_update_in_prepared_state(
-/*===================================*/
- trx_t* trx, /*!< in,out: transaction */
- const trx_undo_t* undo) /*!< in: update UNDO record */
-{
- /* This is single-threaded startup code, we do not need the
- protection of trx->mutex or trx_sys->mutex here. */
-
- if (undo->state == TRX_UNDO_PREPARED) {
- fprintf(stderr,
- "InnoDB: Transaction " TRX_ID_FMT
- " was in the XA prepared state.\n", trx->id);
-
- if (srv_force_recovery == 0) {
- if (trx_state_eq(trx, TRX_STATE_NOT_STARTED)) {
- if (!IS_XTRABACKUP()) {
- trx_sys->n_prepared_trx++;
- trx_sys->n_prepared_recovered_trx++;
- }
- } else {
- ut_ad(trx_state_eq(trx, TRX_STATE_PREPARED));
- }
- /* XtraBackup should rollback prepared XA
- transactions */
- trx->state = IS_XTRABACKUP()?TRX_STATE_ACTIVE: TRX_STATE_PREPARED;
- } else {
- fprintf(stderr,
- "InnoDB: Since innodb_force_recovery"
- " > 0, we will rollback it anyway.\n");
-
- trx->state = TRX_STATE_ACTIVE;
- }
- } else {
- trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
- }
-}
-
-/****************************************************************//**
-Resurrect the transactions that were doing updates the time of the
-crash, they need to be undone. */
-static
-void
-trx_resurrect_update(
-/*=================*/
- trx_t* trx, /*!< in/out: transaction */
- trx_undo_t* undo, /*!< in/out: update UNDO record */
- trx_rseg_t* rseg) /*!< in/out: rollback segment */
-{
- trx->rseg = rseg;
- trx->xid = undo->xid;
- trx->id = undo->trx_id;
- trx->update_undo = undo;
- trx->is_recovered = TRUE;
-
- /* This is single-threaded startup code, we do not need the
- protection of trx->mutex or trx_sys->mutex here. */
-
- if (undo->state != TRX_UNDO_ACTIVE) {
- trx_resurrect_update_in_prepared_state(trx, undo);
-
- /* We give a dummy value for the trx number */
-
- trx->no = trx->id;
-
- } else {
- trx->state = TRX_STATE_ACTIVE;
-
- /* A running transaction always has the number field inited to
- TRX_ID_MAX */
-
- trx->no = TRX_ID_MAX;
- }
-
- /* trx_start_low() is not called with resurrect, so need to initialize
- start time here.*/
- if (trx->state == TRX_STATE_ACTIVE
- || trx->state == TRX_STATE_PREPARED) {
- trx->start_time = ut_time();
- }
-
- if (undo->dict_operation) {
- trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
- trx->table_id = undo->table_id;
- }
-
- if (!undo->empty && undo->top_undo_no >= trx->undo_no) {
-
- trx->undo_no = undo->top_undo_no + 1;
- }
-}
-
-/****************************************************************//**
-Creates trx objects for transactions and initializes the trx list of
-trx_sys at database start. Rollback segment and undo log lists must
-already exist when this function is called, because the lists of
-transactions to be rolled back or cleaned up are built based on the
-undo log lists. */
-UNIV_INTERN
-void
-trx_lists_init_at_db_start(void)
-/*============================*/
-{
- ulint i;
-
- ut_a(srv_is_being_started);
-
- UT_LIST_INIT(trx_sys->ro_trx_list);
- UT_LIST_INIT(trx_sys->rw_trx_list);
- UT_LIST_INIT(trx_sys->trx_serial_list);
-
- /* Look from the rollback segments if there exist undo logs for
- transactions */
-
- for (i = 0; i < TRX_SYS_N_RSEGS; ++i) {
- trx_undo_t* undo;
- trx_rseg_t* rseg;
-
- rseg = trx_sys->rseg_array[i];
-
- if (rseg == NULL) {
- continue;
- }
-
- /* Resurrect transactions that were doing inserts. */
- for (undo = UT_LIST_GET_FIRST(rseg->insert_undo_list);
- undo != NULL;
- undo = UT_LIST_GET_NEXT(undo_list, undo)) {
- trx_t* trx;
-
- trx = trx_resurrect_insert(undo, rseg);
-
- if (trx->state == TRX_STATE_ACTIVE ||
- trx->state == TRX_STATE_PREPARED) {
-
- trx_reserve_descriptor(trx);
- }
- trx_list_rw_insert_ordered(trx);
-
- trx_resurrect_table_locks(trx, undo);
- }
-
- /* Ressurrect transactions that were doing updates. */
- for (undo = UT_LIST_GET_FIRST(rseg->update_undo_list);
- undo != NULL;
- undo = UT_LIST_GET_NEXT(undo_list, undo)) {
- trx_t* trx;
- ibool trx_created;
-
- /* Check the trx_sys->rw_trx_list first. */
- mutex_enter(&trx_sys->mutex);
- trx = trx_get_rw_trx_by_id(undo->trx_id);
- mutex_exit(&trx_sys->mutex);
-
- if (trx == NULL) {
- trx = trx_allocate_for_background();
- trx_created = TRUE;
- } else {
- trx_created = FALSE;
- }
-
- trx_resurrect_update(trx, undo, rseg);
-
- if (trx_created) {
- if (trx->state == TRX_STATE_ACTIVE ||
- trx->state == TRX_STATE_PREPARED) {
-
- trx_reserve_descriptor(trx);
- }
- trx_list_rw_insert_ordered(trx);
- }
-
- trx_resurrect_table_locks(trx, undo);
- }
- }
-}
-
-/******************************************************************//**
-Assigns a rollback segment to a transaction in a round-robin fashion.
-@return assigned rollback segment instance */
-static
-trx_rseg_t*
-trx_assign_rseg_low(
-/*================*/
- ulong max_undo_logs, /*!< in: maximum number of UNDO logs to use */
- ulint n_tablespaces) /*!< in: number of rollback tablespaces */
-{
- ulint i;
- trx_rseg_t* rseg;
- static ulint latest_rseg = 0;
-
- if (srv_read_only_mode) {
- ut_a(max_undo_logs == ULONG_UNDEFINED);
- return(NULL);
- }
-
- /* This breaks true round robin but that should be OK. */
-
- ut_a(max_undo_logs > 0 && max_undo_logs <= TRX_SYS_N_RSEGS);
-
- i = latest_rseg++;
- i %= max_undo_logs;
-
- /* Note: The assumption here is that there can't be any gaps in
- the array. Once we implement more flexible rollback segment
- management this may not hold. The assertion checks for that case. */
-
- if (trx_sys->rseg_array[0] == NULL) {
- return(NULL);
- }
-
- /* Skip the system tablespace if we have more than one tablespace
- defined for rollback segments. We want all UNDO records to be in
- the non-system tablespaces. */
-
- do {
- rseg = trx_sys->rseg_array[i];
- ut_a(rseg == NULL || i == rseg->id);
-
- i = (rseg == NULL) ? 0 : i + 1;
-
- } while (rseg == NULL
- || (rseg->space == 0
- && n_tablespaces > 0
- && trx_sys->rseg_array[1] != NULL));
-
- return(rseg);
-}
-
-/****************************************************************//**
-Assign a read-only transaction a rollback-segment, if it is attempting
-to write to a TEMPORARY table. */
-UNIV_INTERN
-void
-trx_assign_rseg(
-/*============*/
- trx_t* trx) /*!< A read-only transaction that
- needs to be assigned a RBS. */
-{
- ut_a(trx->rseg == 0);
- ut_a(trx->read_only);
- ut_a(!srv_read_only_mode);
- ut_a(!trx_is_autocommit_non_locking(trx));
-
- trx->rseg = trx_assign_rseg_low(srv_undo_logs, srv_undo_tablespaces);
-}
-
-/****************************************************************//**
-Starts a transaction. */
-static
-void
-trx_start_low(
-/*==========*/
- trx_t* trx) /*!< in: transaction */
-{
- ut_ad(trx->rseg == NULL);
-
- ut_ad(trx->start_file != 0);
- ut_ad(trx->start_line != 0);
- ut_ad(!trx->is_recovered);
- ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
- ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
-
- /* Check whether it is an AUTOCOMMIT SELECT */
- trx->auto_commit = (trx->api_trx && trx->api_auto_commit)
- || thd_trx_is_auto_commit(trx->mysql_thd);
-
- trx->read_only =
- (trx->api_trx && !trx->read_write)
- || (!trx->ddl && thd_trx_is_read_only(trx->mysql_thd))
- || srv_read_only_mode;
-
- if (!trx->auto_commit) {
- ++trx->will_lock;
- } else if (trx->will_lock == 0) {
- trx->read_only = TRUE;
- }
-
- if (!trx->read_only) {
- trx->rseg = trx_assign_rseg_low(
- srv_undo_logs, srv_undo_tablespaces);
- }
-
-#ifdef WITH_WSREP
- memset(&trx->xid, 0, sizeof(trx->xid));
- trx->xid.formatID = -1;
-#endif /* WITH_WSREP */
-
- /* The initial value for trx->no: TRX_ID_MAX is used in
- read_view_open_now: */
-
- trx->no = TRX_ID_MAX;
-
- ut_a(ib_vector_is_empty(trx->autoinc_locks));
- ut_a(ib_vector_is_empty(trx->lock.table_locks));
-
- mutex_enter(&trx_sys->mutex);
-
- /* If this transaction came from trx_allocate_for_mysql(),
- trx->in_mysql_trx_list would hold. In that case, the trx->state
- change must be protected by the trx_sys->mutex, so that
- lock_print_info_all_transactions() will have a consistent view. */
-
- trx->state = TRX_STATE_ACTIVE;
-
- trx->id = trx_sys_get_new_trx_id();
-
- /* Cache the state of fake_changes that transaction will use for
- lifetime. Any change in session/global fake_changes configuration during
- lifetime of transaction will not be honored by already started
- transaction. */
- trx->fake_changes = thd_fake_changes(trx->mysql_thd);
-
- ut_ad(!trx->in_rw_trx_list);
- ut_ad(!trx->in_ro_trx_list);
-
- if (trx->read_only) {
-
- /* Note: The trx_sys_t::ro_trx_list doesn't really need to
- be ordered, we should exploit this using a list type that
- doesn't need a list wide lock to increase concurrency. */
-
- if (!trx_is_autocommit_non_locking(trx)) {
- UT_LIST_ADD_FIRST(trx_list, trx_sys->ro_trx_list, trx);
- ut_d(trx->in_ro_trx_list = TRUE);
- }
- } else {
-
- ut_ad(trx->rseg != NULL
- || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
-
- ut_ad(!trx_is_autocommit_non_locking(trx));
- UT_LIST_ADD_FIRST(trx_list, trx_sys->rw_trx_list, trx);
- ut_d(trx->in_rw_trx_list = TRUE);
-
-#ifdef UNIV_DEBUG
- if (trx->id > trx_sys->rw_max_trx_id) {
- trx_sys->rw_max_trx_id = trx->id;
- }
-#endif /* UNIV_DEBUG */
-
- trx_reserve_descriptor(trx);
- }
-
- ut_ad(trx_sys_validate_trx_list());
-
- mutex_exit(&trx_sys->mutex);
-
- trx->start_time = ut_time();
-
- trx->start_time_micro =
- trx->mysql_thd ? thd_query_start_micro(trx->mysql_thd) : 0;
-
- MONITOR_INC(MONITOR_TRX_ACTIVE);
-}
-
-/****************************************************************//**
-Set the transaction serialisation number. */
-static
-void
-trx_serialisation_number_get(
-/*=========================*/
- trx_t* trx) /*!< in: transaction */
-{
- trx_rseg_t* rseg;
-
- rseg = trx->rseg;
-
- ut_ad(mutex_own(&rseg->mutex));
-
- mutex_enter(&trx_sys->mutex);
-
- trx->no = trx_sys_get_new_trx_id();
-
- if (UNIV_LIKELY(!trx->in_trx_serial_list)) {
-
- UT_LIST_ADD_LAST(trx_serial_list, trx_sys->trx_serial_list,
- trx);
-
- trx->in_trx_serial_list = true;
- }
-
- /* If the rollack segment is not empty then the
- new trx_t::no can't be less than any trx_t::no
- already in the rollback segment. User threads only
- produce events when a rollback segment is empty. */
-
- if (rseg->last_page_no == FIL_NULL) {
- void* ptr;
- rseg_queue_t rseg_queue;
-
- rseg_queue.rseg = rseg;
- rseg_queue.trx_no = trx->no;
-
- mutex_enter(&purge_sys->bh_mutex);
-
- /* This is to reduce the pressure on the trx_sys_t::mutex
- though in reality it should make very little (read no)
- difference because this code path is only taken when the
- rbs is empty. */
-
- mutex_exit(&trx_sys->mutex);
-
- ptr = ib_bh_push(purge_sys->ib_bh, &rseg_queue);
- ut_a(ptr);
-
- mutex_exit(&purge_sys->bh_mutex);
- } else {
- mutex_exit(&trx_sys->mutex);
- }
-}
-
-/****************************************************************//**
-Assign the transaction its history serialisation number and write the
-update UNDO log record to the assigned rollback segment. */
-static MY_ATTRIBUTE((nonnull))
-void
-trx_write_serialisation_history(
-/*============================*/
- trx_t* trx, /*!< in/out: transaction */
- mtr_t* mtr) /*!< in/out: mini-transaction */
-{
-#ifdef WITH_WSREP
- trx_sysf_t* sys_header;
-#endif /* WITH_WSREP */
- trx_rseg_t* rseg;
-
- rseg = trx->rseg;
-
- /* Change the undo log segment states from TRX_UNDO_ACTIVE
- to some other state: these modifications to the file data
- structure define the transaction as committed in the file
- based domain, at the serialization point of the log sequence
- number lsn obtained below. */
-
- if (trx->update_undo != NULL) {
- page_t* undo_hdr_page;
- trx_undo_t* undo = trx->update_undo;
-
- /* We have to hold the rseg mutex because update
- log headers have to be put to the history list in the
- (serialisation) order of the UNDO trx number. This is
- required for the purge in-memory data structures too. */
-
- mutex_enter(&rseg->mutex);
-
- /* Assign the transaction serialisation number and also
- update the purge min binary heap if this is the first
- UNDO log being written to the assigned rollback segment. */
-
- trx_serialisation_number_get(trx);
-
- /* It is not necessary to obtain trx->undo_mutex here
- because only a single OS thread is allowed to do the
- transaction commit for this transaction. */
-
- undo_hdr_page = trx_undo_set_state_at_finish(undo, mtr);
-
- trx_undo_update_cleanup(trx, undo_hdr_page, mtr);
- } else {
- mutex_enter(&rseg->mutex);
- }
-
- if (trx->insert_undo != NULL) {
- trx_undo_set_state_at_finish(trx->insert_undo, mtr);
- }
-
- mutex_exit(&rseg->mutex);
-
- MONITOR_INC(MONITOR_TRX_COMMIT_UNDO);
-
-#ifdef WITH_WSREP
- sys_header = trx_sysf_get(mtr);
- /* Update latest MySQL wsrep XID in trx sys header. */
- if (wsrep_is_wsrep_xid(&trx->xid))
- {
- trx_sys_update_wsrep_checkpoint(&trx->xid, sys_header, mtr);
- }
-#endif /* WITH_WSREP */
-
- /* Update the latest MySQL binlog name and offset info
- in trx sys header if MySQL binlogging is on or the database
- server is a MySQL replication slave */
-
- if (trx->mysql_log_file_name
- && trx->mysql_log_file_name[0] != '\0') {
-
- trx_sys_update_mysql_binlog_offset(
- trx->mysql_log_file_name,
- trx->mysql_log_offset,
- TRX_SYS_MYSQL_LOG_INFO,
-#ifdef WITH_WSREP
- sys_header,
-#endif /* WITH_WSREP */
- mtr);
-
- trx->mysql_log_file_name = NULL;
- }
-}
-
-/********************************************************************
-Finalize a transaction containing updates for a FTS table. */
-static MY_ATTRIBUTE((nonnull))
-void
-trx_finalize_for_fts_table(
-/*=======================*/
- fts_trx_table_t* ftt) /* in: FTS trx table */
-{
- fts_t* fts = ftt->table->fts;
- fts_doc_ids_t* doc_ids = ftt->added_doc_ids;
-
- mutex_enter(&fts->bg_threads_mutex);
-
- if (fts->fts_status & BG_THREAD_STOP) {
- /* The table is about to be dropped, no use
- adding anything to its work queue. */
-
- mutex_exit(&fts->bg_threads_mutex);
- } else {
- mem_heap_t* heap;
- mutex_exit(&fts->bg_threads_mutex);
-
- ut_a(fts->add_wq);
-
- heap = static_cast<mem_heap_t*>(doc_ids->self_heap->arg);
-
- ib_wqueue_add(fts->add_wq, doc_ids, heap);
-
- /* fts_trx_table_t no longer owns the list. */
- ftt->added_doc_ids = NULL;
- }
-}
-
-/******************************************************************//**
-Finalize a transaction containing updates to FTS tables. */
-static MY_ATTRIBUTE((nonnull))
-void
-trx_finalize_for_fts(
-/*=================*/
- trx_t* trx, /*!< in/out: transaction */
- bool is_commit) /*!< in: true if the transaction was
- committed, false if it was rolled back. */
-{
- if (is_commit) {
- const ib_rbt_node_t* node;
- ib_rbt_t* tables;
- fts_savepoint_t* savepoint;
-
- savepoint = static_cast<fts_savepoint_t*>(
- ib_vector_last(trx->fts_trx->savepoints));
-
- tables = savepoint->tables;
-
- for (node = rbt_first(tables);
- node;
- node = rbt_next(tables, node)) {
- fts_trx_table_t** ftt;
-
- ftt = rbt_value(fts_trx_table_t*, node);
-
- if ((*ftt)->added_doc_ids) {
- trx_finalize_for_fts_table(*ftt);
- }
- }
- }
-
- fts_trx_free(trx->fts_trx);
- trx->fts_trx = NULL;
-}
-
-/**********************************************************************//**
-If required, flushes the log to disk based on the value of
-innodb_flush_log_at_trx_commit. */
-static
-void
-trx_flush_log_if_needed_low(
-/*========================*/
- lsn_t lsn, /*!< in: lsn up to which logs are to be
- flushed. */
- trx_t* trx) /*!< in: transaction */
-{
- ulint flush_log_at_trx_commit;
-
- flush_log_at_trx_commit = srv_use_global_flush_log_at_trx_commit
- ? thd_flush_log_at_trx_commit(NULL)
- : thd_flush_log_at_trx_commit(trx->mysql_thd);
-
- switch (flush_log_at_trx_commit) {
- case 0:
- /* Do nothing */
- break;
- case 1:
- case 3:
- /* Write the log and optionally flush it to disk */
- log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
- srv_unix_file_flush_method != SRV_UNIX_NOSYNC);
- break;
- case 2:
- /* Write the log but do not flush it to disk */
- log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
-
- break;
- default:
- ut_error;
- }
-}
-
-/**********************************************************************//**
-If required, flushes the log to disk based on the value of
-innodb_flush_log_at_trx_commit. */
-static MY_ATTRIBUTE((nonnull))
-void
-trx_flush_log_if_needed(
-/*====================*/
- lsn_t lsn, /*!< in: lsn up to which logs are to be
- flushed. */
- trx_t* trx) /*!< in/out: transaction */
-{
- trx->op_info = "flushing log";
- trx_flush_log_if_needed_low(lsn, trx);
- trx->op_info = "";
-}
-
-/****************************************************************//**
-Commits a transaction in memory. */
-static MY_ATTRIBUTE((nonnull))
-void
-trx_commit_in_memory(
-/*=================*/
- trx_t* trx, /*!< in/out: transaction */
- lsn_t lsn) /*!< in: log sequence number of the mini-transaction
- commit of trx_write_serialisation_history(), or 0
- if the transaction did not modify anything */
-{
- trx->must_flush_log_later = FALSE;
-
- if (trx_is_autocommit_non_locking(trx)) {
- ut_ad(trx->read_only);
- ut_a(!trx->is_recovered);
- ut_ad(trx->rseg == NULL);
- ut_ad(!trx->in_ro_trx_list);
- ut_ad(!trx->in_rw_trx_list);
-
- /* Note: We are asserting without holding the lock mutex. But
- that is OK because this transaction is not waiting and cannot
- be rolled back and no new locks can (or should not) be added
- becuase it is flagged as a non-locking read-only transaction. */
-
- ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
-
- /* This state change is not protected by any mutex, therefore
- there is an inherent race here around state transition during
- printouts. We ignore this race for the sake of efficiency.
- However, the trx_sys_t::mutex will protect the trx_t instance
- and it cannot be removed from the mysql_trx_list and freed
- without first acquiring the trx_sys_t::mutex. */
-
- ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
-
- trx->state = TRX_STATE_NOT_STARTED;
-
- read_view_remove(trx->global_read_view, false);
-
- MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
- } else {
- lock_trx_release_locks(trx);
-
- /* Remove the transaction from the list of active
- transactions now that it no longer holds any user locks. */
-
- ut_ad(trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
-
- mutex_enter(&trx_sys->mutex);
-
- assert_trx_in_list(trx);
-
- if (trx->read_only) {
- UT_LIST_REMOVE(trx_list, trx_sys->ro_trx_list, trx);
- ut_d(trx->in_ro_trx_list = FALSE);
- MONITOR_INC(MONITOR_TRX_RO_COMMIT);
- } else {
- UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx);
- ut_d(trx->in_rw_trx_list = FALSE);
- ut_ad(trx_sys->descr_n_used <=
- UT_LIST_GET_LEN(trx_sys->rw_trx_list));
- MONITOR_INC(MONITOR_TRX_RW_COMMIT);
- }
-
- /* If this transaction came from trx_allocate_for_mysql(),
- trx->in_mysql_trx_list would hold. In that case, the
- trx->state change must be protected by trx_sys->mutex, so that
- lock_print_info_all_transactions() will have a consistent
- view. */
-
- trx->state = TRX_STATE_NOT_STARTED;
-
- /* We already own the trx_sys_t::mutex, by doing it here we
- avoid a potential context switch later. */
- read_view_remove(trx->global_read_view, true);
-
- ut_ad(trx_sys_validate_trx_list());
-
- mutex_exit(&trx_sys->mutex);
- }
-
- if (trx->global_read_view != NULL) {
-
- trx->global_read_view = NULL;
- }
-
- trx->read_view = NULL;
-
- if (lsn) {
- ulint flush_log_at_trx_commit;
-
- if (trx->insert_undo != NULL) {
-
- trx_undo_insert_cleanup(trx);
- }
-
- if (srv_use_global_flush_log_at_trx_commit) {
- flush_log_at_trx_commit = thd_flush_log_at_trx_commit(NULL);
- } else {
- flush_log_at_trx_commit = thd_flush_log_at_trx_commit(trx->mysql_thd);
- }
-
- /* NOTE that we could possibly make a group commit more
- efficient here: call os_thread_yield here to allow also other
- trxs to come to commit! */
-
- /*-------------------------------------*/
-
- /* Depending on the my.cnf options, we may now write the log
- buffer to the log files, making the transaction durable if
- the OS does not crash. We may also flush the log files to
- disk, making the transaction durable also at an OS crash or a
- power outage.
-
- The idea in InnoDB's group commit is that a group of
- transactions gather behind a trx doing a physical disk write
- to log files, and when that physical write has been completed,
- one of those transactions does a write which commits the whole
- group. Note that this group commit will only bring benefit if
- there are > 2 users in the database. Then at least 2 users can
- gather behind one doing the physical log write to disk.
-
- If we are calling trx_commit() under prepare_commit_mutex, we
- will delay possible log write and flush to a separate function
- trx_commit_complete_for_mysql(), which is only called when the
- thread has released the mutex. This is to make the
- group commit algorithm to work. Otherwise, the prepare_commit
- mutex would serialize all commits and prevent a group of
- transactions from gathering. */
-
- if (trx->flush_log_later) {
- /* Do nothing yet */
- trx->must_flush_log_later = TRUE;
- } else if (flush_log_at_trx_commit == 0
- || thd_requested_durability(trx->mysql_thd)
- == HA_IGNORE_DURABILITY) {
- /* Do nothing */
- } else {
- trx_flush_log_if_needed(lsn, trx);
- }
-
- trx->commit_lsn = lsn;
-
- /* Tell server some activity has happened, since the trx
- does changes something. Background utility threads like
- master thread, purge thread or page_cleaner thread might
- have some work to do. */
- srv_active_wake_master_thread();
- }
-
- /* undo_no is non-zero if we're doing the final commit. */
- bool not_rollback = trx->undo_no != 0;
- /* Free all savepoints, starting from the first. */
- trx_named_savept_t* savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
- trx_roll_savepoints_free(trx, savep);
-
- trx->rseg = NULL;
- trx->undo_no = 0;
- trx->last_sql_stat_start.least_undo_no = 0;
-
- trx->ddl = false;
-#ifdef UNIV_DEBUG
- ut_ad(trx->start_file != 0);
- ut_ad(trx->start_line != 0);
- trx->start_file = 0;
- trx->start_line = 0;
-#endif /* UNIV_DEBUG */
-
- trx->will_lock = 0;
- trx->read_only = FALSE;
- trx->auto_commit = FALSE;
-
- if (trx->fts_trx) {
- trx_finalize_for_fts(trx, not_rollback);
- }
-
- ut_ad(trx->lock.wait_thr == NULL);
- ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
- ut_ad(!trx->in_ro_trx_list);
- ut_ad(!trx->in_rw_trx_list);
-
-#ifdef WITH_WSREP
- if (trx->mysql_thd && wsrep_on(trx->mysql_thd)) {
- trx->lock.was_chosen_as_deadlock_victim = FALSE;
- }
-#endif
- trx->dict_operation = TRX_DICT_OP_NONE;
-
- trx->error_state = DB_SUCCESS;
-
- /* trx->in_mysql_trx_list would hold between
- trx_allocate_for_mysql() and trx_free_for_mysql(). It does not
- hold for recovered transactions or system transactions. */
-}
-
-/****************************************************************//**
-Commits a transaction and a mini-transaction. */
-UNIV_INTERN
-void
-trx_commit_low(
-/*===========*/
- trx_t* trx, /*!< in/out: transaction */
- mtr_t* mtr) /*!< in/out: mini-transaction (will be committed),
- or NULL if trx made no modifications */
-{
- lsn_t lsn;
-
- assert_trx_nonlocking_or_in_list(trx);
- ut_ad(!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
- ut_ad(!mtr || mtr->state == MTR_ACTIVE);
- ut_ad(!mtr == !(trx->insert_undo || trx->update_undo));
-
- /* undo_no is non-zero if we're doing the final commit. */
- if (trx->fts_trx && trx->undo_no != 0) {
- dberr_t error;
-
- ut_a(!trx_is_autocommit_non_locking(trx));
-
- error = fts_commit(trx);
-
- /* FTS-FIXME: Temporarily tolerate DB_DUPLICATE_KEY
- instead of dying. This is a possible scenario if there
- is a crash between insert to DELETED table committing
- and transaction committing. The fix would be able to
- return error from this function */
- if (error != DB_SUCCESS && error != DB_DUPLICATE_KEY) {
- /* FTS-FIXME: once we can return values from this
- function, we should do so and signal an error
- instead of just dying. */
-
- ut_error;
- }
- }
-
- if (mtr) {
- trx_write_serialisation_history(trx, mtr);
- /* The following call commits the mini-transaction, making the
- whole transaction committed in the file-based world, at this
- log sequence number. The transaction becomes 'durable' when
- we write the log to disk, but in the logical sense the commit
- in the file-based data structures (undo logs etc.) happens
- here.
-
- NOTE that transaction numbers, which are assigned only to
- transactions with an update undo log, do not necessarily come
- in exactly the same order as commit lsn's, if the transactions
- have different rollback segments. To get exactly the same
- order we should hold the kernel mutex up to this point,
- adding to the contention of the kernel mutex. However, if
- a transaction T2 is able to see modifications made by
- a transaction T1, T2 will always get a bigger transaction
- number and a bigger commit lsn than T1. */
-
- /*--------------*/
- mtr_commit(mtr);
- /*--------------*/
- lsn = mtr->end_lsn;
- } else {
- lsn = 0;
- }
-
- trx_commit_in_memory(trx, lsn);
-}
-
-/****************************************************************//**
-Commits a transaction. */
-UNIV_INTERN
-void
-trx_commit(
-/*=======*/
- trx_t* trx) /*!< in/out: transaction */
-{
- mtr_t local_mtr;
- mtr_t* mtr;
-
- if (trx->insert_undo || trx->update_undo) {
- mtr = &local_mtr;
- mtr_start(mtr);
- } else {
- mtr = NULL;
- }
-
- trx_commit_low(trx, mtr);
-}
-
-/****************************************************************//**
-Cleans up a transaction at database startup. The cleanup is needed if
-the transaction already got to the middle of a commit when the database
-crashed, and we cannot roll it back. */
-UNIV_INTERN
-void
-trx_cleanup_at_db_startup(
-/*======================*/
- trx_t* trx) /*!< in: transaction */
-{
- ut_ad(trx->is_recovered);
-
- if (trx->insert_undo != NULL) {
-
- trx_undo_insert_cleanup(trx);
- }
-
- trx->rseg = NULL;
- trx->undo_no = 0;
- trx->last_sql_stat_start.least_undo_no = 0;
-
- mutex_enter(&trx_sys->mutex);
-
- ut_a(!trx->read_only);
-
- UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx);
- ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->rw_trx_list));
-
- assert_trx_in_rw_list(trx);
- ut_d(trx->in_rw_trx_list = FALSE);
-
- trx->state = TRX_STATE_NOT_STARTED;
- trx_release_descriptor(trx);
-
- mutex_exit(&trx_sys->mutex);
-
- /* Change the transaction state without mutex protection, now
- that it no longer is in the trx_list. Recovered transactions
- are never placed in the mysql_trx_list. */
- ut_ad(trx->is_recovered);
- ut_ad(!trx->in_ro_trx_list);
- ut_ad(!trx->in_rw_trx_list);
- ut_ad(!trx->in_mysql_trx_list);
-}
-
-/********************************************************************//**
-Assigns a read view for a consistent read query. All the consistent reads
-within the same transaction will get the same read view, which is created
-when this function is first called for a new started transaction.
-@return consistent read view */
-UNIV_INTERN
-read_view_t*
-trx_assign_read_view(
-/*=================*/
- trx_t* trx) /*!< in: active transaction */
-{
- ut_ad(trx->state == TRX_STATE_ACTIVE);
-
- if (trx->read_view != NULL) {
- return(trx->read_view);
- }
-
- trx->read_view = read_view_open_now(trx->id, trx->prebuilt_view);
- trx->global_read_view = trx->read_view;
-
- return(trx->read_view);
-}
-
-/********************************************************************//**
-Clones the read view from another transaction. All consistent reads within
-the receiver transaction will get the same read view as the donor transaction
-@return read view clone */
-UNIV_INTERN
-read_view_t*
-trx_clone_read_view(
-/*================*/
- trx_t* trx, /*!< in: receiver transaction */
- trx_t* from_trx) /*!< in: donor transaction */
-{
- ut_ad(lock_mutex_own());
- ut_ad(mutex_own(&trx_sys->mutex));
- ut_ad(trx_mutex_own(from_trx));
- ut_ad(trx->read_view == NULL);
-
- if (from_trx->state != TRX_STATE_ACTIVE ||
- from_trx->read_view == NULL) {
-
- return(NULL);
- }
-
- trx->read_view = read_view_clone(from_trx->read_view,
- trx->prebuilt_view);
-
- read_view_add(trx->read_view);
-
- trx->global_read_view = trx->read_view;
-
- return(trx->read_view);
-}
-
-/****************************************************************//**
-Prepares a transaction for commit/rollback. */
-UNIV_INTERN
-void
-trx_commit_or_rollback_prepare(
-/*===========================*/
- trx_t* trx) /*!< in/out: transaction */
-{
- /* We are reading trx->state without holding trx_sys->mutex
- here, because the commit or rollback should be invoked for a
- running (or recovered prepared) transaction that is associated
- with the current thread. */
-
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
-#ifdef WITH_WSREP
- ut_d(trx->start_file = __FILE__);
- ut_d(trx->start_line = __LINE__);
-#endif /* WITH_WSREP */
- trx_start_low(trx);
- /* fall through */
- case TRX_STATE_ACTIVE:
- case TRX_STATE_PREPARED:
- /* If the trx is in a lock wait state, moves the waiting
- query thread to the suspended state */
-
- if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
-
- ulint sec;
- ulint ms;
- ib_uint64_t now;
-
- ut_a(trx->lock.wait_thr != NULL);
- trx->lock.wait_thr->state = QUE_THR_SUSPENDED;
- trx->lock.wait_thr = NULL;
-
- if (UNIV_UNLIKELY(trx->take_stats)) {
- ut_usectime(&sec, &ms);
- now = (ib_uint64_t)sec * 1000000 + ms;
- trx->lock_que_wait_timer
- += (ulint)
- (now - trx->lock_que_wait_ustarted);
- }
-
- trx->lock.que_state = TRX_QUE_RUNNING;
- }
-
- ut_a(trx->lock.n_active_thrs == 1);
- return;
- case TRX_STATE_COMMITTED_IN_MEMORY:
- break;
- }
-
- ut_error;
-}
-
-/*********************************************************************//**
-Creates a commit command node struct.
-@return own: commit node struct */
-UNIV_INTERN
-commit_node_t*
-trx_commit_node_create(
-/*===================*/
- mem_heap_t* heap) /*!< in: mem heap where created */
-{
- commit_node_t* node;
-
- node = static_cast<commit_node_t*>(mem_heap_alloc(heap, sizeof(*node)));
- node->common.type = QUE_NODE_COMMIT;
- node->state = COMMIT_NODE_SEND;
-
- return(node);
-}
-
-/***********************************************************//**
-Performs an execution step for a commit type node in a query graph.
-@return query thread to run next, or NULL */
-UNIV_INTERN
-que_thr_t*
-trx_commit_step(
-/*============*/
- que_thr_t* thr) /*!< in: query thread */
-{
- commit_node_t* node;
-
- node = static_cast<commit_node_t*>(thr->run_node);
-
- ut_ad(que_node_get_type(node) == QUE_NODE_COMMIT);
-
- if (thr->prev_node == que_node_get_parent(node)) {
- node->state = COMMIT_NODE_SEND;
- }
-
- if (node->state == COMMIT_NODE_SEND) {
- trx_t* trx;
-
- node->state = COMMIT_NODE_WAIT;
-
- trx = thr_get_trx(thr);
-
- ut_a(trx->lock.wait_thr == NULL);
- ut_a(trx->lock.que_state != TRX_QUE_LOCK_WAIT);
-
- trx_commit_or_rollback_prepare(trx);
-
- trx->lock.que_state = TRX_QUE_COMMITTING;
-
- trx_commit(trx);
-
- ut_ad(trx->lock.wait_thr == NULL);
-
- trx->lock.que_state = TRX_QUE_RUNNING;
-
- thr = NULL;
- } else {
- ut_ad(node->state == COMMIT_NODE_WAIT);
-
- node->state = COMMIT_NODE_SEND;
-
- thr->run_node = que_node_get_parent(node);
- }
-
- return(thr);
-}
-
-/**********************************************************************//**
-Does the transaction commit for MySQL.
-@return DB_SUCCESS or error number */
-UNIV_INTERN
-dberr_t
-trx_commit_for_mysql(
-/*=================*/
- trx_t* trx) /*!< in/out: transaction */
-{
- /* Because we do not do the commit by sending an Innobase
- sig to the transaction, we must here make sure that trx has been
- started. */
-
- ut_a(trx);
-
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
- /* Update the info whether we should skip XA steps that eat
- CPU time.
-
- For the duration of the transaction trx->support_xa is
- not reread from thd so any changes in the value take
- effect in the next transaction. This is to avoid a
- scenario where some undo log records generated by a
- transaction contain XA information and other undo log
- records, generated by the same transaction do not. */
- trx->support_xa = thd_supports_xa(trx->mysql_thd);
-
- ut_d(trx->start_file = __FILE__);
- ut_d(trx->start_line = __LINE__);
-
- trx_start_low(trx);
- /* fall through */
- case TRX_STATE_ACTIVE:
- case TRX_STATE_PREPARED:
- trx->op_info = "committing";
- trx_commit(trx);
- MONITOR_DEC(MONITOR_TRX_ACTIVE);
- trx->op_info = "";
- return(DB_SUCCESS);
- case TRX_STATE_COMMITTED_IN_MEMORY:
- break;
- }
- ut_error;
- return(DB_CORRUPTION);
-}
-
-/**********************************************************************//**
-If required, flushes the log to disk if we called trx_commit_for_mysql()
-with trx->flush_log_later == TRUE. */
-UNIV_INTERN
-void
-trx_commit_complete_for_mysql(
-/*==========================*/
- trx_t* trx) /*!< in/out: transaction */
-{
- ut_a(trx);
-
- if (!trx->must_flush_log_later
- || thd_requested_durability(trx->mysql_thd)
- == HA_IGNORE_DURABILITY) {
- return;
- }
-
- ulint flush_log_at_trx_commit;
-
- flush_log_at_trx_commit = srv_use_global_flush_log_at_trx_commit
- ? thd_flush_log_at_trx_commit(NULL)
- : thd_flush_log_at_trx_commit(trx->mysql_thd);
-
- if (flush_log_at_trx_commit == 1 && trx->active_commit_ordered) {
- return;
- }
-
- trx_flush_log_if_needed(trx->commit_lsn, trx);
-
- trx->must_flush_log_later = FALSE;
-}
-
-/**********************************************************************//**
-Marks the latest SQL statement ended. */
-UNIV_INTERN
-void
-trx_mark_sql_stat_end(
-/*==================*/
- trx_t* trx) /*!< in: trx handle */
-{
- ut_a(trx);
-
- switch (trx->state) {
- case TRX_STATE_PREPARED:
- case TRX_STATE_COMMITTED_IN_MEMORY:
- break;
- case TRX_STATE_NOT_STARTED:
- trx->undo_no = 0;
- /* fall through */
- case TRX_STATE_ACTIVE:
- trx->last_sql_stat_start.least_undo_no = trx->undo_no;
-
- if (trx->fts_trx) {
- fts_savepoint_laststmt_refresh(trx);
- }
-
- return;
- }
-
- ut_error;
-}
-
-/**********************************************************************//**
-Prints info about a transaction.
-Caller must hold trx_sys->mutex. */
-UNIV_INTERN
-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) */
-{
- ibool newline;
- const char* op_info;
-
- ut_ad(mutex_own(&trx_sys->mutex));
-
- fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id);
-
- /* trx->state cannot change from or to NOT_STARTED while we
- are holding the trx_sys->mutex. It may change from ACTIVE to
- PREPARED or COMMITTED. */
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
- fputs(", not started", f);
- goto state_ok;
- case TRX_STATE_ACTIVE:
- fprintf(f, ", ACTIVE %lu sec",
- (ulong) difftime(time(NULL), trx->start_time));
- goto state_ok;
- case TRX_STATE_PREPARED:
- fprintf(f, ", ACTIVE (PREPARED) %lu sec",
- (ulong) difftime(time(NULL), trx->start_time));
- goto state_ok;
- case TRX_STATE_COMMITTED_IN_MEMORY:
- fputs(", COMMITTED IN MEMORY", f);
- goto state_ok;
- }
- fprintf(f, ", state %lu", (ulong) trx->state);
- ut_ad(0);
-state_ok:
-
- /* prevent a race condition */
- op_info = trx->op_info;
-
- if (*op_info) {
- putc(' ', f);
- fputs(op_info, f);
- }
-
- if (trx->is_recovered) {
- fputs(" recovered trx", f);
- }
-
- if (trx->declared_to_be_inside_innodb) {
- fprintf(f, ", thread declared inside InnoDB %lu",
- (ulong) trx->n_tickets_to_enter_innodb);
- }
-
- putc('\n', f);
-
- if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) {
- fprintf(f, "mysql tables in use %lu, locked %lu\n",
- (ulong) trx->n_mysql_tables_in_use,
- (ulong) trx->mysql_n_tables_locked);
- }
-
- newline = TRUE;
-
- /* trx->lock.que_state of an ACTIVE transaction may change
- while we are not holding trx->mutex. We perform a dirty read
- for performance reasons. */
-
- switch (trx->lock.que_state) {
- case TRX_QUE_RUNNING:
- newline = FALSE; break;
- case TRX_QUE_LOCK_WAIT:
- fputs("LOCK WAIT ", f); break;
- case TRX_QUE_ROLLING_BACK:
- fputs("ROLLING BACK ", f); break;
- case TRX_QUE_COMMITTING:
- fputs("COMMITTING ", f); break;
- default:
- fprintf(f, "que state %lu ", (ulong) trx->lock.que_state);
- }
-
- if (n_trx_locks > 0 || heap_size > 400) {
- newline = TRUE;
-
- fprintf(f, "%lu lock struct(s), heap size %lu,"
- " %lu row lock(s)",
- (ulong) n_trx_locks,
- (ulong) heap_size,
- (ulong) n_rec_locks);
- }
-
- if (trx->has_search_latch) {
- newline = TRUE;
- fputs(", holds adaptive hash latch", f);
- }
-
- if (trx->undo_no != 0) {
- newline = TRUE;
- fprintf(f, ", undo log entries " TRX_ID_FMT, trx->undo_no);
- }
-
- if (newline) {
- putc('\n', f);
- }
-
- if (trx->mysql_thd != NULL) {
- innobase_mysql_print_thd(
- f, trx->mysql_thd, static_cast<uint>(max_query_len));
- }
-}
-
-/**********************************************************************//**
-Prints info about a transaction.
-The caller must hold lock_sys->mutex and trx_sys->mutex.
-When possible, use trx_print() instead. */
-UNIV_INTERN
-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 */
-{
- ut_ad(lock_mutex_own());
- ut_ad(mutex_own(&trx_sys->mutex));
-
- trx_print_low(f, trx, max_query_len,
- lock_number_of_rows_locked(&trx->lock),
- UT_LIST_GET_LEN(trx->lock.trx_locks),
- mem_heap_get_size(trx->lock.lock_heap));
-}
-
-#ifdef WITH_WSREP
-/**********************************************************************//**
-Prints info about a transaction.
-Transaction information may be retrieved without having trx_sys->mutex acquired
-so it may not be completely accurate. The caller must own lock_sys->mutex
-and the trx must have some locks to make sure that it does not escape
-without locking lock_sys->mutex. */
-UNIV_INTERN
-void
-wsrep_trx_print_locking(
-/*==========*/
- 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 */
-{
- ibool newline;
- const char* op_info;
-
- ut_ad(lock_mutex_own());
- ut_ad(trx->lock.trx_locks.count > 0);
-
- fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id);
-
- /* trx->state may change since trx_sys->mutex is not required */
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
- fputs(", not started", f);
- goto state_ok;
- case TRX_STATE_ACTIVE:
- fprintf(f, ", ACTIVE %lu sec",
- (ulong) difftime(time(NULL), trx->start_time));
- goto state_ok;
- case TRX_STATE_PREPARED:
- fprintf(f, ", ACTIVE (PREPARED) %lu sec",
- (ulong) difftime(time(NULL), trx->start_time));
- goto state_ok;
- case TRX_STATE_COMMITTED_IN_MEMORY:
- fputs(", COMMITTED IN MEMORY", f);
- goto state_ok;
- }
- fprintf(f, ", state %lu", (ulong) trx->state);
- ut_ad(0);
-state_ok:
-
- /* prevent a race condition */
- op_info = trx->op_info;
-
- if (*op_info) {
- putc(' ', f);
- fputs(op_info, f);
- }
-
- if (trx->is_recovered) {
- fputs(" recovered trx", f);
- }
-
- if (trx->declared_to_be_inside_innodb) {
- fprintf(f, ", thread declared inside InnoDB %lu",
- (ulong) trx->n_tickets_to_enter_innodb);
- }
-
- putc('\n', f);
-
- if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) {
- fprintf(f, "mysql tables in use %lu, locked %lu\n",
- (ulong) trx->n_mysql_tables_in_use,
- (ulong) trx->mysql_n_tables_locked);
- }
-
- newline = TRUE;
-
- /* trx->lock.que_state of an ACTIVE transaction may change
- while we are not holding trx->mutex. We perform a dirty read
- for performance reasons. */
-
- switch (trx->lock.que_state) {
- case TRX_QUE_RUNNING:
- newline = FALSE; break;
- case TRX_QUE_LOCK_WAIT:
- fputs("LOCK WAIT ", f); break;
- case TRX_QUE_ROLLING_BACK:
- fputs("ROLLING BACK ", f); break;
- case TRX_QUE_COMMITTING:
- fputs("COMMITTING ", f); break;
- default:
- fprintf(f, "que state %lu ", (ulong) trx->lock.que_state);
- }
-
- if (trx->has_search_latch) {
- newline = TRUE;
- fputs(", holds adaptive hash latch", f);
- }
-
- if (trx->undo_no != 0) {
- newline = TRUE;
- fprintf(f, ", undo log entries " TRX_ID_FMT, trx->undo_no);
- }
-
- if (newline) {
- putc('\n', f);
- }
-
- if (trx->mysql_thd != NULL) {
- innobase_mysql_print_thd(
- f, trx->mysql_thd, static_cast<uint>(max_query_len));
- }
-}
-#endif /* WITH_WSREP */
-
-/**********************************************************************//**
-Prints info about a transaction.
-Acquires and releases lock_sys->mutex and trx_sys->mutex. */
-UNIV_INTERN
-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 */
-{
- ulint n_rec_locks;
- ulint n_trx_locks;
- ulint heap_size;
-
- lock_mutex_enter();
- n_rec_locks = lock_number_of_rows_locked(&trx->lock);
- n_trx_locks = UT_LIST_GET_LEN(trx->lock.trx_locks);
- heap_size = mem_heap_get_size(trx->lock.lock_heap);
- lock_mutex_exit();
-
- mutex_enter(&trx_sys->mutex);
- trx_print_low(f, trx, max_query_len,
- n_rec_locks, n_trx_locks, heap_size);
- mutex_exit(&trx_sys->mutex);
-}
-
-#ifdef UNIV_DEBUG
-/**********************************************************************//**
-Asserts that a transaction has been started.
-The caller must hold trx_sys->mutex.
-@return TRUE if started */
-UNIV_INTERN
-ibool
-trx_assert_started(
-/*===============*/
- const trx_t* trx) /*!< in: transaction */
-{
- ut_ad(mutex_own(&trx_sys->mutex));
-
- /* Non-locking autocommits should not hold any locks and this
- function is only called from the locking code. */
- assert_trx_in_list(trx);
-
- /* trx->state can change from or to NOT_STARTED while we are holding
- trx_sys->mutex for non-locking autocommit selects but not for other
- types of transactions. It may change from ACTIVE to PREPARED. Unless
- we are holding lock_sys->mutex, it may also change to COMMITTED. */
-
- switch (trx->state) {
- case TRX_STATE_PREPARED:
- return(TRUE);
-
- case TRX_STATE_ACTIVE:
- case TRX_STATE_COMMITTED_IN_MEMORY:
- return(TRUE);
-
- case TRX_STATE_NOT_STARTED:
- break;
- }
-
- ut_error;
- return(FALSE);
-}
-#endif /* UNIV_DEBUG */
-
-/*******************************************************************//**
-Compares the "weight" (or size) of two transactions. The heavier the weight,
-the more reluctant we will be to choose the transaction as a deadlock victim.
-@return TRUE if weight(a) >= weight(b) */
-UNIV_INTERN
-ibool
-trx_weight_ge(
-/*==========*/
- const trx_t* a, /*!< in: the first transaction to be compared */
- const trx_t* b) /*!< in: the second transaction to be compared */
-{
- int pref;
-
- /* First ask the upper server layer if it has any preference for which
- to prefer as a deadlock victim. */
- pref= thd_deadlock_victim_preference(a->mysql_thd, b->mysql_thd);
- if (pref < 0) {
- return FALSE;
- } else if (pref > 0) {
- return TRUE;
- }
-
- /* Upper server layer had no preference, we fall back to comparing the
- number of altered/locked rows. */
-
-#if 0
- fprintf(stderr,
- "%s TRX_WEIGHT(a): %lld+%lu, TRX_WEIGHT(b): %lld+%lu\n",
- __func__,
- a->undo_no, UT_LIST_GET_LEN(a->lock.trx_locks),
- b->undo_no, UT_LIST_GET_LEN(b->lock.trx_locks));
-#endif
-
- return(TRX_WEIGHT(a) >= TRX_WEIGHT(b));
-}
-
-/****************************************************************//**
-Prepares a transaction. */
-static
-void
-trx_prepare(
-/*========*/
- trx_t* trx) /*!< in/out: transaction */
-{
- trx_rseg_t* rseg;
- lsn_t lsn;
- mtr_t mtr;
-
- rseg = trx->rseg;
- /* Only fresh user transactions can be prepared.
- Recovered transactions cannot. */
- ut_a(!trx->is_recovered);
-
- if (trx->insert_undo != NULL || trx->update_undo != NULL) {
-
- mtr_start(&mtr);
-
- /* Change the undo log segment states from TRX_UNDO_ACTIVE
- to TRX_UNDO_PREPARED: these modifications to the file data
- structure define the transaction as prepared in the
- file-based world, at the serialization point of lsn. */
-
- mutex_enter(&rseg->mutex);
-
- if (trx->insert_undo != NULL) {
-
- /* It is not necessary to obtain trx->undo_mutex here
- because only a single OS thread is allowed to do the
- transaction prepare for this transaction. */
-
- trx_undo_set_state_at_prepare(trx, trx->insert_undo,
- &mtr);
- }
-
- if (trx->update_undo) {
- trx_undo_set_state_at_prepare(
- trx, trx->update_undo, &mtr);
- }
-
- mutex_exit(&rseg->mutex);
-
- /*--------------*/
- mtr_commit(&mtr); /* This mtr commit makes the
- transaction prepared in the file-based
- world */
- /*--------------*/
- lsn = mtr.end_lsn;
- ut_ad(lsn);
- } else {
- lsn = 0;
- }
-
- /*--------------------------------------*/
- ut_a(trx->state == TRX_STATE_ACTIVE);
- mutex_enter(&trx_sys->mutex);
- trx->state = TRX_STATE_PREPARED;
- trx_sys->n_prepared_trx++;
- mutex_exit(&trx_sys->mutex);
- /*--------------------------------------*/
-
- if (lsn) {
- /* Depending on the my.cnf options, we may now write the log
- buffer to the log files, making the prepared state of the
- transaction durable if the OS does not crash. We may also
- flush the log files to disk, making the prepared state of the
- transaction durable also at an OS crash or a power outage.
-
- The idea in InnoDB's group prepare is that a group of
- transactions gather behind a trx doing a physical disk write
- to log files, and when that physical write has been completed,
- one of those transactions does a write which prepares the whole
- group. Note that this group prepare will only bring benefit if
- there are > 2 users in the database. Then at least 2 users can
- gather behind one doing the physical log write to disk.
-
- TODO: find out if MySQL holds some mutex when calling this.
- That would spoil our group prepare algorithm. */
-
- trx_flush_log_if_needed(lsn, trx);
- }
-}
-
-/**********************************************************************//**
-Does the transaction prepare for MySQL. */
-UNIV_INTERN
-void
-trx_prepare_for_mysql(
-/*==================*/
- trx_t* trx) /*!< in/out: trx handle */
-{
- trx_start_if_not_started_xa(trx);
-
- trx->op_info = "preparing";
-
- trx_prepare(trx);
-
- trx->op_info = "";
-}
-
-/**********************************************************************//**
-This function is used to find number of prepared transactions and
-their transaction objects for a recovery.
-@return number of prepared transactions stored in xid_list */
-UNIV_INTERN
-int
-trx_recover_for_mysql(
-/*==================*/
- XID* xid_list, /*!< in/out: prepared transactions */
- ulint len) /*!< in: number of slots in xid_list */
-{
- const trx_t* trx;
- ulint count = 0;
-
- ut_ad(xid_list);
- ut_ad(len);
-
- /* We should set those transactions which are in the prepared state
- to the xid_list */
-
- mutex_enter(&trx_sys->mutex);
-
- for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
- trx != NULL;
- trx = UT_LIST_GET_NEXT(trx_list, trx)) {
-
- assert_trx_in_rw_list(trx);
-
- /* The state of a read-write transaction cannot change
- from or to NOT_STARTED while we are holding the
- trx_sys->mutex. It may change to PREPARED, but not if
- trx->is_recovered. It may also change to COMMITTED. */
- if (trx_state_eq(trx, TRX_STATE_PREPARED)) {
- xid_list[count] = trx->xid;
-
- if (count == 0) {
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Starting recovery for"
- " XA transactions...\n");
- }
-
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Transaction " TRX_ID_FMT " in"
- " prepared state after recovery\n",
- trx->id);
-
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Transaction contains changes"
- " to " TRX_ID_FMT " rows\n",
- trx->undo_no);
-
- count++;
-
- if (count == len) {
- break;
- }
- }
- }
-
- mutex_exit(&trx_sys->mutex);
-
- if (count > 0){
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: %d transactions in prepared state"
- " after recovery\n",
- int (count));
- }
-
- return(int (count));
-}
-
-/*******************************************************************//**
-This function is used to find one X/Open XA distributed transaction
-which is in the prepared state
-@return trx on match, the trx->xid will be invalidated;
-note that the trx may have been committed, unless the caller is
-holding lock_sys->mutex */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-trx_t*
-trx_get_trx_by_xid_low(
-/*===================*/
- const XID* xid) /*!< in: X/Open XA transaction
- identifier */
-{
- trx_t* trx;
-
- ut_ad(mutex_own(&trx_sys->mutex));
-
- for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
- trx != NULL;
- trx = UT_LIST_GET_NEXT(trx_list, trx)) {
-
- assert_trx_in_rw_list(trx);
-
- /* Compare two X/Open XA transaction id's: their
- length should be the same and binary comparison
- of gtrid_length+bqual_length bytes should be
- the same */
-
- if (trx->is_recovered
- && trx_state_eq(trx, TRX_STATE_PREPARED)
- && xid->gtrid_length == trx->xid.gtrid_length
- && xid->bqual_length == trx->xid.bqual_length
- && memcmp(xid->data, trx->xid.data,
- xid->gtrid_length + xid->bqual_length) == 0) {
-
- /* Invalidate the XID, so that subsequent calls
- will not find it. */
- memset(&trx->xid, 0, sizeof(trx->xid));
- trx->xid.formatID = -1;
- break;
- }
- }
-
- return(trx);
-}
-
-/*******************************************************************//**
-This function is used to find one X/Open XA distributed transaction
-which is in the prepared state
-@return trx or NULL; on match, the trx->xid will be invalidated;
-note that the trx may have been committed, unless the caller is
-holding lock_sys->mutex */
-UNIV_INTERN
-trx_t*
-trx_get_trx_by_xid(
-/*===============*/
- const XID* xid) /*!< in: X/Open XA transaction identifier */
-{
- trx_t* trx;
-
- if (xid == NULL) {
-
- return(NULL);
- }
-
- mutex_enter(&trx_sys->mutex);
-
- /* Recovered/Resurrected transactions are always only on the
- trx_sys_t::rw_trx_list. */
- trx = trx_get_trx_by_xid_low(xid);
-
- mutex_exit(&trx_sys->mutex);
-
- return(trx);
-}
-
-/*************************************************************//**
-Starts the transaction if it is not yet started. */
-UNIV_INTERN
-void
-trx_start_if_not_started_xa_low(
-/*============================*/
- trx_t* trx) /*!< in: transaction */
-{
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
-
- /* Update the info whether we should skip XA steps
- that eat CPU time.
-
- For the duration of the transaction trx->support_xa is
- not reread from thd so any changes in the value take
- effect in the next transaction. This is to avoid a
- scenario where some undo generated by a transaction,
- has XA stuff, and other undo, generated by the same
- transaction, doesn't. */
- trx->support_xa = thd_supports_xa(trx->mysql_thd);
-
- trx_start_low(trx);
- /* fall through */
- case TRX_STATE_ACTIVE:
- return;
- case TRX_STATE_PREPARED:
- case TRX_STATE_COMMITTED_IN_MEMORY:
- break;
- }
-
- ut_error;
-}
-
-/*************************************************************//**
-Starts the transaction if it is not yet started. */
-UNIV_INTERN
-void
-trx_start_if_not_started_low(
-/*=========================*/
- trx_t* trx) /*!< in: transaction */
-{
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
-#ifdef WITH_WSREP
- ut_d(trx->start_file = __FILE__);
- ut_d(trx->start_line = __LINE__);
-#endif /* WITH_WSREP */
- trx_start_low(trx);
- /* fall through */
- case TRX_STATE_ACTIVE:
- return;
- case TRX_STATE_PREPARED:
- case TRX_STATE_COMMITTED_IN_MEMORY:
- break;
- }
-
- ut_error;
-}
-
-/*************************************************************//**
-Starts the transaction for a DDL operation. */
-UNIV_INTERN
-void
-trx_start_for_ddl_low(
-/*==================*/
- trx_t* trx, /*!< in/out: transaction */
- trx_dict_op_t op) /*!< in: dictionary operation type */
-{
- switch (trx->state) {
- case TRX_STATE_NOT_STARTED:
- /* Flag this transaction as a dictionary operation, so that
- the data dictionary will be locked in crash recovery. */
-
- trx_set_dict_operation(trx, op);
-
- /* Ensure it is not flagged as an auto-commit-non-locking
- transation. */
- trx->will_lock = 1;
-
- trx->ddl = true;
-
-#ifdef WITH_WSREP
- ut_d(trx->start_file = __FILE__);
- ut_d(trx->start_line = __LINE__);
-#endif /* WITH_WSREP */
- trx_start_low(trx);
- return;
-
- case TRX_STATE_ACTIVE:
- /* We have this start if not started idiom, therefore we
- can't add stronger checks here. */
- trx->ddl = true;
-
- ut_ad(trx->dict_operation != TRX_DICT_OP_NONE);
- ut_ad(trx->will_lock > 0);
- return;
- case TRX_STATE_PREPARED:
- case TRX_STATE_COMMITTED_IN_MEMORY:
- break;
- }
-
- ut_error;
-}