summaryrefslogtreecommitdiff
path: root/storage/innobase
diff options
context:
space:
mode:
authorSunny Bains <Sunny.Bains@Oracle.Com>2010-10-21 22:22:27 +1100
committerSunny Bains <Sunny.Bains@Oracle.Com>2010-10-21 22:22:27 +1100
commit38f54271922aaec6e47a589fdf6d0388197c8928 (patch)
tree1848a3def3d8202f490bd7598fa14d2d32c2de39 /storage/innobase
parentd1a349325c42fd633f3f55054f47685f2087453d (diff)
downloadmariadb-git-38f54271922aaec6e47a589fdf6d0388197c8928.tar.gz
Bug #57243 Inconsistent use of trans_register_ha() API in InnoDB
Remove trx_t::active_trans. Split into two separate fields with distinct responsibilities. trx_t::is_registered and trx_t::owns_prepare_mutex. There are wrapper functions for using this fields in ha_innodb.cc. The wrapper functions check for invariants. Fix some formatting to conform to InnoDB guidelines. Remove innobase_register_stmt() and innobase_register_trx_and_stmt(). Add: trx_is_started() trx_deregister_from_2pc() trx_register_for_2pc() trx_is_registered_for_2pc() trx_owns_prepare_commit_mutex_set() trx_has_prepare_commit_mutex() rb://479, Approved by Jimmy Yang.
Diffstat (limited to 'storage/innobase')
-rw-r--r--storage/innobase/handler/ha_innodb.cc327
-rw-r--r--storage/innobase/include/trx0trx.h17
-rw-r--r--storage/innobase/trx/trx0trx.c7
3 files changed, 185 insertions, 166 deletions
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index f31d92f3200..97ddbfc8199 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -32,7 +32,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*****************************************************************************/
/* TODO list for the InnoDB handler in 5.0:
- - Remove the flag trx->active_trans and look at trx->conc_state
- fix savepoint functions to use savepoint storage area
- Find out what kind of problems the OS X case-insensitivity causes to
table and database names; should we 'normalize' the names like we do
@@ -1500,7 +1499,7 @@ Gets the InnoDB transaction handle for a MySQL handler object, creates
an InnoDB transaction struct if the corresponding MySQL thread struct still
lacks one.
@return InnoDB transaction handle */
-static
+static inline
trx_t*
check_trx_exists(
/*=============*/
@@ -1522,6 +1521,77 @@ check_trx_exists(
return(trx);
}
+/*********************************************************************//**
+Note that a transaction has been registered with MySQL.
+@return true if transaction is registered with MySQL 2PC coordinator */
+static inline
+bool
+trx_is_registered_for_2pc(
+/*=========================*/
+ const trx_t* trx) /* in: transaction */
+{
+ return(trx->is_registered == 1);
+}
+
+/*********************************************************************//**
+Note that a transaction owns the prepare_commit_mutex. */
+static inline
+void
+trx_owns_prepare_commit_mutex_set(
+/*==============================*/
+ trx_t* trx) /* in: transaction */
+{
+ ut_a(trx_is_registered_for_2pc(trx));
+ trx->owns_prepare_mutex = 1;
+}
+
+/*********************************************************************//**
+Note that a transaction has been registered with MySQL 2PC coordinator. */
+static inline
+void
+trx_register_for_2pc(
+/*==================*/
+ trx_t* trx) /* in: transaction */
+{
+ trx->is_registered = 1;
+ ut_ad(trx->owns_prepare_mutex == 0);
+}
+
+/*********************************************************************//**
+Note that a transaction has been deregistered. */
+static inline
+void
+trx_deregister_from_2pc(
+/*====================*/
+ trx_t* trx) /* in: transaction */
+{
+ trx->is_registered = 0;
+ trx->owns_prepare_mutex = 0;
+}
+
+/*********************************************************************//**
+Check whether atransaction owns the prepare_commit_mutex.
+@return true if transaction owns the prepare commit mutex */
+static inline
+bool
+trx_has_prepare_commit_mutex(
+/*=========================*/
+ const trx_t* trx) /* in: transaction */
+{
+ return(trx->owns_prepare_mutex == 1);
+}
+
+/*********************************************************************//**
+Check if transaction is started.
+@reutrn true if transaction is in state started */
+static
+bool
+trx_is_started(
+/*===========*/
+ trx_t* trx) /* in: transaction */
+{
+ return(trx->conc_state != TRX_NOT_STARTED);
+}
/*********************************************************************//**
Construct ha_innobase handler. */
@@ -1585,48 +1655,31 @@ ha_innobase::update_thd()
}
/*********************************************************************//**
-Registers that InnoDB takes part in an SQL statement, so that MySQL knows to
-roll back the statement if the statement results in an error. This MUST be
-called for every SQL statement that may be rolled back by MySQL. Calling this
-several times to register the same statement is allowed, too. */
+Registers an InnoDB transaction with the MySQL 2PC coordinator, so that
+the MySQL XA code knows to call the InnoDB prepare and commit, or rollback
+for the transaction. This MUST be called for every transaction for which
+the user may call commit or rollback. Calling this several times to register
+the same transaction is allowed, too. This function also registers the
+current SQL statement. */
static inline
void
-innobase_register_stmt(
-/*===================*/
- handlerton* hton, /*!< in: Innobase hton */
- THD* thd) /*!< in: MySQL thd (connection) object */
+innobase_register_trx(
+/*==================*/
+ handlerton* hton, /* in: Innobase handlerton */
+ THD* thd, /* in: MySQL thd (connection) object */
+ trx_t* trx) /* in: transaction to register */
{
- DBUG_ASSERT(hton == innodb_hton_ptr);
- /* Register the statement */
trans_register_ha(thd, FALSE, hton);
-}
-/*********************************************************************//**
-Registers an InnoDB transaction in MySQL, so that the MySQL XA code knows
-to call the InnoDB prepare and commit, or rollback for the transaction. This
-MUST be called for every transaction for which the user may call commit or
-rollback. Calling this several times to register the same transaction is
-allowed, too.
-This function also registers the current SQL statement. */
-static inline
-void
-innobase_register_trx_and_stmt(
-/*===========================*/
- handlerton *hton, /*!< in: Innobase handlerton */
- THD* thd) /*!< in: MySQL thd (connection) object */
-{
- /* NOTE that actually innobase_register_stmt() registers also
- the transaction in the AUTOCOMMIT=1 mode. */
+ if (!trx_is_registered_for_2pc(trx)
+ && thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
- innobase_register_stmt(hton, thd);
-
- if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
-
- /* No autocommit mode, register for a transaction */
trans_register_ha(thd, TRUE, hton);
}
-}
+ trx_register_for_2pc(trx);
+}
+
/* BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB
------------------------------------------------------------
@@ -1772,14 +1825,8 @@ innobase_query_caching_of_table_permitted(
#ifdef __WIN__
innobase_casedn_str(norm_name);
#endif
- /* The call of row_search_.. will start a new transaction if it is
- not yet started */
-
- if (trx->active_trans == 0) {
- innobase_register_trx_and_stmt(innodb_hton_ptr, thd);
- trx->active_trans = 1;
- }
+ innobase_register_trx(innodb_hton_ptr, thd, trx);
if (row_search_check_if_query_cache_permitted(trx, norm_name)) {
@@ -2046,14 +2093,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
trx_assign_read_view(prebuilt->trx);
- /* Set the MySQL flag to mark that there is an active transaction */
-
- if (prebuilt->trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(ht, user_thd);
-
- prebuilt->trx->active_trans = 1;
- }
+ innobase_register_trx(ht, user_thd, prebuilt->trx);
/* We did the necessary inits in this function, no need to repeat them
in row_search_for_mysql */
@@ -2548,12 +2588,10 @@ innobase_commit_low(
/*================*/
trx_t* trx) /*!< in: transaction handle */
{
- if (trx->conc_state == TRX_NOT_STARTED) {
+ if (trx_is_started(trx)) {
- return;
+ trx_commit_for_mysql(trx);
}
-
- trx_commit_for_mysql(trx);
}
/*****************************************************************//**
@@ -2595,10 +2633,7 @@ innobase_start_trx_and_assign_read_view(
/* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
- innobase_register_trx_and_stmt(hton, thd);
- trx->active_trans = 1;
- }
+ innobase_register_trx(hton, current_thd, trx);
DBUG_RETURN(0);
}
@@ -2632,29 +2667,19 @@ innobase_commit(
trx_search_latch_release_if_reserved(trx);
}
- /* The flag trx->active_trans is set to 1 in
-
- 1. ::external_lock(),
- 2. ::start_stmt(),
- 3. innobase_query_caching_of_table_permitted(),
- 4. innobase_savepoint(),
- 5. ::init_table_handle_for_HANDLER(),
- 6. innobase_start_trx_and_assign_read_view(),
- 7. ::transactional_table_lock()
+ /* Transaction is deregistered only in a commit or a rollback. If
+ it is deregistered we know there cannot be resources to be freed
+ and we could return immediately. For the time being, we play safe
+ and do the cleanup though there should be nothing to clean up. */
- and it is only set to 0 in a commit or a rollback. If it is 0 we know
- there cannot be resources to be freed and we could return immediately.
- For the time being, we play safe and do the cleanup though there should
- be nothing to clean up. */
+ if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
- if (trx->active_trans == 0
- && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but"
- " trx->conc_state != TRX_NOT_STARTED");
+ sql_print_error("Transaction not registered for MySQL 2PC, "
+ "but transaction is active");
}
+
if (all
- || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
+ || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
/* We were instructed to commit the whole transaction, or
this is an SQL statement end and autocommit is on */
@@ -2709,15 +2734,15 @@ retry:
mysql_mutex_unlock(&commit_cond_m);
}
- if (trx->active_trans == 2) {
-
+ if (trx_has_prepare_commit_mutex(trx)) {
+
mysql_mutex_unlock(&prepare_commit_mutex);
- }
+ }
+
+ trx_deregister_from_2pc(trx);
/* Now do a write + flush of logs. */
trx_commit_complete_for_mysql(trx);
- trx->active_trans = 0;
-
} else {
/* We just mark the SQL statement ended and do not do a
transaction commit */
@@ -2786,10 +2811,10 @@ innobase_rollback(
row_unlock_table_autoinc_for_mysql(trx);
if (all
- || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+ || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
error = trx_rollback_for_mysql(trx);
- trx->active_trans = 0;
+ trx_deregister_from_2pc(trx);
} else {
error = trx_rollback_last_sql_stat_for_mysql(trx);
}
@@ -2932,8 +2957,8 @@ innobase_savepoint(
innobase_release_stat_resources(trx);
- /* cannot happen outside of transaction */
- DBUG_ASSERT(trx->active_trans);
+ /* Cannot happen outside of transaction */
+ DBUG_ASSERT(trx_is_registered_for_2pc(trx));
/* TODO: use provided savepoint data area to store savepoint data */
char name[64];
@@ -2963,16 +2988,15 @@ innobase_close_connection(
ut_a(trx);
- if (trx->active_trans == 0
- && trx->conc_state != TRX_NOT_STARTED) {
+ if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
- sql_print_error("trx->active_trans == 0, but"
- " trx->conc_state != TRX_NOT_STARTED");
+ sql_print_error("Transaction not registered for MySQL 2PC, "
+ "but transaction is active");
}
- if (trx->conc_state != TRX_NOT_STARTED &&
- global_system_variables.log_warnings) {
+ if (trx_is_started(trx) && global_system_variables.log_warnings) {
+
sql_print_warning(
"MySQL is closing a connection that has an active "
"InnoDB transaction. %llu row modifications will "
@@ -4872,7 +4896,7 @@ no_commit:
/* Altering to InnoDB format */
innobase_commit(ht, user_thd, 1);
/* Note that this transaction is still active. */
- prebuilt->trx->active_trans = 1;
+ trx_register_for_2pc(prebuilt->trx);
/* We will need an IX lock on the destination table. */
prebuilt->sql_stat_start = TRUE;
} else {
@@ -4888,7 +4912,7 @@ no_commit:
locks, so they have to be acquired again. */
innobase_commit(ht, user_thd, 1);
/* Note that this transaction is still active. */
- prebuilt->trx->active_trans = 1;
+ trx_register_for_2pc(prebuilt->trx);
/* Re-acquire the table lock on the source table. */
row_lock_table_for_mysql(prebuilt, src_table, mode);
/* We will need an IX lock on the destination table. */
@@ -8700,39 +8724,30 @@ ha_innobase::start_stmt(
prepared for an update of a row */
prebuilt->select_lock_type = LOCK_X;
- } else {
- if (trx->isolation_level != TRX_ISO_SERIALIZABLE
- && thd_sql_command(thd) == SQLCOM_SELECT
- && lock_type == TL_READ) {
- /* For other than temporary tables, we obtain
- no lock for consistent read (plain SELECT). */
+ } else if (trx->isolation_level != TRX_ISO_SERIALIZABLE
+ && thd_sql_command(thd) == SQLCOM_SELECT
+ && lock_type == TL_READ) {
- prebuilt->select_lock_type = LOCK_NONE;
- } else {
- /* Not a consistent read: restore the
- select_lock_type value. The value of
- stored_select_lock_type was decided in:
- 1) ::store_lock(),
- 2) ::external_lock(),
- 3) ::init_table_handle_for_HANDLER(), and
- 4) ::transactional_table_lock(). */
+ /* For other than temporary tables, we obtain
+ no lock for consistent read (plain SELECT). */
- prebuilt->select_lock_type =
- prebuilt->stored_select_lock_type;
- }
- }
+ prebuilt->select_lock_type = LOCK_NONE;
+ } else {
+ /* Not a consistent read: restore the
+ select_lock_type value. The value of
+ stored_select_lock_type was decided in:
+ 1) ::store_lock(),
+ 2) ::external_lock(),
+ 3) ::init_table_handle_for_HANDLER(), and
+ 4) ::transactional_table_lock(). */
- trx->detailed_error[0] = '\0';
+ prebuilt->select_lock_type = prebuilt->stored_select_lock_type;
+ }
- /* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
+ *trx->detailed_error = 0;
- innobase_register_trx_and_stmt(ht, thd);
- trx->active_trans = 1;
- } else {
- innobase_register_stmt(ht, thd);
- }
+ innobase_register_trx(ht, thd, trx);
return(0);
}
@@ -8821,22 +8836,14 @@ ha_innobase::external_lock(
if (lock_type != F_UNLCK) {
/* MySQL is setting a new table lock */
- trx->detailed_error[0] = '\0';
-
- /* Set the MySQL flag to mark that there is an active
- transaction */
- if (trx->active_trans == 0) {
+ *trx->detailed_error = 0;
- innobase_register_trx_and_stmt(ht, thd);
- trx->active_trans = 1;
- } else if (trx->n_mysql_tables_in_use == 0) {
- innobase_register_stmt(ht, thd);
- }
+ innobase_register_trx(ht, thd, trx);
if (trx->isolation_level == TRX_ISO_SERIALIZABLE
- && prebuilt->select_lock_type == LOCK_NONE
- && thd_test_options(thd,
- OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+ && prebuilt->select_lock_type == LOCK_NONE
+ && thd_test_options(
+ thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
/* To get serializable execution, we let InnoDB
conceptually add 'LOCK IN SHARE MODE' to all SELECTs
@@ -8906,19 +8913,20 @@ ha_innobase::external_lock(
trx->mysql_n_tables_locked = 0;
prebuilt->used_in_HANDLER = FALSE;
- if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
- if (trx->active_trans != 0) {
+ if (!thd_test_options(
+ thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+
+ if (trx_is_started(trx)) {
innobase_commit(ht, thd, TRUE);
}
- } else {
- if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
- && trx->global_read_view) {
- /* At low transaction isolation levels we let
- each consistent read set its own snapshot */
+ } else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
+ && trx->global_read_view) {
- read_view_close_for_mysql(trx);
- }
+ /* At low transaction isolation levels we let
+ each consistent read set its own snapshot */
+
+ read_view_close_for_mysql(trx);
}
}
@@ -8987,12 +8995,7 @@ ha_innobase::transactional_table_lock(
/* MySQL is setting a new transactional table lock */
- /* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(ht, thd);
- trx->active_trans = 1;
- }
+ innobase_register_trx(ht, thd, trx);
if (THDVAR(thd, table_locks) && thd_in_lock_tables(thd)) {
ulint error = DB_SUCCESS;
@@ -9005,7 +9008,8 @@ ha_innobase::transactional_table_lock(
DBUG_RETURN((int) error);
}
- if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+ if (thd_test_options(
+ thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
/* Store the current undo_no of the transaction
so that we know where to roll back if we have
@@ -10046,19 +10050,19 @@ innobase_xa_prepare(
innobase_release_stat_resources(trx);
- if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {
+ if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
+ sql_print_error("Transaction not registered for MySQL 2PC, "
+ "but transaction is active");
}
if (all
- || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
+ || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
/* We were instructed to prepare the whole transaction, or
this is an SQL statement end and autocommit is on */
- ut_ad(trx->active_trans);
+ ut_ad(trx_is_registered_for_2pc(trx));
error = (int) trx_prepare_for_mysql(trx);
} else {
@@ -10082,9 +10086,10 @@ innobase_xa_prepare(
srv_active_wake_master_thread();
- if (thd_sql_command(thd) != SQLCOM_XA_PREPARE &&
- (all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
- {
+ if (thd_sql_command(thd) != SQLCOM_XA_PREPARE
+ && (all
+ || !thd_test_options(
+ thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
/* For ibbackup to work the order of transactions in binlog
and InnoDB must be the same. Consider the situation
@@ -10105,7 +10110,7 @@ innobase_xa_prepare(
will be between XA PREPARE and XA COMMIT, and we don't want
to block for undefined period of time. */
mysql_mutex_lock(&prepare_commit_mutex);
- trx->active_trans = 2;
+ trx_owns_prepare_commit_mutex_set(trx);
}
return(error);
@@ -10140,8 +10145,8 @@ static
int
innobase_commit_by_xid(
/*===================*/
- handlerton *hton,
- XID* xid) /*!< in: X/Open XA transaction identification */
+ handlerton* hton,
+ XID* xid) /*!< in: X/Open XA transaction identification */
{
trx_t* trx;
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 6a817ccdc8e..b4a4aa0ca44 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -470,6 +470,20 @@ struct trx_struct{
of view of concurrency control:
TRX_ACTIVE, TRX_COMMITTED_IN_MEMORY,
... */
+ /*------------------------------*/
+ /* MySQL has a transaction coordinator to coordinate two phase
+ commit between multiple storage engines and the binary log. When
+ an engine participates in a transaction, it's responsible for
+ registering itself using the trans_register_ha() API. */
+ unsigned is_registered:1;/* This flag is set to 1 after the
+ transaction has been registered with
+ the coordinator using the XA API, and
+ is set to 0 after commit or rollback. */
+ unsigned owns_prepare_mutex:1;/* 1 if owns prepare mutex, if
+ this is set to 1 then registered should
+ also be set to 1. This is used in the
+ XA code */
+ /*------------------------------*/
ulint isolation_level;/* TRX_ISO_REPEATABLE_READ, ... */
ulint check_foreigns; /* normally TRUE, but if the user
wants to suppress foreign key checks,
@@ -500,9 +514,6 @@ struct trx_struct{
in that case we must flush the log
in trx_commit_complete_for_mysql() */
ulint duplicates; /*!< TRX_DUP_IGNORE | TRX_DUP_REPLACE */
- ulint active_trans; /*!< 1 - if a transaction in MySQL
- is active. 2 - if prepare_commit_mutex
- was taken */
ulint has_search_latch;
/* TRUE if this trx has latched the
search system latch in S-mode */
diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c
index 6ef47a8dc16..6bae0527b53 100644
--- a/storage/innobase/trx/trx0trx.c
+++ b/storage/innobase/trx/trx0trx.c
@@ -105,7 +105,11 @@ trx_create(
trx->is_purge = 0;
trx->is_recovered = 0;
trx->conc_state = TRX_NOT_STARTED;
- trx->start_time = time(NULL);
+
+ trx->is_registered = 0;
+ trx->owns_prepare_mutex = 0;
+
+ trx->start_time = ut_time();
trx->isolation_level = TRX_ISO_REPEATABLE_READ;
@@ -124,7 +128,6 @@ trx_create(
trx->table_id = 0;
trx->mysql_thd = NULL;
- trx->active_trans = 0;
trx->duplicates = 0;
trx->n_mysql_tables_in_use = 0;