Backport of:
------------------------------------------------------------ revno: 2630.22.3 committer: Davi Arnaut <Davi.Arnaut@Sun.COM> branch nick: 4284-6.0 timestamp: Thu 2008-08-07 22:33:43 -0300 message: WL#4284: Transactional DDL locking Make transaction management more modular through a new interface. The overall objective of this change is to provide groundwork for the design of transactional DDL locking by cleaning up the transaction high level API to better distinguish operations implicit and explicit, and single statement transaction from operations on the normal transaction. Having a a high-level interface for transaction management provides a better base for implementing transactional concepts that are not always tied to storage engines and also makes it easier to interect with other higher level modules of the server.
This commit is contained in:
parent
3a8a750919
commit
c43f894c51
@ -107,7 +107,8 @@ sql_src=log_event.h mysql_priv.h rpl_constants.h \
|
|||||||
rpl_utility.h rpl_tblmap.h rpl_tblmap.cc \
|
rpl_utility.h rpl_tblmap.h rpl_tblmap.cc \
|
||||||
log_event.cc my_decimal.h my_decimal.cc \
|
log_event.cc my_decimal.h my_decimal.cc \
|
||||||
log_event_old.h log_event_old.cc \
|
log_event_old.h log_event_old.cc \
|
||||||
rpl_record_old.h rpl_record_old.cc
|
rpl_record_old.h rpl_record_old.cc \
|
||||||
|
transaction.h
|
||||||
strings_src=decimal.c
|
strings_src=decimal.c
|
||||||
|
|
||||||
link_sources:
|
link_sources:
|
||||||
|
@ -133,7 +133,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
|
|||||||
../sql/partition_info.cc ../sql/sql_connect.cc
|
../sql/partition_info.cc ../sql/sql_connect.cc
|
||||||
../sql/scheduler.cc ../sql/event_parse_data.cc
|
../sql/scheduler.cc ../sql/event_parse_data.cc
|
||||||
../sql/sql_signal.cc ../sql/rpl_handler.cc
|
../sql/sql_signal.cc ../sql/rpl_handler.cc
|
||||||
../sql/mdl.cc
|
../sql/mdl.cc ../sql/transaction.cc
|
||||||
${GEN_SOURCES}
|
${GEN_SOURCES}
|
||||||
${LIB_SOURCES})
|
${LIB_SOURCES})
|
||||||
|
|
||||||
|
@ -75,8 +75,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
|
|||||||
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
|
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
|
||||||
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
|
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
|
||||||
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
|
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
|
||||||
debug_sync.cc \
|
debug_sync.cc sql_tablespace.cc transaction.cc \
|
||||||
sql_tablespace.cc \
|
|
||||||
rpl_injector.cc my_user.c partition_info.cc \
|
rpl_injector.cc my_user.c partition_info.cc \
|
||||||
sql_servers.cc event_parse_data.cc sql_signal.cc \
|
sql_servers.cc event_parse_data.cc sql_signal.cc \
|
||||||
rpl_handler.cc mdl.cc
|
rpl_handler.cc mdl.cc
|
||||||
|
@ -77,6 +77,7 @@ SET (SQL_SOURCE
|
|||||||
sql_connect.cc scheduler.cc
|
sql_connect.cc scheduler.cc
|
||||||
sql_profile.cc event_parse_data.cc
|
sql_profile.cc event_parse_data.cc
|
||||||
sql_signal.cc rpl_handler.cc mdl.cc
|
sql_signal.cc rpl_handler.cc mdl.cc
|
||||||
|
transaction.cc
|
||||||
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
|
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
|
||||||
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
|
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
|
||||||
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
|
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
|
||||||
|
@ -113,7 +113,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
|
|||||||
sql_partition.h partition_info.h partition_element.h \
|
sql_partition.h partition_info.h partition_element.h \
|
||||||
contributors.h sql_servers.h sql_signal.h records.h \
|
contributors.h sql_servers.h sql_signal.h records.h \
|
||||||
sql_prepare.h rpl_handler.h replication.h mdl.h \
|
sql_prepare.h rpl_handler.h replication.h mdl.h \
|
||||||
sql_plist.h
|
sql_plist.h transaction.h
|
||||||
|
|
||||||
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
|
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
|
||||||
item.cc item_sum.cc item_buff.cc item_func.cc \
|
item.cc item_sum.cc item_buff.cc item_func.cc \
|
||||||
@ -159,7 +159,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
|
|||||||
sql_plugin.cc sql_binlog.cc \
|
sql_plugin.cc sql_binlog.cc \
|
||||||
sql_builtin.cc sql_tablespace.cc partition_info.cc \
|
sql_builtin.cc sql_tablespace.cc partition_info.cc \
|
||||||
sql_servers.cc event_parse_data.cc sql_signal.cc \
|
sql_servers.cc event_parse_data.cc sql_signal.cc \
|
||||||
rpl_handler.cc mdl.cc
|
rpl_handler.cc mdl.cc transaction.cc
|
||||||
|
|
||||||
nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
|
nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "rpl_handler.h"
|
#include "rpl_handler.h"
|
||||||
#include "rpl_filter.h"
|
#include "rpl_filter.h"
|
||||||
#include <myisampack.h>
|
#include <myisampack.h>
|
||||||
|
#include "transaction.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "probes_mysql.h"
|
#include "probes_mysql.h"
|
||||||
|
|
||||||
@ -888,16 +889,16 @@ void ha_close_connection(THD* thd)
|
|||||||
a transaction in a given engine is read-write and will not
|
a transaction in a given engine is read-write and will not
|
||||||
involve the two-phase commit protocol!
|
involve the two-phase commit protocol!
|
||||||
|
|
||||||
At the end of a statement, server call
|
At the end of a statement, server call trans_commit_stmt is
|
||||||
ha_autocommit_or_rollback() is invoked. This call in turn
|
invoked. This call in turn invokes handlerton::prepare()
|
||||||
invokes handlerton::prepare() for every involved engine.
|
for every involved engine. Prepare is followed by a call
|
||||||
Prepare is followed by a call to handlerton::commit_one_phase()
|
to handlerton::commit_one_phase() If a one-phase commit
|
||||||
If a one-phase commit will suffice, handlerton::prepare() is not
|
will suffice, handlerton::prepare() is not invoked and
|
||||||
invoked and the server only calls handlerton::commit_one_phase().
|
the server only calls handlerton::commit_one_phase().
|
||||||
At statement commit, the statement-related read-write engine
|
At statement commit, the statement-related read-write
|
||||||
flag is propagated to the corresponding flag in the normal
|
engine flag is propagated to the corresponding flag in the
|
||||||
transaction. When the commit is complete, the list of registered
|
normal transaction. When the commit is complete, the list
|
||||||
engines is cleared.
|
of registered engines is cleared.
|
||||||
|
|
||||||
Rollback is handled in a similar fashion.
|
Rollback is handled in a similar fashion.
|
||||||
|
|
||||||
@ -908,7 +909,7 @@ void ha_close_connection(THD* thd)
|
|||||||
do not "register" in thd->transaction lists, and thus do not
|
do not "register" in thd->transaction lists, and thus do not
|
||||||
modify the transaction state. Besides, each DDL in
|
modify the transaction state. Besides, each DDL in
|
||||||
MySQL is prefixed with an implicit normal transaction commit
|
MySQL is prefixed with an implicit normal transaction commit
|
||||||
(a call to end_active_trans()), and thus leaves nothing
|
(a call to trans_commit_implicit()), and thus leaves nothing
|
||||||
to modify.
|
to modify.
|
||||||
However, as it has been pointed out with CREATE TABLE .. SELECT,
|
However, as it has been pointed out with CREATE TABLE .. SELECT,
|
||||||
some DDL statements can start a *new* transaction.
|
some DDL statements can start a *new* transaction.
|
||||||
@ -1370,47 +1371,6 @@ int ha_rollback_trans(THD *thd, bool all)
|
|||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
This is used to commit or rollback a single statement depending on
|
|
||||||
the value of error.
|
|
||||||
|
|
||||||
@note
|
|
||||||
Note that if the autocommit is on, then the following call inside
|
|
||||||
InnoDB will commit or rollback the whole transaction (= the statement). The
|
|
||||||
autocommit mechanism built into InnoDB is based on counting locks, but if
|
|
||||||
the user has used LOCK TABLES then that mechanism does not know to do the
|
|
||||||
commit.
|
|
||||||
*/
|
|
||||||
int ha_autocommit_or_rollback(THD *thd, int error)
|
|
||||||
{
|
|
||||||
DBUG_ENTER("ha_autocommit_or_rollback");
|
|
||||||
|
|
||||||
if (thd->transaction.stmt.ha_list)
|
|
||||||
{
|
|
||||||
if (!error)
|
|
||||||
{
|
|
||||||
if (ha_commit_trans(thd, 0))
|
|
||||||
error=1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(void) ha_rollback_trans(thd, 0);
|
|
||||||
if (thd->transaction_rollback_request && !thd->in_sub_stmt)
|
|
||||||
(void) ha_rollback(thd);
|
|
||||||
}
|
|
||||||
|
|
||||||
thd->variables.tx_isolation=thd->session_tx_isolation;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!error)
|
|
||||||
RUN_HOOK(transaction, after_commit, (thd, FALSE));
|
|
||||||
else
|
|
||||||
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
|
|
||||||
}
|
|
||||||
DBUG_RETURN(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct xahton_st {
|
struct xahton_st {
|
||||||
XID *xid;
|
XID *xid;
|
||||||
@ -3496,7 +3456,7 @@ int ha_enable_transaction(THD *thd, bool on)
|
|||||||
So, let's commit an open transaction (if any) now.
|
So, let's commit an open transaction (if any) now.
|
||||||
*/
|
*/
|
||||||
if (!(error= ha_commit_trans(thd, 0)))
|
if (!(error= ha_commit_trans(thd, 0)))
|
||||||
error= end_trans(thd, COMMIT);
|
error= trans_commit_implicit(thd);
|
||||||
}
|
}
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
@ -1945,10 +1945,6 @@ extern TYPELIB tx_isolation_typelib;
|
|||||||
extern TYPELIB myisam_stats_method_typelib;
|
extern TYPELIB myisam_stats_method_typelib;
|
||||||
extern ulong total_ha, total_ha_2pc;
|
extern ulong total_ha, total_ha_2pc;
|
||||||
|
|
||||||
/* Wrapper functions */
|
|
||||||
#define ha_commit(thd) (ha_commit_trans((thd), TRUE))
|
|
||||||
#define ha_rollback(thd) (ha_rollback_trans((thd), TRUE))
|
|
||||||
|
|
||||||
/* lookups */
|
/* lookups */
|
||||||
handlerton *ha_default_handlerton(THD *thd);
|
handlerton *ha_default_handlerton(THD *thd);
|
||||||
plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name);
|
plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name);
|
||||||
@ -2025,13 +2021,12 @@ int ha_release_temporary_latches(THD *thd);
|
|||||||
int ha_start_consistent_snapshot(THD *thd);
|
int ha_start_consistent_snapshot(THD *thd);
|
||||||
int ha_commit_or_rollback_by_xid(XID *xid, bool commit);
|
int ha_commit_or_rollback_by_xid(XID *xid, bool commit);
|
||||||
int ha_commit_one_phase(THD *thd, bool all);
|
int ha_commit_one_phase(THD *thd, bool all);
|
||||||
|
int ha_commit_trans(THD *thd, bool all);
|
||||||
int ha_rollback_trans(THD *thd, bool all);
|
int ha_rollback_trans(THD *thd, bool all);
|
||||||
int ha_prepare(THD *thd);
|
int ha_prepare(THD *thd);
|
||||||
int ha_recover(HASH *commit_list);
|
int ha_recover(HASH *commit_list);
|
||||||
|
|
||||||
/* transactions: these functions never call handlerton functions directly */
|
/* transactions: these functions never call handlerton functions directly */
|
||||||
int ha_commit_trans(THD *thd, bool all);
|
|
||||||
int ha_autocommit_or_rollback(THD *thd, int error);
|
|
||||||
int ha_enable_transaction(THD *thd, bool on);
|
int ha_enable_transaction(THD *thd, bool on);
|
||||||
|
|
||||||
/* savepoints */
|
/* savepoints */
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "rpl_filter.h"
|
#include "rpl_filter.h"
|
||||||
#include "rpl_utility.h"
|
#include "rpl_utility.h"
|
||||||
#include "rpl_record.h"
|
#include "rpl_record.h"
|
||||||
|
#include "transaction.h"
|
||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
|
|
||||||
#endif /* MYSQL_CLIENT */
|
#endif /* MYSQL_CLIENT */
|
||||||
@ -3231,7 +3232,7 @@ Default database: '%s'. Query: '%s'",
|
|||||||
them back here.
|
them back here.
|
||||||
*/
|
*/
|
||||||
if (expected_error && expected_error == actual_error)
|
if (expected_error && expected_error == actual_error)
|
||||||
ha_autocommit_or_rollback(thd, TRUE);
|
trans_rollback_stmt(thd);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
If we expected a non-zero error code and get nothing and, it is a concurrency
|
If we expected a non-zero error code and get nothing and, it is a concurrency
|
||||||
@ -3240,7 +3241,8 @@ Default database: '%s'. Query: '%s'",
|
|||||||
else if (expected_error && !actual_error &&
|
else if (expected_error && !actual_error &&
|
||||||
(concurrency_error_code(expected_error) ||
|
(concurrency_error_code(expected_error) ||
|
||||||
ignored_error_code(expected_error)))
|
ignored_error_code(expected_error)))
|
||||||
ha_autocommit_or_rollback(thd, TRUE);
|
trans_rollback_stmt(thd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Other cases: mostly we expected no error and get one.
|
Other cases: mostly we expected no error and get one.
|
||||||
*/
|
*/
|
||||||
@ -5315,7 +5317,7 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli)
|
|||||||
/* For a slave Xid_log_event is COMMIT */
|
/* For a slave Xid_log_event is COMMIT */
|
||||||
general_log_print(thd, COM_QUERY,
|
general_log_print(thd, COM_QUERY,
|
||||||
"COMMIT /* implicit, from Xid_log_event */");
|
"COMMIT /* implicit, from Xid_log_event */");
|
||||||
return end_trans(thd, COMMIT);
|
return trans_commit(thd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_event::enum_skip_reason
|
Log_event::enum_skip_reason
|
||||||
@ -7607,7 +7609,7 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
|
|||||||
are involved, commit the transaction and flush the pending event to the
|
are involved, commit the transaction and flush the pending event to the
|
||||||
binlog.
|
binlog.
|
||||||
*/
|
*/
|
||||||
error= ha_autocommit_or_rollback(thd, 0);
|
error= trans_commit_stmt(thd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Now what if this is not a transactional engine? we still need to
|
Now what if this is not a transactional engine? we still need to
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "log_event_old.h"
|
#include "log_event_old.h"
|
||||||
#include "rpl_record_old.h"
|
#include "rpl_record_old.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
||||||
|
|
||||||
@ -1795,7 +1796,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
|
|||||||
are involved, commit the transaction and flush the pending event to the
|
are involved, commit the transaction and flush the pending event to the
|
||||||
binlog.
|
binlog.
|
||||||
*/
|
*/
|
||||||
if ((error= ha_autocommit_or_rollback(thd, 0)))
|
if ((error= trans_commit_stmt(thd)))
|
||||||
rli->report(ERROR_LEVEL, error,
|
rli->report(ERROR_LEVEL, error,
|
||||||
"Error in %s event: commit of row events failed, "
|
"Error in %s event: commit of row events failed, "
|
||||||
"table `%s`.`%s`",
|
"table `%s`.`%s`",
|
||||||
|
@ -883,15 +883,6 @@ bool parse_sql(THD *thd,
|
|||||||
Parser_state *parser_state,
|
Parser_state *parser_state,
|
||||||
Object_creation_ctx *creation_ctx);
|
Object_creation_ctx *creation_ctx);
|
||||||
|
|
||||||
enum enum_mysql_completiontype {
|
|
||||||
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
|
|
||||||
COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
|
|
||||||
};
|
|
||||||
|
|
||||||
bool begin_trans(THD *thd);
|
|
||||||
bool end_active_trans(THD *thd);
|
|
||||||
int end_trans(THD *thd, enum enum_mysql_completiontype completion);
|
|
||||||
|
|
||||||
Item *negate_expression(THD *thd, Item *expr);
|
Item *negate_expression(THD *thd, Item *expr);
|
||||||
|
|
||||||
/* log.cc */
|
/* log.cc */
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "mysql_priv.h"
|
#include "mysql_priv.h"
|
||||||
#include "rpl_injector.h"
|
#include "rpl_injector.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
injector::transaction - member definitions
|
injector::transaction - member definitions
|
||||||
@ -35,7 +36,7 @@ injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd)
|
|||||||
m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0));
|
m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0));
|
||||||
m_start_pos.m_file_pos= log_info.pos;
|
m_start_pos.m_file_pos= log_info.pos;
|
||||||
|
|
||||||
begin_trans(m_thd);
|
trans_begin(m_thd);
|
||||||
|
|
||||||
thd->set_current_stmt_binlog_row_based();
|
thd->set_current_stmt_binlog_row_based();
|
||||||
}
|
}
|
||||||
@ -81,8 +82,8 @@ int injector::transaction::commit()
|
|||||||
is committed by committing the statement transaction
|
is committed by committing the statement transaction
|
||||||
explicitly.
|
explicitly.
|
||||||
*/
|
*/
|
||||||
ha_autocommit_or_rollback(m_thd, 0);
|
trans_commit_stmt(m_thd);
|
||||||
end_trans(m_thd, COMMIT);
|
trans_commit(m_thd);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <my_dir.h> // For MY_STAT
|
#include <my_dir.h> // For MY_STAT
|
||||||
#include "sql_repl.h" // For check_binlog_magic
|
#include "sql_repl.h" // For check_binlog_magic
|
||||||
#include "rpl_utility.h"
|
#include "rpl_utility.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
static int count_relay_log_space(Relay_log_info* rli);
|
static int count_relay_log_space(Relay_log_info* rli);
|
||||||
|
|
||||||
@ -1183,8 +1184,8 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
|
|||||||
*/
|
*/
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
ha_autocommit_or_rollback(thd, 1); // if a "statement transaction"
|
trans_rollback_stmt(thd); // if a "statement transaction"
|
||||||
end_trans(thd, ROLLBACK); // if a "real transaction"
|
trans_rollback(thd); // if a "real transaction"
|
||||||
}
|
}
|
||||||
m_table_map.clear_tables();
|
m_table_map.clear_tables();
|
||||||
slave_close_thread_tables(thd);
|
slave_close_thread_tables(thd);
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
|
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
/* WITH_NDBCLUSTER_STORAGE_ENGINE */
|
/* WITH_NDBCLUSTER_STORAGE_ENGINE */
|
||||||
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
|
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
|
||||||
@ -3190,7 +3191,7 @@ static bool set_option_autocommit(THD *thd, set_var *var)
|
|||||||
*/
|
*/
|
||||||
if (var->save_result.ulong_value != 0 &&
|
if (var->save_result.ulong_value != 0 &&
|
||||||
(thd->options & OPTION_NOT_AUTOCOMMIT) &&
|
(thd->options & OPTION_NOT_AUTOCOMMIT) &&
|
||||||
ha_commit(thd))
|
trans_commit(thd))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (var->save_result.ulong_value != 0)
|
if (var->save_result.ulong_value != 0)
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "sql_repl.h"
|
#include "sql_repl.h"
|
||||||
#include "rpl_filter.h"
|
#include "rpl_filter.h"
|
||||||
#include "repl_failsafe.h"
|
#include "repl_failsafe.h"
|
||||||
|
#include "transaction.h"
|
||||||
#include <thr_alarm.h>
|
#include <thr_alarm.h>
|
||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
#include <sql_common.h>
|
#include <sql_common.h>
|
||||||
@ -2430,7 +2431,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
exec_res= 0;
|
exec_res= 0;
|
||||||
end_trans(thd, ROLLBACK);
|
trans_rollback(thd);
|
||||||
/* chance for concurrent connection to get more locks */
|
/* chance for concurrent connection to get more locks */
|
||||||
safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
|
safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
|
||||||
(CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli);
|
(CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sp.h"
|
#include "sp.h"
|
||||||
#include "sql_trigger.h"
|
#include "sql_trigger.h"
|
||||||
|
#include "transaction.h"
|
||||||
#include "sql_prepare.h"
|
#include "sql_prepare.h"
|
||||||
#include <m_ctype.h>
|
#include <m_ctype.h>
|
||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
@ -1402,7 +1403,7 @@ void close_thread_tables(THD *thd,
|
|||||||
if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
|
if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
|
||||||
{
|
{
|
||||||
thd->stmt_da->can_overwrite_status= TRUE;
|
thd->stmt_da->can_overwrite_status= TRUE;
|
||||||
ha_autocommit_or_rollback(thd, thd->is_error());
|
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
|
||||||
thd->stmt_da->can_overwrite_status= FALSE;
|
thd->stmt_da->can_overwrite_status= FALSE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "sp_rcontext.h"
|
#include "sp_rcontext.h"
|
||||||
#include "sp_cache.h"
|
#include "sp_cache.h"
|
||||||
|
#include "transaction.h"
|
||||||
#include "debug_sync.h"
|
#include "debug_sync.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -987,7 +988,8 @@ void THD::cleanup(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
ha_rollback(this);
|
transaction.xid_state.xa_state= XA_NOTR;
|
||||||
|
trans_rollback(this);
|
||||||
xid_cache_delete(&transaction.xid_state);
|
xid_cache_delete(&transaction.xid_state);
|
||||||
}
|
}
|
||||||
locked_tables_list.unlock_locked_tables(this);
|
locked_tables_list.unlock_locked_tables(this);
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "sql_select.h"
|
#include "sql_select.h"
|
||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sql_trigger.h"
|
#include "sql_trigger.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Implement DELETE SQL word.
|
Implement DELETE SQL word.
|
||||||
@ -1071,8 +1072,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
|
|||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(thd->stmt_da->is_error());
|
DBUG_ASSERT(thd->stmt_da->is_error());
|
||||||
ha_autocommit_or_rollback(thd, TRUE);
|
trans_rollback_stmt(thd);
|
||||||
end_active_trans(thd);
|
trans_rollback(thd);
|
||||||
}
|
}
|
||||||
thd->current_stmt_binlog_row_based= save_binlog_row_based;
|
thd->current_stmt_binlog_row_based= save_binlog_row_based;
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
/* Execute DO statement */
|
/* Execute DO statement */
|
||||||
|
|
||||||
#include "mysql_priv.h"
|
#include "mysql_priv.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
bool mysql_do(THD *thd, List<Item> &values)
|
bool mysql_do(THD *thd, List<Item> &values)
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ bool mysql_do(THD *thd, List<Item> &values)
|
|||||||
will clear the error and the rollback in the end of
|
will clear the error and the rollback in the end of
|
||||||
dispatch_command() won't work.
|
dispatch_command() won't work.
|
||||||
*/
|
*/
|
||||||
ha_autocommit_or_rollback(thd, thd->is_error());
|
trans_rollback_stmt(thd);
|
||||||
thd->clear_error(); // DO always is OK
|
thd->clear_error(); // DO always is OK
|
||||||
}
|
}
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "sql_show.h"
|
#include "sql_show.h"
|
||||||
#include "slave.h"
|
#include "slave.h"
|
||||||
#include "rpl_mi.h"
|
#include "rpl_mi.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
|
static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
|
||||||
@ -2539,7 +2540,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
|
|||||||
*/
|
*/
|
||||||
di->table->file->ha_release_auto_increment();
|
di->table->file->ha_release_auto_increment();
|
||||||
mysql_unlock_tables(thd, lock);
|
mysql_unlock_tables(thd, lock);
|
||||||
ha_autocommit_or_rollback(thd, 0);
|
trans_commit_stmt(thd);
|
||||||
di->group_count=0;
|
di->group_count=0;
|
||||||
pthread_mutex_lock(&di->mutex);
|
pthread_mutex_lock(&di->mutex);
|
||||||
}
|
}
|
||||||
@ -2559,7 +2560,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
|
|||||||
first call to ha_*_row() instead. Remove code that are used to
|
first call to ha_*_row() instead. Remove code that are used to
|
||||||
cover for the case outlined above.
|
cover for the case outlined above.
|
||||||
*/
|
*/
|
||||||
ha_autocommit_or_rollback(thd, 1);
|
trans_rollback_stmt(thd);
|
||||||
|
|
||||||
DBUG_LEAVE;
|
DBUG_LEAVE;
|
||||||
}
|
}
|
||||||
@ -3863,8 +3864,8 @@ bool select_create::send_eof()
|
|||||||
*/
|
*/
|
||||||
if (!table->s->tmp_table)
|
if (!table->s->tmp_table)
|
||||||
{
|
{
|
||||||
ha_autocommit_or_rollback(thd, 0);
|
trans_commit_stmt(thd);
|
||||||
end_active_trans(thd);
|
trans_commit_implicit(thd);
|
||||||
}
|
}
|
||||||
|
|
||||||
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
|
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
|
||||||
|
512
sql/sql_parse.cc
512
sql/sql_parse.cc
@ -28,6 +28,7 @@
|
|||||||
#include "sp_cache.h"
|
#include "sp_cache.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "sql_trigger.h"
|
#include "sql_trigger.h"
|
||||||
|
#include "transaction.h"
|
||||||
#include "sql_prepare.h"
|
#include "sql_prepare.h"
|
||||||
#include "probes_mysql.h"
|
#include "probes_mysql.h"
|
||||||
|
|
||||||
@ -88,113 +89,6 @@ const char *xa_state_names[]={
|
|||||||
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
|
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
Mark a XA transaction as rollback-only if the RM unilaterally
|
|
||||||
rolled back the transaction branch.
|
|
||||||
|
|
||||||
@note If a rollback was requested by the RM, this function sets
|
|
||||||
the appropriate rollback error code and transits the state
|
|
||||||
to XA_ROLLBACK_ONLY.
|
|
||||||
|
|
||||||
@return TRUE if transaction was rolled back or if the transaction
|
|
||||||
state is XA_ROLLBACK_ONLY. FALSE otherwise.
|
|
||||||
*/
|
|
||||||
static bool xa_trans_rolled_back(XID_STATE *xid_state)
|
|
||||||
{
|
|
||||||
if (xid_state->rm_error)
|
|
||||||
{
|
|
||||||
switch (xid_state->rm_error) {
|
|
||||||
case ER_LOCK_WAIT_TIMEOUT:
|
|
||||||
my_error(ER_XA_RBTIMEOUT, MYF(0));
|
|
||||||
break;
|
|
||||||
case ER_LOCK_DEADLOCK:
|
|
||||||
my_error(ER_XA_RBDEADLOCK, MYF(0));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
my_error(ER_XA_RBROLLBACK, MYF(0));
|
|
||||||
}
|
|
||||||
xid_state->xa_state= XA_ROLLBACK_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (xid_state->xa_state == XA_ROLLBACK_ONLY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Rollback work done on behalf of at ransaction branch.
|
|
||||||
*/
|
|
||||||
static bool xa_trans_rollback(THD *thd)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Resource Manager error is meaningless at this point, as we perform
|
|
||||||
explicit rollback request by user. We must reset rm_error before
|
|
||||||
calling ha_rollback(), so thd->transaction.xid structure gets reset
|
|
||||||
by ha_rollback()/THD::transaction::cleanup().
|
|
||||||
*/
|
|
||||||
thd->transaction.xid_state.rm_error= 0;
|
|
||||||
|
|
||||||
bool status= test(ha_rollback(thd));
|
|
||||||
|
|
||||||
thd->options&= ~(ulong) OPTION_BEGIN;
|
|
||||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
|
||||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
||||||
xid_cache_delete(&thd->transaction.xid_state);
|
|
||||||
thd->transaction.xid_state.xa_state= XA_NOTR;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_active_trans(THD *thd)
|
|
||||||
{
|
|
||||||
int error=0;
|
|
||||||
DBUG_ENTER("end_active_trans");
|
|
||||||
if (unlikely(thd->in_sub_stmt))
|
|
||||||
{
|
|
||||||
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
if (thd->transaction.xid_state.xa_state != XA_NOTR)
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
|
|
||||||
OPTION_TABLE_LOCK))
|
|
||||||
{
|
|
||||||
DBUG_PRINT("info",("options: 0x%llx", thd->options));
|
|
||||||
/* Safety if one did "drop table" on locked tables */
|
|
||||||
if (!thd->locked_tables_mode)
|
|
||||||
thd->options&= ~OPTION_TABLE_LOCK;
|
|
||||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
||||||
if (ha_commit(thd))
|
|
||||||
error=1;
|
|
||||||
}
|
|
||||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
|
||||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
|
||||||
DBUG_RETURN(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool begin_trans(THD *thd)
|
|
||||||
{
|
|
||||||
int error=0;
|
|
||||||
if (unlikely(thd->in_sub_stmt))
|
|
||||||
{
|
|
||||||
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
thd->locked_tables_list.unlock_locked_tables(thd);
|
|
||||||
|
|
||||||
if (end_active_trans(thd))
|
|
||||||
error= -1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
thd->options|= OPTION_BEGIN;
|
|
||||||
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
/**
|
/**
|
||||||
@ -256,9 +150,9 @@ static bool opt_implicit_commit(THD *thd, uint mask)
|
|||||||
if (!skip)
|
if (!skip)
|
||||||
{
|
{
|
||||||
/* Commit or rollback the statement transaction. */
|
/* Commit or rollback the statement transaction. */
|
||||||
ha_autocommit_or_rollback(thd, thd->is_error());
|
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
|
||||||
/* Commit the normal transaction if one is active. */
|
/* Commit the normal transaction if one is active. */
|
||||||
res= end_active_trans(thd);
|
res= trans_commit_implicit(thd);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_RETURN(res);
|
DBUG_RETURN(res);
|
||||||
@ -691,80 +585,6 @@ void cleanup_items(Item *item)
|
|||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Ends the current transaction and (maybe) begin the next.
|
|
||||||
|
|
||||||
@param thd Current thread
|
|
||||||
@param completion Completion type
|
|
||||||
|
|
||||||
@retval
|
|
||||||
0 OK
|
|
||||||
*/
|
|
||||||
|
|
||||||
int end_trans(THD *thd, enum enum_mysql_completiontype completion)
|
|
||||||
{
|
|
||||||
bool do_release= 0;
|
|
||||||
int res= 0;
|
|
||||||
DBUG_ENTER("end_trans");
|
|
||||||
|
|
||||||
if (unlikely(thd->in_sub_stmt))
|
|
||||||
{
|
|
||||||
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
if (thd->transaction.xid_state.xa_state != XA_NOTR)
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
switch (completion) {
|
|
||||||
case COMMIT:
|
|
||||||
/*
|
|
||||||
We don't use end_active_trans() here to ensure that this works
|
|
||||||
even if there is a problem with the OPTION_AUTO_COMMIT flag
|
|
||||||
(Which of course should never happen...)
|
|
||||||
*/
|
|
||||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
||||||
res= ha_commit(thd);
|
|
||||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
|
||||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
|
||||||
break;
|
|
||||||
case COMMIT_RELEASE:
|
|
||||||
do_release= 1; /* fall through */
|
|
||||||
case COMMIT_AND_CHAIN:
|
|
||||||
res= end_active_trans(thd);
|
|
||||||
if (!res && completion == COMMIT_AND_CHAIN)
|
|
||||||
res= begin_trans(thd);
|
|
||||||
break;
|
|
||||||
case ROLLBACK_RELEASE:
|
|
||||||
do_release= 1; /* fall through */
|
|
||||||
case ROLLBACK:
|
|
||||||
case ROLLBACK_AND_CHAIN:
|
|
||||||
{
|
|
||||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
||||||
if (ha_rollback(thd))
|
|
||||||
res= -1;
|
|
||||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
|
||||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
|
||||||
if (!res && (completion == ROLLBACK_AND_CHAIN))
|
|
||||||
res= begin_trans(thd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res= -1;
|
|
||||||
my_error(ER_UNKNOWN_COM_ERROR, MYF(0));
|
|
||||||
DBUG_RETURN(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res < 0)
|
|
||||||
my_error(thd->killed_errno(), MYF(0));
|
|
||||||
else if ((res == 0) && do_release)
|
|
||||||
thd->killed= THD::KILL_CONNECTION;
|
|
||||||
|
|
||||||
DBUG_RETURN(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1346,7 +1166,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
bool not_used;
|
bool not_used;
|
||||||
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
|
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
|
||||||
ulong options= (ulong) (uchar) packet[0];
|
ulong options= (ulong) (uchar) packet[0];
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
break;
|
break;
|
||||||
if (check_global_access(thd,RELOAD_ACL))
|
if (check_global_access(thd,RELOAD_ACL))
|
||||||
break;
|
break;
|
||||||
@ -1374,7 +1194,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
#endif
|
#endif
|
||||||
if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used))
|
if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used))
|
||||||
break;
|
break;
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
break;
|
break;
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
@ -1532,7 +1352,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
|
|
||||||
/* If commit fails, we should be able to reset the OK status. */
|
/* If commit fails, we should be able to reset the OK status. */
|
||||||
thd->stmt_da->can_overwrite_status= TRUE;
|
thd->stmt_da->can_overwrite_status= TRUE;
|
||||||
ha_autocommit_or_rollback(thd, thd->is_error());
|
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
|
||||||
thd->stmt_da->can_overwrite_status= FALSE;
|
thd->stmt_da->can_overwrite_status= FALSE;
|
||||||
|
|
||||||
thd->transaction.stmt.reset();
|
thd->transaction.stmt.reset();
|
||||||
@ -3485,7 +3305,7 @@ end_with_restore_list:
|
|||||||
thd->locked_tables_list.unlock_locked_tables(thd);
|
thd->locked_tables_list.unlock_locked_tables(thd);
|
||||||
if (thd->options & OPTION_TABLE_LOCK)
|
if (thd->options & OPTION_TABLE_LOCK)
|
||||||
{
|
{
|
||||||
end_active_trans(thd);
|
trans_commit_implicit(thd);
|
||||||
thd->options&= ~(OPTION_TABLE_LOCK);
|
thd->options&= ~(OPTION_TABLE_LOCK);
|
||||||
}
|
}
|
||||||
if (thd->global_read_lock)
|
if (thd->global_read_lock)
|
||||||
@ -3495,7 +3315,7 @@ end_with_restore_list:
|
|||||||
case SQLCOM_LOCK_TABLES:
|
case SQLCOM_LOCK_TABLES:
|
||||||
thd->locked_tables_list.unlock_locked_tables(thd);
|
thd->locked_tables_list.unlock_locked_tables(thd);
|
||||||
/* we must end the trasaction first, regardless of anything */
|
/* we must end the trasaction first, regardless of anything */
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
goto error;
|
goto error;
|
||||||
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
|
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
|
||||||
FALSE, UINT_MAX, FALSE))
|
FALSE, UINT_MAX, FALSE))
|
||||||
@ -3524,8 +3344,8 @@ end_with_restore_list:
|
|||||||
can free its locks if LOCK TABLES locked some tables before finding
|
can free its locks if LOCK TABLES locked some tables before finding
|
||||||
that it can't lock a table in its list
|
that it can't lock a table in its list
|
||||||
*/
|
*/
|
||||||
ha_autocommit_or_rollback(thd, 1);
|
trans_rollback_stmt(thd);
|
||||||
end_active_trans(thd);
|
trans_commit_implicit(thd);
|
||||||
thd->options&= ~(OPTION_TABLE_LOCK);
|
thd->options&= ~(OPTION_TABLE_LOCK);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -4007,132 +3827,50 @@ end_with_restore_list:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SQLCOM_BEGIN:
|
case SQLCOM_BEGIN:
|
||||||
if (thd->transaction.xid_state.xa_state != XA_NOTR)
|
if (trans_begin(thd, lex->start_transaction_opt))
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (begin_trans(thd))
|
|
||||||
goto error;
|
goto error;
|
||||||
if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
|
|
||||||
{
|
|
||||||
if (ha_start_consistent_snapshot(thd))
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_COMMIT:
|
case SQLCOM_COMMIT:
|
||||||
DBUG_ASSERT(thd->lock == NULL ||
|
DBUG_ASSERT(thd->lock == NULL ||
|
||||||
thd->locked_tables_mode == LTM_LOCK_TABLES);
|
thd->locked_tables_mode == LTM_LOCK_TABLES);
|
||||||
if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
|
if (trans_commit(thd))
|
||||||
lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
|
|
||||||
goto error;
|
goto error;
|
||||||
|
/* Begin transaction with the same isolation level. */
|
||||||
|
if (lex->tx_chain && trans_begin(thd))
|
||||||
|
goto error;
|
||||||
|
/* Disconnect the current client connection. */
|
||||||
|
if (lex->tx_release)
|
||||||
|
thd->killed= THD::KILL_CONNECTION;
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_ROLLBACK:
|
case SQLCOM_ROLLBACK:
|
||||||
DBUG_ASSERT(thd->lock == NULL ||
|
DBUG_ASSERT(thd->lock == NULL ||
|
||||||
thd->locked_tables_mode == LTM_LOCK_TABLES);
|
thd->locked_tables_mode == LTM_LOCK_TABLES);
|
||||||
if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
|
if (trans_rollback(thd))
|
||||||
lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
|
|
||||||
goto error;
|
goto error;
|
||||||
|
/* Begin transaction with the same isolation level. */
|
||||||
|
if (lex->tx_chain && trans_begin(thd))
|
||||||
|
goto error;
|
||||||
|
/* Disconnect the current client connection. */
|
||||||
|
if (lex->tx_release)
|
||||||
|
thd->killed= THD::KILL_CONNECTION;
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_RELEASE_SAVEPOINT:
|
case SQLCOM_RELEASE_SAVEPOINT:
|
||||||
{
|
if (trans_release_savepoint(thd, lex->ident))
|
||||||
SAVEPOINT *sv;
|
goto error;
|
||||||
for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
|
my_ok(thd);
|
||||||
{
|
|
||||||
if (my_strnncoll(system_charset_info,
|
|
||||||
(uchar *)lex->ident.str, lex->ident.length,
|
|
||||||
(uchar *)sv->name, sv->length) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (sv)
|
|
||||||
{
|
|
||||||
if (ha_release_savepoint(thd, sv))
|
|
||||||
res= TRUE; // cannot happen
|
|
||||||
else
|
|
||||||
my_ok(thd);
|
|
||||||
thd->transaction.savepoints=sv->prev;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
|
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
|
||||||
{
|
if (trans_rollback_to_savepoint(thd, lex->ident))
|
||||||
SAVEPOINT *sv;
|
goto error;
|
||||||
for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
|
my_ok(thd);
|
||||||
{
|
|
||||||
if (my_strnncoll(system_charset_info,
|
|
||||||
(uchar *)lex->ident.str, lex->ident.length,
|
|
||||||
(uchar *)sv->name, sv->length) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (sv)
|
|
||||||
{
|
|
||||||
if (ha_rollback_to_savepoint(thd, sv))
|
|
||||||
res= TRUE; // cannot happen
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (((thd->options & OPTION_KEEP_LOG) ||
|
|
||||||
thd->transaction.all.modified_non_trans_table) &&
|
|
||||||
!thd->slave_thread)
|
|
||||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
||||||
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
|
||||||
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
|
|
||||||
my_ok(thd);
|
|
||||||
}
|
|
||||||
thd->transaction.savepoints=sv;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case SQLCOM_SAVEPOINT:
|
case SQLCOM_SAVEPOINT:
|
||||||
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
|
if (trans_savepoint(thd, lex->ident))
|
||||||
thd->in_sub_stmt) || !opt_using_transactions)
|
goto error;
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
else
|
|
||||||
{
|
|
||||||
SAVEPOINT **sv, *newsv;
|
|
||||||
for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev)
|
|
||||||
{
|
|
||||||
if (my_strnncoll(system_charset_info,
|
|
||||||
(uchar *)lex->ident.str, lex->ident.length,
|
|
||||||
(uchar *)(*sv)->name, (*sv)->length) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (*sv) /* old savepoint of the same name exists */
|
|
||||||
{
|
|
||||||
newsv=*sv;
|
|
||||||
ha_release_savepoint(thd, *sv); // it cannot fail
|
|
||||||
*sv=(*sv)->prev;
|
|
||||||
}
|
|
||||||
else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
|
|
||||||
savepoint_alloc_size)) == 0)
|
|
||||||
{
|
|
||||||
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
newsv->name=strmake_root(&thd->transaction.mem_root,
|
|
||||||
lex->ident.str, lex->ident.length);
|
|
||||||
newsv->length=lex->ident.length;
|
|
||||||
/*
|
|
||||||
if we'll get an error here, don't add new savepoint to the list.
|
|
||||||
we'll lose a little bit of memory in transaction mem_root, but it'll
|
|
||||||
be free'd when transaction ends anyway
|
|
||||||
*/
|
|
||||||
if (ha_savepoint(thd, newsv))
|
|
||||||
res= TRUE;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newsv->prev=thd->transaction.savepoints;
|
|
||||||
thd->transaction.savepoints=newsv;
|
|
||||||
my_ok(thd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SQLCOM_CREATE_PROCEDURE:
|
case SQLCOM_CREATE_PROCEDURE:
|
||||||
case SQLCOM_CREATE_SPFUNCTION:
|
case SQLCOM_CREATE_SPFUNCTION:
|
||||||
@ -4456,7 +4194,7 @@ create_sp_error:
|
|||||||
lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
|
lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
goto error;
|
goto error;
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
if (sp_automatic_privileges && !opt_noacl &&
|
if (sp_automatic_privileges && !opt_noacl &&
|
||||||
@ -4620,185 +4358,29 @@ create_sp_error:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SQLCOM_XA_START:
|
case SQLCOM_XA_START:
|
||||||
if (thd->transaction.xid_state.xa_state == XA_IDLE &&
|
if (trans_xa_start(thd))
|
||||||
thd->lex->xa_opt == XA_RESUME)
|
goto error;
|
||||||
{
|
|
||||||
if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_NOTA, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
thd->transaction.xid_state.xa_state=XA_ACTIVE;
|
|
||||||
my_ok(thd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (thd->lex->xa_opt != XA_NONE)
|
|
||||||
{ // JOIN is not supported yet. TODO
|
|
||||||
my_error(ER_XAER_INVAL, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (thd->transaction.xid_state.xa_state != XA_NOTR)
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (thd->locked_tables_mode || thd->active_transaction())
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_OUTSIDE, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (xid_cache_search(thd->lex->xid))
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_DUPID, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
|
|
||||||
thd->transaction.xid_state.xa_state=XA_ACTIVE;
|
|
||||||
thd->transaction.xid_state.rm_error= 0;
|
|
||||||
thd->transaction.xid_state.xid.set(thd->lex->xid);
|
|
||||||
xid_cache_insert(&thd->transaction.xid_state);
|
|
||||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
|
||||||
thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN);
|
|
||||||
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_XA_END:
|
case SQLCOM_XA_END:
|
||||||
/* fake it */
|
if (trans_xa_end(thd))
|
||||||
if (thd->lex->xa_opt != XA_NONE)
|
goto error;
|
||||||
{ // SUSPEND and FOR MIGRATE are not supported yet. TODO
|
|
||||||
my_error(ER_XAER_INVAL, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_NOTA, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (xa_trans_rolled_back(&thd->transaction.xid_state))
|
|
||||||
break;
|
|
||||||
thd->transaction.xid_state.xa_state=XA_IDLE;
|
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_XA_PREPARE:
|
case SQLCOM_XA_PREPARE:
|
||||||
if (thd->transaction.xid_state.xa_state != XA_IDLE)
|
if (trans_xa_prepare(thd))
|
||||||
{
|
goto error;
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_NOTA, MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ha_prepare(thd))
|
|
||||||
{
|
|
||||||
my_error(ER_XA_RBROLLBACK, MYF(0));
|
|
||||||
xid_cache_delete(&thd->transaction.xid_state);
|
|
||||||
thd->transaction.xid_state.xa_state=XA_NOTR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
thd->transaction.xid_state.xa_state=XA_PREPARED;
|
|
||||||
my_ok(thd);
|
my_ok(thd);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_XA_COMMIT:
|
case SQLCOM_XA_COMMIT:
|
||||||
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
if (trans_xa_commit(thd))
|
||||||
{
|
goto error;
|
||||||
XID_STATE *xs=xid_cache_search(thd->lex->xid);
|
my_ok(thd);
|
||||||
if (!xs || xs->in_thd)
|
|
||||||
my_error(ER_XAER_NOTA, MYF(0));
|
|
||||||
else if (xa_trans_rolled_back(xs))
|
|
||||||
{
|
|
||||||
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
|
|
||||||
xid_cache_delete(xs);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
|
|
||||||
xid_cache_delete(xs);
|
|
||||||
my_ok(thd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (xa_trans_rolled_back(&thd->transaction.xid_state))
|
|
||||||
{
|
|
||||||
xa_trans_rollback(thd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (thd->transaction.xid_state.xa_state == XA_IDLE &&
|
|
||||||
thd->lex->xa_opt == XA_ONE_PHASE)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
if ((r= ha_commit(thd)))
|
|
||||||
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
|
|
||||||
else
|
|
||||||
my_ok(thd);
|
|
||||||
}
|
|
||||||
else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
|
|
||||||
thd->lex->xa_opt == XA_NONE)
|
|
||||||
{
|
|
||||||
if (wait_if_global_read_lock(thd, 0, 0))
|
|
||||||
{
|
|
||||||
ha_rollback(thd);
|
|
||||||
my_error(ER_XAER_RMERR, MYF(0));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ha_commit_one_phase(thd, 1))
|
|
||||||
my_error(ER_XAER_RMERR, MYF(0));
|
|
||||||
else
|
|
||||||
my_ok(thd);
|
|
||||||
start_waiting_global_read_lock(thd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
|
||||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
|
||||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
||||||
xid_cache_delete(&thd->transaction.xid_state);
|
|
||||||
thd->transaction.xid_state.xa_state=XA_NOTR;
|
|
||||||
break;
|
break;
|
||||||
case SQLCOM_XA_ROLLBACK:
|
case SQLCOM_XA_ROLLBACK:
|
||||||
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
if (trans_xa_rollback(thd))
|
||||||
{
|
goto error;
|
||||||
XID_STATE *xs=xid_cache_search(thd->lex->xid);
|
my_ok(thd);
|
||||||
if (!xs || xs->in_thd)
|
|
||||||
my_error(ER_XAER_NOTA, MYF(0));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool ok= !xa_trans_rolled_back(xs);
|
|
||||||
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
|
|
||||||
xid_cache_delete(xs);
|
|
||||||
if (ok)
|
|
||||||
my_ok(thd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (thd->transaction.xid_state.xa_state != XA_IDLE &&
|
|
||||||
thd->transaction.xid_state.xa_state != XA_PREPARED &&
|
|
||||||
thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY)
|
|
||||||
{
|
|
||||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
|
||||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (xa_trans_rollback(thd))
|
|
||||||
my_error(ER_XAER_RMERR, MYF(0));
|
|
||||||
else
|
|
||||||
my_ok(thd);
|
|
||||||
break;
|
break;
|
||||||
case SQLCOM_XA_RECOVER:
|
case SQLCOM_XA_RECOVER:
|
||||||
res= mysql_xa_recover(thd);
|
res= mysql_xa_recover(thd);
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <m_ctype.h>
|
#include <m_ctype.h>
|
||||||
#include "my_md5.h"
|
#include "my_md5.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||||
#include "ha_partition.h"
|
#include "ha_partition.h"
|
||||||
@ -4327,8 +4328,8 @@ static int fast_end_partition(THD *thd, ulonglong copied,
|
|||||||
if (!is_empty)
|
if (!is_empty)
|
||||||
query_cache_invalidate3(thd, table_list, 0);
|
query_cache_invalidate3(thd, table_list, 0);
|
||||||
|
|
||||||
error= ha_autocommit_or_rollback(thd, 0);
|
error= trans_commit_stmt(thd);
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
error= 1;
|
error= 1;
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sql_trigger.h"
|
#include "sql_trigger.h"
|
||||||
#include "sql_show.h"
|
#include "sql_show.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
#ifdef __WIN__
|
#ifdef __WIN__
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
@ -4668,8 +4669,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||||||
DBUG_PRINT("admin", ("calling prepare_func"));
|
DBUG_PRINT("admin", ("calling prepare_func"));
|
||||||
switch ((*prepare_func)(thd, table, check_opt)) {
|
switch ((*prepare_func)(thd, table, check_opt)) {
|
||||||
case 1: // error, message written to net
|
case 1: // error, message written to net
|
||||||
ha_autocommit_or_rollback(thd, 1);
|
trans_rollback_stmt(thd);
|
||||||
end_trans(thd, ROLLBACK);
|
trans_rollback(thd);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
DBUG_PRINT("admin", ("simple error, admin next table"));
|
DBUG_PRINT("admin", ("simple error, admin next table"));
|
||||||
continue;
|
continue;
|
||||||
@ -4740,8 +4741,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||||||
length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
|
length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
|
||||||
table_name);
|
table_name);
|
||||||
protocol->store(buff, length, system_charset_info);
|
protocol->store(buff, length, system_charset_info);
|
||||||
ha_autocommit_or_rollback(thd, 0);
|
trans_commit_stmt(thd);
|
||||||
end_trans(thd, COMMIT);
|
trans_commit(thd);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
lex->reset_query_tables_list(FALSE);
|
lex->reset_query_tables_list(FALSE);
|
||||||
table->table=0; // For query cache
|
table->table=0; // For query cache
|
||||||
@ -4790,7 +4791,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||||||
HA_ADMIN_NEEDS_ALTER))
|
HA_ADMIN_NEEDS_ALTER))
|
||||||
{
|
{
|
||||||
DBUG_PRINT("admin", ("recreating table"));
|
DBUG_PRINT("admin", ("recreating table"));
|
||||||
ha_autocommit_or_rollback(thd, 1);
|
trans_rollback_stmt(thd);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
|
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
|
||||||
result_code= mysql_recreate_table(thd, table);
|
result_code= mysql_recreate_table(thd, table);
|
||||||
@ -4910,7 +4911,7 @@ send_result_message:
|
|||||||
system_charset_info);
|
system_charset_info);
|
||||||
if (protocol->write())
|
if (protocol->write())
|
||||||
goto err;
|
goto err;
|
||||||
ha_autocommit_or_rollback(thd, 0);
|
trans_commit_stmt(thd);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
|
DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
|
||||||
TABLE_LIST *save_next_local= table->next_local,
|
TABLE_LIST *save_next_local= table->next_local,
|
||||||
@ -4927,7 +4928,7 @@ send_result_message:
|
|||||||
*/
|
*/
|
||||||
if (thd->stmt_da->is_ok())
|
if (thd->stmt_da->is_ok())
|
||||||
thd->stmt_da->reset_diagnostics_area();
|
thd->stmt_da->reset_diagnostics_area();
|
||||||
ha_autocommit_or_rollback(thd, 0);
|
trans_commit_stmt(thd);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
if (!result_code) // recreation went ok
|
if (!result_code) // recreation went ok
|
||||||
{
|
{
|
||||||
@ -5022,8 +5023,8 @@ send_result_message:
|
|||||||
query_cache_invalidate3(thd, table->table, 0);
|
query_cache_invalidate3(thd, table->table, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ha_autocommit_or_rollback(thd, 0);
|
trans_commit_stmt(thd);
|
||||||
end_trans(thd, COMMIT);
|
trans_commit_implicit(thd);
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
table->table=0; // For query cache
|
table->table=0; // For query cache
|
||||||
if (protocol->write())
|
if (protocol->write())
|
||||||
@ -5034,8 +5035,8 @@ send_result_message:
|
|||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
ha_autocommit_or_rollback(thd, 1);
|
trans_rollback_stmt(thd);
|
||||||
end_trans(thd, ROLLBACK);
|
trans_rollback(thd);
|
||||||
close_thread_tables(thd); // Shouldn't be needed
|
close_thread_tables(thd); // Shouldn't be needed
|
||||||
if (table)
|
if (table)
|
||||||
table->table=0;
|
table->table=0;
|
||||||
@ -5550,15 +5551,15 @@ mysql_discard_or_import_tablespace(THD *thd,
|
|||||||
query_cache_invalidate3(thd, table_list, 0);
|
query_cache_invalidate3(thd, table_list, 0);
|
||||||
|
|
||||||
/* The ALTER TABLE is always in its own transaction */
|
/* The ALTER TABLE is always in its own transaction */
|
||||||
error = ha_autocommit_or_rollback(thd, 0);
|
error= trans_commit_stmt(thd);
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
error=1;
|
error=1;
|
||||||
if (error)
|
if (error)
|
||||||
goto err;
|
goto err;
|
||||||
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
|
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
|
||||||
|
|
||||||
err:
|
err:
|
||||||
ha_autocommit_or_rollback(thd, error);
|
trans_rollback_stmt(thd);
|
||||||
thd->tablespace_op=FALSE;
|
thd->tablespace_op=FALSE;
|
||||||
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
@ -7208,8 +7209,8 @@ view_err:
|
|||||||
thd_proc_info(thd, "manage keys");
|
thd_proc_info(thd, "manage keys");
|
||||||
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
|
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
|
||||||
alter_info->keys_onoff);
|
alter_info->keys_onoff);
|
||||||
error= ha_autocommit_or_rollback(thd, 0);
|
error= trans_commit_stmt(thd);
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
error= 1;
|
error= 1;
|
||||||
}
|
}
|
||||||
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
|
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
|
||||||
@ -7297,7 +7298,7 @@ view_err:
|
|||||||
|
|
||||||
/* Need to commit before a table is unlocked (NDB requirement). */
|
/* Need to commit before a table is unlocked (NDB requirement). */
|
||||||
DBUG_PRINT("info", ("Committing before unlocking table"));
|
DBUG_PRINT("info", ("Committing before unlocking table"));
|
||||||
if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
|
if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
|
||||||
goto err_new_table_cleanup;
|
goto err_new_table_cleanup;
|
||||||
committed= 1;
|
committed= 1;
|
||||||
}
|
}
|
||||||
@ -7803,9 +7804,9 @@ copy_data_between_tables(TABLE *from,TABLE *to,
|
|||||||
Ensure that the new table is saved properly to disk so that we
|
Ensure that the new table is saved properly to disk so that we
|
||||||
can do a rename
|
can do a rename
|
||||||
*/
|
*/
|
||||||
if (ha_autocommit_or_rollback(thd, 0))
|
if (trans_commit_stmt(thd))
|
||||||
error=1;
|
error=1;
|
||||||
if (end_active_trans(thd))
|
if (trans_commit_implicit(thd))
|
||||||
error=1;
|
error=1;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
648
sql/transaction.cc
Normal file
648
sql/transaction.cc
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
/* Copyright (C) 2008 Sun/MySQL
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; version 2 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
||||||
|
#pragma implementation // gcc: Class implementation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mysql_priv.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
#include "rpl_handler.h"
|
||||||
|
|
||||||
|
/* Conditions under which the transaction state must not change. */
|
||||||
|
static bool trans_check(THD *thd)
|
||||||
|
{
|
||||||
|
enum xa_states xa_state= thd->transaction.xid_state.xa_state;
|
||||||
|
DBUG_ENTER("trans_check");
|
||||||
|
|
||||||
|
if (unlikely(thd->in_sub_stmt))
|
||||||
|
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
||||||
|
if (xa_state != XA_NOTR)
|
||||||
|
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
|
||||||
|
else
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Mark a XA transaction as rollback-only if the RM unilaterally
|
||||||
|
rolled back the transaction branch.
|
||||||
|
|
||||||
|
@note If a rollback was requested by the RM, this function sets
|
||||||
|
the appropriate rollback error code and transits the state
|
||||||
|
to XA_ROLLBACK_ONLY.
|
||||||
|
|
||||||
|
@return TRUE if transaction was rolled back or if the transaction
|
||||||
|
state is XA_ROLLBACK_ONLY. FALSE otherwise.
|
||||||
|
*/
|
||||||
|
static bool xa_trans_rolled_back(XID_STATE *xid_state)
|
||||||
|
{
|
||||||
|
if (xid_state->rm_error)
|
||||||
|
{
|
||||||
|
switch (xid_state->rm_error) {
|
||||||
|
case ER_LOCK_WAIT_TIMEOUT:
|
||||||
|
my_error(ER_XA_RBTIMEOUT, MYF(0));
|
||||||
|
break;
|
||||||
|
case ER_LOCK_DEADLOCK:
|
||||||
|
my_error(ER_XA_RBDEADLOCK, MYF(0));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
my_error(ER_XA_RBROLLBACK, MYF(0));
|
||||||
|
}
|
||||||
|
xid_state->xa_state= XA_ROLLBACK_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (xid_state->xa_state == XA_ROLLBACK_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Begin a new transaction.
|
||||||
|
|
||||||
|
@note Beginning a transaction implicitly commits any current
|
||||||
|
transaction and releases existing locks.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
@param flags Transaction flags
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_begin(THD *thd, uint flags)
|
||||||
|
{
|
||||||
|
int res= FALSE;
|
||||||
|
DBUG_ENTER("trans_begin");
|
||||||
|
|
||||||
|
if (trans_check(thd))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
thd->locked_tables_list.unlock_locked_tables(thd);
|
||||||
|
|
||||||
|
DBUG_ASSERT(!thd->locked_tables_mode);
|
||||||
|
|
||||||
|
if (trans_commit_implicit(thd))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
thd->options|= OPTION_BEGIN;
|
||||||
|
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
||||||
|
|
||||||
|
if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
|
||||||
|
res= ha_start_consistent_snapshot(thd);
|
||||||
|
|
||||||
|
DBUG_RETURN(test(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Commit the current transaction, making its changes permanent.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_commit(THD *thd)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
DBUG_ENTER("trans_commit");
|
||||||
|
|
||||||
|
if (trans_check(thd))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||||
|
res= ha_commit_trans(thd, TRUE);
|
||||||
|
if (res)
|
||||||
|
/*
|
||||||
|
if res is non-zero, then ha_commit_trans has rolled back the
|
||||||
|
transaction, so the hooks for rollback will be called.
|
||||||
|
*/
|
||||||
|
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
|
||||||
|
else
|
||||||
|
RUN_HOOK(transaction, after_commit, (thd, FALSE));
|
||||||
|
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||||
|
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||||
|
thd->lex->start_transaction_opt= 0;
|
||||||
|
|
||||||
|
DBUG_RETURN(test(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Implicitly commit the current transaction.
|
||||||
|
|
||||||
|
@note A implicit commit does not releases existing table locks.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_commit_implicit(THD *thd)
|
||||||
|
{
|
||||||
|
bool res= FALSE;
|
||||||
|
DBUG_ENTER("trans_commit_implicit");
|
||||||
|
|
||||||
|
if (trans_check(thd))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
|
||||||
|
OPTION_TABLE_LOCK))
|
||||||
|
{
|
||||||
|
/* Safety if one did "drop table" on locked tables */
|
||||||
|
if (!thd->locked_tables_mode)
|
||||||
|
thd->options&= ~OPTION_TABLE_LOCK;
|
||||||
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||||
|
res= test(ha_commit_trans(thd, TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||||
|
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||||
|
|
||||||
|
DBUG_RETURN(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Rollback the current transaction, canceling its changes.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_rollback(THD *thd)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
DBUG_ENTER("trans_rollback");
|
||||||
|
|
||||||
|
if (trans_check(thd))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||||
|
res= ha_rollback_trans(thd, TRUE);
|
||||||
|
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
|
||||||
|
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||||
|
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||||
|
thd->lex->start_transaction_opt= 0;
|
||||||
|
|
||||||
|
DBUG_RETURN(test(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Commit the single statement transaction.
|
||||||
|
|
||||||
|
@note Note that if the autocommit is on, then the following call
|
||||||
|
inside InnoDB will commit or rollback the whole transaction
|
||||||
|
(= the statement). The autocommit mechanism built into InnoDB
|
||||||
|
is based on counting locks, but if the user has used LOCK
|
||||||
|
TABLES then that mechanism does not know to do the commit.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_commit_stmt(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("trans_commit_stmt");
|
||||||
|
int res= FALSE;
|
||||||
|
if (thd->transaction.stmt.ha_list)
|
||||||
|
res= ha_commit_trans(thd, FALSE);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
/*
|
||||||
|
if res is non-zero, then ha_commit_trans has rolled back the
|
||||||
|
transaction, so the hooks for rollback will be called.
|
||||||
|
*/
|
||||||
|
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
|
||||||
|
else
|
||||||
|
RUN_HOOK(transaction, after_commit, (thd, FALSE));
|
||||||
|
DBUG_RETURN(test(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Rollback the single statement transaction.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
bool trans_rollback_stmt(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("trans_rollback_stmt");
|
||||||
|
|
||||||
|
if (thd->transaction.stmt.ha_list)
|
||||||
|
{
|
||||||
|
ha_rollback_trans(thd, FALSE);
|
||||||
|
if (thd->transaction_rollback_request && !thd->in_sub_stmt)
|
||||||
|
ha_rollback_trans(thd, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
|
||||||
|
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a named savepoint in the current transaction. */
|
||||||
|
static SAVEPOINT **
|
||||||
|
find_savepoint(THD *thd, LEX_STRING name)
|
||||||
|
{
|
||||||
|
SAVEPOINT **sv= &thd->transaction.savepoints;
|
||||||
|
|
||||||
|
while (*sv)
|
||||||
|
{
|
||||||
|
if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length,
|
||||||
|
(uchar *) (*sv)->name, (*sv)->length) == 0)
|
||||||
|
break;
|
||||||
|
sv= &(*sv)->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set a named transaction savepoint.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
@param name Savepoint name
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_savepoint(THD *thd, LEX_STRING name)
|
||||||
|
{
|
||||||
|
SAVEPOINT **sv, *newsv;
|
||||||
|
DBUG_ENTER("trans_savepoint");
|
||||||
|
|
||||||
|
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
|
||||||
|
thd->in_sub_stmt) || !opt_using_transactions)
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
|
||||||
|
sv= find_savepoint(thd, name);
|
||||||
|
|
||||||
|
if (*sv) /* old savepoint of the same name exists */
|
||||||
|
{
|
||||||
|
newsv= *sv;
|
||||||
|
ha_release_savepoint(thd, *sv);
|
||||||
|
*sv= (*sv)->prev;
|
||||||
|
}
|
||||||
|
else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
|
||||||
|
savepoint_alloc_size)) == NULL)
|
||||||
|
{
|
||||||
|
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length);
|
||||||
|
newsv->length= name.length;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if we'll get an error here, don't add new savepoint to the list.
|
||||||
|
we'll lose a little bit of memory in transaction mem_root, but it'll
|
||||||
|
be free'd when transaction ends anyway
|
||||||
|
*/
|
||||||
|
if (ha_savepoint(thd, newsv))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
newsv->prev= thd->transaction.savepoints;
|
||||||
|
thd->transaction.savepoints= newsv;
|
||||||
|
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Rollback a transaction to the named savepoint.
|
||||||
|
|
||||||
|
@note Modifications that the current transaction made to
|
||||||
|
rows after the savepoint was set are undone in the
|
||||||
|
rollback.
|
||||||
|
|
||||||
|
@note Savepoints that were set at a later time than the
|
||||||
|
named savepoint are deleted.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
@param name Savepoint name
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
|
||||||
|
{
|
||||||
|
int res= FALSE;
|
||||||
|
SAVEPOINT *sv= *find_savepoint(thd, name);
|
||||||
|
DBUG_ENTER("trans_rollback_to_savepoint");
|
||||||
|
|
||||||
|
if (sv == NULL)
|
||||||
|
{
|
||||||
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ha_rollback_to_savepoint(thd, sv))
|
||||||
|
res= TRUE;
|
||||||
|
else if (((thd->options & OPTION_KEEP_LOG) ||
|
||||||
|
thd->transaction.all.modified_non_trans_table) &&
|
||||||
|
!thd->slave_thread)
|
||||||
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||||
|
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
||||||
|
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
|
||||||
|
|
||||||
|
thd->transaction.savepoints= sv;
|
||||||
|
|
||||||
|
DBUG_RETURN(test(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Remove the named savepoint from the set of savepoints of
|
||||||
|
the current transaction.
|
||||||
|
|
||||||
|
@note No commit or rollback occurs. It is an error if the
|
||||||
|
savepoint does not exist.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
@param name Savepoint name
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_release_savepoint(THD *thd, LEX_STRING name)
|
||||||
|
{
|
||||||
|
int res= FALSE;
|
||||||
|
SAVEPOINT *sv= *find_savepoint(thd, name);
|
||||||
|
DBUG_ENTER("trans_release_savepoint");
|
||||||
|
|
||||||
|
if (sv == NULL)
|
||||||
|
{
|
||||||
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ha_release_savepoint(thd, sv))
|
||||||
|
res= TRUE;
|
||||||
|
|
||||||
|
thd->transaction.savepoints= sv->prev;
|
||||||
|
|
||||||
|
DBUG_RETURN(test(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Starts an XA transaction with the given xid value.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_xa_start(THD *thd)
|
||||||
|
{
|
||||||
|
enum xa_states xa_state= thd->transaction.xid_state.xa_state;
|
||||||
|
DBUG_ENTER("trans_xa_start");
|
||||||
|
|
||||||
|
if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
|
||||||
|
{
|
||||||
|
bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid);
|
||||||
|
if (not_equal)
|
||||||
|
my_error(ER_XAER_NOTA, MYF(0));
|
||||||
|
else
|
||||||
|
thd->transaction.xid_state.xa_state= XA_ACTIVE;
|
||||||
|
DBUG_RETURN(not_equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: JOIN is not supported yet. */
|
||||||
|
if (thd->lex->xa_opt != XA_NONE)
|
||||||
|
my_error(ER_XAER_INVAL, MYF(0));
|
||||||
|
else if (xa_state != XA_NOTR)
|
||||||
|
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
|
||||||
|
else if (thd->locked_tables_mode || thd->active_transaction())
|
||||||
|
my_error(ER_XAER_OUTSIDE, MYF(0));
|
||||||
|
else if (xid_cache_search(thd->lex->xid))
|
||||||
|
my_error(ER_XAER_DUPID, MYF(0));
|
||||||
|
else if (!trans_begin(thd))
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
|
||||||
|
thd->transaction.xid_state.xa_state= XA_ACTIVE;
|
||||||
|
thd->transaction.xid_state.rm_error= 0;
|
||||||
|
thd->transaction.xid_state.xid.set(thd->lex->xid);
|
||||||
|
xid_cache_insert(&thd->transaction.xid_state);
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Put a XA transaction in the IDLE state.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_xa_end(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("trans_xa_end");
|
||||||
|
|
||||||
|
/* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
|
||||||
|
if (thd->lex->xa_opt != XA_NONE)
|
||||||
|
my_error(ER_XAER_INVAL, MYF(0));
|
||||||
|
else if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
|
||||||
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
||||||
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
||||||
|
else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
||||||
|
my_error(ER_XAER_NOTA, MYF(0));
|
||||||
|
else if (!xa_trans_rolled_back(&thd->transaction.xid_state))
|
||||||
|
thd->transaction.xid_state.xa_state= XA_IDLE;
|
||||||
|
|
||||||
|
DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Put a XA transaction in the PREPARED state.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_xa_prepare(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("trans_xa_prepare");
|
||||||
|
|
||||||
|
if (thd->transaction.xid_state.xa_state != XA_IDLE)
|
||||||
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
||||||
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
||||||
|
else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
||||||
|
my_error(ER_XAER_NOTA, MYF(0));
|
||||||
|
else if (ha_prepare(thd))
|
||||||
|
{
|
||||||
|
xid_cache_delete(&thd->transaction.xid_state);
|
||||||
|
thd->transaction.xid_state.xa_state= XA_NOTR;
|
||||||
|
my_error(ER_XA_RBROLLBACK, MYF(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
thd->transaction.xid_state.xa_state= XA_PREPARED;
|
||||||
|
|
||||||
|
DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_PREPARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Commit and terminate the a XA transaction.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_xa_commit(THD *thd)
|
||||||
|
{
|
||||||
|
bool res= TRUE;
|
||||||
|
enum xa_states xa_state= thd->transaction.xid_state.xa_state;
|
||||||
|
DBUG_ENTER("trans_xa_commit");
|
||||||
|
|
||||||
|
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
||||||
|
{
|
||||||
|
XID_STATE *xs= xid_cache_search(thd->lex->xid);
|
||||||
|
res= !xs || xs->in_thd;
|
||||||
|
if (res)
|
||||||
|
my_error(ER_XAER_NOTA, MYF(0));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res= xa_trans_rolled_back(xs);
|
||||||
|
ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
|
||||||
|
xid_cache_delete(xs);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xa_trans_rolled_back(&thd->transaction.xid_state))
|
||||||
|
{
|
||||||
|
if ((res= test(ha_rollback_trans(thd, TRUE))))
|
||||||
|
my_error(ER_XAER_RMERR, MYF(0));
|
||||||
|
}
|
||||||
|
else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
|
||||||
|
{
|
||||||
|
int r= ha_commit_trans(thd, TRUE);
|
||||||
|
if ((res= test(r)))
|
||||||
|
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
|
||||||
|
}
|
||||||
|
else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
|
||||||
|
{
|
||||||
|
if (wait_if_global_read_lock(thd, 0, 0))
|
||||||
|
{
|
||||||
|
ha_rollback_trans(thd, TRUE);
|
||||||
|
my_error(ER_XAER_RMERR, MYF(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res= test(ha_commit_one_phase(thd, 1));
|
||||||
|
if (res)
|
||||||
|
my_error(ER_XAER_RMERR, MYF(0));
|
||||||
|
start_waiting_global_read_lock(thd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||||
|
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||||
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||||
|
xid_cache_delete(&thd->transaction.xid_state);
|
||||||
|
thd->transaction.xid_state.xa_state= XA_NOTR;
|
||||||
|
|
||||||
|
DBUG_RETURN(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Roll back and terminate a XA transaction.
|
||||||
|
|
||||||
|
@param thd Current thread
|
||||||
|
|
||||||
|
@retval FALSE Success
|
||||||
|
@retval TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool trans_xa_rollback(THD *thd)
|
||||||
|
{
|
||||||
|
bool res= TRUE;
|
||||||
|
enum xa_states xa_state= thd->transaction.xid_state.xa_state;
|
||||||
|
DBUG_ENTER("trans_xa_rollback");
|
||||||
|
|
||||||
|
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
||||||
|
{
|
||||||
|
XID_STATE *xs= xid_cache_search(thd->lex->xid);
|
||||||
|
if (!xs || xs->in_thd)
|
||||||
|
my_error(ER_XAER_NOTA, MYF(0));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xa_trans_rolled_back(xs);
|
||||||
|
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
|
||||||
|
xid_cache_delete(xs);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(thd->stmt_da->is_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
|
||||||
|
{
|
||||||
|
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Resource Manager error is meaningless at this point, as we perform
|
||||||
|
explicit rollback request by user. We must reset rm_error before
|
||||||
|
calling ha_rollback(), so thd->transaction.xid structure gets reset
|
||||||
|
by ha_rollback()/THD::transaction::cleanup().
|
||||||
|
*/
|
||||||
|
thd->transaction.xid_state.rm_error= 0;
|
||||||
|
if ((res= test(ha_rollback_trans(thd, TRUE))))
|
||||||
|
my_error(ER_XAER_RMERR, MYF(0));
|
||||||
|
|
||||||
|
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||||
|
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||||
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||||
|
xid_cache_delete(&thd->transaction.xid_state);
|
||||||
|
thd->transaction.xid_state.xa_state= XA_NOTR;
|
||||||
|
|
||||||
|
DBUG_RETURN(res);
|
||||||
|
}
|
46
sql/transaction.h
Normal file
46
sql/transaction.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* Copyright (C) 2008 Sun/MySQL
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; version 2 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
#ifndef TRANSACTION_H
|
||||||
|
#define TRANSACTION_H
|
||||||
|
|
||||||
|
#ifdef USE_PRAGMA_INTERFACE
|
||||||
|
#pragma interface /* gcc class implementation */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
|
||||||
|
class THD;
|
||||||
|
|
||||||
|
bool trans_begin(THD *thd, uint flags= 0);
|
||||||
|
bool trans_commit(THD *thd);
|
||||||
|
bool trans_commit_implicit(THD *thd);
|
||||||
|
bool trans_rollback(THD *thd);
|
||||||
|
|
||||||
|
bool trans_commit_stmt(THD *thd);
|
||||||
|
bool trans_rollback_stmt(THD *thd);
|
||||||
|
|
||||||
|
bool trans_savepoint(THD *thd, LEX_STRING name);
|
||||||
|
bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name);
|
||||||
|
bool trans_release_savepoint(THD *thd, LEX_STRING name);
|
||||||
|
|
||||||
|
bool trans_xa_start(THD *thd);
|
||||||
|
bool trans_xa_end(THD *thd);
|
||||||
|
bool trans_xa_prepare(THD *thd);
|
||||||
|
bool trans_xa_commit(THD *thd);
|
||||||
|
bool trans_xa_rollback(THD *thd);
|
||||||
|
|
||||||
|
#endif /* TRANSACTION_H */
|
Loading…
x
Reference in New Issue
Block a user