diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-07-27 08:52:01 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-07-27 08:52:01 +0300 |
commit | cf1fc59856daa4c5730737541126d5492a091722 (patch) | |
tree | 5a845894074aedceed8d71a4381aaab39c700fd3 /storage/innobase | |
parent | 0bd9f755b798718dab8e354d39238d5e1457fd39 (diff) | |
download | mariadb-git-cf1fc59856daa4c5730737541126d5492a091722.tar.gz |
MDEV-25594: Improve debug checks
trx_t::will_lock: Changed the type to bool.
trx_t::is_autocommit_non_locking(): Replaces
trx_is_autocommit_non_locking().
trx_is_ac_nl_ro(): Remove (replaced with equivalent assertion expressions).
assert_trx_nonlocking_or_in_list(): Remove.
Replaced with at least as strict checks in each place.
check_trx_state(): Moved to a static function; partially replaced with
individual debug assertions implementing equivalent or stricter checks.
This is a backport of commit 7b51d11cca8898f319ddde5d7048cb81b43fef06
from 10.5.
Diffstat (limited to 'storage/innobase')
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 72 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 3 | ||||
-rw-r--r-- | storage/innobase/include/trx0i_s.h | 6 | ||||
-rw-r--r-- | storage/innobase/include/trx0sys.ic | 6 | ||||
-rw-r--r-- | storage/innobase/include/trx0trx.h | 124 | ||||
-rw-r--r-- | storage/innobase/include/trx0trx.ic | 19 | ||||
-rw-r--r-- | storage/innobase/lock/lock0lock.cc | 39 | ||||
-rw-r--r-- | storage/innobase/read/read0read.cc | 4 | ||||
-rw-r--r-- | storage/innobase/trx/trx0i_s.cc | 20 | ||||
-rw-r--r-- | storage/innobase/trx/trx0roll.cc | 39 | ||||
-rw-r--r-- | storage/innobase/trx/trx0sys.cc | 7 | ||||
-rw-r--r-- | storage/innobase/trx/trx0trx.cc | 84 |
12 files changed, 185 insertions, 238 deletions
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 81f1dae85b6..00c9595aa87 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4503,7 +4503,7 @@ innobase_commit_low( if (trx_is_started(trx)) { trx_commit_for_mysql(trx); } else { - trx->will_lock = 0; + trx->will_lock = false; #ifdef WITH_WSREP trx->wsrep = false; #endif /* WITH_WSREP */ @@ -4860,7 +4860,7 @@ innobase_rollback_trx( lock_unlock_table_autoinc(trx); if (!trx->has_logged()) { - trx->will_lock = 0; + trx->will_lock = false; #ifdef WITH_WSREP trx->wsrep = false; #endif @@ -8176,20 +8176,12 @@ ha_innobase::write_row( if (high_level_read_only) { ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); - } else if (UNIV_UNLIKELY(m_prebuilt->trx != trx)) { - ib::error() << "The transaction object for the table handle is" - " at " << static_cast<const void*>(m_prebuilt->trx) - << ", but for the current thread it is at " - << static_cast<const void*>(trx); - - fputs("InnoDB: Dump of 200 bytes around m_prebuilt: ", stderr); - ut_print_buf(stderr, ((const byte*) m_prebuilt) - 100, 200); - fputs("\nInnoDB: Dump of 200 bytes around ha_data: ", stderr); - ut_print_buf(stderr, ((const byte*) trx) - 100, 200); - putc('\n', stderr); - ut_error; - } else if (!trx_is_started(trx)) { - ++trx->will_lock; + } + + ut_a(m_prebuilt->trx == trx); + + if (!trx_is_started(trx)) { + trx->will_lock = true; } #ifdef WITH_WSREP @@ -8964,7 +8956,7 @@ ha_innobase::update_row( ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); } else if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } if (m_upd_buf == NULL) { @@ -9110,7 +9102,7 @@ ha_innobase::delete_row( ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); } else if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } if (!m_prebuilt->upd_node) { @@ -9990,7 +9982,7 @@ ha_innobase::ft_init() them as regular read only transactions for now. */ if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(rnd_init(false)); @@ -10056,7 +10048,7 @@ ha_innobase::ft_init_ext( them as regular read only transactions for now. */ if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } dict_table_t* ft_table = m_prebuilt->table; @@ -13081,7 +13073,7 @@ create_table_info_t::allocate_trx() { m_trx = innobase_trx_allocate(m_thd); - m_trx->will_lock++; + m_trx->will_lock = true; m_trx->ddl = true; } @@ -13372,13 +13364,7 @@ inline int ha_innobase::delete_table(const char* name, enum_sql_command sqlcom) ut_a(name_len < 1000); - /* Either the transaction is already flagged as a locking transaction - or it hasn't been started yet. */ - - ut_a(!trx_is_started(trx) || trx->will_lock > 0); - - /* We are doing a DDL operation. */ - ++trx->will_lock; + trx->will_lock = true; /* Drop the table in InnoDB */ @@ -13555,14 +13541,7 @@ innobase_drop_database( #endif /* _WIN32 */ trx_t* trx = innobase_trx_allocate(thd); - - /* Either the transaction is already flagged as a locking transaction - or it hasn't been started yet. */ - - ut_a(!trx_is_started(trx) || trx->will_lock > 0); - - /* We are doing a DDL operation. */ - ++trx->will_lock; + trx->will_lock = true; ulint dummy; @@ -13606,7 +13585,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from, DEBUG_SYNC_C("innodb_rename_table_ready"); trx_start_if_not_started(trx, true); - ut_ad(trx->will_lock > 0); + ut_ad(trx->will_lock); if (commit) { /* Serialize data dictionary operations with dictionary mutex: @@ -13651,7 +13630,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from, /* Transaction must be flagged as a locking transaction or it hasn't been started yet. */ - ut_a(trx->will_lock > 0); + ut_a(trx->will_lock); error = row_rename_table_for_mysql(norm_from, norm_to, trx, commit, commit); @@ -13737,7 +13716,7 @@ int ha_innobase::truncate() if (!srv_safe_truncate) { if (!trx_is_started(m_prebuilt->trx)) { - ++m_prebuilt->trx->will_lock; + m_prebuilt->trx->will_lock = true; } dberr_t err = row_truncate_table_for_mysql( @@ -13791,8 +13770,7 @@ int ha_innobase::truncate() heap, ib_table->name.m_name, ib_table->id); const char* name = mem_heap_strdup(heap, ib_table->name.m_name); trx_t* trx = innobase_trx_allocate(m_user_thd); - - ++trx->will_lock; + trx->will_lock = true; trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); row_mysql_lock_data_dictionary(trx); dict_stats_wait_bg_to_stop_using_table(ib_table, trx); @@ -13877,9 +13855,7 @@ ha_innobase::rename_table( } trx_t* trx = innobase_trx_allocate(thd); - - /* We are doing a DDL operation. */ - ++trx->will_lock; + trx->will_lock = true; trx_set_dict_operation(trx, TRX_DICT_OP_INDEX); dberr_t error = innobase_rename_table(trx, from, to, true); @@ -15880,7 +15856,7 @@ ha_innobase::start_stmt( innobase_register_trx(ht, thd, trx); if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(0); @@ -16103,7 +16079,7 @@ ha_innobase::external_lock( && (m_prebuilt->select_lock_type != LOCK_NONE || m_prebuilt->stored_select_lock_type != LOCK_NONE)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(0); @@ -16150,7 +16126,7 @@ ha_innobase::external_lock( && (m_prebuilt->select_lock_type != LOCK_NONE || m_prebuilt->stored_select_lock_type != LOCK_NONE)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(0); @@ -16829,7 +16805,7 @@ ha_innobase::store_lock( && (m_prebuilt->select_lock_type != LOCK_NONE || m_prebuilt->stored_select_lock_type != LOCK_NONE)) { - ++trx->will_lock; + trx->will_lock = true; } return(to); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 24a7c4a74ad..88e02ee4e32 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -967,7 +967,7 @@ ha_innobase::check_if_supported_inplace_alter( } } - m_prebuilt->trx->will_lock++; + m_prebuilt->trx->will_lock = true; if (!online) { /* We already determined that only a non-locking @@ -8954,7 +8954,6 @@ foreign_fail: m_prebuilt = ctx->prebuilt; } trx_start_if_not_started(user_trx, true); - user_trx->will_lock++; m_prebuilt->trx = user_trx; } DBUG_INJECT_CRASH("ib_commit_inplace_crash", diff --git a/storage/innobase/include/trx0i_s.h b/storage/innobase/include/trx0i_s.h index 7e766072272..ca65e053502 100644 --- a/storage/innobase/include/trx0i_s.h +++ b/storage/innobase/include/trx0i_s.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -164,8 +164,8 @@ struct i_s_trx_row_t { /*!< detailed_error in trx_t */ ulint trx_is_read_only; /*!< trx_t::read_only */ - ulint trx_is_autocommit_non_locking; - /*!< trx_is_autocommit_non_locking(trx) + bool trx_is_autocommit_non_locking; + /*!< trx:t::is_autocommit_non_locking() */ }; diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index c85695630f0..8518934e94f 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2019, MariaDB Corporation. +Copyright (c) 2018, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -251,7 +251,9 @@ trx_rw_min_trx_id_low(void) if (trx == NULL) { id = trx_sys->max_trx_id; } else { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); id = trx->id; } diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index a7591fa1b19..90365400d72 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -502,99 +502,6 @@ from innodb_lock_wait_timeout via trx_t::mysql_thd. ? thd_lock_wait_timeout((t)->mysql_thd) \ : 0) -/** -Determine if the transaction is a non-locking autocommit select -(implied read-only). -@param t transaction -@return true if non-locking autocommit select transaction. */ -#define trx_is_autocommit_non_locking(t) \ -((t)->auto_commit && (t)->will_lock == 0) - -/** -Determine if the transaction is a non-locking autocommit select -with an explicit check for the read-only status. -@param t transaction -@return true if non-locking autocommit read-only transaction. */ -#define trx_is_ac_nl_ro(t) \ -((t)->read_only && trx_is_autocommit_non_locking((t))) - -/** -Assert that the transaction is in the trx_sys_t::rw_trx_list */ -#define assert_trx_in_rw_list(t) do { \ - ut_ad(!(t)->read_only); \ - ut_ad((t)->in_rw_trx_list \ - == !((t)->read_only || !(t)->rsegs.m_redo.rseg)); \ - check_trx_state(t); \ -} while (0) - -/** -Check transaction state */ -#define check_trx_state(t) do { \ - ut_ad(!trx_is_autocommit_non_locking((t))); \ - switch ((t)->state) { \ - case TRX_STATE_PREPARED: \ - case TRX_STATE_PREPARED_RECOVERED: \ - case TRX_STATE_ACTIVE: \ - case TRX_STATE_COMMITTED_IN_MEMORY: \ - continue; \ - case TRX_STATE_NOT_STARTED: \ - break; \ - } \ - ut_error; \ -} while (0) - -/** Check if transaction is free so that it can be re-initialized. -@param t transaction handle */ -#define assert_trx_is_free(t) do { \ - ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \ - ut_ad(!(t)->id); \ - ut_ad(!(t)->has_logged()); \ - ut_ad(!(t)->is_referenced()); \ - ut_ad(!(t)->is_wsrep()); \ - ut_ad(!MVCC::is_view_active((t)->read_view)); \ - ut_ad((t)->lock.wait_thr == NULL); \ - ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \ - ut_ad((t)->lock.table_locks.empty()); \ - ut_ad(!(t)->autoinc_locks \ - || ib_vector_is_empty((t)->autoinc_locks)); \ - ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \ -} while(0) - -/** Check if transaction is in-active so that it can be freed and put back to -transaction pool. -@param t transaction handle */ -#define assert_trx_is_inactive(t) do { \ - assert_trx_is_free((t)); \ - ut_ad((t)->dict_operation_lock_mode == 0); \ -} while(0) - -#ifdef UNIV_DEBUG -/*******************************************************************//** -Assert that an autocommit non-locking select cannot be in the -rw_trx_list and that it is a read-only transaction. -The tranasction must be in the mysql_trx_list. */ -# define assert_trx_nonlocking_or_in_list(t) \ - do { \ - if (trx_is_autocommit_non_locking(t)) { \ - trx_state_t t_state = (t)->state; \ - ut_ad((t)->read_only); \ - ut_ad(!(t)->is_recovered); \ - ut_ad(!(t)->in_rw_trx_list); \ - ut_ad((t)->in_mysql_trx_list); \ - ut_ad(t_state == TRX_STATE_NOT_STARTED \ - || t_state == TRX_STATE_ACTIVE); \ - } else { \ - check_trx_state(t); \ - } \ - } while (0) -#else /* UNIV_DEBUG */ -/*******************************************************************//** -Assert that an autocommit non-locking slect cannot be in the -rw_trx_list and that it is a read-only transaction. -The tranasction must be in the mysql_trx_list. */ -# define assert_trx_nonlocking_or_in_list(trx) ((void)0) -#endif /* UNIV_DEBUG */ - typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_list; /*******************************************************************//** @@ -1070,16 +977,15 @@ public: /*------------------------------*/ bool read_only; /*!< true if transaction is flagged as a READ-ONLY transaction. - if auto_commit && will_lock == 0 + if auto_commit && !will_lock then it will be handled as a AC-NL-RO-SELECT (Auto Commit Non-Locking Read Only Select). A read only transaction will not be assigned an UNDO log. */ bool auto_commit; /*!< true if it is an autocommit */ - ib_uint32_t will_lock; /*!< Will acquire some locks. Increment - each time we determine that a lock will - be acquired by the MySQL layer. */ + bool will_lock; /*!< set to inform trx_start_low() that + the transaction may acquire locks */ /*------------------------------*/ fts_trx_t* fts_trx; /*!< FTS information, or NULL if transaction hasn't modified tables @@ -1199,10 +1105,28 @@ public: inline void free(); + void assert_freed() const + { + ut_ad(state == TRX_STATE_NOT_STARTED); + ut_ad(!id); + ut_ad(!has_logged()); + ut_ad(!const_cast<trx_t*>(this)->is_referenced()); + ut_ad(!is_wsrep()); + ut_ad(!trx_get_read_view(this)); + ut_ad(!lock.wait_thr); + ut_ad(UT_LIST_GET_LEN(lock.trx_locks) == 0); + ut_ad(lock.table_locks.empty()); + ut_ad(!autoinc_locks || ib_vector_is_empty(autoinc_locks)); + ut_ad(dict_operation == TRX_DICT_OP_NONE); + } + + /** @return whether this is a non-locking autocommit transaction */ + bool is_autocommit_non_locking() const { return auto_commit && !will_lock; } + private: - /** Assign a rollback segment for modifying temporary tables. - @return the assigned rollback segment */ - trx_rseg_t* assign_temp_rseg(); + /** Assign a rollback segment for modifying temporary tables. + @return the assigned rollback segment */ + trx_rseg_t *assign_temp_rseg(); }; /** diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic index 4a5b1ba717f..2a53509b206 100644 --- a/storage/innobase/include/trx0trx.ic +++ b/storage/innobase/include/trx0trx.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2019, MariaDB Corporation. +Copyright (c) 2016, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -50,17 +50,18 @@ trx_state_eq( switch (trx->state) { case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED: - ut_ad(!trx_is_autocommit_non_locking(trx)); + case TRX_STATE_COMMITTED_IN_MEMORY: + ut_ad(!trx->is_autocommit_non_locking()); return(trx->state == state); case TRX_STATE_ACTIVE: - - assert_trx_nonlocking_or_in_list(trx); - return(state == trx->state); - - case TRX_STATE_COMMITTED_IN_MEMORY: - - check_trx_state(trx); + if (trx->is_autocommit_non_locking()) { + ut_ad(!trx->is_recovered); + ut_ad(trx->read_only); + ut_ad(trx->mysql_thd); + ut_ad(!trx->in_rw_trx_list); + ut_ad(trx->in_mysql_trx_list); + } return(state == trx->state); case TRX_STATE_NOT_STARTED: diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 506106a2269..4daf4fb07b8 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -1376,6 +1376,19 @@ wsrep_print_wait_locks( } #endif /* WITH_WSREP */ +#ifdef UNIV_DEBUG +/** Check transaction state */ +static void check_trx_state(const trx_t *trx) +{ + ut_ad(!trx->auto_commit || trx->will_lock); + const trx_state_t state= trx->state; + ut_ad(state == TRX_STATE_ACTIVE || + state == TRX_STATE_PREPARED_RECOVERED || + state == TRX_STATE_PREPARED || + state == TRX_STATE_COMMITTED_IN_MEMORY); +} +#endif + /** Create a new record lock and inserts it to the lock queue, without checking for deadlocks or conflicts. @param[in] type_mode lock mode and wait flag; type will be replaced @@ -3589,8 +3602,8 @@ lock_table_create( ut_ad(table && trx); ut_ad(lock_mutex_own()); ut_ad(trx_mutex_own(trx)); - - check_trx_state(trx); + ut_ad(trx->is_recovered || trx->state == TRX_STATE_ACTIVE); + ut_ad(!trx->auto_commit || trx->will_lock); if ((type_mode & LOCK_MODE_MASK) == LOCK_AUTO_INC) { ++table->n_waiting_or_granted_auto_inc_locks; @@ -4545,7 +4558,10 @@ lock_remove_recovered_trx_record_locks( trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); + ut_ad(trx->state != TRX_STATE_NOT_STARTED); if (!trx->is_recovered) { continue; @@ -5181,7 +5197,8 @@ lock_rec_queue_validate( ut_ad(!index || lock->index == index); trx_mutex_enter(lock->trx); - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->read_only + || !lock->trx->is_autocommit_non_locking()); ut_ad(trx_state_eq(lock->trx, TRX_STATE_COMMITTED_IN_MEMORY) || !lock_get_wait(lock) @@ -5260,8 +5277,7 @@ lock_rec_queue_validate( for (lock = lock_rec_get_first(lock_sys->rec_hash, block, heap_no); lock != NULL; lock = lock_rec_get_next_const(heap_no, lock)) { - - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->is_autocommit_non_locking()); if (index) { ut_a(lock->index == index); @@ -5357,7 +5373,8 @@ loop: } } - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->read_only + || !lock->trx->is_autocommit_non_locking()); /* Only validate the record queues when this thread is not holding a space->latch. */ @@ -5465,7 +5482,7 @@ lock_rec_validate( ib_uint64_t current; - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->is_autocommit_non_locking()); ut_ad(lock_get_type(lock) == LOCK_REC); current = ut_ull_create( @@ -7075,7 +7092,8 @@ DeadlockChecker::search() ut_ad(m_start != NULL); ut_ad(m_wait_lock != NULL); - check_trx_state(m_wait_lock->trx); + ut_ad(!m_wait_lock->trx->auto_commit || m_wait_lock->trx->will_lock); + ut_d(check_trx_state(m_wait_lock->trx)); ut_ad(m_mark_start <= s_lock_mark_counter); /* Look at the locks ahead of wait_lock in the lock queue. */ @@ -7235,7 +7253,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx) { ut_ad(lock_mutex_own()); ut_ad(trx_mutex_own(trx)); - check_trx_state(trx); + ut_ad(trx->state == TRX_STATE_ACTIVE); + ut_ad(!trx->auto_commit || trx->will_lock); ut_ad(!srv_read_only_mode); if (!innobase_deadlock_detect) { diff --git a/storage/innobase/read/read0read.cc b/storage/innobase/read/read0read.cc index 3fd52d5d6dd..a115672a8a6 100644 --- a/storage/innobase/read/read0read.cc +++ b/storage/innobase/read/read0read.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2019, MariaDB Corporation. +Copyright (c) 2019, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -574,7 +574,7 @@ MVCC::view_open(ReadView*& view, trx_t* trx) Therefore we must set the low limit id after we reset the closed status after the check. */ - if (trx_is_autocommit_non_locking(trx) && view->empty()) { + if (trx->is_autocommit_non_locking() && view->empty()) { view->m_closed = false; diff --git a/storage/innobase/trx/trx0i_s.cc b/storage/innobase/trx/trx0i_s.cc index 2b9d6c96acd..05579aeb223 100644 --- a/storage/innobase/trx/trx0i_s.cc +++ b/storage/innobase/trx/trx0i_s.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -577,7 +577,7 @@ thd_done: row->trx_is_read_only = trx->read_only; - row->trx_is_autocommit_non_locking = trx_is_autocommit_non_locking(trx); + row->trx_is_autocommit_non_locking = trx->is_autocommit_non_locking(); return(TRUE); } @@ -1259,7 +1259,21 @@ fetch_data_into_cache_low( continue; } - assert_trx_nonlocking_or_in_list(trx); + +#ifdef UNIV_DEBUG + if (trx->is_autocommit_non_locking()) { + ut_ad(trx->read_only); + ut_ad(!trx->is_recovered); + ut_ad(trx->mysql_thd); + ut_ad(trx->in_mysql_trx_list); + const trx_state_t state = trx->state; + ut_ad(state == TRX_STATE_NOT_STARTED + || state == TRX_STATE_ACTIVE); + } + else { + ut_ad(trx->state != TRX_STATE_NOT_STARTED); + } +#endif /* UNIV_DEBUG */ ut_ad(trx->in_rw_trx_list == rw_trx_list); diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index c986a866fe2..ec2d3c1d03f 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2020, MariaDB Corporation. +Copyright (c) 2016, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -81,12 +81,19 @@ trx_rollback_to_savepoint_low( heap = mem_heap_create(512); roll_node = roll_node_create(heap); + ut_ad(!trx->in_rollback); if (savept != NULL) { roll_node->savept = savept; - check_trx_state(trx); + ut_ad(trx->mysql_thd); + ut_ad(trx->in_mysql_trx_list); + ut_ad(!trx->is_recovered); + ut_ad(trx->state == TRX_STATE_ACTIVE); } else { - assert_trx_nonlocking_or_in_list(trx); + ut_d(trx_state_t state = trx->state); + ut_ad(state == TRX_STATE_ACTIVE + || state == TRX_STATE_PREPARED + || state == TRX_STATE_PREPARED_RECOVERED); } trx->error_state = DB_SUCCESS; @@ -187,7 +194,8 @@ dberr_t trx_rollback_for_mysql(trx_t* trx) switch (trx->state) { case TRX_STATE_NOT_STARTED: - trx->will_lock = 0; + trx->will_lock = false; + ut_ad(trx->mysql_thd); ut_ad(trx->in_mysql_trx_list); #ifdef WITH_WSREP trx->wsrep = false; @@ -196,12 +204,14 @@ dberr_t trx_rollback_for_mysql(trx_t* trx) case TRX_STATE_ACTIVE: ut_ad(trx->in_mysql_trx_list); - assert_trx_nonlocking_or_in_list(trx); + ut_ad(trx->mysql_thd); + ut_ad(!trx->is_recovered); + ut_ad(!trx->is_autocommit_non_locking() || trx->read_only); return(trx_rollback_for_mysql_low(trx)); case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED: - ut_ad(!trx_is_autocommit_non_locking(trx)); + ut_ad(!trx->is_autocommit_non_locking()); if (trx->has_logged_persistent()) { /* The XA ROLLBACK of a XA PREPARE transaction will consist of multiple mini-transactions. @@ -245,7 +255,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx) return(trx_rollback_for_mysql_low(trx)); case TRX_STATE_COMMITTED_IN_MEMORY: - check_trx_state(trx); + ut_ad(!trx->is_autocommit_non_locking()); break; } @@ -274,7 +284,9 @@ trx_rollback_last_sql_stat_for_mysql( return(DB_SUCCESS); case TRX_STATE_ACTIVE: - assert_trx_nonlocking_or_in_list(trx); + ut_ad(trx->mysql_thd); + ut_ad(!trx->is_recovered); + ut_ad(!trx->is_autocommit_non_locking() || trx->read_only); trx->op_info = "rollback of SQL statement"; @@ -768,7 +780,11 @@ trx_roll_must_shutdown() t != NULL; t = UT_LIST_GET_NEXT(trx_list, t)) { - assert_trx_in_rw_list(t); + ut_ad(!t->read_only); + ut_ad(t->in_rw_trx_list); + ut_ad(!t->is_autocommit_non_locking()); + ut_ad(t->state != TRX_STATE_NOT_STARTED); + if (t->is_recovered && trx_state_eq(t, TRX_STATE_ACTIVE)) { n_trx++; @@ -831,7 +847,10 @@ trx_rollback_or_clean_recovered( trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); + ut_ad(trx->state != TRX_STATE_NOT_STARTED); /* If this function does a cleanup or rollback then it will release the trx_sys->mutex, therefore diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 9138e9475bf..c8c68c37333 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -513,7 +513,9 @@ trx_sys_init_at_db_start() trx = UT_LIST_GET_NEXT(trx_list, trx)) { ut_ad(trx->is_recovered); - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); if (trx_state_eq(trx, TRX_STATE_ACTIVE)) { rows_to_undo += trx->undo_no; @@ -1026,7 +1028,8 @@ trx_sys_validate_trx_list_low( trx != NULL; prev_trx = trx, trx = UT_LIST_GET_NEXT(trx_list, prev_trx)) { - check_trx_state(trx); + ut_ad(!trx->is_autocommit_non_locking()); + ut_ad(trx->state != TRX_STATE_NOT_STARTED); ut_a(prev_trx == NULL || prev_trx->id > trx->id); } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index d1b35bd84a3..3e558a7181d 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -133,7 +133,7 @@ trx_init( trx->auto_commit = false; - trx->will_lock = 0; + trx->will_lock = false; trx->ddl = false; @@ -336,13 +336,13 @@ trx_t *trx_allocate_for_background() MEM_MAKE_DEFINED(trx, sizeof *trx); #endif - assert_trx_is_free(trx); + trx->assert_freed(); mem_heap_t* heap; ib_alloc_t* alloc; /* We just got trx from pool, it should be non locking */ - ut_ad(trx->will_lock == 0); + ut_ad(!trx->will_lock); ut_ad(trx->state == TRX_STATE_NOT_STARTED); DBUG_LOG("trx", "Create: " << trx); @@ -369,7 +369,8 @@ trx_t *trx_allocate_for_background() /** Free the memory to trx_pools */ inline void trx_t::free() { - assert_trx_is_inactive(this); + assert_freed(); + ut_ad(!dict_operation_lock_mode); MEM_CHECK_DEFINED(this, sizeof *this); @@ -539,7 +540,8 @@ trx_validate_state_before_free(trx_t* trx) } trx->dict_operation = TRX_DICT_OP_NONE; - assert_trx_is_inactive(trx); + trx->assert_freed(); + ut_ad(!trx->dict_operation_lock_mode); } /** Free and initialize a transaction object instantinated during recovery. @@ -636,7 +638,9 @@ trx_free_prepared( trx->release_locks(); trx_undo_free_prepared(trx); - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); ut_a(!trx->read_only); @@ -685,7 +689,7 @@ trx_disconnect_from_mysql( trx->is_recovered = true; trx->mysql_thd = NULL; /* todo/fixme: suggest to do it at innodb prepare */ - trx->will_lock = 0; + trx->will_lock = false; } trx_sys_mutex_exit(); @@ -1177,11 +1181,10 @@ void trx_t::remove_flush_observer() /** Assign a rollback segment for modifying temporary tables. @return the assigned rollback segment */ -trx_rseg_t* -trx_t::assign_temp_rseg() +trx_rseg_t *trx_t::assign_temp_rseg() { ut_ad(!rsegs.m_noredo.rseg); - ut_ad(!trx_is_autocommit_non_locking(this)); + ut_ad(!is_autocommit_non_locking()); compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS)); /* Choose a temporary rollback segment between 0 and 127 @@ -1235,8 +1238,8 @@ trx_start_low( && thd_trx_is_read_only(trx->mysql_thd)); if (!trx->auto_commit) { - ++trx->will_lock; - } else if (trx->will_lock == 0) { + trx->will_lock = true; + } else if (!trx->will_lock) { trx->read_only = true; } @@ -1305,7 +1308,7 @@ trx_start_low( trx_sys_mutex_exit(); } else { - if (!trx_is_autocommit_non_locking(trx)) { + if (!trx->is_autocommit_non_locking()) { /* If this is a read-only transaction that is writing to a temporary table then it needs a transaction id @@ -1691,12 +1694,16 @@ trx_commit_in_memory( { trx->must_flush_log_later = false; - if (trx_is_autocommit_non_locking(trx)) { + if (trx->is_autocommit_non_locking()) { ut_ad(trx->id == 0); ut_ad(trx->read_only); + ut_ad(!trx->will_lock); ut_a(!trx->is_recovered); ut_ad(trx->rsegs.m_redo.rseg == NULL); ut_ad(!trx->in_rw_trx_list); + ut_ad(trx->in_mysql_trx_list); + ut_ad(trx->mysql_thd); + ut_ad(trx->state == TRX_STATE_ACTIVE); /* Note: We are asserting without holding the lock mutex. But that is OK because this transaction is not waiting and cannot @@ -1712,8 +1719,6 @@ trx_commit_in_memory( 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)); - if (trx->read_view != NULL) { trx_sys->mvcc->view_close(trx->read_view, false); } @@ -1866,7 +1871,7 @@ trx_commit_in_memory( /* 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. */ - assert_trx_is_free(trx); + trx->assert_freed(); trx_init(trx); @@ -1885,30 +1890,20 @@ trx_commit_low( mtr_t* mtr) /*!< in/out: mini-transaction (will be committed), or NULL if trx made no modifications */ { - assert_trx_nonlocking_or_in_list(trx); - ut_ad(!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)); ut_ad(!mtr || mtr->is_active()); ut_ad(!mtr == !trx->has_logged()); /* undo_no is non-zero if we're doing the final commit. */ if (trx->fts_trx != NULL && trx->undo_no != 0) { - dberr_t error; - - ut_a(!trx_is_autocommit_non_locking(trx)); - - error = fts_commit(trx); + ut_a(!trx->is_autocommit_non_locking()); /* 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 (dberr_t error = fts_commit(trx)) { + ut_a(error == DB_DUPLICATE_KEY); } } @@ -2279,7 +2274,6 @@ trx_print_low( /*!< in: mem_heap_get_size(trx->lock.lock_heap) */ { ibool newline; - const char* op_info; ut_ad(trx_sys_mutex_own()); @@ -2308,9 +2302,7 @@ trx_print_low( fprintf(f, ", state %lu", (ulong) trx->state); ut_ad(0); state_ok: - - /* prevent a race condition */ - op_info = trx->op_info; + const char* op_info = trx->op_info; if (*op_info) { putc(' ', f); @@ -2549,7 +2541,7 @@ trx_assert_started( /* Non-locking autocommits should not hold any locks and this function is only called from the locking code. */ - check_trx_state(trx); + ut_ad(!trx->is_autocommit_non_locking()); /* 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 @@ -2754,7 +2746,9 @@ trx_recover_for_mysql( trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); /* The state of a read-write transaction cannot change from or to NOT_STARTED while we are holding the @@ -2821,7 +2815,9 @@ trx_t* trx_get_trx_by_xid_low(const XID* xid) trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { trx_mutex_enter(trx); - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); /* Compare two X/Open XA transaction id's: their length should be the same and binary comparison @@ -2948,7 +2944,7 @@ trx_start_internal_low( /* Ensure it is not flagged as an auto-commit-non-locking transaction. */ - trx->will_lock = 1; + trx->will_lock = true; trx->internal = true; @@ -2964,7 +2960,7 @@ trx_start_internal_read_only_low( /* Ensure it is not flagged as an auto-commit-non-locking transaction. */ - trx->will_lock = 1; + trx->will_lock = true; trx->internal = true; @@ -2985,13 +2981,7 @@ trx_start_for_ddl_low( 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; - trx_start_internal_low(trx); return; @@ -3002,7 +2992,7 @@ trx_start_for_ddl_low( trx->ddl = true; ut_ad(trx->dict_operation != TRX_DICT_OP_NONE); - ut_ad(trx->will_lock > 0); + ut_ad(trx->will_lock); return; case TRX_STATE_PREPARED: @@ -3028,7 +3018,7 @@ trx_set_rw_mode( { ut_ad(trx->rsegs.m_redo.rseg == 0); ut_ad(!trx->in_rw_trx_list); - ut_ad(!trx_is_autocommit_non_locking(trx)); + ut_ad(!trx->is_autocommit_non_locking()); ut_ad(!trx->read_only); if (high_level_read_only) { |