summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2018-03-11 23:34:23 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2018-03-16 15:50:04 +0200
commitbd7ed1b923e8ddd896103d73461c4313a175cca6 (patch)
treef7309f0a96715fe8582949820360bfc0dbb21b56
parente15e879fae949a05de549a6676ae66d4f7f8c566 (diff)
downloadmariadb-git-bd7ed1b923e8ddd896103d73461c4313a175cca6.tar.gz
MDEV-13935 INSERT stuck at state Unlocking tables
Revert the dead code for MySQL 5.7 multi-master replication (GCS), also known as WL#6835: InnoDB: GCS Replication: Deterministic Deadlock Handling (High Prio Transactions in InnoDB). Also, make innodb_lock_schedule_algorithm=vats skip SPATIAL INDEX, because the code does not seem to be compatible with them. Add FIXME comments to some SPATIAL INDEX locking code. It looks like Galera write-set replication might not work with SPATIAL INDEX.
-rw-r--r--extra/mariabackup/xtrabackup.cc1
-rw-r--r--storage/innobase/gis/gis0rtree.cc2
-rw-r--r--storage/innobase/gis/gis0sea.cc1
-rw-r--r--storage/innobase/handler/ha_innodb.cc331
-rw-r--r--storage/innobase/handler/handler0alter.cc3
-rw-r--r--storage/innobase/include/ha_prototypes.h23
-rw-r--r--storage/innobase/include/hash0hash.h3
-rw-r--r--storage/innobase/include/lock0lock.h79
-rw-r--r--storage/innobase/include/lock0lock.ic46
-rw-r--r--storage/innobase/include/lock0priv.h401
-rw-r--r--storage/innobase/include/lock0priv.ic2
-rw-r--r--storage/innobase/include/trx0trx.h330
-rw-r--r--storage/innobase/include/trx0trx.ic69
-rw-r--r--storage/innobase/include/trx0types.h22
-rw-r--r--storage/innobase/include/trx0undo.h5
-rw-r--r--storage/innobase/lock/lock0lock.cc1065
-rw-r--r--storage/innobase/lock/lock0prdt.cc62
-rw-r--r--storage/innobase/lock/lock0wait.cc6
-rw-r--r--storage/innobase/page/page0page.cc3
-rw-r--r--storage/innobase/page/page0zip.cc1
-rw-r--r--storage/innobase/row/row0ins.cc2
-rw-r--r--storage/innobase/row/row0merge.cc4
-rw-r--r--storage/innobase/row/row0mysql.cc3
-rw-r--r--storage/innobase/row/row0sel.cc18
-rw-r--r--storage/innobase/row/row0trunc.cc5
-rw-r--r--storage/innobase/trx/trx0roll.cc33
-rw-r--r--storage/innobase/trx/trx0trx.cc284
27 files changed, 521 insertions, 2283 deletions
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index e560ce1e042..c5bdec35f90 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -69,6 +69,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <row0mysql.h>
#include <row0quiesce.h>
#include <srv0start.h>
+#include "trx0sys.h"
#include <buf0dblwr.h>
#include <list>
diff --git a/storage/innobase/gis/gis0rtree.cc b/storage/innobase/gis/gis0rtree.cc
index b8220d73ec0..d45e40c8151 100644
--- a/storage/innobase/gis/gis0rtree.cc
+++ b/storage/innobase/gis/gis0rtree.cc
@@ -34,7 +34,7 @@ Created 2013/03/27 Allen Lai and Jimmy Yang
#include "rem0cmp.h"
#include "lock0lock.h"
#include "ibuf0ibuf.h"
-#include "trx0trx.h"
+#include "trx0undo.h"
#include "srv0mon.h"
#include "gis0geo.h"
diff --git a/storage/innobase/gis/gis0sea.cc b/storage/innobase/gis/gis0sea.cc
index dcf8cc6f781..173fc76ddfc 100644
--- a/storage/innobase/gis/gis0sea.cc
+++ b/storage/innobase/gis/gis0sea.cc
@@ -37,6 +37,7 @@ Created 2014/01/16 Jimmy Yang
#include "ibuf0ibuf.h"
#include "trx0trx.h"
#include "srv0mon.h"
+#include "que0que.h"
#include "gis0geo.h"
/** Restore the stored position of a persistent cursor bufferfixing the page */
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 2a860ff8e7c..b8e72f398f1 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1721,41 +1721,6 @@ innobase_reset_background_thd(MYSQL_THD thd)
}
-#if 0
-/**
-Check if the transaction can be rolled back
-@param[in] requestor Session requesting the lock
-@param[in] holder Session that holds the lock
-@return the session that will be rolled back, null don't care */
-
-THD*
-thd_trx_arbitrate(THD* requestor, THD* holder)
-{
- /* Non-user (thd==0) transactions by default can't rollback, in
- practice DDL transactions should never rollback and that's because
- they should never wait on table/record locks either */
-
- ut_a(holder != NULL);
- ut_a(holder != requestor);
-
- THD* victim = thd_tx_arbitrate(requestor, holder);
-
- ut_a(victim == NULL || victim == requestor || victim == holder);
-
- return(victim);
-}
-
-/**
-@param[in] thd Session to check
-@return the priority */
-
-int
-thd_trx_priority(THD* thd)
-{
- return(thd == NULL ? 0 : thd_tx_priority(thd));
-}
-#endif
-
/******************************************************************//**
Check if the transaction is an auto-commit transaction. TRUE also
implies that it is a SELECT (read-only) transaction.
@@ -2057,7 +2022,6 @@ convert_error_code_to_mysql(
case DB_RECORD_NOT_FOUND:
return(HA_ERR_NO_ACTIVE_RECORD);
- case DB_FORCED_ABORT:
case DB_DEADLOCK:
/* Since we rolled back the whole transaction, we must
tell it also to MySQL so that MySQL knows to empty the
@@ -2879,10 +2843,6 @@ check_trx_exists(
return trx;
} else {
trx = innobase_trx_allocate(thd);
- /* User trx can be forced to rollback,
- so we unset the disable flag. */
- ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE);
- trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
thd_set_ha_data(thd, innodb_hton_ptr, trx);
return trx;
}
@@ -3094,11 +3054,8 @@ ha_innobase::update_thd(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
- ut_ad(trx_in_innodb.is_aborted()
- || (trx->dict_operation_lock_mode == 0
- && trx->dict_operation == TRX_DICT_OP_NONE));
+ ut_ad(trx->dict_operation_lock_mode == 0);
+ ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
if (m_prebuilt->trx != trx) {
@@ -3564,8 +3521,6 @@ ha_innobase::init_table_handle_for_HANDLER(void)
trx_start_if_not_started_xa(m_prebuilt->trx, false);
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
/* Assign a read view if the transaction does not have it yet */
trx_assign_read_view(m_prebuilt->trx);
@@ -4549,8 +4504,6 @@ innobase_start_trx_and_assign_read_view(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
innobase_srv_conc_force_exit_innodb(trx);
/* The transaction should not be active yet, start it */
@@ -4687,7 +4640,6 @@ innobase_commit_ordered(
DBUG_ASSERT(hton == innodb_hton_ptr);
trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
/* We cannot throw error here; instead we will catch this error
@@ -4731,16 +4683,6 @@ innobase_commit(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(hton, thd, commit_trx);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, thd));
- }
-
ut_ad(trx->dict_operation_lock_mode == 0);
ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
@@ -4832,11 +4774,8 @@ innobase_rollback(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
- ut_ad(trx_in_innodb.is_aborted()
- || (trx->dict_operation_lock_mode == 0
- && trx->dict_operation == TRX_DICT_OP_NONE));
+ ut_ad(trx->dict_operation_lock_mode == 0);
+ ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
innobase_srv_conc_force_exit_innodb(trx);
@@ -4847,10 +4786,7 @@ innobase_rollback(
/* If we had reserved the auto-inc lock for some table (if
we come here to roll back the latest SQL statement) we
release it now before a possibly lengthy rollback */
-
- if (!trx_in_innodb.is_aborted()) {
- lock_unlock_table_autoinc(trx);
- }
+ lock_unlock_table_autoinc(trx);
/* This is a statement level variable. */
@@ -4863,18 +4799,6 @@ innobase_rollback(
error = trx_rollback_for_mysql(trx);
- if (trx->state == TRX_STATE_FORCED_ROLLBACK) {
-#ifndef DBUG_OFF
- char buffer[1024];
-
- DBUG_LOG("trx", "Forced rollback: "
- << thd_get_error_context_description(
- thd, buffer, sizeof buffer, 512));
-#endif /* !DBUG_OFF */
-
- trx->state = TRX_STATE_NOT_STARTED;
- }
-
trx_deregister_from_2pc(trx);
} else {
@@ -4901,9 +4825,7 @@ innobase_rollback_trx(
/* If we had reserved the auto-inc lock for some table (if
we come here to roll back the latest SQL statement) we
release it now before a possibly lengthy rollback */
- if (!TrxInInnoDB::is_aborted(trx)) {
- lock_unlock_table_autoinc(trx);
- }
+ lock_unlock_table_autoinc(trx);
if (!trx->has_logged()) {
trx->will_lock = 0;
@@ -5075,8 +4997,6 @@ innobase_rollback_to_savepoint(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
innobase_srv_conc_force_exit_innodb(trx);
/* TODO: use provided savepoint data area to store savepoint data */
@@ -5117,8 +5037,6 @@ innobase_rollback_to_savepoint_can_release_mdl(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
/* If transaction has not acquired any locks then it is safe
to release MDL after rollback to savepoint */
if (UT_LIST_GET_LEN(trx->lock.trx_locks) == 0) {
@@ -5152,8 +5070,6 @@ innobase_release_savepoint(
trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
/* TODO: use provided savepoint data area to store savepoint data */
longlong2str((ulint) savepoint, name, 36);
@@ -5187,8 +5103,6 @@ innobase_savepoint(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
innobase_srv_conc_force_exit_innodb(trx);
/* Cannot happen outside of transaction */
@@ -5224,7 +5138,6 @@ innobase_close_connection(
DBUG_ASSERT(hton == innodb_hton_ptr);
trx_t* trx = thd_to_trx(thd);
- bool free_trx = false;
/* During server initialization MySQL layer will try to open
some of the master-slave tables those residing in InnoDB.
@@ -5241,16 +5154,6 @@ innobase_close_connection(
if (trx) {
- TrxInInnoDB trx_in_innodb(trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- while (trx_is_started(trx)) {
-
- os_thread_sleep(20);
- }
- }
-
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
sql_print_error("Transaction not registered for MariaDB 2PC, "
@@ -5268,9 +5171,8 @@ innobase_close_connection(
if (trx->has_logged_persistent()) {
trx_disconnect_prepared(trx);
} else {
- trx_rollback_for_mysql(trx);
trx_deregister_from_2pc(trx);
- free_trx = true;
+ goto rollback_and_free;
}
} else {
sql_print_warning(
@@ -5278,24 +5180,15 @@ innobase_close_connection(
"InnoDB transaction. " TRX_ID_FMT " row modifications "
"will roll back.",
trx->undo_no);
- ut_d(ib::warn()
- << "trx: " << trx << " started on: "
- << innobase_basename(trx->start_file)
- << ":" << trx->start_line);
- innobase_rollback_trx(trx);
- free_trx = true;
+ goto rollback_and_free;
}
} else {
+rollback_and_free:
innobase_rollback_trx(trx);
- free_trx = true;
+ trx_free_for_mysql(trx);
}
}
- /* Free trx only after TrxInInnoDB is deleted. */
- if (free_trx) {
- trx_free_for_mysql(trx);
- }
-
DBUG_RETURN(0);
}
@@ -8320,15 +8213,6 @@ ha_innobase::write_row(
DBUG_ENTER("ha_innobase::write_row");
trx_t* trx = thd_to_trx(m_user_thd);
- TrxInInnoDB trx_in_innodb(trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
/* Validation checks before we commence write_row operation. */
if (high_level_read_only) {
@@ -9161,14 +9045,6 @@ ha_innobase::update_row(
goto func_exit;
}
- if (TrxInInnoDB::is_aborted(trx)) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
/* This is not a delete */
m_prebuilt->upd_node->is_delete = FALSE;
@@ -9258,18 +9134,9 @@ ha_innobase::delete_row(
{
dberr_t error;
trx_t* trx = thd_to_trx(m_user_thd);
- TrxInInnoDB trx_in_innodb(trx);
DBUG_ENTER("ha_innobase::delete_row");
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
ut_a(m_prebuilt->trx == trx);
if (high_level_read_only) {
@@ -9340,19 +9207,7 @@ ha_innobase::unlock_row(void)
DBUG_VOID_RETURN;
}
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
- if (trx_in_innodb.is_aborted()) {
- DBUG_VOID_RETURN;
- }
-
- /* Ideally, this assert must be in the beginning of the function.
- But there are some calls to this function from the SQL layer when the
- transaction is in state TRX_STATE_NOT_STARTED. The check on
- m_prebuilt->select_lock_type above gets around this issue. */
-
- ut_ad(trx_state_eq(m_prebuilt->trx, TRX_STATE_ACTIVE, true)
- || trx_state_eq(m_prebuilt->trx, TRX_STATE_FORCED_ROLLBACK, true));
+ ut_ad(trx_state_eq(m_prebuilt->trx, TRX_STATE_ACTIVE, true));
switch (m_prebuilt->row_read_type) {
case ROW_READ_WITH_LOCKS:
@@ -9634,14 +9489,6 @@ ha_innobase::index_read(
innobase_srv_conc_enter_innodb(m_prebuilt);
- if (TrxInInnoDB::is_aborted(m_prebuilt->trx)) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
ret = row_search_mvcc(
buf, mode, m_prebuilt, match_mode, 0);
@@ -9822,16 +9669,6 @@ ha_innobase::change_active_index(
ut_ad(m_user_thd == ha_thd());
ut_a(m_prebuilt->trx == thd_to_trx(m_user_thd));
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
active_index = keynr;
m_prebuilt->index = innobase_get_index(keynr);
@@ -9950,14 +9787,6 @@ ha_innobase::general_fetch(
ut_ad(trx == thd_to_trx(m_user_thd));
- if (TrxInInnoDB::is_aborted(trx)) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
if (m_prebuilt->table->is_readable()) {
} else if (m_prebuilt->table->corrupted) {
DBUG_RETURN(HA_ERR_CRASHED);
@@ -10124,7 +9953,6 @@ ha_innobase::rnd_init(
/*==================*/
bool scan) /*!< in: true if table/index scan FALSE otherwise */
{
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
int err;
/* Store the active index value so that we can restore the original
@@ -10174,8 +10002,6 @@ ha_innobase::rnd_next(
DBUG_ENTER("rnd_next");
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
if (m_start_of_scan) {
error = index_first(buf);
@@ -10299,21 +10125,6 @@ ha_innobase::ft_init_ext(
trx_t* trx = m_prebuilt->trx;
- TrxInInnoDB trx_in_innodb(trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- int err;
- err = convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd);
-
- my_error(err, MYF(0));
-
- return(NULL);
- }
-
/* FTS queries are not treated as autocommit non-locking selects.
This is because the FTS implementation can acquire locks behind
the scenes. This has not been verified but it is safer to treat
@@ -10455,16 +10266,6 @@ ha_innobase::ft_read(
/*=================*/
uchar* buf) /*!< in/out: buf contain result row */
{
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(ht, m_user_thd, false);
-
- return(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
row_prebuilt_t* ft_prebuilt;
ft_prebuilt = reinterpret_cast<NEW_FT_INFO*>(ft_handler)->ft_prebuilt;
@@ -13268,15 +13069,6 @@ ha_innobase::discard_or_import_tablespace(
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
}
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
- if (trx_in_innodb.is_aborted()) {
- innobase_rollback(ht, m_user_thd, false);
-
- DBUG_RETURN(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, m_user_thd));
- }
-
trx_start_if_not_started(m_prebuilt->trx, true);
/* Obtain an exclusive lock on the table. */
@@ -13376,8 +13168,6 @@ ha_innobase::truncate()
update_thd(ha_thd());
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
if (!trx_is_started(m_prebuilt->trx)) {
++m_prebuilt->trx->will_lock;
}
@@ -13452,8 +13242,6 @@ ha_innobase::delete_table(
trx_t* parent_trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(parent_trx);
-
/* Remove the to-be-dropped table from the list of modified tables
by parent_trx. Otherwise we may end up with an orphaned pointer to
the table object from parent_trx::mod_tables. This could happen in:
@@ -13697,8 +13485,6 @@ innobase_rename_table(
DEBUG_SYNC_C("innodb_rename_table_ready");
- TrxInInnoDB trx_in_innodb(trx);
-
trx_start_if_not_started(trx, true);
/* Serialize data dictionary operations with dictionary mutex:
@@ -13790,13 +13576,6 @@ ha_innobase::rename_table(
DBUG_RETURN(HA_ERR_TABLE_READONLY);
}
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- trx_t* parent_trx = check_trx_exists(thd);
-
- TrxInInnoDB trx_in_innodb(parent_trx);
-
trx_t* trx = innobase_trx_allocate(thd);
/* We are doing a DDL operation. */
@@ -13880,8 +13659,6 @@ ha_innobase::records_in_range(
m_prebuilt->trx->op_info = "estimating records in index range";
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
active_index = keynr;
key = table->key_info + active_index;
@@ -14013,8 +13790,6 @@ ha_innobase::estimate_rows_upper_bound()
update_thd(ha_thd());
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
m_prebuilt->trx->op_info = "calculating upper bound for table rows";
index = dict_table_get_first_index(m_prebuilt->table);
@@ -14856,8 +14631,6 @@ ha_innobase::optimize(
HA_CHECK_OPT* check_opt) /*!< in: currently ignored */
{
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
/* FTS-FIXME: Since MySQL doesn't support engine-specific commands,
we have to hijack some existing command in order to be able to test
the new admin commands added in InnoDB's FTS support. For now, we
@@ -14926,8 +14699,6 @@ ha_innobase::check(
ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
ut_a(m_prebuilt->trx == thd_to_trx(thd));
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
if (m_prebuilt->mysql_template == NULL) {
/* Build the template; we will use a dummy template
in index scans done in checking */
@@ -15418,8 +15189,6 @@ ha_innobase::get_foreign_key_list(
{
update_thd(ha_thd());
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
m_prebuilt->trx->op_info = "getting list of foreign keys";
mutex_enter(&dict_sys->mutex);
@@ -15458,8 +15227,6 @@ ha_innobase::get_parent_foreign_key_list(
{
update_thd(ha_thd());
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
m_prebuilt->trx->op_info = "getting list of referencing foreign keys";
mutex_enter(&dict_sys->mutex);
@@ -15556,8 +15323,6 @@ ha_innobase::get_cascade_foreign_key_table_list(
THD* thd,
List<st_handler_tablename>* fk_table_list)
{
- TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
-
m_prebuilt->trx->op_info = "getting cascading foreign keys";
std::list<table_list_item, ut_allocator<table_list_item> > table_list;
@@ -15807,15 +15572,6 @@ ha_innobase::end_stmt()
/* This is a statement level counter. */
m_prebuilt->autoinc_last_value = 0;
- /* This transaction had called ha_innobase::start_stmt() */
- trx_t* trx = m_prebuilt->trx;
-
- if (trx->lock.start_stmt) {
- TrxInInnoDB::end_stmt(trx);
-
- trx->lock.start_stmt = false;
- }
-
return(0);
}
@@ -15855,8 +15611,6 @@ ha_innobase::start_stmt(
ut_ad(m_prebuilt->table != NULL);
- TrxInInnoDB trx_in_innodb(trx);
-
trx = m_prebuilt->trx;
innobase_srv_conc_force_exit_innodb(trx);
@@ -15929,14 +15683,6 @@ ha_innobase::start_stmt(
++trx->will_lock;
}
- /* Only do it once per transaction. */
- if (!trx->lock.start_stmt && lock_type != TL_UNLOCK) {
-
- TrxInInnoDB::begin_stmt(trx);
-
- trx->lock.start_stmt = true;
- }
-
DBUG_RETURN(0);
}
@@ -16167,13 +15913,8 @@ ha_innobase::external_lock(
++trx->will_lock;
}
- TrxInInnoDB::begin_stmt(trx);
-
DBUG_RETURN(0);
} else {
-
- TrxInInnoDB::end_stmt(trx);
-
DEBUG_SYNC_C("ha_innobase_end_statement");
}
@@ -16268,8 +16009,6 @@ innodb_show_status(
innobase_srv_conc_force_exit_innodb(trx);
- TrxInInnoDB trx_in_innodb(trx);
-
/* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
bytes of text. */
@@ -16854,8 +16593,6 @@ ha_innobase::store_lock(
trx_t* trx = check_trx_exists(thd);
- TrxInInnoDB trx_in_innodb(trx);
-
/* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
Be careful to ignore TL_IGNORE if we are going to do something with
only 'real' locks! */
@@ -17176,8 +16913,6 @@ ha_innobase::get_auto_increment(
trx = m_prebuilt->trx;
- TrxInInnoDB trx_in_innodb(trx);
-
/* Note: We can't rely on *first_value since some MySQL engines,
in particular the partition engine, don't initialize it to 0 when
invoking this method. So we are not sure if it's guaranteed to
@@ -17578,16 +17313,6 @@ innobase_xa_prepare(
innobase_srv_conc_force_exit_innodb(trx);
- TrxInInnoDB trx_in_innodb(trx);
-
- if (trx_in_innodb.is_aborted()) {
-
- innobase_rollback(hton, thd, prepare_trx);
-
- return(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, thd));
- }
-
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
sql_print_error("Transaction not registered for MariaDB 2PC,"
@@ -17602,18 +17327,7 @@ innobase_xa_prepare(
ut_ad(trx_is_registered_for_2pc(trx));
- dberr_t err = trx_prepare_for_mysql(trx);
-
- ut_ad(err == DB_SUCCESS || err == DB_FORCED_ABORT);
-
- if (err == DB_FORCED_ABORT) {
-
- innobase_rollback(hton, thd, prepare_trx);
-
- return(convert_error_code_to_mysql(
- DB_FORCED_ABORT, 0, thd));
- }
-
+ trx_prepare_for_mysql(trx);
} else {
/* We just mark the SQL statement ended and do not do a
transaction prepare */
@@ -17690,14 +17404,10 @@ innobase_commit_by_xid(
}
if (trx_t* trx = trx_get_trx_by_xid(xid)) {
- ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE);
/* use cases are: disconnected xa, slave xa, recovery */
- {
- TrxInInnoDB trx_in_innodb(trx);
- innobase_commit_low(trx);
- ut_ad(trx->mysql_thd == NULL);
- trx_deregister_from_2pc(trx);
- }
+ innobase_commit_low(trx);
+ ut_ad(trx->mysql_thd == NULL);
+ trx_deregister_from_2pc(trx);
ut_ad(!trx->will_lock); /* trx cache requirement */
trx_free_for_background(trx);
@@ -17726,14 +17436,9 @@ innobase_rollback_by_xid(
}
if (trx_t* trx = trx_get_trx_by_xid(xid)) {
- int ret;
- ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE);
- {
- TrxInInnoDB trx_in_innodb(trx);
- ret = innobase_rollback_trx(trx);
- trx_deregister_from_2pc(trx);
- ut_ad(!trx->will_lock);
- }
+ int ret = innobase_rollback_trx(trx);
+ trx_deregister_from_2pc(trx);
+ ut_ad(!trx->will_lock);
trx_free_for_background(trx);
return(ret);
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index c7368a43192..074401e37d9 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -2804,8 +2804,7 @@ online_retry_drop_indexes_with_trx(
dict_table_t* table, /*!< in/out: table */
trx_t* trx) /*!< in/out: transaction */
{
- ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED)
- || trx_state_eq(trx, TRX_STATE_FORCED_ROLLBACK));
+ ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h
index 5b97b4b3a88..86defe9b166 100644
--- a/storage/innobase/include/ha_prototypes.h
+++ b/storage/innobase/include/ha_prototypes.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2006, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2017, 2018, 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
@@ -361,27 +361,6 @@ thd_trx_is_read_only(
/*=================*/
THD* thd); /*!< in/out: thread handle */
-#if 0
-/**
-Check if the transaction can be rolled back
-@param[in] requestor Session requesting the lock
-@param[in] holder Session that holds the lock
-@return the session that will be rolled back, null don't care */
-
-THD*
-thd_trx_arbitrate(THD* requestor, THD* holder);
-
-/**
-@param[in] thd Session to check
-@return the priority */
-
-int
-thd_trx_priority(THD* thd);
-
-#else
-static inline THD* thd_trx_arbitrate(THD*, THD*) { return NULL; }
-static inline int thd_trx_priority(THD*) { return 0; }
-#endif
/******************************************************************//**
Check if the transaction is an auto-commit transaction. TRUE also
implies that it is a SELECT (read-only) transaction.
diff --git a/storage/innobase/include/hash0hash.h b/storage/innobase/include/hash0hash.h
index 3d099cd2f3a..cbb6da488b5 100644
--- a/storage/innobase/include/hash0hash.h
+++ b/storage/innobase/include/hash0hash.h
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2018, 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
@@ -122,7 +123,6 @@ do {\
}\
} while (0)
-#ifdef WITH_WSREP
/*******************************************************************//**
Inserts a struct to the head of hash table. */
@@ -148,7 +148,6 @@ do { \
cell3333->node = DATA; \
} \
} while (0)
-#endif /*WITH_WSREP */
#ifdef UNIV_HASH_DEBUG
# define HASH_ASSERT_VALID(DATA) ut_a((void*) (DATA) != (void*) -1)
# define HASH_INVALIDATE(DATA, NAME) *(void**) (&DATA->NAME) = (void*) -1
diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
index 03535b6aaed..ddbbdeebfee 100644
--- a/storage/innobase/include/lock0lock.h
+++ b/storage/innobase/include/lock0lock.h
@@ -954,6 +954,30 @@ struct lock_sys_t{
is running */
};
+/*********************************************************************//**
+Creates a new record lock and inserts it to the lock queue. Does NOT check
+for deadlocks or lock compatibility!
+@return created lock */
+UNIV_INLINE
+lock_t*
+lock_rec_create(
+/*============*/
+#ifdef WITH_WSREP
+ lock_t* c_lock, /*!< conflicting lock */
+ que_thr_t* thr, /*!< thread owning trx */
+#endif
+ ulint type_mode,/*!< in: lock mode and wait
+ flag, type is ignored and
+ replaced by LOCK_REC */
+ const buf_block_t* block, /*!< in: buffer block containing
+ the record */
+ ulint heap_no,/*!< in: heap number of the record */
+ dict_index_t* index, /*!< in: index of record */
+ trx_t* trx, /*!< in,out: transaction */
+ bool caller_owns_trx_mutex);
+ /*!< in: true if caller owns
+ trx mutex */
+
/*************************************************************//**
Removes a record lock request, waiting or granted, from the queue. */
void
@@ -963,6 +987,61 @@ lock_rec_discard(
record locks which are contained
in this lock object are removed */
+/** 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
+ with LOCK_REC
+@param[in] space tablespace id
+@param[in] page_no index page number
+@param[in] page R-tree index page, or NULL
+@param[in] heap_no record heap number in the index page
+@param[in] index the index tree
+@param[in,out] trx transaction
+@param[in] holds_trx_mutex whether the caller holds trx->mutex
+@return created lock */
+lock_t*
+lock_rec_create_low(
+#ifdef WITH_WSREP
+ lock_t* c_lock, /*!< conflicting lock */
+ que_thr_t* thr, /*!< thread owning trx */
+#endif
+ ulint type_mode,
+ ulint space,
+ ulint page_no,
+ const page_t* page,
+ ulint heap_no,
+ dict_index_t* index,
+ trx_t* trx,
+ bool holds_trx_mutex);
+/** Enqueue a waiting request for a lock which cannot be granted immediately.
+Check for deadlocks.
+@param[in] type_mode the requested lock mode (LOCK_S or LOCK_X)
+ possibly ORed with LOCK_GAP or
+ LOCK_REC_NOT_GAP, ORed with
+ LOCK_INSERT_INTENTION if this
+ waiting lock request is set
+ when performing an insert of
+ an index record
+@param[in] block leaf page in the index
+@param[in] heap_no record heap number in the block
+@param[in] index index tree
+@param[in,out] thr query thread
+@param[in] prdt minimum bounding box (spatial index)
+@retval DB_LOCK_WAIT if the waiting lock was enqueued
+@retval DB_DEADLOCK if this transaction was chosen as the victim
+@retval DB_SUCCESS_LOCKED_REC if the other transaction was chosen as a victim
+ (or it happened to commit) */
+dberr_t
+lock_rec_enqueue_waiting(
+#ifdef WITH_WSREP
+ lock_t* c_lock, /*!< conflicting lock */
+#endif
+ ulint type_mode,
+ const buf_block_t* block,
+ ulint heap_no,
+ dict_index_t* index,
+ que_thr_t* thr,
+ lock_prdt_t* prdt);
/*************************************************************//**
Moves the explicit locks on user records to another page if a record
list start is moved to another page. */
diff --git a/storage/innobase/include/lock0lock.ic b/storage/innobase/include/lock0lock.ic
index b73843e7a1f..475f2ccedf1 100644
--- a/storage/innobase/include/lock0lock.ic
+++ b/storage/innobase/include/lock0lock.ic
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2018, 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
@@ -24,19 +24,9 @@ The transaction lock system
Created 5/7/1996 Heikki Tuuri
*******************************************************/
-#include "srv0srv.h"
#include "dict0dict.h"
-#include "row0row.h"
-#include "trx0sys.h"
-#include "trx0trx.h"
#include "buf0buf.h"
#include "page0page.h"
-#include "page0cur.h"
-#include "row0vers.h"
-#include "que0que.h"
-#include "btr0cur.h"
-#include "read0read.h"
-#include "log0recv.h"
/*********************************************************************//**
Calculates the fold value of a page file address: used in inserting or
@@ -108,3 +98,37 @@ lock_hash_get(
}
}
+/*********************************************************************//**
+Creates a new record lock and inserts it to the lock queue. Does NOT check
+for deadlocks or lock compatibility!
+@return created lock */
+UNIV_INLINE
+lock_t*
+lock_rec_create(
+/*============*/
+#ifdef WITH_WSREP
+ lock_t* c_lock, /*!< conflicting lock */
+ que_thr_t* thr, /*!< thread owning trx */
+#endif
+ ulint type_mode,/*!< in: lock mode and wait
+ flag, type is ignored and
+ replaced by LOCK_REC */
+ const buf_block_t* block, /*!< in: buffer block containing
+ the record */
+ ulint heap_no,/*!< in: heap number of the record */
+ dict_index_t* index, /*!< in: index of record */
+ trx_t* trx, /*!< in,out: transaction */
+ bool caller_owns_trx_mutex)
+ /*!< in: TRUE if caller owns
+ trx mutex */
+{
+ btr_assert_not_corrupted(block, index);
+ return lock_rec_create_low(
+#ifdef WITH_WSREP
+ c_lock, thr,
+#endif
+ type_mode,
+ block->page.id.space(), block->page.id.page_no(),
+ block->frame, heap_no,
+ index, trx, caller_owns_trx_mutex);
+}
diff --git a/storage/innobase/include/lock0priv.h b/storage/innobase/include/lock0priv.h
index 43f59151991..0f35e0ca6d0 100644
--- a/storage/innobase/include/lock0priv.h
+++ b/storage/innobase/include/lock0priv.h
@@ -562,407 +562,6 @@ enum lock_rec_req_status {
LOCK_REC_SUCCESS_CREATED
};
-/**
-Record lock ID */
-struct RecID {
-
- RecID(ulint space_id, ulint page_no, ulint heap_no)
- :
- m_space_id(static_cast<uint32_t>(space_id)),
- m_page_no(static_cast<uint32_t>(page_no)),
- m_heap_no(static_cast<uint32_t>(heap_no)),
- m_fold(lock_rec_fold(m_space_id, m_page_no))
- {
- ut_ad(space_id < UINT32_MAX);
- ut_ad(page_no < UINT32_MAX);
- ut_ad(heap_no < UINT32_MAX);
- }
-
- RecID(const buf_block_t* block, ulint heap_no)
- :
- m_space_id(block->page.id.space()),
- m_page_no(block->page.id.page_no()),
- m_heap_no(static_cast<uint32_t>(heap_no)),
- m_fold(lock_rec_fold(m_space_id, m_page_no))
- {
- ut_ad(heap_no < UINT32_MAX);
- }
-
- /**
- @return the "folded" value of {space, page_no} */
- ulint fold() const
- {
- return(m_fold);
- }
-
- /**
- Tablespace ID */
- uint32_t m_space_id;
-
- /**
- Page number within the space ID */
- uint32_t m_page_no;
-
- /**
- Heap number within the page */
- uint32_t m_heap_no;
-
- /**
- Hashed key value */
- ulint m_fold;
-};
-
-/**
-Create record locks */
-class RecLock {
-public:
-
- /**
- @param[in,out] thr Transaction query thread requesting the record
- lock
- @param[in] index Index on which record lock requested
- @param[in] rec_id Record lock tuple {space, page_no, heap_no}
- @param[in] mode The lock mode */
- RecLock(que_thr_t* thr,
- dict_index_t* index,
- const RecID& rec_id,
- ulint mode)
- :
- m_thr(thr),
- m_trx(thr_get_trx(thr)),
- m_mode(mode),
- m_index(index),
- m_rec_id(rec_id)
- {
- ut_ad(is_predicate_lock(m_mode));
-
- init(NULL);
- }
-
- /**
- @param[in,out] thr Transaction query thread requesting the record
- lock
- @param[in] index Index on which record lock requested
- @param[in] block Buffer page containing record
- @param[in] heap_no Heap number within the block
- @param[in] mode The lock mode
- @param[in] prdt The predicate for the rtree lock */
- RecLock(que_thr_t* thr,
- dict_index_t* index,
- const buf_block_t*
- block,
- ulint heap_no,
- ulint mode,
- lock_prdt_t* prdt = NULL)
- :
- m_thr(thr),
- m_trx(thr_get_trx(thr)),
- m_mode(mode),
- m_index(index),
- m_rec_id(block, heap_no)
- {
- btr_assert_not_corrupted(block, index);
-
- init(block->frame);
- }
-
- /**
- @param[in] index Index on which record lock requested
- @param[in] rec_id Record lock tuple {space, page_no, heap_no}
- @param[in] mode The lock mode */
- RecLock(dict_index_t* index,
- const RecID& rec_id,
- ulint mode)
- :
- m_thr(),
- m_trx(),
- m_mode(mode),
- m_index(index),
- m_rec_id(rec_id)
- {
- ut_ad(is_predicate_lock(m_mode));
-
- init(NULL);
- }
-
- /**
- @param[in] index Index on which record lock requested
- @param[in] block Buffer page containing record
- @param[in] heap_no Heap number withing block
- @param[in] mode The lock mode */
- RecLock(dict_index_t* index,
- const buf_block_t*
- block,
- ulint heap_no,
- ulint mode)
- :
- m_thr(),
- m_trx(),
- m_mode(mode),
- m_index(index),
- m_rec_id(block, heap_no)
- {
- btr_assert_not_corrupted(block, index);
-
- init(block->frame);
- }
-
- /**
- Enqueue a lock wait for a transaction. If it is a high priority
- transaction (cannot rollback) then jump ahead in the record lock wait
- queue and if the transaction at the head of the queue is itself waiting
- roll it back.
- @param[in, out] wait_for The lock that the the joining
- transaction is waiting for
- @param[in] prdt Predicate [optional]
- @return DB_LOCK_WAIT, DB_DEADLOCK, or
- DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
- there was a deadlock, but another transaction was chosen
- as a victim, and we got the lock immediately: no need to
- wait then */
- dberr_t add_to_waitq(
- lock_t* wait_for,
- const lock_prdt_t*
- prdt = NULL);
-
- /**
- Create a lock for a transaction and initialise it.
- @param[in, out] trx Transaction requesting the new lock
- @param[in] owns_trx_mutex true if caller owns the trx_t::mutex
- @param[in] add_to_hash add the lock to hash table
- @param[in] prdt Predicate lock (optional)
- @param[in,out] c_lock Conflicting lock request or NULL
- in Galera conflicting lock is selected
- as deadlock victim if requester
- is BF transaction.
- @return new lock instance */
- lock_t* create(
- trx_t* trx,
- bool owns_trx_mutex,
- bool add_to_hash,
- const lock_prdt_t*
- prdt = NULL
-#ifdef WITH_WSREP
- ,lock_t* c_lock = NULL
-#endif /* WITH_WSREP */
- );
-
- /**
- Check of the lock is on m_rec_id.
- @param[in] lock Lock to compare with
- @return true if the record lock is on m_rec_id*/
- bool is_on_row(const lock_t* lock) const;
-
- /**
- Create the lock instance
- @param[in, out] trx The transaction requesting the lock
- @param[in, out] index Index on which record lock is required
- @param[in] mode The lock mode desired
- @param[in] rec_id The record id
- @param[in] size Size of the lock + bitmap requested
- @return a record lock instance */
- static lock_t* lock_alloc(
- trx_t* trx,
- dict_index_t* index,
- ulint mode,
- const RecID& rec_id,
- ulint size);
-
-private:
- /*
- @return the record lock size in bytes */
- size_t lock_size() const
- {
- return(m_size);
- }
-
- /**
- Do some checks and prepare for creating a new record lock */
- void prepare() const;
-
- /**
- Collect the transactions that will need to be rolled back asynchronously
- @param[in, out] trx Transaction to be rolled back */
- void mark_trx_for_rollback(trx_t* trx);
-
- /**
- Jump the queue for the record over all low priority transactions and
- add the lock. If all current granted locks are compatible, grant the
- lock. Otherwise, mark all granted transaction for asynchronous
- rollback and add to hit list.
- @param[in, out] lock Lock being requested
- @param[in] conflict_lock First conflicting lock from the head
- @return true if the lock is granted */
- bool jump_queue(lock_t* lock, const lock_t* conflict_lock);
-
- /** Find position in lock queue and add the high priority transaction
- lock. Intention and GAP only locks can be granted even if there are
- waiting locks in front of the queue. To add the High priority
- transaction in a safe position we keep the following rule.
-
- 1. If the lock can be granted, add it before the first waiting lock
- in the queue so that all currently waiting locks need to do conflict
- check before getting granted.
-
- 2. If the lock has to wait, add it after the last granted lock or the
- last waiting high priority transaction in the queue whichever is later.
- This ensures that the transaction is granted only after doing conflict
- check with all granted transactions.
- @param[in] lock Lock being requested
- @param[in] conflict_lock First conflicting lock from the head
- @param[out] high_priority high priority transaction ahead in queue
- @return true if the lock can be granted */
- bool
- lock_add_priority(
- lock_t* lock,
- const lock_t* conflict_lock,
- bool* high_priority);
-
- /** Iterate over the granted locks and prepare the hit list for ASYNC Rollback.
- If the transaction is waiting for some other lock then wake up with deadlock error.
- Currently we don't mark following transactions for ASYNC Rollback.
- 1. Read only transactions
- 2. Background transactions
- 3. Other High priority transactions
- @param[in] lock Lock being requested
- @param[in] conflict_lock First conflicting lock from the head */
- void make_trx_hit_list(lock_t* lock, const lock_t* conflict_lock);
-
- /**
- Setup the requesting transaction state for lock grant
- @param[in,out] lock Lock for which to change state */
- void set_wait_state(lock_t* lock);
-
- /**
- Add the lock to the record lock hash and the transaction's lock list
- @param[in,out] lock Newly created record lock to add to the
- rec hash and the transaction lock list
- @param[in] add_to_hash If the lock should be added to the hash table */
- void lock_add(lock_t* lock, bool add_to_hash);
-
- /**
- Check and resolve any deadlocks
- @param[in, out] lock The lock being acquired
- @return DB_LOCK_WAIT, DB_DEADLOCK, or
- DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
- there was a deadlock, but another transaction was chosen
- as a victim, and we got the lock immediately: no need to
- wait then */
- dberr_t deadlock_check(lock_t* lock);
-
- /**
- Check the outcome of the deadlock check
- @param[in,out] victim_trx Transaction selected for rollback
- @param[in,out] lock Lock being requested
- @return DB_LOCK_WAIT, DB_DEADLOCK or DB_SUCCESS_LOCKED_REC */
- dberr_t check_deadlock_result(const trx_t* victim_trx, lock_t* lock);
-
- /**
- Setup the context from the requirements */
- void init(const page_t* page)
- {
- ut_ad(lock_mutex_own());
- ut_ad(!srv_read_only_mode);
- ut_ad(dict_index_is_clust(m_index)
- || !dict_index_is_online_ddl(m_index));
- ut_ad(m_thr == NULL || m_trx == thr_get_trx(m_thr));
-
- m_size = is_predicate_lock(m_mode)
- ? lock_size(m_mode) : lock_size(page);
-
- /** If rec is the supremum record, then we reset the
- gap and LOCK_REC_NOT_GAP bits, as all locks on the
- supremum are automatically of the gap type */
-
- if (m_rec_id.m_heap_no == PAGE_HEAP_NO_SUPREMUM) {
- ut_ad(!(m_mode & LOCK_REC_NOT_GAP));
-
- m_mode &= ~(LOCK_GAP | LOCK_REC_NOT_GAP);
- }
- }
-
- /**
- Calculate the record lock physical size required for a predicate lock.
- @param[in] mode For predicate locks the lock mode
- @return the size of the lock data structure required in bytes */
- static size_t lock_size(ulint mode)
- {
- ut_ad(is_predicate_lock(mode));
-
- /* The lock is always on PAGE_HEAP_NO_INFIMUM(0),
- so we only need 1 bit (which is rounded up to 1
- byte) for lock bit setting */
-
- size_t n_bytes;
-
- if (mode & LOCK_PREDICATE) {
- const ulint align = UNIV_WORD_SIZE - 1;
-
- /* We will attach the predicate structure
- after lock. Make sure the memory is
- aligned on 8 bytes, the mem_heap_alloc
- will align it with MEM_SPACE_NEEDED
- anyway. */
-
- n_bytes = (1 + sizeof(lock_prdt_t) + align) & ~align;
-
- /* This should hold now */
-
- ut_ad(n_bytes == sizeof(lock_prdt_t) + UNIV_WORD_SIZE);
-
- } else {
- n_bytes = 1;
- }
-
- return(n_bytes);
- }
-
- /**
- Calculate the record lock physical size required, non-predicate lock.
- @param[in] page For non-predicate locks the buffer page
- @return the size of the lock data structure required in bytes */
- static size_t lock_size(const page_t* page)
- {
- ulint n_recs = page_dir_get_n_heap(page);
-
- /* Make lock bitmap bigger by a safety margin */
-
- return(1 + ((n_recs + LOCK_PAGE_BITMAP_MARGIN) / 8));
- }
-
- /**
- @return true if the requested lock mode is for a predicate
- or page lock */
- static bool is_predicate_lock(ulint mode)
- {
- return(mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE));
- }
-
-private:
- /** The query thread of the transaction */
- que_thr_t* m_thr;
-
- /**
- Transaction requesting the record lock */
- trx_t* m_trx;
-
- /**
- Lock mode requested */
- ulint m_mode;
-
- /**
- Size of the record lock in bytes */
- size_t m_size;
-
- /**
- Index on which the record lock is required */
- dict_index_t* m_index;
-
- /**
- The record lock tuple {space, page_no, heap_no} */
- RecID m_rec_id;
-};
-
#ifdef UNIV_DEBUG
/** The count of the types of locks. */
static const ulint lock_types = UT_ARR_SIZE(lock_compatibility_matrix);
diff --git a/storage/innobase/include/lock0priv.ic b/storage/innobase/include/lock0priv.ic
index f6e5f7acb8f..150a80b7be4 100644
--- a/storage/innobase/include/lock0priv.ic
+++ b/storage/innobase/include/lock0priv.ic
@@ -32,6 +32,8 @@ methods but they are used only in that file. */
#error Do not include lock0priv.ic outside of the lock/ module
#endif
+#include "row0row.h"
+
/*********************************************************************//**
Gets the type of a lock.
@return LOCK_TABLE or LOCK_REC */
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 50e270fb3c5..6ed69e6c5c4 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -28,13 +28,11 @@ Created 3/26/1996 Heikki Tuuri
#define trx0trx_h
#include <set>
-#include <list>
#include "ha_prototypes.h"
#include "dict0types.h"
#include "trx0types.h"
-#include "ut0new.h"
#include "lock0types.h"
#include "log0log.h"
@@ -43,7 +41,6 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0xa.h"
#include "ut0vec.h"
#include "fts0fts.h"
-#include "srv0srv.h"
// Forward declaration
struct mtr_t;
@@ -255,14 +252,9 @@ dberr_t
trx_commit_for_mysql(
/*=================*/
trx_t* trx); /*!< in/out: transaction */
-
-/**
-Does the transaction prepare for MySQL.
-@param[in, out] trx Transaction instance to prepare */
-
-dberr_t
-trx_prepare_for_mysql(trx_t* trx);
-
+/** XA PREPARE a transaction.
+@param[in,out] trx transaction to prepare */
+void trx_prepare_for_mysql(trx_t* trx);
/**********************************************************************//**
This function is used to find number of prepared transactions and
their transaction objects for a recovery.
@@ -552,29 +544,6 @@ Check if the transaction is being referenced. */
#define trx_is_referenced(t) ((t)->n_ref > 0)
/**
-@param[in] requestor Transaction requesting the lock
-@param[in] holder Transaction holding the lock
-@return the transaction that will be rolled back, null don't care */
-
-UNIV_INLINE
-const trx_t*
-trx_arbitrate(const trx_t* requestor, const trx_t* holder);
-
-/**
-@param[in] trx Transaction to check
-@return true if the transaction is a high priority transaction.*/
-UNIV_INLINE
-bool
-trx_is_high_priority(const trx_t* trx);
-
-/**
-Kill all transactions that are blocking this transaction from acquiring locks.
-@param[in,out] trx High priority transaction */
-
-void
-trx_kill_blocking(trx_t* trx);
-
-/**
Transactions that aren't started by the MySQL server don't set
the trx_t::mysql_thd field. For such transactions we set the lock
wait timeout to 0 instead of the user configured value that comes
@@ -622,7 +591,6 @@ Check transaction state */
case TRX_STATE_COMMITTED_IN_MEMORY: \
continue; \
case TRX_STATE_NOT_STARTED: \
- case TRX_STATE_FORCED_ROLLBACK: \
break; \
} \
ut_error; \
@@ -631,8 +599,7 @@ Check transaction state */
/** 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) \
- || trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \
+ ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \
ut_ad(!trx->has_logged()); \
ut_ad(!MVCC::is_view_active((t)->read_view)); \
ut_ad((t)->lock.wait_thr == NULL); \
@@ -662,7 +629,6 @@ The tranasction must be in the mysql_trx_list. */
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_FORCED_ROLLBACK \
|| t_state == TRX_STATE_ACTIVE); \
} else { \
check_trx_state(t); \
@@ -769,10 +735,6 @@ struct trx_lock_t {
Protected by both the lock sys mutex
and the trx_t::mutex. */
ulint n_rec_locks; /*!< number of rec locks in this trx */
-
- /** The transaction called ha_innobase::start_stmt() to
- lock a table. Most likely a temporary table. */
- bool start_stmt;
};
/** Type used to store the list of tables that are modified by a given
@@ -858,47 +820,12 @@ struct trx_rsegs_t {
trx_temp_undo_t m_noredo;
};
-struct TrxVersion {
- TrxVersion(trx_t* trx);
-
- /**
- @return true if the trx_t instance is the same */
- bool operator==(const TrxVersion& rhs) const
- {
- return(rhs.m_trx == m_trx);
- }
-
- trx_t* m_trx;
- ulint m_version;
-};
-
-typedef std::list<TrxVersion, ut_allocator<TrxVersion> > hit_list_t;
-
struct trx_t {
TrxMutex mutex; /*!< Mutex protecting the fields
state and lock (except some fields
of lock, which are protected by
lock_sys->mutex) */
- /* Note: in_depth was split from in_innodb for fixing a RO
- performance issue. Acquiring the trx_t::mutex for each row
- costs ~3% in performance. It is not required for correctness.
- Therefore we increment/decrement in_depth without holding any
- mutex. The assumption is that the Server will only ever call
- the handler from one thread. This is not true for kill_connection.
- Therefore in innobase_kill_connection. We don't increment this
- counter via TrxInInnoDB. */
-
- ib_uint32_t in_depth; /*!< Track nested TrxInInnoDB
- count */
-
- ib_uint32_t in_innodb; /*!< if the thread is executing
- in the InnoDB context count > 0. */
-
- bool abort; /*!< if this flag is set then
- this transaction must abort when
- it can */
-
trx_id_t id; /*!< transaction id */
trx_id_t no; /*!< transaction serialization number:
@@ -915,7 +842,6 @@ struct trx_t {
Possible states:
TRX_STATE_NOT_STARTED
- TRX_STATE_FORCED_ROLLBACK
TRX_STATE_ACTIVE
TRX_STATE_PREPARED
TRX_STATE_COMMITTED_IN_MEMORY (alias below COMMITTED)
@@ -995,22 +921,6 @@ struct trx_t {
protected by trx_sys->mutex when
trx->in_rw_trx_list holds */
- hit_list_t hit_list; /*!< List of transactions to kill,
- when a high priority transaction
- is blocked on a lock wait. */
-
- os_thread_id_t killed_by; /*!< The thread ID that wants to
- kill this transaction asynchronously.
- This is required because we recursively
- enter the handlerton methods and need
- to distinguish between the kill thread
- and the transaction thread.
-
- Note: We need to be careful w.r.t the
- Thread Pool. The thread doing the kill
- should not leave InnoDB between the
- mark and the actual async kill because
- the running thread can change. */
/* These fields are not protected by any mutex. */
const char* op_info; /*!< English text describing the
@@ -1223,12 +1133,6 @@ struct trx_t {
signify that it is no longer
"active". */
- /** Version of this instance. It is incremented each time the
- instance is re-used in trx_start_low(). It is used to track
- whether a transaction has been restarted since it was tagged
- for asynchronous rollback. */
- ulint version;
-
XID* xid; /*!< X/Open XA transaction
identification to identify a
transaction branch */
@@ -1292,13 +1196,9 @@ private:
Check if transaction is started.
@param[in] trx Transaction whose state we need to check
@reutrn true if transaction is in state started */
-inline
-bool
-trx_is_started(
- const trx_t* trx)
+inline bool trx_is_started(const trx_t* trx)
{
- return(trx->state != TRX_STATE_NOT_STARTED
- && trx->state != TRX_STATE_FORCED_ROLLBACK);
+ return trx->state != TRX_STATE_NOT_STARTED;
}
/* Transaction isolation levels (trx->isolation_level) */
@@ -1371,224 +1271,6 @@ struct commit_node_t{
mutex_exit(&t->mutex); \
} while (0)
-/** Track if a transaction is executing inside InnoDB code. It acts
-like a gate between the Server and InnoDB. */
-class TrxInInnoDB {
-public:
- /**
- @param[in,out] trx Transaction entering InnoDB via the handler
- @param[in] disable true if called from COMMIT/ROLLBACK method */
- TrxInInnoDB(trx_t* trx, bool disable = false)
- :
- m_trx(trx)
- {
- enter(trx, disable);
- }
-
- /**
- Destructor */
- ~TrxInInnoDB()
- {
- exit(m_trx);
- }
-
- /**
- @return true if the transaction has been marked for asynchronous
- rollback */
- bool is_aborted() const
- {
- return(is_aborted(m_trx));
- }
-
- /**
- @return true if the transaction can't be rolled back asynchronously */
- bool is_rollback_disabled() const
- {
- return((m_trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE) > 0);
- }
-
- /**
- @return true if the transaction has been marked for asynchronous
- rollback */
- static bool is_aborted(const trx_t* trx)
- {
- if (trx->state == TRX_STATE_NOT_STARTED) {
- return(false);
- }
-
- ut_ad(srv_read_only_mode || trx->in_depth > 0);
- ut_ad(srv_read_only_mode || trx->in_innodb > 0);
-
- return(trx->abort
- || trx->state == TRX_STATE_FORCED_ROLLBACK);
- }
-
- /**
- Start statement requested for transaction.
- @param[in, out] trx Transaction at the start of a SQL statement */
- static void begin_stmt(trx_t* trx)
- {
- enter(trx, false);
- }
-
- /**
- Note an end statement for transaction
- @param[in, out] trx Transaction at end of a SQL statement */
- static void end_stmt(trx_t* trx)
- {
- exit(trx);
- }
-
- /**
- @return true if the rollback is being initiated by the thread that
- marked the transaction for asynchronous rollback */
- static bool is_async_rollback(const trx_t* trx)
- {
- return(trx->killed_by == os_thread_get_curr_id());
- }
-
-private:
- /**
- Note that we have crossed into InnoDB code.
- @param[in] disable true if called from COMMIT/ROLLBACK method */
- static void enter(trx_t* trx, bool disable)
- {
- if (srv_read_only_mode) {
-
- return;
- }
-
- ut_ad(!is_async_rollback(trx));
-
- /* If it hasn't already been marked for async rollback.
- and it will be committed/rolled back. */
- if (disable) {
-
- trx_mutex_enter(trx);
- if (!is_forced_rollback(trx)
- && is_started(trx)
- && !trx_is_autocommit_non_locking(trx)) {
-
- ut_ad(trx->killed_by == 0);
-
- /* This transaction has crossed the point of
- no return and cannot be rolled back
- asynchronously now. It must commit or rollback
- synhronously. */
-
- trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
- }
- trx_mutex_exit(trx);
- }
-
- /* Avoid excessive mutex acquire/release */
- ++trx->in_depth;
-
- /* If trx->in_depth is greater than 1 then
- transaction is already in InnoDB. */
- if (trx->in_depth > 1) {
-
- return;
- }
-
- trx_mutex_enter(trx);
-
- wait(trx);
-
- ut_ad((trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) == 0);
-
- ++trx->in_innodb;
-
- trx_mutex_exit(trx);
- }
-
- /**
- Note that we are exiting InnoDB code */
- static void exit(trx_t* trx)
- {
- if (srv_read_only_mode) {
-
- return;
- }
-
- /* Avoid excessive mutex acquire/release */
-
- ut_ad(trx->in_depth > 0);
-
- --trx->in_depth;
-
- if (trx->in_depth > 0) {
-
- return;
- }
-
- trx_mutex_enter(trx);
-
- ut_ad((trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) > 0);
-
- --trx->in_innodb;
-
- trx_mutex_exit(trx);
- }
-
- /*
- @return true if it is a forced rollback, asynchronously */
- static bool is_forced_rollback(const trx_t* trx)
- {
- ut_ad(trx_mutex_own(trx));
-
- return((trx->in_innodb & TRX_FORCE_ROLLBACK)) > 0;
- }
-
- /**
- Wait for the asynchronous rollback to complete, if it is in progress */
- static void wait(trx_t* trx)
- {
- ut_ad(trx_mutex_own(trx));
-
- ulint loop_count = 0;
- /* start with optimistic sleep time - 20 micro seconds. */
- ulint sleep_time = 20;
-
- while (is_forced_rollback(trx)) {
-
- /* Wait for the async rollback to complete */
-
- trx_mutex_exit(trx);
-
- loop_count++;
- /* If the wait is long, don't hog the cpu. */
- if (loop_count < 100) {
- /* 20 microseconds */
- sleep_time = 20;
- } else if (loop_count < 1000) {
- /* 1 millisecond */
- sleep_time = 1000;
- } else {
- /* 100 milliseconds */
- sleep_time = 100000;
- }
-
- os_thread_sleep(sleep_time);
-
- trx_mutex_enter(trx);
- }
- }
-
- /**
- @return true if transaction is started */
- static bool is_started(const trx_t* trx)
- {
- ut_ad(trx_mutex_own(trx));
-
- return(trx_is_started(trx));
- }
-private:
- /**
- Transaction instance crossing the handler boundary from the Server. */
- trx_t* m_trx;
-};
-
#include "trx0trx.ic"
#endif
diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic
index 6fa00c5333f..7721e28bfb6 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, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2016, 2018, 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
@@ -64,11 +64,8 @@ trx_state_eq(
return(state == trx->state);
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
-
/* These states are not allowed for running transactions. */
ut_a(state == TRX_STATE_NOT_STARTED
- || state == TRX_STATE_FORCED_ROLLBACK
|| (relaxed
&& thd_get_error_number(trx->mysql_thd)));
@@ -280,67 +277,3 @@ trx_get_read_view(
{
return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view);
}
-
-/**
-@param[in] trx Transaction to check
-@return true if the transaction is a high priority transaction.*/
-UNIV_INLINE
-bool
-trx_is_high_priority(const trx_t* trx)
-{
- if (trx->mysql_thd == NULL) {
- return(false);
- }
-
- return(thd_trx_priority(trx->mysql_thd) > 0);
-}
-
-/**
-@param[in] requestor Transaction requesting the lock
-@param[in] holder Transaction holding the lock
-@return the transaction that will be rolled back, null don't care */
-UNIV_INLINE
-const trx_t*
-trx_arbitrate(const trx_t* requestor, const trx_t* holder)
-{
- ut_ad(!trx_is_autocommit_non_locking(holder));
- ut_ad(!trx_is_autocommit_non_locking(requestor));
-
- /* Note: Background stats collection transactions also acquire
- locks on user tables. They don't have an associated MySQL session
- instance. */
-
- if (requestor->mysql_thd == NULL) {
-
- ut_ad(!trx_is_high_priority(requestor));
-
- if (trx_is_high_priority(holder)) {
- return(requestor);
- } else {
- return(NULL);
- }
-
- } else if (holder->mysql_thd == NULL) {
-
- ut_ad(!trx_is_high_priority(holder));
-
- if (trx_is_high_priority(requestor)) {
- return(holder);
- }
-
- return(NULL);
- }
-
- const THD* victim = thd_trx_arbitrate(
- requestor->mysql_thd, holder->mysql_thd);
-
- ut_ad(victim == NULL
- || victim == requestor->mysql_thd
- || victim == holder->mysql_thd);
-
- if (victim != NULL) {
- return(victim == requestor->mysql_thd ? requestor : holder);
- }
-
- return(NULL);
-}
diff --git a/storage/innobase/include/trx0types.h b/storage/innobase/include/trx0types.h
index 8092246c7fa..b42871bef31 100644
--- a/storage/innobase/include/trx0types.h
+++ b/storage/innobase/include/trx0types.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2018, 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
@@ -53,21 +53,6 @@ static const ulint TRX_SYS_SPACE = 0;
/** Random value to check for corruption of trx_t */
static const ulint TRX_MAGIC_N = 91118598;
-/** If this flag is set then the transaction cannot be rolled back
-asynchronously. */
-static const ib_uint32_t TRX_FORCE_ROLLBACK_DISABLE = 1 << 29;
-
-/** Was the transaction rolled back asynchronously or by the
-owning thread. This flag is relevant only if TRX_FORCE_ROLLBACK
-is set. */
-static const ib_uint32_t TRX_FORCE_ROLLBACK_ASYNC = 1 << 30;
-
-/** Mark the transaction for forced rollback */
-static const ib_uint32_t TRX_FORCE_ROLLBACK = 1U << 31;
-
-/** For masking out the above four flags */
-static const ib_uint32_t TRX_FORCE_ROLLBACK_MASK = 0x1FFFFFFF;
-
/** Transaction execution states when trx->state == TRX_STATE_ACTIVE */
enum trx_que_t {
TRX_QUE_RUNNING, /*!< transaction is running */
@@ -79,13 +64,8 @@ enum trx_que_t {
/** Transaction states (trx_t::state) */
enum trx_state_t {
-
TRX_STATE_NOT_STARTED,
- /** Same as not started but with additional semantics that it
- was rolled back asynchronously the last time it was active. */
- TRX_STATE_FORCED_ROLLBACK,
-
TRX_STATE_ACTIVE,
/** Support for 2PC/XA */
diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h
index 20e7aaa4913..f744364d966 100644
--- a/storage/innobase/include/trx0undo.h
+++ b/storage/innobase/include/trx0undo.h
@@ -28,12 +28,7 @@ Created 3/26/1996 Heikki Tuuri
#define trx0undo_h
#ifndef UNIV_INNOCHECKSUM
-#include "univ.i"
-#include "trx0types.h"
-#include "mtr0mtr.h"
#include "trx0sys.h"
-#include "page0types.h"
-#include "trx0xa.h"
/** The LSB of the "is insert" flag in DB_ROLL_PTR */
#define ROLL_PTR_INSERT_FLAG_POS 55
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index db079abf10f..213b370ee36 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -44,6 +44,7 @@ Created 5/7/1996 Heikki Tuuri
#include "ut0new.h"
#include "row0sel.h"
#include "row0mysql.h"
+#include "row0vers.h"
#include "pars0pars.h"
#include <set>
@@ -1506,128 +1507,210 @@ wsrep_print_wait_locks(
}
#endif /* WITH_WSREP */
-/**
-Check of the lock is on m_rec_id.
-@param[in] lock Lock to compare with
-@return true if the record lock is on m_rec_id*/
-/**
-@param[in] rhs Lock to compare with
-@return true if the record lock equals rhs */
-bool
-RecLock::is_on_row(const lock_t* lock) const
+/** 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
+ with LOCK_REC
+@param[in] space tablespace id
+@param[in] page_no index page number
+@param[in] page R-tree index page, or NULL
+@param[in] heap_no record heap number in the index page
+@param[in] index the index tree
+@param[in,out] trx transaction
+@param[in] holds_trx_mutex whether the caller holds trx->mutex
+@return created lock */
+lock_t*
+lock_rec_create_low(
+#ifdef WITH_WSREP
+ lock_t* c_lock, /*!< conflicting lock */
+ que_thr_t* thr, /*!< thread owning trx */
+#endif
+ ulint type_mode,
+ ulint space,
+ ulint page_no,
+ const page_t* page,
+ ulint heap_no,
+ dict_index_t* index,
+ trx_t* trx,
+ bool holds_trx_mutex)
{
- ut_ad(lock_get_type_low(lock) == LOCK_REC);
-
- const lock_rec_t& other = lock->un_member.rec_lock;
-
- return(other.space == m_rec_id.m_space_id
- && other.page_no == m_rec_id.m_page_no
- && lock_rec_get_nth_bit(lock, m_rec_id.m_heap_no));
-}
+ lock_t* lock;
+ ulint n_bits;
+ ulint n_bytes;
-/**
-Do some checks and prepare for creating a new record lock */
-void
-RecLock::prepare() const
-{
ut_ad(lock_mutex_own());
- ut_ad(m_trx == thr_get_trx(m_thr));
-
- /* Test if there already is some other reason to suspend thread:
- we do not enqueue a lock request if the query thread should be
- stopped anyway */
+ ut_ad(holds_trx_mutex == trx_mutex_own(trx));
+ ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
- if (que_thr_stop(m_thr)) {
- ut_error;
+#ifdef UNIV_DEBUG
+ /* Non-locking autocommit read-only transactions should not set
+ any locks. See comment in trx_set_rw_mode explaining why this
+ conditional check is required in debug code. */
+ if (holds_trx_mutex) {
+ check_trx_state(trx);
}
+#endif /* UNIV_DEBUG */
- switch (trx_get_dict_operation(m_trx)) {
- case TRX_DICT_OP_NONE:
- break;
- case TRX_DICT_OP_TABLE:
- case TRX_DICT_OP_INDEX:
- ib::error() << "A record lock wait happens in a dictionary"
- " operation. index " << m_index->name
- << " of table " << m_index->table->name
- << ". " << BUG_REPORT_MSG;
- ut_ad(0);
- }
+ /* If rec is the supremum record, then we reset the gap and
+ LOCK_REC_NOT_GAP bits, as all locks on the supremum are
+ automatically of the gap type */
- ut_ad(m_index->table->n_ref_count > 0
- || !m_index->table->can_be_evicted);
-}
+ if (UNIV_UNLIKELY(heap_no == PAGE_HEAP_NO_SUPREMUM)) {
+ ut_ad(!(type_mode & LOCK_REC_NOT_GAP));
+ type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP);
+ }
-/**
-Create the lock instance
-@param[in, out] trx The transaction requesting the lock
-@param[in, out] index Index on which record lock is required
-@param[in] mode The lock mode desired
-@param[in] rec_id The record id
-@param[in] size Size of the lock + bitmap requested
-@return a record lock instance */
-lock_t*
-RecLock::lock_alloc(
- trx_t* trx,
- dict_index_t* index,
- ulint mode,
- const RecID& rec_id,
- ulint size)
-{
- ut_ad(lock_mutex_own());
+ if (UNIV_LIKELY(!(type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE)))) {
+ /* Make lock bitmap bigger by a safety margin */
+ n_bits = page_dir_get_n_heap(page) + LOCK_PAGE_BITMAP_MARGIN;
+ n_bytes = 1 + n_bits / 8;
+ } else {
+ ut_ad(heap_no == PRDT_HEAPNO);
- lock_t* lock;
+ /* The lock is always on PAGE_HEAP_NO_INFIMUM (0), so
+ we only need 1 bit (which round up to 1 byte) for
+ lock bit setting */
+ n_bytes = 1;
- if (trx->lock.rec_cached >= trx->lock.rec_pool.size()
- || sizeof(*lock) + size > REC_LOCK_SIZE) {
+ if (type_mode & LOCK_PREDICATE) {
+ ulint tmp = UNIV_WORD_SIZE - 1;
- ulint n_bytes = size + sizeof(*lock);
- mem_heap_t* heap = trx->lock.lock_heap;
+ /* We will attach predicate structure after lock.
+ Make sure the memory is aligned on 8 bytes,
+ the mem_heap_alloc will align it with
+ MEM_SPACE_NEEDED anyway. */
+ n_bytes = (n_bytes + sizeof(lock_prdt_t) + tmp) & ~tmp;
+ ut_ad(n_bytes == sizeof(lock_prdt_t) + UNIV_WORD_SIZE);
+ }
+ }
- lock = reinterpret_cast<lock_t*>(mem_heap_alloc(heap, n_bytes));
+ if (trx->lock.rec_cached >= trx->lock.rec_pool.size()
+ || sizeof *lock + n_bytes > REC_LOCK_SIZE) {
+ lock = static_cast<lock_t*>(
+ mem_heap_alloc(trx->lock.lock_heap,
+ sizeof *lock + n_bytes));
} else {
-
- lock = trx->lock.rec_pool[trx->lock.rec_cached];
- ++trx->lock.rec_cached;
+ lock = trx->lock.rec_pool[trx->lock.rec_cached++];
}
lock->trx = trx;
-
+ lock->type_mode = (type_mode & ~LOCK_TYPE_MASK) | LOCK_REC;
lock->index = index;
+ lock->un_member.rec_lock.space = uint32_t(space);
+ lock->un_member.rec_lock.page_no = uint32_t(page_no);
- /* Setup the lock attributes */
-
- lock->type_mode = uint32_t(LOCK_REC | (mode & ~LOCK_TYPE_MASK));
+ if (UNIV_LIKELY(!(type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE)))) {
+ lock->un_member.rec_lock.n_bits = uint32_t(n_bytes * 8);
+ } else {
+ /* Predicate lock always on INFIMUM (0) */
+ lock->un_member.rec_lock.n_bits = 8;
+ }
+ lock_rec_bitmap_reset(lock);
+ lock_rec_set_nth_bit(lock, heap_no);
+ index->table->n_rec_locks++;
+ ut_ad(index->table->n_ref_count > 0 || !index->table->can_be_evicted);
- lock_rec_t& rec_lock = lock->un_member.rec_lock;
+#ifdef WITH_WSREP
+ if (c_lock && wsrep_on_trx(trx)
+ && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
+ lock_t *hash = (lock_t *)c_lock->hash;
+ lock_t *prev = NULL;
- /* Predicate lock always on INFIMUM (0) */
+ while (hash && wsrep_thd_is_BF(hash->trx->mysql_thd, TRUE)
+ && wsrep_trx_order_before(hash->trx->mysql_thd,
+ trx->mysql_thd)) {
+ prev = hash;
+ hash = (lock_t *)hash->hash;
+ }
+ lock->hash = hash;
+ if (prev) {
+ prev->hash = lock;
+ } else {
+ c_lock->hash = lock;
+ }
+ /*
+ * delayed conflict resolution '...kill_one_trx' was not called,
+ * if victim was waiting for some other lock
+ */
+ trx_mutex_enter(c_lock->trx);
+ if (c_lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
- if (is_predicate_lock(mode)) {
+ c_lock->trx->lock.was_chosen_as_deadlock_victim = TRUE;
- rec_lock.n_bits = 8;
+ if (wsrep_debug) {
+ wsrep_print_wait_locks(c_lock);
+ }
- memset(&lock[1], 0x0, 1);
+ trx->lock.que_state = TRX_QUE_LOCK_WAIT;
+ lock_set_lock_and_trx_wait(lock, trx);
+ UT_LIST_ADD_LAST(trx->lock.trx_locks, lock);
- } else {
- ut_ad(8 * size < UINT32_MAX);
- rec_lock.n_bits = static_cast<uint32_t>(8 * size);
+ trx->lock.wait_thr = thr;
+ thr->state = QUE_THR_LOCK_WAIT;
- memset(&lock[1], 0x0, size);
- }
+ /* have to release trx mutex for the duration of
+ victim lock release. This will eventually call
+ lock_grant, which wants to grant trx mutex again
+ */
+ if (holds_trx_mutex) {
+ trx_mutex_exit(trx);
+ }
+ lock_cancel_waiting_and_release(
+ c_lock->trx->lock.wait_lock);
- rec_lock.space = rec_id.m_space_id;
+ if (holds_trx_mutex) {
+ trx_mutex_enter(trx);
+ }
- rec_lock.page_no = rec_id.m_page_no;
+ /* trx might not wait for c_lock, but some other lock
+ does not matter if wait_lock was released above
+ */
+ if (c_lock->trx->lock.wait_lock == c_lock) {
+ lock_reset_lock_and_trx_wait(lock);
+ }
- /* Set the bit corresponding to rec */
+ trx_mutex_exit(c_lock->trx);
- lock_rec_set_nth_bit(lock, rec_id.m_heap_no);
+ if (wsrep_debug) {
+ ib::info() << "WSREP: c_lock canceled "
+ << ib::hex(c_lock->trx->id)
+ << " SQL: "
+ << wsrep_thd_query(
+ c_lock->trx->mysql_thd);
+ }
- MONITOR_INC(MONITOR_NUM_RECLOCK);
+ /* have to bail out here to avoid lock_set_lock... */
+ return(lock);
+ }
+ trx_mutex_exit(c_lock->trx);
+ } else
+#endif /* WITH_WSREP */
+ if (!(type_mode & (LOCK_WAIT | LOCK_PREDICATE | LOCK_PRDT_PAGE))
+ && innodb_lock_schedule_algorithm
+ == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
+ && !thd_is_replication_slave_thread(trx->mysql_thd)) {
+ HASH_PREPEND(lock_t, hash, lock_sys->rec_hash,
+ lock_rec_fold(space, page_no), lock);
+ } else {
+ HASH_INSERT(lock_t, hash, lock_hash_get(type_mode),
+ lock_rec_fold(space, page_no), lock);
+ }
+ if (!holds_trx_mutex) {
+ trx_mutex_enter(trx);
+ }
+ ut_ad(trx_mutex_own(trx));
+ if (type_mode & LOCK_WAIT) {
+ lock_set_lock_and_trx_wait(lock, trx);
+ }
+ UT_LIST_ADD_LAST(trx->lock.trx_locks, lock);
+ if (!holds_trx_mutex) {
+ trx_mutex_exit(trx);
+ }
MONITOR_INC(MONITOR_RECLOCK_CREATED);
+ MONITOR_INC(MONITOR_NUM_RECLOCK);
- return(lock);
+ return lock;
}
/*********************************************************************//**
@@ -1638,6 +1721,7 @@ If only one of them is a wait lock, it has lower priority.
If either is a high priority transaction, the lock has higher priority.
Otherwise, the one with an older transaction has higher priority.
@returns true if lock1 has higher priority, false otherwise. */
+static
bool
has_higher_priority(
lock_t *lock1,
@@ -1654,9 +1738,6 @@ has_higher_priority(
} else if (!lock_get_wait(lock2)) {
return false;
}
- if (trx_is_high_priority(lock1->trx)) {
- return false;
- }
return lock1->trx->start_time_micro <= lock2->trx->start_time_micro;
}
@@ -1781,389 +1862,118 @@ lock_rec_insert_to_head(
}
}
-/**
-Add the lock to the record lock hash and the transaction's lock list
-@param[in,out] lock Newly created record lock to add to the rec hash
-@param[in] add_to_hash If the lock should be added to the hash table */
-void
-RecLock::lock_add(lock_t* lock, bool add_to_hash)
-{
- ut_ad(lock_mutex_own());
- ut_ad(trx_mutex_own(lock->trx));
-
- bool wait_lock = m_mode & LOCK_WAIT;
-
- if (add_to_hash) {
- ulint key = m_rec_id.fold();
- hash_table_t *lock_hash = lock_hash_get(m_mode);
-
- ++lock->index->table->n_rec_locks;
-
- if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
- && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) {
- if (wait_lock) {
- HASH_INSERT(lock_t, hash, lock_hash, key, lock);
- } else {
- lock_rec_insert_to_head(lock, m_rec_id.fold());
- }
- } else {
- HASH_INSERT(lock_t, hash, lock_hash, key, lock);
- }
- }
-
- if (wait_lock) {
- lock_set_lock_and_trx_wait(lock, lock->trx);
- }
-
- UT_LIST_ADD_LAST(lock->trx->lock.trx_locks, lock);
-}
-
-/**
-Create a new lock.
-@param[in,out] trx Transaction requesting the lock
-@param[in] owns_trx_mutex true if caller owns the trx_t::mutex
-@param[in] add_to_hash add the lock to hash table
-@param[in] prdt Predicate lock (optional)
-@param[in,out] c_lock Conflicting lock request or NULL
- in Galera conflicting lock is selected
- as deadlock victim if requester
- is BF transaction.
-@return a new lock instance */
-lock_t*
-RecLock::create(
- trx_t* trx,
- bool owns_trx_mutex,
- bool add_to_hash,
- const lock_prdt_t* prdt
+/** Enqueue a waiting request for a lock which cannot be granted immediately.
+Check for deadlocks.
+@param[in] type_mode the requested lock mode (LOCK_S or LOCK_X)
+ possibly ORed with LOCK_GAP or
+ LOCK_REC_NOT_GAP, ORed with
+ LOCK_INSERT_INTENTION if this
+ waiting lock request is set
+ when performing an insert of
+ an index record
+@param[in] block leaf page in the index
+@param[in] heap_no record heap number in the block
+@param[in] index index tree
+@param[in,out] thr query thread
+@param[in] prdt minimum bounding box (spatial index)
+@retval DB_LOCK_WAIT if the waiting lock was enqueued
+@retval DB_DEADLOCK if this transaction was chosen as the victim
+@retval DB_SUCCESS_LOCKED_REC if the other transaction was chosen as a victim
+ (or it happened to commit) */
+dberr_t
+lock_rec_enqueue_waiting(
#ifdef WITH_WSREP
- ,lock_t* c_lock
-#endif /* WITH_WSREP */
-)
+ lock_t* c_lock, /*!< conflicting lock */
+#endif
+ ulint type_mode,
+ const buf_block_t* block,
+ ulint heap_no,
+ dict_index_t* index,
+ que_thr_t* thr,
+ lock_prdt_t* prdt)
{
ut_ad(lock_mutex_own());
- ut_ad(owns_trx_mutex == trx_mutex_own(trx));
-
- /* Create the explicit lock instance and initialise it. */
+ ut_ad(!srv_read_only_mode);
+ ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
- lock_t* lock = lock_alloc(trx, m_index, m_mode, m_rec_id, m_size);
+ trx_t* trx = thr_get_trx(thr);
- if (prdt != NULL && (m_mode & LOCK_PREDICATE)) {
+ ut_ad(trx_mutex_own(trx));
+ ut_a(!que_thr_stop(thr));
- lock_prdt_set_prdt(lock, prdt);
+ switch (trx_get_dict_operation(trx)) {
+ case TRX_DICT_OP_NONE:
+ break;
+ case TRX_DICT_OP_TABLE:
+ case TRX_DICT_OP_INDEX:
+ ib::error() << "A record lock wait happens in a dictionary"
+ " operation. index "
+ << index->name
+ << " of table "
+ << index->table->name
+ << ". " << BUG_REPORT_MSG;
+ ut_ad(0);
}
+ /* Enqueue the lock request that will wait to be granted, note that
+ we already own the trx mutex. */
+ lock_t* lock = lock_rec_create(
#ifdef WITH_WSREP
- if (c_lock && wsrep_on_trx(trx) &&
- wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
- lock_t *hash = (lock_t *)c_lock->hash;
- lock_t *prev = NULL;
-
- while (hash &&
- wsrep_thd_is_BF(((lock_t *)hash)->trx->mysql_thd, TRUE) &&
- wsrep_trx_order_before(
- ((lock_t *)hash)->trx->mysql_thd,
- trx->mysql_thd)) {
- prev = hash;
- hash = (lock_t *)hash->hash;
- }
-
- lock->hash = hash;
-
- if (prev) {
- prev->hash = lock;
- } else {
- c_lock->hash = lock;
- }
- /*
- * delayed conflict resolution '...kill_one_trx' was not called,
- * if victim was waiting for some other lock
- */
- trx_mutex_enter(c_lock->trx);
- if (c_lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
-
- c_lock->trx->lock.was_chosen_as_deadlock_victim = TRUE;
-
- if (wsrep_debug) {
- wsrep_print_wait_locks(c_lock);
- }
-
- trx->lock.que_state = TRX_QUE_LOCK_WAIT;
- lock_set_lock_and_trx_wait(lock, trx);
- UT_LIST_ADD_LAST(trx->lock.trx_locks, lock);
-
- ut_ad(m_thr != NULL);
- trx->lock.wait_thr = m_thr;
- m_thr->state = QUE_THR_LOCK_WAIT;
-
- /* have to release trx mutex for the duration of
- victim lock release. This will eventually call
- lock_grant, which wants to grant trx mutex again
- */
- if (owns_trx_mutex) {
- trx_mutex_exit(trx);
- }
-
- lock_cancel_waiting_and_release(
- c_lock->trx->lock.wait_lock);
-
- if (owns_trx_mutex) {
- trx_mutex_enter(trx);
- }
-
- /* trx might not wait for c_lock, but some other lock
- does not matter if wait_lock was released above
- */
- if (c_lock->trx->lock.wait_lock == c_lock) {
- if (wsrep_debug) {
- ib::info() <<
- "victim trx waits for some other lock than c_lock";
- }
- lock_reset_lock_and_trx_wait(lock);
- }
-
- trx_mutex_exit(c_lock->trx);
-
- if (wsrep_debug) {
- ib::info() << "WSREP: c_lock canceled " << ib::hex(c_lock->trx->id);
- ib::info() << " SQL1: "
- << wsrep_thd_query(c_lock->trx->mysql_thd);
- ib::info() << " SQL2: "
- << wsrep_thd_query(trx->mysql_thd);
- }
-
- ++lock->index->table->n_rec_locks;
- /* have to bail out here to avoid lock_set_lock... */
- return(lock);
- }
- trx_mutex_exit(c_lock->trx);
- /* we don't want to add to hash anymore, but need other updates from lock_add */
- ++lock->index->table->n_rec_locks;
- lock_add(lock, false);
- } else {
-#endif /* WITH_WSREP */
-
- /* Ensure that another transaction doesn't access the trx
- lock state and lock data structures while we are adding the
- lock and changing the transaction state to LOCK_WAIT */
-
- if (!owns_trx_mutex) {
- trx_mutex_enter(trx);
- }
-
- lock_add(lock, add_to_hash);
+ c_lock, thr,
+#endif
+ type_mode | LOCK_WAIT, block, heap_no, index, trx, TRUE);
- if (!owns_trx_mutex) {
- trx_mutex_exit(trx);
- }
-#ifdef WITH_WSREP
+ if (prdt && type_mode & LOCK_PREDICATE) {
+ lock_prdt_set_prdt(lock, prdt);
}
-#endif /* WITH_WSREP */
-
- return(lock);
-}
-
-/**
-Check the outcome of the deadlock check
-@param[in,out] victim_trx Transaction selected for rollback
-@param[in,out] lock Lock being requested
-@return DB_LOCK_WAIT, DB_DEADLOCK or DB_SUCCESS_LOCKED_REC */
-dberr_t
-RecLock::check_deadlock_result(const trx_t* victim_trx, lock_t* lock)
-{
- ut_ad(lock_mutex_own());
- ut_ad(m_trx == lock->trx);
- ut_ad(trx_mutex_own(m_trx));
-
- if (victim_trx != NULL) {
-
- ut_ad(victim_trx == m_trx);
+ if (const trx_t* victim =
+ DeadlockChecker::check_and_resolve(lock, trx)) {
+ ut_ad(victim == trx);
lock_reset_lock_and_trx_wait(lock);
+ lock_rec_reset_nth_bit(lock, heap_no);
+ return DB_DEADLOCK;
+ }
- lock_rec_reset_nth_bit(lock, m_rec_id.m_heap_no);
-
- return(DB_DEADLOCK);
-
- } else if (m_trx->lock.wait_lock == NULL) {
-
+ if (!trx->lock.wait_lock) {
/* If there was a deadlock but we chose another
transaction as a victim, it is possible that we
already have the lock now granted! */
-
- return(DB_SUCCESS_LOCKED_REC);
- }
-
- return(DB_LOCK_WAIT);
-}
-
-/**
-Check and resolve any deadlocks
-@param[in, out] lock The lock being acquired
-@return DB_LOCK_WAIT, DB_DEADLOCK, or
- DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
- there was a deadlock, but another transaction was chosen
- as a victim, and we got the lock immediately: no need to
- wait then */
-dberr_t
-RecLock::deadlock_check(lock_t* lock)
-{
- ut_ad(lock_mutex_own());
- ut_ad(lock->trx == m_trx);
- ut_ad(trx_mutex_own(m_trx));
-
- const trx_t* victim_trx =
- DeadlockChecker::check_and_resolve(lock, m_trx);
-
- /* Check the outcome of the deadlock test. It is possible that
- the transaction that blocked our lock was rolled back and we
- were granted our lock. */
-
- dberr_t err = check_deadlock_result(victim_trx, lock);
-
- if (err == DB_LOCK_WAIT) {
-
- set_wait_state(lock);
-
- MONITOR_INC(MONITOR_LOCKREC_WAIT);
- }
-
- return(err);
-}
-
-/**
-Collect the transactions that will need to be rolled back asynchronously
-@param[in, out] trx Transaction to be rolled back */
-void
-RecLock::mark_trx_for_rollback(trx_t* trx)
-{
- trx->abort = true;
-
- ut_ad(!trx->read_only);
- ut_ad(trx_mutex_own(m_trx));
- ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK));
- ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC));
- ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE));
-
- /* Note that we will attempt an async rollback. The _ASYNC
- flag will be cleared if the transaction is rolled back
- synchronously before we get a chance to do it. */
-
- trx->in_innodb |= TRX_FORCE_ROLLBACK | TRX_FORCE_ROLLBACK_ASYNC;
-
- ut_a(!trx->killed_by);
- my_atomic_storelong(&trx->killed_by, (long) os_thread_get_curr_id());
-
- m_trx->hit_list.push_back(hit_list_t::value_type(trx));
-
-#ifdef UNIV_DEBUG
- THD* thd = trx->mysql_thd;
-
- if (thd != NULL) {
-
- char buffer[1024];
- ib::info() << "Blocking transaction: ID: " << ib::hex(trx->id) << " - "
- << " Blocked transaction ID: "<< ib::hex(m_trx->id) << " - "
- << thd_get_error_context_description(thd, buffer, sizeof(buffer),
- 512);
- }
-#endif /* UNIV_DEBUG */
-}
-
-/**
-Setup the requesting transaction state for lock grant
-@param[in,out] lock Lock for which to change state */
-void
-RecLock::set_wait_state(lock_t* lock)
-{
- ut_ad(lock_mutex_own());
- ut_ad(m_trx == lock->trx);
- ut_ad(trx_mutex_own(m_trx));
- ut_ad(lock_get_wait(lock));
-
- m_trx->lock.wait_started = ut_time();
-
- m_trx->lock.que_state = TRX_QUE_LOCK_WAIT;
-
- m_trx->lock.was_chosen_as_deadlock_victim = false;
-
- bool stopped = que_thr_stop(m_thr);
- ut_a(stopped);
-}
-
-/**
-Enqueue a lock wait for normal transaction. If it is a high priority transaction
-then jump the record lock wait queue and if the transaction at the head of the
-queue is itself waiting roll it back, also do a deadlock check and resolve.
-@param[in, out] wait_for The lock that the joining transaction is
- waiting for
-@param[in] prdt Predicate [optional]
-@return DB_LOCK_WAIT, DB_DEADLOCK, or
- DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
- there was a deadlock, but another transaction was chosen
- as a victim, and we got the lock immediately: no need to
- wait then */
-dberr_t
-RecLock::add_to_waitq(lock_t* wait_for, const lock_prdt_t* prdt)
-{
- ut_ad(lock_mutex_own());
- ut_ad(m_trx == thr_get_trx(m_thr));
- ut_ad(trx_mutex_own(m_trx));
-
- DEBUG_SYNC_C("rec_lock_add_to_waitq");
-
- m_mode |= LOCK_WAIT;
-
- /* Do the preliminary checks, and set query thread state */
-
- prepare();
-
- bool high_priority = trx_is_high_priority(m_trx);
-
- /* Don't queue the lock to hash table, if high priority transaction. */
- lock_t* lock = create(
- m_trx, true, !high_priority, prdt
#ifdef WITH_WSREP
- ,wait_for
-#endif /* WITH_WSREP */
- );
+ if (wsrep_debug) {
+ ib::info() << "WSREP: BF thread got lock granted early, ID " << ib::hex(trx->id)
+ << " query: " << wsrep_thd_query(trx->mysql_thd);
+ }
+#endif
+ return DB_SUCCESS_LOCKED_REC;
+ }
- /* Attempt to jump over the low priority waiting locks. */
- if (high_priority && jump_queue(lock, wait_for)) {
+ trx->lock.que_state = TRX_QUE_LOCK_WAIT;
- /* Lock is granted */
- return(DB_SUCCESS);
- }
+ trx->lock.was_chosen_as_deadlock_victim = false;
+ trx->lock.wait_started = ut_time();
-#ifdef WITH_WSREP
- if (!lock_get_wait(lock) && wsrep_thd_is_BF(m_trx->mysql_thd, FALSE)) {
- if (wsrep_debug) {
- ib::info() << "WSREP: BF thread got lock granted early, ID " << ib::hex(lock->trx->id)
- << " query: " << wsrep_thd_query(m_trx->mysql_thd);
- }
- return(DB_SUCCESS);
- }
-#endif /* WITH_WSREP */
- ut_ad(lock_get_wait(lock));
+ ut_a(que_thr_stop(thr));
- dberr_t err = deadlock_check(lock);
- ut_ad(trx_mutex_own(m_trx));
+ DBUG_LOG("ib_lock", "trx " << ib::hex(trx->id)
+ << " waits for lock in index " << index->name
+ << " of table " << index->table->name);
- // Move it only when it does not cause a deadlock.
- if (err != DB_DEADLOCK
- && innodb_lock_schedule_algorithm
- == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
- && !thd_is_replication_slave_thread(lock->trx->mysql_thd)
- && !trx_is_high_priority(lock->trx)) {
+ MONITOR_INC(MONITOR_LOCKREC_WAIT);
- HASH_DELETE(lock_t, hash, lock_hash_get(lock->type_mode),
- m_rec_id.fold(), lock);
+ if (innodb_lock_schedule_algorithm
+ == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
+ && !prdt
+ && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) {
+ HASH_DELETE(lock_t, hash, lock_sys->rec_hash,
+ lock_rec_lock_fold(lock), lock);
dberr_t res = lock_rec_insert_by_trx_age(lock);
if (res != DB_SUCCESS) {
return res;
}
}
- return(err);
+ return DB_LOCK_WAIT;
}
/*********************************************************************//**
@@ -2288,9 +2098,11 @@ lock_rec_add_to_queue(
}
}
- RecLock rec_lock(index, block, heap_no, type_mode);
-
- rec_lock.create(trx, caller_owns_trx_mutex, true);
+ lock_rec_create(
+#ifdef WITH_WSREP
+ NULL, NULL,
+#endif
+ type_mode, block, heap_no, index, trx, caller_owns_trx_mutex);
}
/*********************************************************************//**
@@ -2341,12 +2153,13 @@ lock_rec_lock_fast(
lock_rec_req_status status = LOCK_REC_SUCCESS;
if (lock == NULL) {
-
if (!impl) {
- RecLock rec_lock(index, block, heap_no, mode);
-
/* Note that we don't own the trx mutex. */
- rec_lock.create(trx, false, true);
+ lock = lock_rec_create(
+#ifdef WITH_WSREP
+ NULL, NULL,
+#endif
+ mode, block, heap_no, index, trx, false);
}
status = LOCK_REC_SUCCESS_CREATED;
@@ -2419,40 +2232,32 @@ lock_rec_lock_slow(
trx_mutex_enter(trx);
if (lock_rec_has_expl(mode, block, heap_no, trx)) {
-
- /* The trx already has a strong enough lock on rec: do
- nothing */
-
+ /* The trx already has a strong enough lock: do nothing */
err = DB_SUCCESS;
-
+ } else if (
+#ifdef WITH_WSREP
+ lock_t* c_lock =
+#endif /* WITH_WSREP */
+ lock_rec_other_has_conflicting(
+ static_cast<enum lock_mode>(mode),
+ block, heap_no, trx)) {
+ /* If another transaction has a non-gap conflicting
+ request in the queue, as this transaction does not
+ have a lock strong enough already granted on the
+ record, we have to wait. */
+ err = lock_rec_enqueue_waiting(
+#ifdef WITH_WSREP
+ c_lock,
+#endif /* WITH_WSREP */
+ mode, block, heap_no, index, thr, NULL);
+ } else if (!impl) {
+ /* Set the requested lock on the record, note that
+ we already own the transaction mutex. */
+ lock_rec_add_to_queue(
+ LOCK_REC | mode, block, heap_no, index, trx, TRUE);
+ err = DB_SUCCESS_LOCKED_REC;
} else {
- lock_t* wait_for = lock_rec_other_has_conflicting(
- mode, block, heap_no, trx);
-
- if (wait_for != NULL) {
-
- /* If another transaction has a non-gap conflicting
- request in the queue, as this transaction does not
- have a lock strong enough already granted on the
- record, we may have to wait. */
-
- RecLock rec_lock(thr, index, block, heap_no, mode);
-
- err = rec_lock.add_to_waitq(wait_for);
-
- } else if (!impl) {
-
- /* Set the requested lock on the record, note that
- we already own the transaction mutex. */
-
- lock_rec_add_to_queue(
- LOCK_REC | mode, block, heap_no, index, trx,
- true);
-
- err = DB_SUCCESS_LOCKED_REC;
- } else {
- err = DB_SUCCESS;
- }
+ err = DB_SUCCESS;
}
trx_mutex_exit(trx);
@@ -2630,234 +2435,6 @@ lock_grant(
}
}
-/**
-Jump the queue for the record over all low priority transactions and
-add the lock. If all current granted locks are compatible, grant the
-lock. Otherwise, mark all granted transaction for asynchronous
-rollback and add to hit list.
-@param[in, out] lock Lock being requested
-@param[in] conflict_lock First conflicting lock from the head
-@return true if the lock is granted */
-bool
-RecLock::jump_queue(
- lock_t* lock,
- const lock_t* conflict_lock)
-{
- ut_ad(m_trx == lock->trx);
- ut_ad(trx_mutex_own(m_trx));
- ut_ad(conflict_lock->trx != m_trx);
- ut_ad(trx_is_high_priority(m_trx));
- ut_ad(m_rec_id.m_heap_no != ULINT32_UNDEFINED);
-
- bool high_priority = false;
-
- /* Find out the position to add the lock. If there are other high
- priority transactions in waiting state then we should add it after
- the last high priority transaction. Otherwise, we can add it after
- the last granted lock jumping over the wait queue. */
- bool grant_lock = lock_add_priority(lock, conflict_lock,
- &high_priority);
-
- if (grant_lock) {
-
- ut_ad(conflict_lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT);
- ut_ad(conflict_lock->trx->lock.wait_lock == conflict_lock);
-
- DBUG_LOG("trx",
- "Granting High Priority Transaction "
- << ib::hex(lock->trx->id) << " a lock jumping over"
- << " waiting Transaction " << ib::hex(conflict_lock->trx->id));
-
- lock_reset_lock_and_trx_wait(lock);
- return(true);
- }
-
- /* If another high priority transaction is found waiting
- victim transactions are already marked for rollback. */
- if (high_priority) {
-
- return(false);
- }
-
- /* The lock is placed after the last granted lock in the queue. Check and add
- low priority transactinos to hit list for ASYNC rollback. */
- make_trx_hit_list(lock, conflict_lock);
-
- return(false);
-}
-
-/** Find position in lock queue and add the high priority transaction
-lock. Intention and GAP only locks can be granted even if there are
-waiting locks in front of the queue. To add the High priority
-transaction in a safe position we keep the following rule.
-
-1. If the lock can be granted, add it before the first waiting lock
-in the queue so that all currently waiting locks need to do conflict
-check before getting granted.
-
-2. If the lock has to wait, add it after the last granted lock or the
-last waiting high priority transaction in the queue whichever is later.
-This ensures that the transaction is granted only after doing conflict
-check with all granted transactions.
-@param[in] lock Lock being requested
-@param[in] conflict_lock First conflicting lock from the head
-@param[out] high_priority high priority transaction ahead in queue
-@return true if the lock can be granted */
-bool
-RecLock::lock_add_priority(
- lock_t* lock,
- const lock_t* conflict_lock,
- bool* high_priority)
-{
- ut_ad(high_priority);
-
- *high_priority = false;
-
- /* If the first conflicting lock is waiting for the current row,
- then all other granted locks are compatible and the lock can be
- directly granted if no other high priority transactions are
- waiting. We need to recheck with all granted transaction as there
- could be granted GAP or Intention locks down the queue. */
- bool grant_lock = (conflict_lock->is_waiting());
- lock_t* lock_head = NULL;
- lock_t* grant_position = NULL;
- lock_t* add_position = NULL;
-
- /* Different lock (such as predicate lock) are on different hash */
- hash_table_t* lock_hash = lock_hash_get(m_mode);
-
- HASH_SEARCH(hash, lock_hash, m_rec_id.fold(), lock_t*,
- lock_head, ut_ad(lock_head->is_record_lock()), true);
-
- ut_ad(lock_head);
-
- for (lock_t* next = lock_head; next != NULL; next = next->hash) {
-
- /* check only for locks on the current row */
- if (!is_on_row(next)) {
- continue;
- }
-
- if (next->is_waiting()) {
- /* grant lock position is the granted lock just before
- the first wait lock in the queue. */
- if (grant_position == NULL) {
- grant_position = add_position;
- }
-
- if (trx_is_high_priority(next->trx)) {
-
- *high_priority = true;
- grant_lock = false;
- add_position = next;
- }
- } else {
-
- add_position = next;
- /* Cannot grant lock if there is any conflicting
- granted lock. */
- if (grant_lock && lock_has_to_wait(lock, next)) {
- grant_lock = false;
- }
- }
- }
-
- /* If the lock is to be granted it is safe to add before the first
- waiting lock in the queue. */
- if (grant_lock) {
-
- ut_ad(!lock_has_to_wait(lock, grant_position));
- add_position = grant_position;
- }
-
- ut_ad(add_position != NULL);
-
- /* Add the lock to lock hash table. */
- lock->hash = add_position->hash;
- add_position->hash = lock;
- ++lock->index->table->n_rec_locks;
-
- return(grant_lock);
-}
-
-/** Iterate over the granted locks and prepare the hit list for ASYNC Rollback.
-If the transaction is waiting for some other lock then wake up with deadlock error.
-Currently we don't mark following transactions for ASYNC Rollback.
-1. Read only transactions
-2. Background transactions
-3. Other High priority transactions
-@param[in] lock Lock being requested
-@param[in] conflict_lock First conflicting lock from the head */
-void
-RecLock::make_trx_hit_list(
- lock_t* lock,
- const lock_t* conflict_lock)
-{
- const lock_t* next;
-
- for (next = conflict_lock; next != NULL; next = next->hash) {
-
- /* All locks ahead in the queue are checked. */
- if (next == lock) {
-
- ut_ad(next->is_waiting());
- break;
- }
-
- trx_t* trx = next->trx;
- /* Check only for conflicting, granted locks on the current row.
- Currently, we don't rollback read only transactions, transactions
- owned by background threads. */
- if (trx == lock->trx
- || !is_on_row(next)
- || next->is_waiting()
- || trx->read_only
- || trx->mysql_thd == NULL
- || !lock_has_to_wait(lock, next)) {
-
- continue;
- }
-
- trx_mutex_enter(trx);
-
- /* Skip high priority transactions, if already marked for abort
- by some other transaction or if ASYNC rollback is disabled. A
- transaction must complete kill/abort of a victim transaction once
- marked and added to hit list. */
- if (trx_is_high_priority(trx)
- || (trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE) != 0
- || trx->abort) {
-
- trx_mutex_exit(trx);
- continue;
- }
-
- /* If the transaction is waiting on some other resource then
- wake it up with DEAD_LOCK error so that it can rollback. */
- if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
-
- /* Assert that it is not waiting for current record. */
- ut_ad(trx->lock.wait_lock != next);
-
- DBUG_LOG("trx", "High Priority Transaction "
- << ib::hex(lock->trx->id)
- << " waking up blocking transaction "
- << ib::hex(trx->id));
-
- trx->lock.was_chosen_as_deadlock_victim = true;
- lock_cancel_waiting_and_release(trx->lock.wait_lock);
- trx_mutex_exit(trx);
- continue;
- }
-
- /* Mark for ASYNC Rollback and add to hit list. */
- mark_trx_for_rollback(trx);
- trx_mutex_exit(trx);
- }
-
- ut_ad(next == lock);
-}
-
/*************************************************************//**
Cancels a waiting record lock request and releases the waiting transaction
that requested it. NOTE: does NOT check if waiting lock requests behind this
@@ -2981,7 +2558,6 @@ lock_rec_dequeue_from_page(
space = in_lock->un_member.rec_lock.space;
page_no = in_lock->un_member.rec_lock.page_no;
- ut_ad(in_lock->index->table->n_rec_locks > 0);
in_lock->index->table->n_rec_locks--;
lock_hash = lock_hash_get(in_lock->type_mode);
@@ -3042,7 +2618,6 @@ lock_rec_discard(
space = in_lock->un_member.rec_lock.space;
page_no = in_lock->un_member.rec_lock.page_no;
- ut_ad(in_lock->index->table->n_rec_locks > 0);
in_lock->index->table->n_rec_locks--;
HASH_DELETE(lock_t, hash, lock_hash_get(in_lock->type_mode),
@@ -3826,10 +3401,10 @@ lock_update_merge_right(
#ifdef UNIV_DEBUG
/* there should exist no page lock on the left page,
otherwise, it will be blocked from merge */
- ulint space = left_block->page.id.space();
- ulint page_no = left_block->page.id.page_no();
+ ulint space = left_block->page.id.space();
+ ulint page_no = left_block->page.id.page_no();
ut_ad(lock_rec_get_first_on_page_addr(
- lock_sys->prdt_page_hash, space, page_no) == NULL);
+ lock_sys->prdt_page_hash, space, page_no) == NULL);
#endif /* UNIV_DEBUG */
lock_rec_free_all_from_discard_page(left_block);
@@ -3954,7 +3529,7 @@ lock_update_merge_left(
ulint space = right_block->page.id.space();
ulint page_no = right_block->page.id.page_no();
lock_t* lock_test = lock_rec_get_first_on_page_addr(
- lock_sys->prdt_page_hash, space, page_no);
+ lock_sys->prdt_page_hash, space, page_no);
ut_ad(!lock_test);
#endif /* UNIV_DEBUG */
@@ -4001,9 +3576,9 @@ lock_update_discard(
const buf_block_t* block) /*!< in: index page
which will be discarded */
{
+ const page_t* page = block->frame;
const rec_t* rec;
ulint heap_no;
- const page_t* page = block->frame;
lock_mutex_enter();
@@ -4444,10 +4019,9 @@ lock_table_remove_low(
/*********************************************************************//**
Enqueues a waiting request for a table lock which cannot be granted
immediately. Checks for deadlocks.
-@return DB_LOCK_WAIT, DB_DEADLOCK, or
-DB_SUCCESS; DB_SUCCESS means that there was a deadlock, but another
-transaction was chosen as a victim, and we got the lock immediately:
-no need to wait then */
+@retval DB_LOCK_WAIT if the waiting lock was enqueued
+@retval DB_DEADLOCK if this transaction was chosen as the victim
+@retval DB_SUCCESS if the other transaction committed or aborted */
static
dberr_t
lock_table_enqueue_waiting(
@@ -5036,7 +4610,7 @@ lock_release(
}
if (count == LOCK_RELEASE_INTERVAL) {
- /* Release the mutex for a while, so that we
+ /* Release the mutex for a while, so that we
do not monopolize it */
lock_mutex_exit();
@@ -6548,19 +6122,21 @@ lock_rec_insert_check_and_lock(
const ulint type_mode = LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION;
- lock_t* wait_for = lock_rec_other_has_conflicting(
- type_mode, block, heap_no, trx);
-
- if (wait_for != NULL) {
-
- RecLock rec_lock(thr, index, block, heap_no, type_mode);
-
+ if (
+#ifdef WITH_WSREP
+ lock_t* c_lock =
+#endif /* WITH_WSREP */
+ lock_rec_other_has_conflicting(type_mode, block, heap_no, trx)) {
+ /* Note that we may get DB_SUCCESS also here! */
trx_mutex_enter(trx);
- err = rec_lock.add_to_waitq(wait_for);
+ err = lock_rec_enqueue_waiting(
+#ifdef WITH_WSREP
+ c_lock,
+#endif /* WITH_WSREP */
+ type_mode, block, heap_no, index, thr, NULL);
trx_mutex_exit(trx);
-
} else {
err = DB_SUCCESS;
}
@@ -7369,7 +6945,6 @@ lock_unlock_table_autoinc(
but not COMMITTED transactions. */
ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED)
- || trx_state_eq(trx, TRX_STATE_FORCED_ROLLBACK)
|| !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
/* This function is invoked for a running transaction by the
@@ -7992,24 +7567,9 @@ DeadlockChecker::select_victim() const
ut_ad(m_start->lock.wait_lock != 0);
ut_ad(m_wait_lock->trx != m_start);
- if (thd_trx_priority(m_start->mysql_thd) > 0
- || thd_trx_priority(m_wait_lock->trx->mysql_thd) > 0) {
-
- const trx_t* victim;
-
- victim = trx_arbitrate(m_start, m_wait_lock->trx);
-
- if (victim != NULL) {
-
- return(victim);
- }
- }
-
if (trx_weight_ge(m_wait_lock->trx, m_start)) {
-
/* The joining transaction is 'smaller',
choose it as the victim and roll it back. */
-
#ifdef WITH_WSREP
if (wsrep_thd_is_BF(m_start->mysql_thd, TRUE)) {
return(m_wait_lock->trx);
@@ -8208,12 +7768,7 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx)
check_trx_state(trx);
ut_ad(!srv_read_only_mode);
- /* If transaction is marked for ASYNC rollback then we should
- not allow it to wait for another lock causing possible deadlock.
- We return current transaction as deadlock victim here. */
- if (trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC) {
- return(trx);
- } else if (!innobase_deadlock_detect) {
+ if (!innobase_deadlock_detect) {
return(NULL);
}
diff --git a/storage/innobase/lock/lock0prdt.cc b/storage/innobase/lock/lock0prdt.cc
index f6859b70297..23a46a002be 100644
--- a/storage/innobase/lock/lock0prdt.cc
+++ b/storage/innobase/lock/lock0prdt.cc
@@ -38,6 +38,7 @@ Created 9/7/2013 Jimmy Yang
#include "ut0vec.h"
#include "btr0btr.h"
#include "dict0boot.h"
+#include "que0que.h"
#include <set>
/*********************************************************************//**
@@ -495,9 +496,18 @@ lock_prdt_add_to_queue(
}
}
- RecLock rec_lock(index, block, PRDT_HEAPNO, type_mode);
+ lock = lock_rec_create(
+#ifdef WITH_WSREP
+ NULL, NULL, /* FIXME: replicate SPATIAL INDEX locks */
+#endif
+ type_mode, block, PRDT_HEAPNO, index, trx,
+ caller_owns_trx_mutex);
- return(rec_lock.create(trx, caller_owns_trx_mutex, true, prdt));
+ if (lock->type_mode & LOCK_PREDICATE) {
+ lock_prdt_set_prdt(lock, prdt);
+ }
+
+ return lock;
}
/*********************************************************************//**
@@ -565,7 +575,7 @@ lock_prdt_insert_check_and_lock(
const ulint mode = LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION;
- lock_t* wait_for = lock_prdt_other_has_conflicting(
+ const lock_t* wait_for = lock_prdt_other_has_conflicting(
mode, block, prdt, trx);
if (wait_for != NULL) {
@@ -574,16 +584,17 @@ lock_prdt_insert_check_and_lock(
/* Allocate MBR on the lock heap */
lock_init_prdt_from_mbr(prdt, mbr, 0, trx->lock.lock_heap);
- RecLock rec_lock(thr, index, block, PRDT_HEAPNO, mode);
-
/* Note that we may get DB_SUCCESS also here! */
-
trx_mutex_enter(trx);
- err = rec_lock.add_to_waitq(wait_for, prdt);
+ err = lock_rec_enqueue_waiting(
+#ifdef WITH_WSREP
+ NULL, /* FIXME: replicate SPATIAL INDEX locks */
+#endif
+ LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION,
+ block, PRDT_HEAPNO, index, thr, prdt);
trx_mutex_exit(trx);
-
} else {
err = DB_SUCCESS;
}
@@ -831,13 +842,14 @@ lock_prdt_lock(
lock_t* lock = lock_rec_get_first_on_page(hash, block);
if (lock == NULL) {
-
- RecLock rec_lock(index, block, PRDT_HEAPNO, prdt_mode);
-
- lock = rec_lock.create(trx, false, true);
+ lock = lock_rec_create(
+#ifdef WITH_WSREP
+ NULL, NULL, /* FIXME: replicate SPATIAL INDEX locks */
+#endif
+ mode | type_mode, block, PRDT_HEAPNO,
+ index, trx, FALSE);
status = LOCK_REC_SUCCESS_CREATED;
-
} else {
trx_mutex_enter(trx);
@@ -861,12 +873,14 @@ lock_prdt_lock(
if (wait_for != NULL) {
- RecLock rec_lock(
- thr, index, block, PRDT_HEAPNO,
- prdt_mode, prdt);
-
- err = rec_lock.add_to_waitq(wait_for);
-
+ err = lock_rec_enqueue_waiting(
+#ifdef WITH_WSREP
+ NULL, /* FIXME: replicate
+ SPATIAL INDEX locks */
+#endif
+ mode | type_mode,
+ block, PRDT_HEAPNO,
+ index, thr, prdt);
} else {
lock_prdt_add_to_queue(
@@ -947,10 +961,12 @@ lock_place_prdt_page_lock(
}
if (lock == NULL) {
- RecID rec_id(space, page_no, PRDT_HEAPNO);
- RecLock rec_lock(index, rec_id, mode);
-
- rec_lock.create(trx, false, true);
+ lock = lock_rec_create_low(
+#ifdef WITH_WSREP
+ NULL, NULL, /* FIXME: replicate SPATIAL INDEX locks */
+#endif
+ mode, space, page_no, NULL, PRDT_HEAPNO,
+ index, trx, FALSE);
#ifdef PRDT_DIAG
printf("GIS_DIAGNOSTIC: page lock %d\n", (int) page_no);
diff --git a/storage/innobase/lock/lock0wait.cc b/storage/innobase/lock/lock0wait.cc
index c41821412af..d6c812e1af0 100644
--- a/storage/innobase/lock/lock0wait.cc
+++ b/storage/innobase/lock/lock0wait.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2014, 2017, MariaDB Corporation.
+Copyright (c) 2014, 2018, 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
@@ -419,7 +419,7 @@ lock_wait_suspend_thread(
&& (!wsrep_on_trx(trx) ||
(!wsrep_is_BF_lock_timeout(trx, false) && trx->error_state != DB_DEADLOCK))
#endif /* WITH_WSREP */
- && !trx_is_high_priority(trx)) {
+ ) {
trx->error_state = DB_LOCK_WAIT_TIMEOUT;
@@ -502,7 +502,7 @@ lock_wait_check_and_cancel(
trx_mutex_enter(trx);
- if (trx->lock.wait_lock != NULL && !trx_is_high_priority(trx)) {
+ if (trx->lock.wait_lock != NULL) {
ut_a(trx->lock.que_state == TRX_QUE_LOCK_WAIT);
diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc
index fb528843da6..c33623e9398 100644
--- a/storage/innobase/page/page0page.cc
+++ b/storage/innobase/page/page0page.cc
@@ -2,7 +2,7 @@
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2018, 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
@@ -35,6 +35,7 @@ Created 2/2/1994 Heikki Tuuri
#include "lock0lock.h"
#include "fut0lst.h"
#include "btr0sea.h"
+#include "trx0sys.h"
/* THE INDEX PAGE
==============
diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc
index 271ef4a787e..6b68ee973af 100644
--- a/storage/innobase/page/page0zip.cc
+++ b/storage/innobase/page/page0zip.cc
@@ -45,6 +45,7 @@ const byte field_ref_zero[FIELD_REF_SIZE] = {
#include "btr0cur.h"
#include "page0types.h"
#include "log0recv.h"
+#include "row0row.h"
#include "row0trunc.h"
#include "zlib.h"
#include "buf0buf.h"
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index 98e94e06464..76809222f2c 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -1852,8 +1852,6 @@ do_possible_lock_wait:
my_atomic_addlint(
&check_table->n_foreign_key_checks_running, 1);
- trx_kill_blocking(trx);
-
lock_wait_suspend_thread(thr);
thr->lock_state = QUE_THR_LOCK_NOLOCK;
diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc
index c50e8fbd6ae..cbd5aa2f316 100644
--- a/storage/innobase/row/row0merge.cc
+++ b/storage/innobase/row/row0merge.cc
@@ -36,6 +36,7 @@ Completed by Sunny Bains and Marko Makela
#include "row0ext.h"
#include "row0log.h"
#include "row0ins.h"
+#include "row0row.h"
#include "row0sel.h"
#include "log0crypt.h"
#include "dict0crea.h"
@@ -45,6 +46,7 @@ Completed by Sunny Bains and Marko Makela
#include "ut0sort.h"
#include "row0ftsort.h"
#include "row0import.h"
+#include "row0vers.h"
#include "handler0alter.h"
#include "btr0bulk.h"
#include "fsp0sysspace.h"
@@ -3586,8 +3588,6 @@ row_merge_lock_table(
trx->op_info = "setting table lock for creating or dropping index";
trx->ddl = true;
- /* Trx for DDL should not be forced to rollback for now */
- trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
return(lock_table_for_trx(table, trx, mode));
}
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 49a042acc20..cd3e7253298 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -729,9 +729,6 @@ handle_new_error:
/* MySQL will roll back the latest SQL statement */
break;
case DB_LOCK_WAIT:
-
- trx_kill_blocking(trx);
-
lock_wait_suspend_thread(thr);
if (trx->error_state != DB_SUCCESS) {
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 623961945b2..c42b08d820b 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -4437,17 +4437,12 @@ row_search_mvcc(
naturally moves upward (in fetch next) in alphabetical order,
otherwise downward */
- if (direction == 0) {
-
- if (mode == PAGE_CUR_GE
- || mode == PAGE_CUR_G
+ if (UNIV_UNLIKELY(direction == 0)) {
+ if (mode == PAGE_CUR_GE || mode == PAGE_CUR_G
|| mode >= PAGE_CUR_CONTAIN) {
-
moves_up = TRUE;
}
-
} else if (direction == ROW_SEL_NEXT) {
-
moves_up = TRUE;
}
@@ -5682,15 +5677,6 @@ normal_return:
mtr.commit();
- /* Rollback blocking transactions from hit list for high priority
- transaction, if any. We should not be holding latches here as
- we are going to rollback the blocking transactions. */
- if (!trx->hit_list.empty()) {
-
- ut_ad(trx_is_high_priority(trx));
- trx_kill_blocking(trx);
- }
-
DEBUG_SYNC_C("row_search_for_mysql_before_return");
if (prebuilt->idx_cond != 0) {
diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc
index 068b4d96ed2..94be5152596 100644
--- a/storage/innobase/row/row0trunc.cc
+++ b/storage/innobase/row/row0trunc.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2013, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2018, 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
@@ -36,7 +36,8 @@ Created 2013-04-12 Sunny Bains
#include "srv0start.h"
#include "row0trunc.h"
#include "os0file.h"
-#include <vector>
+#include "que0que.h"
+#include "trx0undo.h"
/* FIXME: For temporary tables, use a simple approach of btr_free()
and btr_create() of each index tree. */
diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc
index 5c2258f25be..a5be54b7b79 100644
--- a/storage/innobase/trx/trx0roll.cc
+++ b/storage/innobase/trx/trx0roll.cc
@@ -183,10 +183,7 @@ trx_rollback_for_mysql_low(
/** Rollback a transaction used in MySQL
@param[in, out] trx transaction
@return error code or DB_SUCCESS */
-static
-dberr_t
-trx_rollback_low(
- trx_t* trx)
+dberr_t trx_rollback_for_mysql(trx_t* trx)
{
/* We are reading trx->state without holding trx_sys->mutex
here, because the rollback should be invoked for a running
@@ -194,7 +191,6 @@ trx_rollback_low(
that is associated with the current thread. */
switch (trx->state) {
- case TRX_STATE_FORCED_ROLLBACK:
case TRX_STATE_NOT_STARTED:
trx->will_lock = 0;
ut_ad(trx->in_mysql_trx_list);
@@ -262,28 +258,6 @@ trx_rollback_low(
}
/*******************************************************************//**
-Rollback a transaction used in MySQL.
-@return error code or DB_SUCCESS */
-dberr_t
-trx_rollback_for_mysql(
-/*===================*/
- trx_t* trx) /*!< in/out: transaction */
-{
- /* Avoid the tracking of async rollback killer
- thread to enter into InnoDB. */
- if (TrxInInnoDB::is_async_rollback(trx)) {
-
- return(trx_rollback_low(trx));
-
- } else {
-
- TrxInInnoDB trx_in_innodb(trx, true);
-
- return(trx_rollback_low(trx));
- }
-}
-
-/*******************************************************************//**
Rollback the latest SQL statement for MySQL.
@return error code or DB_SUCCESS */
dberr_t
@@ -300,7 +274,6 @@ trx_rollback_last_sql_stat_for_mysql(
ut_ad(trx->in_mysql_trx_list);
switch (trx->state) {
- case TRX_STATE_FORCED_ROLLBACK:
case TRX_STATE_NOT_STARTED:
return(DB_SUCCESS);
@@ -487,12 +460,9 @@ trx_rollback_to_savepoint_for_mysql(
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
-
ib::error() << "Transaction has a savepoint "
<< savep->name
<< " though it is not started";
-
return(DB_ERROR);
case TRX_STATE_ACTIVE:
@@ -780,7 +750,6 @@ fake_prepared:
case TRX_STATE_PREPARED:
goto func_exit;
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
break;
}
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index c6e2bd88d62..1c058a3ede5 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -43,7 +43,6 @@ Created 3/26/1996 Heikki Tuuri
#include "srv0mon.h"
#include "srv0srv.h"
#include "fsp0sysspace.h"
-#include "row0mysql.h"
#include "srv0start.h"
#include "trx0purge.h"
#include "trx0rec.h"
@@ -69,15 +68,6 @@ typedef std::set<
std::less<table_id_t>,
ut_allocator<table_id_t> > table_id_set;
-/** Constructor */
-TrxVersion::TrxVersion(trx_t* trx)
- :
- m_trx(trx),
- m_version(trx->version)
-{
- /* No op */
-}
-
/** Set flush observer for the transaction
@param[in/out] trx transaction struct
@param[in] observer flush observer */
@@ -121,14 +111,12 @@ trx_init(
/*=====*/
trx_t* trx)
{
- /* This is called at the end of commit, do not reset the
- trx_t::state here to NOT_STARTED. The FORCED_ROLLBACK
- status is required for asynchronous handling. */
-
trx->id = 0;
trx->no = TRX_ID_MAX;
+ trx->state = TRX_STATE_NOT_STARTED;
+
trx->is_recovered = false;
trx->op_info = "";
@@ -183,29 +171,7 @@ trx_init(
trx->lock.table_cached = 0;
- /* During asynchronous rollback, we should reset forced rollback flag
- only after rollback is complete to avoid race with the thread owning
- the transaction. */
-
- if (!TrxInInnoDB::is_async_rollback(trx)) {
-
- my_atomic_storelong(&trx->killed_by, 0);
-
- /* Note: Do not set to 0, the ref count is decremented inside
- the TrxInInnoDB() destructor. We only need to clear the flags. */
-
- trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
- }
-
- /* Note: It's possible that this list is not empty if a transaction
- was interrupted after it collected the victim transactions and before
- it got a chance to roll them back asynchronously. */
-
- trx->hit_list.clear();
-
trx->flush_observer = NULL;
-
- ++trx->version;
}
/** For managing the life-cycle of the trx_t instance that we get
@@ -220,7 +186,7 @@ struct TrxFactory {
{
/* Explicitly call the constructor of the already
allocated object. trx_t objects are allocated by
- ut_zalloc() in Pool::Pool() which would not call
+ ut_zalloc_nokey() in Pool::Pool() which would not call
the constructors of the trx_t members. */
new(&trx->mod_tables) trx_mod_tables_t();
@@ -230,13 +196,8 @@ struct TrxFactory {
new(&trx->lock.table_locks) lock_pool_t();
- new(&trx->hit_list) hit_list_t();
-
trx_init(trx);
- DBUG_LOG("trx", "Init: " << trx);
- trx->state = TRX_STATE_NOT_STARTED;
-
trx->dict_operation_lock_mode = 0;
trx->xid = UT_NEW_NOKEY(xid_t());
@@ -309,8 +270,6 @@ struct TrxFactory {
trx->lock.table_pool.~lock_pool_t();
trx->lock.table_locks.~lock_pool_t();
-
- trx->hit_list.~hit_list_t();
}
/** Enforce any invariants here, this is called before the transaction
@@ -324,8 +283,7 @@ struct TrxFactory {
ut_ad(!trx->read_only);
- ut_ad(trx->state == TRX_STATE_NOT_STARTED
- || trx->state == TRX_STATE_FORCED_ROLLBACK);
+ ut_ad(trx->state == TRX_STATE_NOT_STARTED);
ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
@@ -344,12 +302,6 @@ struct TrxFactory {
ut_ad(trx->lock.table_locks.empty());
- ut_ad(!trx->abort);
-
- ut_ad(trx->hit_list.empty());
-
- ut_ad(trx->killed_by == 0);
-
return(true);
}
};
@@ -442,15 +394,9 @@ trx_create_low()
/* We just got trx from pool, it should be non locking */
ut_ad(trx->will_lock == 0);
+ ut_ad(trx->state == TRX_STATE_NOT_STARTED);
- /* Background trx should not be forced to rollback,
- we will unset the flag for user trx. */
- trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
-
- /* Trx state can be TRX_STATE_FORCED_ROLLBACK if
- the trx was forced to rollback before it's reused.*/
DBUG_LOG("trx", "Create: " << trx);
- trx->state = TRX_STATE_NOT_STARTED;
heap = mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 8);
@@ -1204,7 +1150,6 @@ trx_start_low(
{
ut_ad(!trx->in_rollback);
ut_ad(!trx->is_recovered);
- ut_ad(trx->hit_list.empty());
ut_ad(trx->start_line != 0);
ut_ad(trx->start_file != 0);
ut_ad(trx->roll_limit == 0);
@@ -1213,10 +1158,6 @@ trx_start_low(
ut_ad(trx->rsegs.m_noredo.rseg == NULL);
ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
- ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK));
- ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC));
-
- ++trx->version;
/* Check whether it is an AUTOCOMMIT SELECT */
trx->auto_commit = thd_trx_is_auto_commit(trx->mysql_thd);
@@ -1731,16 +1672,9 @@ trx_commit_in_memory(
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
- /* AC-NL-RO transactions can't be rolled back asynchronously. */
- ut_ad(!trx->abort);
- ut_ad(!(trx->in_innodb
- & (TRX_FORCE_ROLLBACK | TRX_FORCE_ROLLBACK_ASYNC)));
-
DBUG_LOG("trx", "Autocommit in memory: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
-
} else {
-
if (trx->id > 0) {
/* For consistent snapshot, we need to remove current
transaction from running transaction id list for mvcc
@@ -1864,20 +1798,8 @@ trx_commit_in_memory(
}
#endif
- /* Because we can rollback transactions asynchronously, we change
- the state at the last step. trx_t::abort cannot change once commit
- or rollback has started because we will have released the locks by
- the time we get here. */
-
- if (trx->abort) {
-
- trx->abort = false;
- DBUG_LOG("trx", "Abort: " << trx);
- trx->state = TRX_STATE_FORCED_ROLLBACK;
- } else {
- DBUG_LOG("trx", "Commit in memory: " << trx);
- trx->state = TRX_STATE_NOT_STARTED;
- }
+ DBUG_LOG("trx", "Commit in memory: " << trx);
+ trx->state = TRX_STATE_NOT_STARTED;
/* trx->in_mysql_trx_list would hold between
trx_allocate_for_mysql() and trx_free_for_mysql(). It does not
@@ -2089,8 +2011,6 @@ trx_commit_or_rollback_prepare(
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
-
trx_start_low(trx, true);
/* fall through */
@@ -2194,22 +2114,12 @@ trx_commit_for_mysql(
/*=================*/
trx_t* trx) /*!< in/out: transaction */
{
- TrxInInnoDB trx_in_innodb(trx, true);
-
- if (trx_in_innodb.is_aborted()
- && trx->killed_by != os_thread_get_curr_id()) {
-
- return(DB_FORCED_ABORT);
- }
-
/* 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. */
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
-
ut_d(trx->start_file = __FILE__);
ut_d(trx->start_line = __LINE__);
@@ -2270,7 +2180,6 @@ trx_mark_sql_stat_end(
case TRX_STATE_COMMITTED_IN_MEMORY:
break;
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
trx->undo_no = 0;
trx->undo_rseg_space = 0;
/* fall through */
@@ -2321,9 +2230,6 @@ trx_print_low(
case TRX_STATE_NOT_STARTED:
fputs(", not started", f);
goto state_ok;
- case TRX_STATE_FORCED_ROLLBACK:
- fputs(", forced rollback", f);
- goto state_ok;
case TRX_STATE_ACTIVE:
fprintf(f, ", ACTIVE %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
@@ -2465,10 +2371,6 @@ wsrep_trx_print_locking(
fprintf(f, ", ACTIVE %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
goto state_ok;
- case TRX_STATE_FORCED_ROLLBACK:
- fprintf(f, ", FORCED ROLLBACK, %lu sec",
- (ulong) difftime(time(NULL), trx->start_time));
- goto state_ok;
case TRX_STATE_PREPARED:
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
@@ -2599,7 +2501,6 @@ trx_assert_started(
return(TRUE);
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
break;
}
@@ -2714,10 +2615,6 @@ trx_prepare(
/*========*/
trx_t* trx) /*!< in/out: transaction */
{
- /* This transaction has crossed the point of no return and cannot
- be rolled back asynchronously now. It must commit or rollback
- synhronously. */
-
/* Only fresh user transactions can be prepared.
Recovered transactions cannot. */
ut_a(!trx->is_recovered);
@@ -2755,29 +2652,17 @@ trx_prepare(
}
}
-/**
-Does the transaction prepare for MySQL.
-@param[in, out] trx Transaction instance to prepare */
-dberr_t
-trx_prepare_for_mysql(trx_t* trx)
+/** XA PREPARE a transaction.
+@param[in,out] trx transaction to prepare */
+void trx_prepare_for_mysql(trx_t* trx)
{
trx_start_if_not_started_xa(trx, false);
- TrxInInnoDB trx_in_innodb(trx, true);
-
- if (trx_in_innodb.is_aborted()
- && trx->killed_by != os_thread_get_curr_id()) {
-
- return(DB_FORCED_ABORT);
- }
-
trx->op_info = "preparing";
trx_prepare(trx);
trx->op_info = "";
-
- return(DB_SUCCESS);
}
/**********************************************************************//**
@@ -2925,7 +2810,6 @@ trx_start_if_not_started_xa_low(
{
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
trx_start_low(trx, read_write);
return;
@@ -2958,13 +2842,10 @@ trx_start_if_not_started_low(
{
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
-
trx_start_low(trx, read_write);
return;
case TRX_STATE_ACTIVE:
-
if (read_write && trx->id == 0 && !trx->read_only) {
trx_set_rw_mode(trx);
}
@@ -3021,8 +2902,6 @@ trx_start_for_ddl_low(
{
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
- case TRX_STATE_FORCED_ROLLBACK:
-
/* Flag this transaction as a dictionary operation, so that
the data dictionary will be locked in crash recovery. */
@@ -3114,146 +2993,3 @@ trx_set_rw_mode(
mutex_exit(&trx_sys->mutex);
}
-
-/**
-Kill all transactions that are blocking this transaction from acquiring locks.
-@param[in,out] trx High priority transaction */
-
-void
-trx_kill_blocking(trx_t* trx)
-{
- if (trx->hit_list.empty()) {
- return;
- }
-
- DEBUG_SYNC_C("trx_kill_blocking_enter");
-
- ulint had_dict_lock = trx->dict_operation_lock_mode;
-
- switch (had_dict_lock) {
- case 0:
- break;
-
- case RW_S_LATCH:
- /* Release foreign key check latch */
- row_mysql_unfreeze_data_dictionary(trx);
- break;
-
- default:
- /* There should never be a lock wait when the
- dictionary latch is reserved in X mode. Dictionary
- transactions should only acquire locks on dictionary
- tables, not other tables. All access to dictionary
- tables should be covered by dictionary
- transactions. */
- ut_error;
- }
-
- ut_a(trx->dict_operation_lock_mode == 0);
-
- /** Kill the transactions in the lock acquisition order old -> new. */
- hit_list_t::reverse_iterator end = trx->hit_list.rend();
-
- for (hit_list_t::reverse_iterator it = trx->hit_list.rbegin();
- it != end;
- ++it) {
-
- trx_t* victim_trx = it->m_trx;
- ulint version = it->m_version;
-
- /* Shouldn't commit suicide. */
- ut_ad(victim_trx != trx);
- ut_ad(victim_trx->mysql_thd != trx->mysql_thd);
-
- /* Check that the transaction isn't active inside
- InnoDB code. We have to wait while it is executing
- in the InnoDB context. This can potentially take a
- long time */
-
- trx_mutex_enter(victim_trx);
- ut_ad(version <= victim_trx->version);
-
- ulint loop_count = 0;
- /* start with optimistic sleep time of 20 micro seconds. */
- ulint sleep_time = 20;
-
- while ((victim_trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) > 0
- && victim_trx->version == version) {
-
- trx_mutex_exit(victim_trx);
-
- loop_count++;
- /* If the wait is long, don't hog the cpu. */
- if (loop_count < 100) {
- /* 20 microseconds */
- sleep_time = 20;
- } else if (loop_count < 1000) {
- /* 1 millisecond */
- sleep_time = 1000;
- } else {
- /* 100 milliseconds */
- sleep_time = 100000;
- }
-
- os_thread_sleep(sleep_time);
-
- trx_mutex_enter(victim_trx);
- }
-
- /* Compare the version to check if the transaction has
- already finished */
- if (victim_trx->version != version) {
- trx_mutex_exit(victim_trx);
- continue;
- }
-
- /* We should never kill background transactions. */
- ut_ad(victim_trx->mysql_thd != NULL);
-
- ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE));
- ut_ad(victim_trx->in_innodb & TRX_FORCE_ROLLBACK);
- ut_ad(victim_trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC);
- ut_ad(victim_trx->killed_by == os_thread_get_curr_id());
- ut_ad(victim_trx->version == it->m_version);
-
- /* We don't kill Read Only, Background or high priority
- transactions. */
- ut_a(!victim_trx->read_only);
- ut_a(victim_trx->mysql_thd != NULL);
-
- trx_mutex_exit(victim_trx);
-
-#ifndef DBUG_OFF
- char buffer[1024];
-#endif /* !DBUG_OFF */
-
- DBUG_LOG("trx",
- "High Priority Transaction "
- << trx->id << " killed transaction "
- << victim_trx->id << " in hit list"
- << " - "
- << thd_get_error_context_description(
- victim_trx->mysql_thd,
- buffer, sizeof(buffer), 512));
-
- trx_rollback_for_mysql(victim_trx);
- trx_mutex_enter(victim_trx);
-
- version++;
- ut_ad(victim_trx->version == version);
-
- my_atomic_storelong(&victim_trx->killed_by, 0);
-
- victim_trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
-
- trx_mutex_exit(victim_trx);
- }
-
- trx->hit_list.clear();
-
- if (had_dict_lock) {
-
- row_mysql_freeze_data_dictionary(trx);
- }
-
-}