summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysqltest.cc12
-rw-r--r--mysql-test/suite/innodb/r/innodb_bug59641.result57
-rw-r--r--mysql-test/suite/innodb/t/innodb_bug59641.test66
-rw-r--r--sql/sql_class.cc1
-rw-r--r--storage/innobase/handler/ha_innodb.cc88
-rw-r--r--storage/innobase/handler/ha_innodb.h11
-rw-r--r--storage/innobase/handler/handler0alter.cc43
-rw-r--r--storage/innobase/include/trx0trx.h11
-rw-r--r--storage/innobase/include/trx0undo.h9
-rw-r--r--storage/innobase/log/log0log.c9
-rw-r--r--storage/innobase/trx/trx0sys.c10
-rw-r--r--storage/innobase/trx/trx0trx.c68
-rw-r--r--storage/innobase/trx/trx0undo.c28
13 files changed, 330 insertions, 83 deletions
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index 4f067c54429..6f19952302c 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -4598,13 +4598,14 @@ static int my_kill(int pid, int sig)
command called command
DESCRIPTION
- shutdown [<timeout>]
+ shutdown_server [<timeout>]
*/
void do_shutdown_server(struct st_command *command)
{
- int timeout=60, pid;
+ long timeout=60;
+ int pid;
DYNAMIC_STRING ds_pidfile_name;
MYSQL* mysql = &cur_con->mysql;
static DYNAMIC_STRING ds_timeout;
@@ -4619,8 +4620,9 @@ void do_shutdown_server(struct st_command *command)
if (ds_timeout.length)
{
- timeout= atoi(ds_timeout.str);
- if (timeout == 0)
+ char* endptr;
+ timeout= strtol(ds_timeout.str, &endptr, 10);
+ if (*endptr != '\0')
die("Illegal argument for timeout: '%s'", ds_timeout.str);
}
dynstr_free(&ds_timeout);
@@ -4662,7 +4664,7 @@ void do_shutdown_server(struct st_command *command)
DBUG_PRINT("info", ("Process %d does not exist anymore", pid));
DBUG_VOID_RETURN;
}
- DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout));
+ DBUG_PRINT("info", ("Sleeping, timeout: %ld", timeout));
my_sleep(1000000L);
}
diff --git a/mysql-test/suite/innodb/r/innodb_bug59641.result b/mysql-test/suite/innodb/r/innodb_bug59641.result
new file mode 100644
index 00000000000..361172aa82b
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_bug59641.result
@@ -0,0 +1,57 @@
+CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB;
+INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32);
+COMMIT;
+XA START '123';
+INSERT INTO t VALUES(1,1);
+XA END '123';
+XA PREPARE '123';
+XA START '456';
+INSERT INTO t VALUES(3,47),(5,67);
+UPDATE t SET b=2*b WHERE a BETWEEN 5 AND 8;
+XA END '456';
+XA PREPARE '456';
+XA START '789';
+UPDATE t SET b=4*a WHERE a=32;
+XA END '789';
+XA PREPARE '789';
+call mtr.add_suppression("Found 3 prepared XA transactions");
+SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
+SELECT * FROM t;
+a b
+1 1
+2 2
+3 47
+4 4
+5 134
+8 16
+16 16
+32 128
+COMMIT;
+SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
+SELECT * FROM t;
+a b
+1 1
+2 2
+3 47
+4 4
+5 134
+8 16
+16 16
+32 128
+COMMIT;
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 789
+1 3 0 456
+1 3 0 123
+XA ROLLBACK '123';
+XA ROLLBACK '456';
+XA COMMIT '789';
+SELECT * FROM t;
+a b
+2 2
+4 4
+8 8
+16 16
+32 128
+DROP TABLE t;
diff --git a/mysql-test/suite/innodb/t/innodb_bug59641.test b/mysql-test/suite/innodb/t/innodb_bug59641.test
new file mode 100644
index 00000000000..0237673061c
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_bug59641.test
@@ -0,0 +1,66 @@
+# Bug #59641 Prepared XA transaction causes shutdown hang after a crash
+
+-- source include/not_embedded.inc
+-- source include/have_innodb.inc
+
+CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB;
+INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32);
+COMMIT;
+XA START '123';
+INSERT INTO t VALUES(1,1);
+XA END '123';
+XA PREPARE '123';
+
+CONNECT (con1,localhost,root,,);
+CONNECTION con1;
+
+XA START '456';
+INSERT INTO t VALUES(3,47),(5,67);
+UPDATE t SET b=2*b WHERE a BETWEEN 5 AND 8;
+XA END '456';
+XA PREPARE '456';
+
+CONNECT (con2,localhost,root,,);
+CONNECTION con2;
+
+XA START '789';
+UPDATE t SET b=4*a WHERE a=32;
+XA END '789';
+XA PREPARE '789';
+
+# The server would issue this warning on restart.
+call mtr.add_suppression("Found 3 prepared XA transactions");
+
+# Kill the server without sending a shutdown command
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- shutdown_server 0
+-- source include/wait_until_disconnected.inc
+
+# Restart the server.
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- enable_reconnect
+-- source include/wait_until_connected_again.inc
+SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
+SELECT * FROM t;
+COMMIT;
+
+# Shut down the server. This would hang because of the bug.
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- shutdown_server
+-- source include/wait_until_disconnected.inc
+
+# Restart the server.
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- enable_reconnect
+-- source include/wait_until_connected_again.inc
+
+SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
+SELECT * FROM t;
+COMMIT;
+XA RECOVER;
+XA ROLLBACK '123';
+XA ROLLBACK '456';
+XA COMMIT '789';
+SELECT * FROM t;
+
+DROP TABLE t;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index bc6086eaf19..fe68c989e64 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3675,6 +3675,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state)
xs->xa_state=xa_state;
xs->xid.set(xid);
xs->in_thd=0;
+ xs->rm_error=0;
res=my_hash_insert(&xid_cache, (uchar*)xs);
}
mysql_mutex_unlock(&LOCK_xid_cache);
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index cd6497519e9..42fd8284ad4 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -6244,10 +6244,6 @@ create_table_def(
DBUG_PRINT("enter", ("table_name: %s", table_name));
ut_a(trx->mysql_thd != NULL);
- if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(table_name,
- (THD*) trx->mysql_thd)) {
- DBUG_RETURN(HA_ERR_GENERIC);
- }
/* MySQL does the name length check. But we do additional check
on the name length here */
@@ -6368,6 +6364,8 @@ err_col:
col_len);
}
+ srv_lower_case_table_names = lower_case_table_names;
+
error = row_create_table_for_mysql(table, trx);
if (error == DB_DUPLICATE_KEY) {
@@ -6784,38 +6782,17 @@ ha_innobase::create(
DBUG_RETURN(HA_ERR_TO_BIG_ROW);
}
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- trx = innobase_trx_allocate(thd);
-
- srv_lower_case_table_names = lower_case_table_names;
-
strcpy(name2, name);
normalize_table_name(norm_name, name2);
- /* Latch the InnoDB data dictionary exclusively so that no deadlocks
- or lock waits can happen in it during a table create operation.
- Drop table etc. do this latching in row0mysql.c. */
-
- row_mysql_lock_data_dictionary(trx);
-
/* Create the table definition in InnoDB */
flags = 0;
/* Validate create options if innodb_strict_mode is set. */
if (!create_options_are_valid(thd, form, create_info)) {
- error = ER_ILLEGAL_HA_CREATE_OPTION;
- goto cleanup;
+ DBUG_RETURN(ER_ILLEGAL_HA_CREATE_OPTION);
}
if (create_info->key_block_size) {
@@ -6957,16 +6934,37 @@ ha_innobase::create(
/* Check for name conflicts (with reserved name) for
any user indices to be created. */
- if (innobase_index_name_is_reserved(trx, form->key_info,
+ if (innobase_index_name_is_reserved(thd, form->key_info,
form->s->keys)) {
- error = -1;
- goto cleanup;
+ DBUG_RETURN(-1);
+ }
+
+ if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(norm_name, thd)) {
+ DBUG_RETURN(HA_ERR_GENERIC);
}
if (create_info->options & HA_LEX_CREATE_TMP_TABLE) {
flags |= DICT_TF2_TEMPORARY << DICT_TF2_SHIFT;
}
+ /* Get the transaction associated with the current thd, or create one
+ if not yet created */
+
+ parent_trx = check_trx_exists(thd);
+
+ /* In case MySQL calls this in the middle of a SELECT query, release
+ possible adaptive hash latch to avoid deadlocks of threads */
+
+ trx_search_latch_release_if_reserved(parent_trx);
+
+ trx = innobase_trx_allocate(thd);
+
+ /* Latch the InnoDB data dictionary exclusively so that no deadlocks
+ or lock waits can happen in it during a table create operation.
+ Drop table etc. do this latching in row0mysql.c. */
+
+ row_mysql_lock_data_dictionary(trx);
+
error = create_table_def(trx, form, norm_name,
create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL,
flags);
@@ -7221,14 +7219,14 @@ ha_innobase::delete_table(
trx = innobase_trx_allocate(thd);
- srv_lower_case_table_names = lower_case_table_names;
-
name_len = strlen(name);
ut_a(name_len < 1000);
/* Drop the table in InnoDB */
+ srv_lower_case_table_names = lower_case_table_names;
+
error = row_drop_table_for_mysql(norm_name, trx,
thd_sql_command(thd)
== SQLCOM_DROP_DB);
@@ -7344,8 +7342,6 @@ innobase_rename_table(
char* norm_to;
char* norm_from;
- srv_lower_case_table_names = lower_case_table_names;
-
// Magic number 64 arbitrary
norm_to = (char*) my_malloc(strlen(to) + 64, MYF(0));
norm_from = (char*) my_malloc(strlen(from) + 64, MYF(0));
@@ -7360,6 +7356,8 @@ innobase_rename_table(
row_mysql_lock_data_dictionary(trx);
}
+ srv_lower_case_table_names = lower_case_table_names;
+
error = row_rename_table_for_mysql(
norm_from, norm_to, trx, lock_and_commit);
@@ -10265,7 +10263,7 @@ innobase_commit_by_xid(
if (trx) {
innobase_commit_low(trx);
-
+ trx_free_for_background(trx);
return(XA_OK);
} else {
return(XAER_NOTA);
@@ -10291,7 +10289,9 @@ innobase_rollback_by_xid(
trx = trx_get_trx_by_xid(xid);
if (trx) {
- return(innobase_rollback_trx(trx));
+ int ret = innobase_rollback_trx(trx);
+ trx_free_for_background(trx);
+ return(ret);
} else {
return(XAER_NOTA);
}
@@ -10924,19 +10924,19 @@ static int show_innodb_vars(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
-/***********************************************************************
+/*********************************************************************//**
This function checks each index name for a table against reserved
-system default primary index name 'GEN_CLUST_INDEX'. If a name matches,
-this function pushes an warning message to the client, and returns true. */
+system default primary index name 'GEN_CLUST_INDEX'. If a name
+matches, this function pushes an warning message to the client,
+and returns true.
+@return true if the index name matches the reserved name */
extern "C" UNIV_INTERN
bool
innobase_index_name_is_reserved(
/*============================*/
- /* out: true if an index name
- matches the reserved name */
- const trx_t* trx, /* in: InnoDB transaction handle */
- const KEY* key_info, /* in: Indexes to be created */
- ulint num_of_keys) /* in: Number of indexes to
+ THD* thd, /*!< in/out: MySQL connection */
+ const KEY* key_info, /*!< in: Indexes to be created */
+ ulint num_of_keys) /*!< in: Number of indexes to
be created. */
{
const KEY* key;
@@ -10948,7 +10948,7 @@ innobase_index_name_is_reserved(
if (innobase_strcasecmp(key->name,
innobase_index_reserve_name) == 0) {
/* Push warning to mysql */
- push_warning_printf((THD*) trx->mysql_thd,
+ push_warning_printf(thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WRONG_NAME_FOR_INDEX,
"Cannot Create Index with name "
diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h
index f05ea8801f0..0c0af8dd887 100644
--- a/storage/innobase/handler/ha_innodb.h
+++ b/storage/innobase/handler/ha_innodb.h
@@ -321,15 +321,14 @@ innobase_trx_allocate(
This function checks each index name for a table against reserved
system default primary index name 'GEN_CLUST_INDEX'. If a name
matches, this function pushes an warning message to the client,
-and returns true. */
+and returns true.
+@return true if the index name matches the reserved name */
extern "C"
bool
innobase_index_name_is_reserved(
/*============================*/
- /* out: true if the index name
- matches the reserved name */
- const trx_t* trx, /* in: InnoDB transaction handle */
- const KEY* key_info, /* in: Indexes to be created */
- ulint num_of_keys); /* in: Number of indexes to
+ THD* thd, /*!< in/out: MySQL connection */
+ const KEY* key_info, /*!< in: Indexes to be created */
+ ulint num_of_keys); /*!< in: Number of indexes to
be created. */
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index e0279c13de2..cc7e48ebd44 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -653,44 +653,37 @@ ha_innobase::add_index(
update_thd();
- heap = mem_heap_create(1024);
-
/* In case MySQL calls this in the middle of a SELECT query, release
possible adaptive hash latch to avoid deadlocks of threads. */
trx_search_latch_release_if_reserved(prebuilt->trx);
- trx_start_if_not_started(prebuilt->trx);
- /* Create a background transaction for the operations on
- the data dictionary tables. */
- trx = innobase_trx_allocate(user_thd);
- trx_start_if_not_started(trx);
+ /* Check if the index name is reserved. */
+ if (innobase_index_name_is_reserved(user_thd, key_info, num_of_keys)) {
+ DBUG_RETURN(-1);
+ }
innodb_table = indexed_table
= dict_table_get(prebuilt->table->name, FALSE);
if (UNIV_UNLIKELY(!innodb_table)) {
- error = HA_ERR_NO_SUCH_TABLE;
- goto err_exit;
+ DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
}
- /* Check if the index name is reserved. */
- if (innobase_index_name_is_reserved(trx, key_info, num_of_keys)) {
- error = -1;
- } else {
- /* Check that index keys are sensible */
- error = innobase_check_index_keys(key_info, num_of_keys,
- innodb_table);
- }
+ /* Check that index keys are sensible */
+ error = innobase_check_index_keys(key_info, num_of_keys, innodb_table);
if (UNIV_UNLIKELY(error)) {
-err_exit:
- mem_heap_free(heap);
- trx_general_rollback_for_mysql(trx, NULL);
- trx_free_for_mysql(trx);
- trx_commit_for_mysql(prebuilt->trx);
DBUG_RETURN(error);
}
+ heap = mem_heap_create(1024);
+ trx_start_if_not_started(prebuilt->trx);
+
+ /* Create a background transaction for the operations on
+ the data dictionary tables. */
+ trx = innobase_trx_allocate(user_thd);
+ trx_start_if_not_started(trx);
+
/* Create table containing all indexes to be built in this
alter table add index so that they are in the correct order
in the table. */
@@ -762,8 +755,12 @@ err_exit:
ut_d(dict_table_check_for_dup_indexes(innodb_table,
FALSE));
+ mem_heap_free(heap);
+ trx_general_rollback_for_mysql(trx, NULL);
row_mysql_unlock_data_dictionary(trx);
- goto err_exit;
+ trx_free_for_mysql(trx);
+ trx_commit_for_mysql(prebuilt->trx);
+ DBUG_RETURN(error);
}
trx->table_id = indexed_table->id;
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index ba840978b18..588ddd65e88 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -44,6 +44,9 @@ extern sess_t* trx_dummy_sess;
/** Number of transactions currently allocated for MySQL: protected by
the kernel mutex */
extern ulint trx_n_mysql_transactions;
+/** Number of transactions currently in the XA PREPARED state: protected by
+the kernel mutex */
+extern ulint trx_n_prepared;
/********************************************************************//**
Releases the search latch if trx has reserved it. */
@@ -108,6 +111,14 @@ trx_free(
/*=====*/
trx_t* trx); /*!< in, own: trx object */
/********************************************************************//**
+At shutdown, frees a transaction object that is in the PREPARED state. */
+UNIV_INTERN
+void
+trx_free_prepared(
+/*==============*/
+ trx_t* trx) /*!< in, own: trx object */
+ UNIV_COLD __attribute__((nonnull));
+/********************************************************************//**
Frees a transaction object for MySQL. */
UNIV_INTERN
void
diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h
index 937e9d7ef79..df16c939070 100644
--- a/storage/innobase/include/trx0undo.h
+++ b/storage/innobase/include/trx0undo.h
@@ -296,6 +296,15 @@ void
trx_undo_insert_cleanup(
/*====================*/
trx_t* trx); /*!< in: transaction handle */
+
+/********************************************************************//**
+At shutdown, frees the undo logs of a PREPARED transaction. */
+UNIV_INTERN
+void
+trx_undo_free_prepared(
+/*===================*/
+ trx_t* trx) /*!< in/out: PREPARED transaction */
+ UNIV_COLD __attribute__((nonnull));
#endif /* !UNIV_HOTBACKUP */
/***********************************************************//**
Parses the redo log entry of an undo log page initialization.
diff --git a/storage/innobase/log/log0log.c b/storage/innobase/log/log0log.c
index b07135465c3..fd2258945b6 100644
--- a/storage/innobase/log/log0log.c
+++ b/storage/innobase/log/log0log.c
@@ -3109,12 +3109,13 @@ loop:
goto loop;
}
- /* Check that there are no longer transactions. We need this wait even
- for the 'very fast' shutdown, because the InnoDB layer may have
- committed or prepared transactions and we don't want to lose them. */
+ /* Check that there are no longer transactions, except for
+ PREPARED ones. We need this wait even for the 'very fast'
+ shutdown, because the InnoDB layer may have committed or
+ prepared transactions and we don't want to lose them. */
server_busy = trx_n_mysql_transactions > 0
- || UT_LIST_GET_LEN(trx_sys->trx_list) > 0;
+ || UT_LIST_GET_LEN(trx_sys->trx_list) > trx_n_prepared;
mutex_exit(&kernel_mutex);
if (server_busy || srv_is_any_background_thread_active()) {
diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c
index 7af9fbb1af8..8e595353024 100644
--- a/storage/innobase/trx/trx0sys.c
+++ b/storage/innobase/trx/trx0sys.c
@@ -37,6 +37,7 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0rseg.h"
#include "trx0undo.h"
#include "srv0srv.h"
+#include "srv0start.h"
#include "trx0purge.h"
#include "log0log.h"
#include "log0recv.h"
@@ -1617,10 +1618,12 @@ void
trx_sys_close(void)
/*===============*/
{
+ trx_t* trx;
trx_rseg_t* rseg;
read_view_t* view;
ut_ad(trx_sys != NULL);
+ ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
/* Check that all read views are closed except read view owned
by a purge. */
@@ -1652,6 +1655,13 @@ trx_sys_close(void)
mem_free(trx_doublewrite);
trx_doublewrite = NULL;
+ /* Only prepared transactions may be left in the system. Free them. */
+ ut_a(UT_LIST_GET_LEN(trx_sys->trx_list) == trx_n_prepared);
+
+ while ((trx = UT_LIST_GET_FIRST(trx_sys->trx_list)) != NULL) {
+ trx_free_prepared(trx);
+ }
+
/* There can't be any active transactions. */
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c
index 475e2a51c8c..7b99b86c732 100644
--- a/storage/innobase/trx/trx0trx.c
+++ b/storage/innobase/trx/trx0trx.c
@@ -50,6 +50,9 @@ UNIV_INTERN sess_t* trx_dummy_sess = NULL;
/** Number of transactions currently allocated for MySQL: protected by
the kernel mutex */
UNIV_INTERN ulint trx_n_mysql_transactions = 0;
+/** Number of transactions currently in the XA PREPARED state: protected by
+the kernel mutex */
+UNIV_INTERN ulint trx_n_prepared = 0;
#ifdef UNIV_PFS_MUTEX
/* Key to register the mutex with performance schema */
@@ -338,6 +341,60 @@ trx_free(
}
/********************************************************************//**
+At shutdown, frees a transaction object that is in the PREPARED state. */
+UNIV_INTERN
+void
+trx_free_prepared(
+/*==============*/
+ trx_t* trx) /*!< in, own: trx object */
+{
+ ut_ad(mutex_own(&kernel_mutex));
+ ut_a(trx->conc_state == TRX_PREPARED);
+ ut_a(trx->magic_n == TRX_MAGIC_N);
+
+ /* Prepared transactions are sort of active; they allow
+ ROLLBACK and COMMIT operations. Because the system does not
+ contain any other transactions than prepared transactions at
+ the shutdown stage and because a transaction cannot become
+ PREPARED while holding locks, it is safe to release the locks
+ held by PREPARED transactions here at shutdown.*/
+ lock_release_off_kernel(trx);
+
+ trx_undo_free_prepared(trx);
+
+ mutex_free(&trx->undo_mutex);
+
+ if (trx->undo_no_arr) {
+ trx_undo_arr_free(trx->undo_no_arr);
+ }
+
+ ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
+ ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0);
+
+ ut_a(trx->wait_lock == NULL);
+ ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
+
+ ut_a(!trx->has_search_latch);
+
+ ut_a(trx->dict_operation_lock_mode == 0);
+
+ if (trx->lock_heap) {
+ mem_heap_free(trx->lock_heap);
+ }
+
+ if (trx->global_read_view_heap) {
+ mem_heap_free(trx->global_read_view_heap);
+ }
+
+ ut_a(ib_vector_is_empty(trx->autoinc_locks));
+ ib_vector_free(trx->autoinc_locks);
+
+ UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx);
+
+ mem_free(trx);
+}
+
+/********************************************************************//**
Frees a transaction object for MySQL. */
UNIV_INTERN
void
@@ -467,6 +524,7 @@ trx_lists_init_at_db_start(void)
if (srv_force_recovery == 0) {
trx->conc_state = TRX_PREPARED;
+ trx_n_prepared++;
} else {
fprintf(stderr,
"InnoDB: Since"
@@ -543,6 +601,7 @@ trx_lists_init_at_db_start(void)
trx->conc_state
= TRX_PREPARED;
+ trx_n_prepared++;
} else {
fprintf(stderr,
"InnoDB: Since"
@@ -877,6 +936,11 @@ trx_commit_off_kernel(
ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED);
ut_ad(mutex_own(&kernel_mutex));
+ if (UNIV_UNLIKELY(trx->conc_state == TRX_PREPARED)) {
+ ut_a(trx_n_prepared > 0);
+ trx_n_prepared--;
+ }
+
/* The following assignment makes the transaction committed in memory
and makes its changes to data visible to other transactions.
NOTE that there is a small discrepancy from the strict formal
@@ -1901,6 +1965,7 @@ trx_prepare_off_kernel(
/*--------------------------------------*/
trx->conc_state = TRX_PREPARED;
+ trx_n_prepared++;
/*--------------------------------------*/
if (lsn) {
@@ -2077,7 +2142,8 @@ trx_get_trx_by_xid(
of gtrid_length+bqual_length bytes should be
the same */
- if (trx->conc_state == TRX_PREPARED
+ if (trx->is_recovered
+ && trx->conc_state == TRX_PREPARED
&& xid->gtrid_length == trx->xid.gtrid_length
&& xid->bqual_length == trx->xid.bqual_length
&& memcmp(xid->data, trx->xid.data,
diff --git a/storage/innobase/trx/trx0undo.c b/storage/innobase/trx/trx0undo.c
index 4021a2ed573..070d6332a4f 100644
--- a/storage/innobase/trx/trx0undo.c
+++ b/storage/innobase/trx/trx0undo.c
@@ -36,6 +36,7 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0rseg.h"
#include "trx0trx.h"
#include "srv0srv.h"
+#include "srv0start.h"
#include "trx0rec.h"
#include "trx0purge.h"
@@ -1975,4 +1976,31 @@ trx_undo_insert_cleanup(
mutex_exit(&(rseg->mutex));
}
+
+/********************************************************************//**
+At shutdown, frees the undo logs of a PREPARED transaction. */
+UNIV_INTERN
+void
+trx_undo_free_prepared(
+/*===================*/
+ trx_t* trx) /*!< in/out: PREPARED transaction */
+{
+ mutex_enter(&trx->rseg->mutex);
+
+ ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
+
+ if (trx->update_undo) {
+ ut_a(trx->update_undo->state == TRX_UNDO_PREPARED);
+ UT_LIST_REMOVE(undo_list, trx->rseg->update_undo_list,
+ trx->update_undo);
+ trx_undo_mem_free(trx->update_undo);
+ }
+ if (trx->insert_undo) {
+ ut_a(trx->insert_undo->state == TRX_UNDO_PREPARED);
+ UT_LIST_REMOVE(undo_list, trx->rseg->insert_undo_list,
+ trx->insert_undo);
+ trx_undo_mem_free(trx->insert_undo);
+ }
+ mutex_exit(&trx->rseg->mutex);
+}
#endif /* !UNIV_HOTBACKUP */