diff options
Diffstat (limited to 'storage/xtradb/trx/trx0trx.c')
-rw-r--r-- | storage/xtradb/trx/trx0trx.c | 2445 |
1 files changed, 0 insertions, 2445 deletions
diff --git a/storage/xtradb/trx/trx0trx.c b/storage/xtradb/trx/trx0trx.c deleted file mode 100644 index b2df6d471f1..00000000000 --- a/storage/xtradb/trx/trx0trx.c +++ /dev/null @@ -1,2445 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*****************************************************************************/ - -/**************************************************//** -@file trx/trx0trx.c -The transaction - -Created 3/26/1996 Heikki Tuuri -*******************************************************/ - -#include "trx0trx.h" - -#ifdef UNIV_NONINL -#include "trx0trx.ic" -#endif - -#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 "btr0sea.h" -#include "os0proc.h" -#include "trx0xa.h" -#include "trx0purge.h" -#include "ha_prototypes.h" - -/** Dummy session used currently in MySQL interface */ -UNIV_INTERN sess_t* trx_dummy_sess = NULL; - -/** Number of transactions currently allocated for MySQL: protected by -the kernel mutex */ -UNIV_INTERN ulint trx_n_mysql_transactions = 0; -/** Number of transactions currently in the XA PREPARED state: protected by -the kernel mutex */ -UNIV_INTERN ulint trx_n_prepared = 0; - -#ifdef UNIV_PFS_MUTEX -/* 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(&kernel_mutex)); - ut_ad(!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 = - 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(&kernel_mutex)); - - if (UNIV_LIKELY(trx->is_in_trx_serial_list)) { - - UT_LIST_REMOVE(trx_serial_list, trx_sys->trx_serial_list, - trx); - trx->is_in_trx_serial_list = 0; - } - - 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. -@return own: the transaction */ -UNIV_INTERN -trx_t* -trx_create( -/*=======*/ - sess_t* sess) /*!< in: session */ -{ - trx_t* trx; - - ut_ad(mutex_own(&kernel_mutex)); - ut_ad(sess); - - trx = mem_alloc(sizeof(trx_t)); - - trx->magic_n = TRX_MAGIC_N; - - trx->op_info = ""; - - trx->is_purge = 0; - trx->is_recovered = 0; - trx->state = TRX_NOT_STARTED; - - trx->is_registered = 0; - trx->active_commit_ordered = 0; - - trx->start_time = ut_time(); - - trx->isolation_level = TRX_ISO_REPEATABLE_READ; - - trx->id = 0; - trx->no = IB_ULONGLONG_MAX; - trx->is_in_trx_serial_list = 0; - - trx->support_xa = TRUE; - - trx->fake_changes = FALSE; - - trx->check_foreigns = TRUE; - trx->check_unique_secondary = TRUE; - - trx->flush_log_later = FALSE; - trx->must_flush_log_later = FALSE; - - trx->dict_operation = TRX_DICT_OP_NONE; - trx->table_id = 0; - - trx->mysql_thd = NULL; - trx->duplicates = 0; - - trx->n_mysql_tables_in_use = 0; - trx->mysql_n_tables_locked = 0; - - trx->mysql_log_file_name = NULL; - trx->mysql_log_offset = 0; - trx->mysql_master_log_file_name = ""; - trx->mysql_master_log_pos = 0; - trx->mysql_relay_log_file_name = ""; - trx->mysql_relay_log_pos = 0; - - trx->idle_start = 0; - trx->last_stmt_start = 0; - - mutex_create(trx_undo_mutex_key, &trx->undo_mutex, SYNC_TRX_UNDO); - - trx->rseg = NULL; - - trx->undo_no = 0; - trx->last_sql_stat_start.least_undo_no = 0; - trx->insert_undo = NULL; - trx->update_undo = NULL; - trx->undo_no_arr = NULL; - - trx->error_state = DB_SUCCESS; - trx->error_key_num = 0; - trx->detailed_error[0] = '\0'; - - trx->sess = sess; - trx->que_state = TRX_QUE_RUNNING; - trx->n_active_thrs = 0; - - trx->handling_signals = FALSE; - - UT_LIST_INIT(trx->signals); - UT_LIST_INIT(trx->reply_signals); - - trx->graph = NULL; - - trx->wait_lock = NULL; - trx->was_chosen_as_deadlock_victim = FALSE; - UT_LIST_INIT(trx->wait_thrs); - - trx->lock_heap = mem_heap_create_in_buffer(256); - UT_LIST_INIT(trx->trx_locks); - - UT_LIST_INIT(trx->trx_savepoints); - - trx->dict_operation_lock_mode = 0; - trx->has_search_latch = FALSE; - trx->search_latch_timeout = BTR_SEA_TIMEOUT; - - trx->declared_to_be_inside_innodb = FALSE; - trx->n_tickets_to_enter_innodb = 0; - - trx->global_read_view = NULL; - trx->read_view = NULL; - trx->prebuilt_view = NULL; - - 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; - - /* Set X/Open XA transaction identification to NULL */ - memset(&trx->xid, 0, sizeof(trx->xid)); - trx->xid.formatID = -1; - - trx->n_autoinc_rows = 0; - - /* Remember to free the vector explicitly. */ - trx->autoinc_locks = ib_vector_create( - mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 4), 4); - - return(trx); -} - -/********************************************************************//** -Creates a transaction object for MySQL. -@return own: transaction object */ -UNIV_INTERN -trx_t* -trx_allocate_for_mysql(void) -/*========================*/ -{ - trx_t* trx; - - mutex_enter(&kernel_mutex); - - trx = trx_create(trx_dummy_sess); - - trx_n_mysql_transactions++; - - UT_LIST_ADD_FIRST(mysql_trx_list, trx_sys->mysql_trx_list, trx); - - mutex_exit(&kernel_mutex); - - if (UNIV_UNLIKELY(trx->take_stats)) { - trx->distinct_page_access_hash = mem_alloc(DPAH_SIZE); - memset(trx->distinct_page_access_hash, 0, DPAH_SIZE); - } - - 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; - - mutex_enter(&kernel_mutex); - - trx = trx_create(trx_dummy_sess); - - mutex_exit(&kernel_mutex); - - return(trx); -} - -/********************************************************************//** -Frees a transaction object. */ -UNIV_INTERN -void -trx_free( -/*=====*/ - trx_t* trx) /*!< in, own: trx object */ -{ - ut_ad(mutex_own(&kernel_mutex)); - - if (trx->declared_to_be_inside_innodb) { - ut_print_timestamp(stderr); - fputs(" InnoDB: Error: Freeing a trx which is declared" - " to be processing\n" - "InnoDB: inside InnoDB.\n", stderr); - 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) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: MySQL is freeing a thd\n" - "InnoDB: though trx->n_mysql_tables_in_use is %lu\n" - "InnoDB: and trx->mysql_n_tables_locked is %lu.\n", - (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->magic_n == TRX_MAGIC_N); - - trx->magic_n = 11112222; - - ut_a(trx->state == TRX_NOT_STARTED); - - mutex_free(&(trx->undo_mutex)); - - ut_a(trx->insert_undo == NULL); - ut_a(trx->update_undo == NULL); - - if (trx->undo_no_arr) { - trx_undo_arr_free(trx->undo_no_arr); - } - - ut_a(UT_LIST_GET_LEN(trx->signals) == 0); - ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0); - - ut_a(trx->wait_lock == NULL); - ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0); - - 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_heap) { - mem_heap_free(trx->lock_heap); - } - - ut_a(UT_LIST_GET_LEN(trx->trx_locks) == 0); - - if (trx->prebuilt_view != NULL) { - read_view_free(trx->prebuilt_view); - } - - ut_a(trx->read_view == NULL); - - ut_a(ib_vector_is_empty(trx->autoinc_locks)); - /* We allocated a dedicated heap for the vector. */ - ib_vector_free(trx->autoinc_locks); - - trx_release_descriptor(trx); - - mem_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_ad(mutex_own(&kernel_mutex)); - ut_a(trx->state == TRX_PREPARED); - ut_a(trx->magic_n == TRX_MAGIC_N); - - /* Prepared transactions are sort of active; they allow - ROLLBACK and COMMIT operations. Because the system does not - contain any other transactions than prepared transactions at - the shutdown stage and because a transaction cannot become - PREPARED while holding locks, it is safe to release the locks - held by PREPARED transactions here at shutdown.*/ - lock_release_off_kernel(trx); - - trx_undo_free_prepared(trx); - - mutex_free(&trx->undo_mutex); - - if (trx->undo_no_arr) { - trx_undo_arr_free(trx->undo_no_arr); - } - - ut_a(UT_LIST_GET_LEN(trx->signals) == 0); - ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0); - - ut_a(trx->wait_lock == NULL); - ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0); - - 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_heap) { - mem_heap_free(trx->lock_heap); - } - - ut_a(ib_vector_is_empty(trx->autoinc_locks)); - ib_vector_free(trx->autoinc_locks); - - trx_release_descriptor(trx); - - if (trx->prebuilt_view != NULL) { - read_view_free(trx->prebuilt_view); - } - - UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); - - ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->trx_list)); - - mem_free(trx); -} - -/********************************************************************//** -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(&kernel_mutex); - - UT_LIST_REMOVE(mysql_trx_list, trx_sys->mysql_trx_list, trx); - - trx_free(trx); - - ut_a(trx_n_mysql_transactions > 0); - - trx_n_mysql_transactions--; - - mutex_exit(&kernel_mutex); -} - -/********************************************************************//** -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; - } - - mutex_enter(&kernel_mutex); - - trx_free(trx); - - mutex_exit(&kernel_mutex); -} - -/****************************************************************//** -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_insert_ordered( -/*====================*/ - trx_t* trx) /*!< in: trx handle */ -{ - trx_t* trx2; - - ut_ad(mutex_own(&kernel_mutex)); - - trx2 = UT_LIST_GET_FIRST(trx_sys->trx_list); - - while (trx2 != NULL) { - if (trx->id >= trx2->id) { - - ut_ad(trx->id > trx2->id); - break; - } - trx2 = UT_LIST_GET_NEXT(trx_list, trx2); - } - - if (trx2 != NULL) { - trx2 = UT_LIST_GET_PREV(trx_list, trx2); - - if (trx2 == NULL) { - UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx); - } else { - UT_LIST_INSERT_AFTER(trx_list, trx_sys->trx_list, - trx2, trx); - } - } else { - UT_LIST_ADD_LAST(trx_list, trx_sys->trx_list, trx); - } -} - -/****************************************************************//** -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) -/*============================*/ -{ - trx_rseg_t* rseg; - trx_undo_t* undo; - trx_t* trx; - - ut_ad(mutex_own(&kernel_mutex)); - UT_LIST_INIT(trx_sys->trx_list); - UT_LIST_INIT(trx_sys->trx_serial_list); - - /* Look from the rollback segments if there exist undo logs for - transactions */ - - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); - - while (rseg != NULL) { - undo = UT_LIST_GET_FIRST(rseg->insert_undo_list); - - while (undo != NULL) { - - trx = trx_create(trx_dummy_sess); - - trx->is_recovered = TRUE; - trx->id = undo->trx_id; - trx->xid = undo->xid; - trx->insert_undo = undo; - trx->rseg = rseg; - - 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", - (ullint) trx->id); - - if (srv_force_recovery == 0) { - - trx->state = TRX_PREPARED; - trx_n_prepared++; - } else { - fprintf(stderr, - "InnoDB: Since" - " innodb_force_recovery" - " > 0, we will" - " rollback it" - " anyway.\n"); - - trx->state = TRX_ACTIVE; - } - - trx_reserve_descriptor(trx); - } else { - trx->state = TRX_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_ACTIVE; - - /* A running transaction always has the number - field inited to IB_ULONGLONG_MAX */ - - trx->no = IB_ULONGLONG_MAX; - - trx_reserve_descriptor(trx); - - } - - 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; - } - - trx_list_insert_ordered(trx); - - undo = UT_LIST_GET_NEXT(undo_list, undo); - } - - undo = UT_LIST_GET_FIRST(rseg->update_undo_list); - - while (undo != NULL) { - trx = trx_get_on_id(undo->trx_id); - - if (NULL == trx) { - trx = trx_create(trx_dummy_sess); - - trx->is_recovered = TRUE; - trx->id = undo->trx_id; - trx->xid = undo->xid; - - 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", - (ullint) trx->id); - - if (srv_force_recovery == 0) { - - trx->state - = TRX_PREPARED; - trx_n_prepared++; - } else { - fprintf(stderr, - "InnoDB: Since" - " innodb_force_recovery" - " > 0, we will" - " rollback it" - " anyway.\n"); - - trx->state = TRX_ACTIVE; - trx_reserve_descriptor( - trx); - } - } else { - trx->state - = TRX_COMMITTED_IN_MEMORY; - } - - /* We give a dummy value for the trx - number */ - - trx->no = trx->id; - } else { - trx->state = TRX_ACTIVE; - /* A running transaction always has - the number field inited to - IB_ULONGLONG_MAX */ - - trx->no = IB_ULONGLONG_MAX; - - trx_reserve_descriptor(trx); - } - - trx->rseg = rseg; - trx_list_insert_ordered(trx); - - if (undo->dict_operation) { - trx_set_dict_operation( - trx, TRX_DICT_OP_TABLE); - trx->table_id = undo->table_id; - } - } - - trx->update_undo = undo; - - if ((!undo->empty) - && undo->top_undo_no >= trx->undo_no) { - - trx->undo_no = undo->top_undo_no + 1; - } - - undo = UT_LIST_GET_NEXT(undo_list, undo); - } - - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); - } -} - -/******************************************************************//** -Assigns a rollback segment to a transaction in a round-robin fashion. -@return assigned rollback segment instance */ -UNIV_INLINE -trx_rseg_t* -trx_assign_rseg( -/*============*/ - ulint max_undo_logs) /*!< in: maximum number of UNDO logs to use */ -{ - trx_rseg_t* rseg = trx_sys->latest_rseg; - - ut_ad(mutex_own(&kernel_mutex)); - - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); - - if (rseg == NULL || rseg->id == max_undo_logs - 1) { - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); - } - - trx_sys->latest_rseg = rseg; - - return(rseg); -} - -/****************************************************************//** -Starts a new transaction. -@return TRUE */ -UNIV_INTERN -ibool -trx_start_low( -/*==========*/ - trx_t* trx, /*!< in: transaction */ - ulint rseg_id)/*!< in: rollback segment id; if ULINT_UNDEFINED - is passed, the system chooses the rollback segment - automatically in a round-robin fashion */ -{ - trx_rseg_t* rseg; - - ut_ad(mutex_own(&kernel_mutex)); - ut_ad(trx->rseg == NULL); - - if (trx->is_purge) { - trx->id = 0; - /* Don't reserve a descriptor, since this trx is not added to - trx_list. */ - trx->state = TRX_ACTIVE; - trx->start_time = time(NULL); - - return(TRUE); - } - - ut_ad(trx->state != TRX_ACTIVE); - - ut_a(rseg_id == ULINT_UNDEFINED); - - rseg = trx_assign_rseg(srv_rollback_segments); - - trx->id = trx_sys_get_new_trx_id(); - - /* The initial value for trx->no: IB_ULONGLONG_MAX is used in - read_view_open_now: */ - - trx->no = IB_ULONGLONG_MAX; - - trx->rseg = rseg; - - trx->state = TRX_ACTIVE; - - trx_reserve_descriptor(trx); - - trx->start_time = time(NULL); - - UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx); - - return(TRUE); -} - -/****************************************************************//** -Starts a new transaction. -@return TRUE */ -UNIV_INTERN -ibool -trx_start( -/*======*/ - trx_t* trx, /*!< in: transaction */ - ulint rseg_id)/*!< in: rollback segment id; if ULINT_UNDEFINED - is passed, the system chooses the rollback segment - automatically in a round-robin fashion */ -{ - ibool ret; - - /* 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); - - mutex_enter(&kernel_mutex); - - ret = trx_start_low(trx, rseg_id); - - mutex_exit(&kernel_mutex); - - return(ret); -} - -/****************************************************************//** -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(&kernel_mutex); - - trx->no = trx_sys_get_new_trx_id(); - - if (UNIV_LIKELY(trx->is_in_trx_serial_list == 0)) { - - UT_LIST_ADD_LAST(trx_serial_list, trx_sys->trx_serial_list, - trx); - - trx->is_in_trx_serial_list = 1; - } - - /* 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 kernel 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(&kernel_mutex); - - ptr = ib_bh_push(purge_sys->ib_bh, &rseg_queue); - ut_a(ptr); - - mutex_exit(&purge_sys->bh_mutex); - } else { - mutex_exit(&kernel_mutex); - } -} - -/****************************************************************//** -Assign the transaction its history serialisation number and write the -update UNDO log record to the assigned rollback segment. -@return the LSN of the UNDO log write. */ -static -ib_uint64_t -trx_write_serialisation_history( -/*============================*/ - trx_t* trx) /*!< in: transaction */ -{ - mtr_t mtr; - trx_rseg_t* rseg; - trx_sysf_t* sys_header = NULL; - - ut_ad(!mutex_own(&kernel_mutex)); - - rseg = trx->rseg; - - mtr_start(&mtr); - - /* 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); - - /* 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') { - if (!sys_header) { - sys_header = trx_sysf_get(&mtr); - } - - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_log_file_name, - trx->mysql_log_offset, - TRX_SYS_MYSQL_LOG_INFO, &mtr); - - trx->mysql_log_file_name = NULL; - } - - if (trx->mysql_master_log_file_name[0] != '\0') { - /* This database server is a MySQL replication slave */ - if (!sys_header) { - sys_header = trx_sysf_get(&mtr); - } - - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_relay_log_file_name, - trx->mysql_relay_log_pos, - TRX_SYS_COMMIT_RELAY_LOG_INFO, &mtr); - - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_master_log_file_name, - trx->mysql_master_log_pos, - TRX_SYS_COMMIT_MASTER_LOG_INFO, &mtr); - - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_relay_log_file_name, - trx->mysql_relay_log_pos, - TRX_SYS_MYSQL_RELAY_LOG_INFO, &mtr); - - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_master_log_file_name, - trx->mysql_master_log_pos, - TRX_SYS_MYSQL_MASTER_LOG_INFO, &mtr); - - trx->mysql_master_log_file_name = ""; - } - - /* 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); - /*--------------*/ - - return(mtr.end_lsn); -} - -/****************************************************************//** -Commits a transaction. */ -UNIV_INTERN -void -trx_commit_off_kernel( -/*==================*/ - trx_t* trx) /*!< in: transaction */ -{ - ib_uint64_t lsn; - - ut_ad(mutex_own(&kernel_mutex)); - - trx->must_flush_log_later = FALSE; - - /* If the transaction made any updates then we need to write the - UNDO logs for the updates to the assigned rollback segment. */ - - if (trx->insert_undo != NULL || trx->update_undo != NULL) { - mutex_exit(&kernel_mutex); - - lsn = trx_write_serialisation_history(trx); - - mutex_enter(&kernel_mutex); - } else { - lsn = 0; - } - - ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED); - ut_ad(mutex_own(&kernel_mutex)); - - if (UNIV_UNLIKELY(trx->state == TRX_PREPARED)) { - ut_a(trx_n_prepared > 0); - trx_n_prepared--; - } - - /* The following assignment makes the transaction committed in memory - and makes its changes to data visible to other transactions. - NOTE that there is a small discrepancy from the strict formal - visibility rules here: a human user of the database can see - modifications made by another transaction T even before the necessary - log segment has been flushed to the disk. If the database happens to - crash before the flush, the user has seen modifications from T which - will never be a committed transaction. However, any transaction T2 - which sees the modifications of the committing transaction T, and - which also itself makes modifications to the database, will get an lsn - larger than the committing transaction T. In the case where the log - flush fails, and T never gets committed, also T2 will never get - committed. */ - - /*--------------------------------------*/ - trx->state = TRX_COMMITTED_IN_MEMORY; - /* The following also removes trx from trx_serial_list */ - trx_release_descriptor(trx); - /*--------------------------------------*/ - - /* If we release kernel_mutex below and we are still doing - recovery i.e.: back ground rollback thread is still active - then there is a chance that the rollback thread may see - this trx as COMMITTED_IN_MEMORY and goes adhead to clean it - up calling trx_cleanup_at_db_startup(). This can happen - in the case we are committing a trx here that is left in - PREPARED state during the crash. Note that commit of the - rollback of a PREPARED trx happens in the recovery thread - while the rollback of other transactions happen in the - background thread. To avoid this race we unconditionally - unset the is_recovered flag from the trx. */ - - trx->is_recovered = FALSE; - - lock_release_off_kernel(trx); - - if (trx->global_read_view) { - read_view_close(trx->global_read_view); - trx->global_read_view = NULL; - } - - trx->read_view = NULL; - - if (lsn) { - ulint flush_log_at_trx_commit; - - mutex_exit(&kernel_mutex); - - 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) { - /* Do nothing */ - } else if (flush_log_at_trx_commit == 1 || - flush_log_at_trx_commit == 3) { - if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { - /* Write the log but do not flush it to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, - FALSE); - } else { - /* Write the log to the log files AND flush - them to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); - } - } else if (flush_log_at_trx_commit == 2) { - - /* Write the log but do not flush it to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); - } else { - ut_error; - } - - trx->commit_lsn = lsn; - - /*-------------------------------------*/ - - mutex_enter(&kernel_mutex); - } - - /* Free all savepoints */ - trx_roll_free_all_savepoints(trx); - - trx->state = TRX_NOT_STARTED; - trx->rseg = NULL; - trx->undo_no = 0; - trx->last_sql_stat_start.least_undo_no = 0; - - ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); - ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0); - - UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); - - ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->trx_list)); - - trx->error_state = DB_SUCCESS; -} - -/****************************************************************//** -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 */ -{ - if (trx->insert_undo != NULL) { - - trx_undo_insert_cleanup(trx); - } - - trx->state = TRX_NOT_STARTED; - trx_release_descriptor(trx); - trx->rseg = NULL; - trx->undo_no = 0; - trx->last_sql_stat_start.least_undo_no = 0; - - UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); - - ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->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_ACTIVE); - - if (trx->read_view) { - return(trx->read_view); - } - - mutex_enter(&kernel_mutex); - - trx->read_view = read_view_open_now(trx->id, trx->prebuilt_view, TRUE); - trx->prebuilt_view = trx->read_view; - trx->global_read_view = trx->read_view; - - mutex_exit(&kernel_mutex); - - return(trx->read_view); -} - -/****************************************************************//** -Commits a transaction. NOTE that the kernel mutex is temporarily released. */ -static -void -trx_handle_commit_sig_off_kernel( -/*=============================*/ - trx_t* trx, /*!< in: transaction */ - que_thr_t** next_thr) /*!< in/out: next query thread to run; - if the value which is passed in is - a pointer to a NULL pointer, then the - calling function can start running - a new query thread */ -{ - trx_sig_t* sig; - trx_sig_t* next_sig; - - ut_ad(mutex_own(&kernel_mutex)); - - trx->que_state = TRX_QUE_COMMITTING; - - trx_commit_off_kernel(trx); - - ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); - - /* Remove all TRX_SIG_COMMIT signals from the signal queue and send - reply messages to them */ - - sig = UT_LIST_GET_FIRST(trx->signals); - - while (sig != NULL) { - next_sig = UT_LIST_GET_NEXT(signals, sig); - - if (sig->type == TRX_SIG_COMMIT) { - - trx_sig_reply(sig, next_thr); - trx_sig_remove(trx, sig); - } - - sig = next_sig; - } - - trx->que_state = TRX_QUE_RUNNING; -} - -/***********************************************************//** -The transaction must be in the TRX_QUE_LOCK_WAIT state. Puts it to -the TRX_QUE_RUNNING state and releases query threads which were -waiting for a lock in the wait_thrs list. */ -UNIV_INTERN -void -trx_end_lock_wait( -/*==============*/ - trx_t* trx) /*!< in: transaction */ -{ - que_thr_t* thr; - ulint sec; - ulint ms; - ib_uint64_t now; - - ut_ad(mutex_own(&kernel_mutex)); - ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT); - - thr = UT_LIST_GET_FIRST(trx->wait_thrs); - - while (thr != NULL) { - que_thr_end_wait_no_next_thr(thr); - - UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr); - - thr = UT_LIST_GET_FIRST(trx->wait_thrs); - } - - 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->que_state = TRX_QUE_RUNNING; -} - -/***********************************************************//** -Moves the query threads in the lock wait list to the SUSPENDED state and puts -the transaction to the TRX_QUE_RUNNING state. */ -static -void -trx_lock_wait_to_suspended( -/*=======================*/ - trx_t* trx) /*!< in: transaction in the TRX_QUE_LOCK_WAIT state */ -{ - que_thr_t* thr; - ulint sec; - ulint ms; - ib_uint64_t now; - - ut_ad(mutex_own(&kernel_mutex)); - ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT); - - thr = UT_LIST_GET_FIRST(trx->wait_thrs); - - while (thr != NULL) { - thr->state = QUE_THR_SUSPENDED; - - UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr); - - thr = UT_LIST_GET_FIRST(trx->wait_thrs); - } - - 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->que_state = TRX_QUE_RUNNING; -} - -/***********************************************************//** -Moves the query threads in the sig reply wait list of trx to the SUSPENDED -state. */ -static -void -trx_sig_reply_wait_to_suspended( -/*============================*/ - trx_t* trx) /*!< in: transaction */ -{ - trx_sig_t* sig; - que_thr_t* thr; - - ut_ad(mutex_own(&kernel_mutex)); - - sig = UT_LIST_GET_FIRST(trx->reply_signals); - - while (sig != NULL) { - thr = sig->receiver; - - ut_ad(thr->state == QUE_THR_SIG_REPLY_WAIT); - - thr->state = QUE_THR_SUSPENDED; - - sig->receiver = NULL; - - UT_LIST_REMOVE(reply_signals, trx->reply_signals, sig); - - sig = UT_LIST_GET_FIRST(trx->reply_signals); - } -} - -/*****************************************************************//** -Checks the compatibility of a new signal with the other signals in the -queue. -@return TRUE if the signal can be queued */ -static -ibool -trx_sig_is_compatible( -/*==================*/ - trx_t* trx, /*!< in: trx handle */ - ulint type, /*!< in: signal type */ - ulint sender) /*!< in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */ -{ - trx_sig_t* sig; - - ut_ad(mutex_own(&kernel_mutex)); - - if (UT_LIST_GET_LEN(trx->signals) == 0) { - - return(TRUE); - } - - if (sender == TRX_SIG_SELF) { - if (type == TRX_SIG_ERROR_OCCURRED) { - - return(TRUE); - - } else if (type == TRX_SIG_BREAK_EXECUTION) { - - return(TRUE); - } else { - return(FALSE); - } - } - - ut_ad(sender == TRX_SIG_OTHER_SESS); - - sig = UT_LIST_GET_FIRST(trx->signals); - - if (type == TRX_SIG_COMMIT) { - while (sig != NULL) { - - if (sig->type == TRX_SIG_TOTAL_ROLLBACK) { - - return(FALSE); - } - - sig = UT_LIST_GET_NEXT(signals, sig); - } - - return(TRUE); - - } else if (type == TRX_SIG_TOTAL_ROLLBACK) { - while (sig != NULL) { - - if (sig->type == TRX_SIG_COMMIT) { - - return(FALSE); - } - - sig = UT_LIST_GET_NEXT(signals, sig); - } - - return(TRUE); - - } else if (type == TRX_SIG_BREAK_EXECUTION) { - - return(TRUE); - } else { - ut_error; - - return(FALSE); - } -} - -/****************************************************************//** -Sends a signal to a trx object. */ -UNIV_INTERN -void -trx_sig_send( -/*=========*/ - trx_t* trx, /*!< in: trx handle */ - ulint type, /*!< in: signal type */ - ulint sender, /*!< in: TRX_SIG_SELF or - TRX_SIG_OTHER_SESS */ - que_thr_t* receiver_thr, /*!< in: query thread which wants the - reply, or NULL; if type is - TRX_SIG_END_WAIT, this must be NULL */ - trx_savept_t* savept, /*!< in: possible rollback savepoint, or - NULL */ - que_thr_t** next_thr) /*!< in/out: next query thread to run; - if the value which is passed in is - a pointer to a NULL pointer, then the - calling function can start running - a new query thread; if the parameter - is NULL, it is ignored */ -{ - trx_sig_t* sig; - trx_t* receiver_trx; - - ut_ad(trx); - ut_ad(mutex_own(&kernel_mutex)); - - if (!trx_sig_is_compatible(trx, type, sender)) { - /* The signal is not compatible with the other signals in - the queue: die */ - - ut_error; - } - - /* Queue the signal object */ - - if (UT_LIST_GET_LEN(trx->signals) == 0) { - - /* The signal list is empty: the 'sig' slot must be unused - (we improve performance a bit by avoiding mem_alloc) */ - sig = &(trx->sig); - } else { - /* It might be that the 'sig' slot is unused also in this - case, but we choose the easy way of using mem_alloc */ - - sig = mem_alloc(sizeof(trx_sig_t)); - } - - UT_LIST_ADD_LAST(signals, trx->signals, sig); - - sig->type = type; - sig->sender = sender; - sig->receiver = receiver_thr; - - if (savept) { - sig->savept = *savept; - } - - if (receiver_thr) { - receiver_trx = thr_get_trx(receiver_thr); - - UT_LIST_ADD_LAST(reply_signals, receiver_trx->reply_signals, - sig); - } - - if (trx->sess->state == SESS_ERROR) { - - trx_sig_reply_wait_to_suspended(trx); - } - - if ((sender != TRX_SIG_SELF) || (type == TRX_SIG_BREAK_EXECUTION)) { - ut_error; - } - - /* If there were no other signals ahead in the queue, try to start - handling of the signal */ - - if (UT_LIST_GET_FIRST(trx->signals) == sig) { - - trx_sig_start_handle(trx, next_thr); - } -} - -/****************************************************************//** -Ends signal handling. If the session is in the error state, and -trx->graph_before_signal_handling != NULL, then returns control to the error -handling routine of the graph (currently just returns the control to the -graph root which then will send an error message to the client). */ -UNIV_INTERN -void -trx_end_signal_handling( -/*====================*/ - trx_t* trx) /*!< in: trx */ -{ - ut_ad(mutex_own(&kernel_mutex)); - ut_ad(trx->handling_signals == TRUE); - - trx->handling_signals = FALSE; - - trx->graph = trx->graph_before_signal_handling; - - if (trx->graph && (trx->sess->state == SESS_ERROR)) { - - que_fork_error_handle(trx, trx->graph); - } -} - -/****************************************************************//** -Starts handling of a trx signal. */ -UNIV_INTERN -void -trx_sig_start_handle( -/*=================*/ - trx_t* trx, /*!< in: trx handle */ - que_thr_t** next_thr) /*!< in/out: next query thread to run; - if the value which is passed in is - a pointer to a NULL pointer, then the - calling function can start running - a new query thread; if the parameter - is NULL, it is ignored */ -{ - trx_sig_t* sig; - ulint type; -loop: - /* We loop in this function body as long as there are queued signals - we can process immediately */ - - ut_ad(trx); - ut_ad(mutex_own(&kernel_mutex)); - - if (trx->handling_signals && (UT_LIST_GET_LEN(trx->signals) == 0)) { - - trx_end_signal_handling(trx); - - return; - } - - if (trx->state == TRX_NOT_STARTED) { - - trx_start_low(trx, ULINT_UNDEFINED); - } - - /* If the trx is in a lock wait state, moves the waiting query threads - to the suspended state */ - - if (trx->que_state == TRX_QUE_LOCK_WAIT) { - - trx_lock_wait_to_suspended(trx); - } - - /* If the session is in the error state and this trx has threads - waiting for reply from signals, moves these threads to the suspended - state, canceling wait reservations; note that if the transaction has - sent a commit or rollback signal to itself, and its session is not in - the error state, then nothing is done here. */ - - if (trx->sess->state == SESS_ERROR) { - trx_sig_reply_wait_to_suspended(trx); - } - - /* If there are no running query threads, we can start processing of a - signal, otherwise we have to wait until all query threads of this - transaction are aware of the arrival of the signal. */ - - if (trx->n_active_thrs > 0) { - - return; - } - - if (trx->handling_signals == FALSE) { - trx->graph_before_signal_handling = trx->graph; - - trx->handling_signals = TRUE; - } - - sig = UT_LIST_GET_FIRST(trx->signals); - type = sig->type; - - if (type == TRX_SIG_COMMIT) { - - trx_handle_commit_sig_off_kernel(trx, next_thr); - - } else if ((type == TRX_SIG_TOTAL_ROLLBACK) - || (type == TRX_SIG_ROLLBACK_TO_SAVEPT)) { - - trx_rollback(trx, sig, next_thr); - - /* No further signals can be handled until the rollback - completes, therefore we return */ - - return; - - } else if (type == TRX_SIG_ERROR_OCCURRED) { - - trx_rollback(trx, sig, next_thr); - - /* No further signals can be handled until the rollback - completes, therefore we return */ - - return; - - } else if (type == TRX_SIG_BREAK_EXECUTION) { - - trx_sig_reply(sig, next_thr); - trx_sig_remove(trx, sig); - } else { - ut_error; - } - - goto loop; -} - -/****************************************************************//** -Send the reply message when a signal in the queue of the trx has been -handled. */ -UNIV_INTERN -void -trx_sig_reply( -/*==========*/ - trx_sig_t* sig, /*!< in: signal */ - que_thr_t** next_thr) /*!< in/out: next query thread to run; - if the value which is passed in is - a pointer to a NULL pointer, then the - calling function can start running - a new query thread */ -{ - trx_t* receiver_trx; - - ut_ad(sig); - ut_ad(mutex_own(&kernel_mutex)); - - if (sig->receiver != NULL) { - ut_ad((sig->receiver)->state == QUE_THR_SIG_REPLY_WAIT); - - receiver_trx = thr_get_trx(sig->receiver); - - UT_LIST_REMOVE(reply_signals, receiver_trx->reply_signals, - sig); - ut_ad(receiver_trx->sess->state != SESS_ERROR); - - que_thr_end_wait(sig->receiver, next_thr); - - sig->receiver = NULL; - - } -} - -/****************************************************************//** -Removes a signal object from the trx signal queue. */ -UNIV_INTERN -void -trx_sig_remove( -/*===========*/ - trx_t* trx, /*!< in: trx handle */ - trx_sig_t* sig) /*!< in, own: signal */ -{ - ut_ad(trx && sig); - ut_ad(mutex_own(&kernel_mutex)); - - ut_ad(sig->receiver == NULL); - - UT_LIST_REMOVE(signals, trx->signals, sig); - sig->type = 0; /* reset the field to catch possible bugs */ - - if (sig != &(trx->sig)) { - mem_free(sig); - } -} - -/*********************************************************************//** -Creates a commit command node struct. -@return own: commit node struct */ -UNIV_INTERN -commit_node_t* -commit_node_create( -/*===============*/ - mem_heap_t* heap) /*!< in: mem heap where created */ -{ - commit_node_t* node; - - node = mem_heap_alloc(heap, sizeof(commit_node_t)); - 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; - que_thr_t* next_thr; - - node = 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) { - mutex_enter(&kernel_mutex); - - node->state = COMMIT_NODE_WAIT; - - next_thr = NULL; - - thr->state = QUE_THR_SIG_REPLY_WAIT; - - /* Send the commit signal to the transaction */ - - trx_sig_send(thr_get_trx(thr), TRX_SIG_COMMIT, TRX_SIG_SELF, - thr, NULL, &next_thr); - - mutex_exit(&kernel_mutex); - - return(next_thr); - } - - 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 -ulint -trx_commit_for_mysql( -/*=================*/ - trx_t* trx) /*!< in: trx handle */ -{ - /* 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); - - trx_start_if_not_started(trx); - - trx->op_info = "committing"; - - mutex_enter(&kernel_mutex); - - trx_commit_off_kernel(trx); - - mutex_exit(&kernel_mutex); - - trx->op_info = ""; - - return(DB_SUCCESS); -} - -/**********************************************************************//** -If required, flushes the log to disk if we called trx_commit_for_mysql() -with trx->flush_log_later == TRUE. -@return 0 or error number */ -UNIV_INTERN -ulint -trx_commit_complete_for_mysql( -/*==========================*/ - trx_t* trx) /*!< in: trx handle */ -{ - ib_uint64_t lsn = trx->commit_lsn; - ulint flush_log_at_trx_commit; - - ut_a(trx); - - trx->op_info = "flushing log"; - - 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); - } - - if (!trx->must_flush_log_later) { - /* Do nothing */ - } else if (flush_log_at_trx_commit == 0) { - /* Do nothing */ - } else if (flush_log_at_trx_commit == 1 && trx->active_commit_ordered) { - /* Do nothing - we already flushed the prepare and binlog write - to disk, so transaction is durable (will be recovered from - binlog if necessary) */ - } else if (flush_log_at_trx_commit == 1 || flush_log_at_trx_commit == 3) { - if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { - /* Write the log but do not flush it to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); - } else { - /* Write the log to the log files AND flush them to - disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); - } - } else if (flush_log_at_trx_commit == 2) { - - /* Write the log but do not flush it to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); - } else { - ut_error; - } - - trx->must_flush_log_later = FALSE; - - trx->op_info = ""; - - return(0); -} - -/**********************************************************************//** -Marks the latest SQL statement ended. */ -UNIV_INTERN -void -trx_mark_sql_stat_end( -/*==================*/ - trx_t* trx) /*!< in: trx handle */ -{ - ut_a(trx); - - if (trx->state == TRX_NOT_STARTED) { - trx->undo_no = 0; - } - - trx->last_sql_stat_start.least_undo_no = trx->undo_no; -} - -/**********************************************************************//** -Prints info about a transaction to the given file. The caller must own the -kernel mutex. */ -UNIV_INTERN -void -trx_print( -/*======*/ - FILE* f, /*!< in: output stream */ - 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; - - fprintf(f, "TRANSACTION " TRX_ID_FMT, (ullint) trx->id); - - switch (trx->state) { - case TRX_NOT_STARTED: - fputs(", not started", f); - break; - case TRX_ACTIVE: - fprintf(f, ", ACTIVE %lu sec", - (ulong)difftime(time(NULL), trx->start_time)); - break; - case TRX_PREPARED: - fprintf(f, ", ACTIVE (PREPARED) %lu sec", - (ulong)difftime(time(NULL), trx->start_time)); - break; - case TRX_COMMITTED_IN_MEMORY: - fputs(", COMMITTED IN MEMORY", f); - break; - default: - fprintf(f, " state %lu", (ulong) trx->state); - } - - if (*trx->op_info) { - putc(' ', f); - fputs(trx->op_info, f); - } - - if (trx->is_recovered) { - fputs(" recovered trx", f); - } - - if (trx->is_purge) { - fputs(" purge 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; - - switch (trx->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->que_state); - } - - if (0 < UT_LIST_GET_LEN(trx->trx_locks) - || mem_heap_get_size(trx->lock_heap) > 400) { - newline = TRUE; - - fprintf(f, "%lu lock struct(s), heap size %lu," - " %lu row lock(s)", - (ulong) UT_LIST_GET_LEN(trx->trx_locks), - (ulong) mem_heap_get_size(trx->lock_heap), - (ulong) lock_number_of_rows_locked(trx)); - } - - 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 %llu", - (ullint) trx->undo_no); - } - - if (newline) { - putc('\n', f); - } - - if (trx->mysql_thd != NULL) { - innobase_mysql_print_thd(f, trx->mysql_thd, max_query_len); - } -} - -/*******************************************************************//** -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) */ -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 */ -{ - ibool a_notrans_edit; - ibool b_notrans_edit; - - /* If mysql_thd is NULL for a transaction we assume that it has - not edited non-transactional tables. */ - - a_notrans_edit = a->mysql_thd != NULL - && thd_has_edited_nontrans_tables(a->mysql_thd); - - b_notrans_edit = b->mysql_thd != NULL - && thd_has_edited_nontrans_tables(b->mysql_thd); - - if (a_notrans_edit != b_notrans_edit) { - - return(a_notrans_edit); - } - - /* Either both had edited non-transactional tables or both had - not, 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->trx_locks), - b->undo_no, UT_LIST_GET_LEN(b->trx_locks)); -#endif - - return(TRX_WEIGHT(a) >= TRX_WEIGHT(b)); -} - -/****************************************************************//** -Prepares a transaction. */ -UNIV_INTERN -void -trx_prepare_off_kernel( -/*===================*/ - trx_t* trx) /*!< in: transaction */ -{ - trx_rseg_t* rseg; - ib_uint64_t lsn = 0; - mtr_t mtr; - - ut_ad(mutex_own(&kernel_mutex)); - - rseg = trx->rseg; - - if (trx->insert_undo != NULL || trx->update_undo != NULL) { - - mutex_exit(&kernel_mutex); - - 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)); - - if (trx->mysql_master_log_file_name[0] != '\0') { - /* This database server is a MySQL replication slave */ - trx_sysf_t* sys_header = trx_sysf_get(&mtr); - - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_relay_log_file_name, - trx->mysql_relay_log_pos, - TRX_SYS_MYSQL_RELAY_LOG_INFO, &mtr); - trx_sys_update_mysql_binlog_offset( - sys_header, - trx->mysql_master_log_file_name, - trx->mysql_master_log_pos, - TRX_SYS_MYSQL_MASTER_LOG_INFO, &mtr); - trx->mysql_master_log_file_name = ""; - } - - /*--------------*/ - mtr_commit(&mtr); /* This mtr commit makes the - transaction prepared in the file-based - world */ - /*--------------*/ - lsn = mtr.end_lsn; - - mutex_enter(&kernel_mutex); - } - - ut_ad(mutex_own(&kernel_mutex)); - - /*--------------------------------------*/ - if (UNIV_UNLIKELY(trx->state != TRX_ACTIVE)) { - - trx_reserve_descriptor(trx); - } - trx->state = TRX_PREPARED; - trx_n_prepared++; - /*--------------------------------------*/ - - if (lsn) { - ulint flush_log_at_trx_commit; - - /* 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. */ - - mutex_exit(&kernel_mutex); - - 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); - } - - if (flush_log_at_trx_commit == 0) { - /* Do nothing */ - } else if (flush_log_at_trx_commit == 1 || flush_log_at_trx_commit == 3) { - if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { - /* Write the log but do not flush it to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, - FALSE); - } else { - /* Write the log to the log files AND flush - them to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); - } - } else if (flush_log_at_trx_commit == 2) { - - /* Write the log but do not flush it to disk */ - - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); - } else { - ut_error; - } - - mutex_enter(&kernel_mutex); - } -} - -/**********************************************************************//** -Does the transaction prepare for MySQL. -@return 0 or error number */ -UNIV_INTERN -ulint -trx_prepare_for_mysql( -/*==================*/ - trx_t* trx) /*!< in: trx handle */ -{ - /* Because we do not do the prepare by sending an Innobase - sig to the transaction, we must here make sure that trx has been - started. */ - - ut_a(trx); - - trx->op_info = "preparing"; - - trx_start_if_not_started(trx); - - mutex_enter(&kernel_mutex); - - trx_prepare_off_kernel(trx); - - mutex_exit(&kernel_mutex); - - trx->op_info = ""; - - return(0); -} - -/**********************************************************************//** -This function is used to find number of prepared transactions and -their transaction objects for a recovery. -@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 */ -{ - 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(&kernel_mutex); - - trx = UT_LIST_GET_FIRST(trx_sys->trx_list); - - while (trx) { - if (trx->state == TRX_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", - (ullint) trx->id); - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Transaction contains changes" - " to %llu rows\n", - (ullint) trx->undo_no); - - count++; - - if (count == len) { - break; - } - } - - trx = UT_LIST_GET_NEXT(trx_list, trx); - } - - mutex_exit(&kernel_mutex); - - if (count > 0){ - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: %lu transactions in prepared state" - " after recovery\n", - (ulong) count); - } - - return ((int) count); -} - -/*******************************************************************//** -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 */ -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(&kernel_mutex); - - trx = UT_LIST_GET_FIRST(trx_sys->trx_list); - - while (trx) { - /* Compare two X/Open XA transaction id's: their - length should be the same and binary comparison - of gtrid_length+bqual_length bytes should be - the same */ - - if (trx->is_recovered - && trx->state == TRX_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; - } - - trx = UT_LIST_GET_NEXT(trx_list, trx); - } - - mutex_exit(&kernel_mutex); - - return(trx); -} |