2985 Konstantin Osipov 2009-12-03
Backport of:
------------------------------------------------------------
revno: 3035.4.1
committer: Davi Arnaut <Davi.Arnaut@stripped>
branch nick: 39897-6.0
timestamp: Thu 2009-01-15 12:17:57 -0200
message:
Bug#39897: lock_multi fails in pushbuild: timeout waiting for processlist
The problem is that relying on the "Table lock" thread state in
its current position to detect that a thread is waiting on a lock
is race prone. The "Table lock" state change happens before the
thread actually tries to grab a lock on a table.
The solution is to move the "Table lock" state so that its set
only when a thread is actually going to wait for a lock. The state
change happens after the thread fails to grab the lock (because it
is owned by other thread) and proceeds to wait on a condition.
This is considered part of work related to WL#4284 "Transactional
DDL locking"
Warning: this patch contains an incompatible change.
When waiting on a lock in thr_lock.c, the server used to display "Locked"
processlist state. After this patch, the state is "Table lock".
The new state was actually intended to be display since year 2002,
when Monty added it. But up until removal of thd->locked boolean
member, this state was ignored by SHOW PROCESSLIST code.
@ mysql-test/r/lock_multi.result
A style fix.
@ mysql-test/r/sp-threads.result
Changed output of SHOW PROCESSLIST (new wait state).
@ mysql-test/t/lock_multi.test
Use a more accurate state description when waiting inside thr_lock.c.
@ mysql-test/t/lock_sync.test
Use a more accurate state description when waiting inside thr_lock.c.
@ mysql-test/t/multi_update.test
Use a more accurate state description when waiting inside thr_lock.c.
@ mysql-test/t/query_cache_28249.test
Use a more accurate state description when waiting inside thr_lock.c.
@ mysql-test/t/sp_notembedded.test
Use a more accurate state description when waiting inside thr_lock.c.
@ mysql-test/t/status.test
Use a more accurate state description when waiting inside thr_lock.c.
@ mysys/thr_lock.c
Update thread state while waiting for a table lock.
@ sql/lock.cc
State change was moved inside thr_lock.c.
modified:
mysql-test/r/lock_multi.result
mysql-test/r/sp-threads.result
mysql-test/t/delayed.test
mysql-test/t/insert_notembedded.test
mysql-test/t/lock_multi.test
mysql-test/t/lock_sync.test
mysql-test/t/multi_update.test
mysql-test/t/query_cache_28249.test
mysql-test/t/sp_notembedded.test
mysql-test/t/status.test
mysys/thr_lock.c
sql/lock.cc
2984 Konstantin Osipov 2009-12-03
Backport of:
-----------------------------------------------------------
2497.479.10 Sergei Golubchik 2008-08-27
proc_info_hook, mysys access to thd->proc_info
This patch is necessary for backport of WL#4284 "Transactional
DDL locking".
@ include/my_global.h
proc_info_hook
@ include/my_sys.h
proc_info_hook
@ mysys/my_static.c
proc_info_hook
@ sql/mysqld.cc
proc_info_hook
@ sql/sql_class.cc
proc_info_hook
modified:
include/my_global.h
include/my_sys.h
mysys/my_static.c
sql/mysqld.cc
sql/sql_class.cc
2983 Konstantin Osipov 2009-12-03
Backport of:
------------------------------------------------------------
revno: 2630.22.3
committer: Davi Arnaut <Davi.Arnaut@stripped>
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.
@ client/Makefile.am
Add new file to the build.
@ libmysqld/CMakeLists.txt
Add new file to the build.
@ libmysqld/Makefile.am
Add new file to the build.
@ sql/CMakeLists.txt
Add new file to the build.
@ sql/Makefile.am
Add new file to the build.
@ sql/handler.cc
Remove multiplexer commit or rollback function. Most callers already
have enough information to decided whether to rollback or commit.
Having plain and well named functions makes it easier to read
and understand code.
@ sql/handler.h
Remove wrapper function as the low level transaction functions
shouldn't be called directly anymore.
@ sql/log_event.cc
Rename transaction management functions to the new names.
@ sql/log_event_old.cc
Rename transaction management functions to the new names.
@ sql/mysql_priv.h
Remove obsolete functions for implicit and explicit commit.
@ sql/rpl_injector.cc
Rename transaction management functions to the new names.
@ sql/rpl_rli.cc
Rename transaction management functions to the new names.
@ sql/set_var.cc
Rename transaction management functions to the new names.
@ sql/slave.cc
Rename transaction management functions to the new names.
@ sql/sql_base.cc
Rename transaction management functions to the new names.
@ sql/sql_class.cc
Rename transaction management functions to the new names.
@ sql/sql_delete.cc
Rename transaction management functions to the new names.
@ sql/sql_do.cc
Rename transaction management functions to the new names.
@ sql/sql_insert.cc
Rename transaction management functions to the new names.
@ sql/sql_parse.cc
Rename transaction management functions to the new names.
@ sql/sql_partition.cc
Rename transaction management functions to the new names.
@ sql/sql_table.cc
Rename transaction management functions to the new names.
@ sql/transaction.cc
Implement wrapper functions to differentiate operations on
the single statement transaction from the ones operating
on the normal transaction.
@ sql/transaction.h
Export new functions for dealing with transaction commands.
added:
sql/transaction.cc
sql/transaction.h
modified:
client/Makefile.am
libmysqld/CMakeLists.txt
libmysqld/Makefile.am
sql/CMakeLists.txt
sql/Makefile.am
sql/handler.cc
sql/handler.h
sql/log_event.cc
sql/log_event_old.cc
sql/mysql_priv.h
sql/rpl_injector.cc
sql/rpl_rli.cc
sql/set_var.cc
sql/slave.cc
sql/sql_base.cc
sql/sql_class.cc
sql/sql_delete.cc
sql/sql_do.cc
sql/sql_insert.cc
sql/sql_parse.cc
sql/sql_partition.cc
sql/sql_table.cc
2982 Konstantin Osipov 2009-12-03
Backport of:
------------------------------------------------------------
revno: 2630.13.17
committer: Davi Arnaut <Davi.Arnaut@stripped>
branch nick: 4284-6.0
timestamp: Sun 2008-07-27 10:14:46 -0300
message:
Post-merge fixes:
Remove dependency on binlog, require not embedded as test uses
the event scheduler and disable abort on error for syntax only
available on servers built with debugging support.
This is a patch in scope of WL#4284 "Transactional DDL locking"
@ mysql-test/t/implicit_commit.test
Remove dependency on binlog, require not embedded as test uses
the event scheduler and disable abort on error for syntax only
available on servers built with debugging support.
modified:
mysql-test/t/implicit_commit.test
=== modified file 'client/Makefile.am'
--- a/client/Makefile.am 2009-11-13 12:14:38 +0000
+++ b/client/Makefile.am 2009-12-03 18:37:38 +0000
@@ -107,7 +107,8 @@ sql_src=log_event.h mysql_priv.h rpl_con
rpl_utility.h rpl_tblmap.h rpl_tblmap.cc \
log_event.cc my_decimal.h my_decimal.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
link_sources:
=== modified file 'include/my_global.h'
--- a/include/my_global.h 2009-12-02 21:24:10 +0000
+++ b/include/my_global.h 2009-12-03 18:59:52 +0000
@@ -1561,7 +1561,7 @@ inline void operator delete[](void*, vo
#if !defined(max)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
-#endif
+#endif
/*
Only Linux is known to need an explicit sync of the directory to make sure a
file creation/deletion/renaming in(from,to) this directory durable.
=== modified file 'include/my_sys.h'
--- a/include/my_sys.h 2009-12-01 19:07:18 +0000
+++ b/include/my_sys.h 2009-12-03 18:59:52 +0000
@@ -235,6 +235,9 @@ extern void (*fatal_error_handler_hook)(
extern uint my_file_limit;
extern ulong my_thread_stack_size;
+extern const char *(*proc_info_hook)(void *, const char *, const char *,
+ const char *, const unsigned int);
+
#ifdef HAVE_LARGE_PAGES
extern my_bool my_use_large_pages;
extern uint my_large_page_size;
=== modified file 'libmysqld/CMakeLists.txt'
--- a/libmysqld/CMakeLists.txt 2009-12-03 11:37:42 +0000
+++ b/libmysqld/CMakeLists.txt 2009-12-03 18:37:38 +0000
@@ -133,7 +133,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libm
../sql/partition_info.cc ../sql/sql_connect.cc
../sql/scheduler.cc ../sql/event_parse_data.cc
../sql/sql_signal.cc ../sql/rpl_handler.cc
- ../sql/mdl.cc
+ ../sql/mdl.cc ../sql/transaction.cc
${GEN_SOURCES}
${LIB_SOURCES})
=== modified file 'libmysqld/Makefile.am'
--- a/libmysqld/Makefile.am 2009-12-03 11:37:42 +0000
+++ b/libmysqld/Makefile.am 2009-12-03 18:37:38 +0000
@@ -75,8 +75,7 @@ sqlsources = derror.cc field.cc field_co
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 \
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
- debug_sync.cc \
- sql_tablespace.cc \
+ debug_sync.cc sql_tablespace.cc transaction.cc \
rpl_injector.cc my_user.c partition_info.cc \
sql_servers.cc event_parse_data.cc sql_signal.cc \
rpl_handler.cc mdl.cc
=== modified file 'mysql-test/r/lock_multi.result'
--- a/mysql-test/r/lock_multi.result 2009-11-20 20:12:57 +0000
+++ b/mysql-test/r/lock_multi.result 2009-12-03 20:08:27 +0000
@@ -203,7 +203,7 @@ drop table if exists t1,t2;
create table t1 (a int);
flush status;
lock tables t1 read;
-insert into t1 values(1);;
+insert into t1 values(1);
unlock tables;
drop table t1;
select @tlwa < @tlwb;
=== modified file 'mysql-test/r/sp-threads.result'
--- a/mysql-test/r/sp-threads.result 2006-10-04 14:35:40 +0000
+++ b/mysql-test/r/sp-threads.result 2009-12-03 20:08:27 +0000
@@ -35,7 +35,7 @@ call bug9486();
show processlist;
Id User Host db Command Time State Info
# root localhost test Sleep # NULL
-# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2
+# root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2
# root localhost test Query # NULL show processlist
# root localhost test Sleep # NULL
unlock tables;
=== modified file 'mysql-test/t/delayed.test'
--- a/mysql-test/t/delayed.test 2009-11-02 11:10:04 +0000
+++ b/mysql-test/t/delayed.test 2009-12-03 20:08:27 +0000
@@ -307,7 +307,7 @@ connection update;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where command = "Delayed insert" and state = "upgrading lock";
+ where command = "Delayed insert" and state = "Table lock";
--source include/wait_condition.inc
connect (select,localhost,root,,);
--echo connection: select
=== modified file 'mysql-test/t/insert_notembedded.test'
--- a/mysql-test/t/insert_notembedded.test 2009-11-20 19:51:12 +0000
+++ b/mysql-test/t/insert_notembedded.test 2009-12-03 20:08:27 +0000
@@ -174,7 +174,7 @@ connection default;
# we must wait till the insert opens and locks the table
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and id = $ID;
+ where state = "Table lock" and id = $ID;
--source include/wait_condition.inc
connect (select,localhost,root,,);
--echo connection: select
=== modified file 'mysql-test/t/lock_multi.test'
--- a/mysql-test/t/lock_multi.test 2009-11-30 15:55:03 +0000
+++ b/mysql-test/t/lock_multi.test 2009-12-03 20:08:27 +0000
@@ -22,7 +22,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update low_priority t1 set n = 4";
+ where state = "Table lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc
send
select n from t1;
@@ -30,7 +30,7 @@ connection locker;
# Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "select n from t1";
+ where state = "Table lock" and info = "select n from t1";
--source include/wait_condition.inc
unlock tables;
connection writer;
@@ -50,7 +50,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update low_priority t1 set n = 4";
+ where state = "Table lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc
select n from t1;
connection locker;
@@ -95,7 +95,7 @@ insert t1 select * from t2;
connection locker;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert t1 select * from t2";
+ where state = "Table lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@@ -119,7 +119,7 @@ connection locker;
# Sleep a bit till the insert of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert t1 select * from t2";
+ where state = "Table lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@@ -163,8 +163,8 @@ SELECT user.Select_priv FROM user, db WH
connection locker;
# Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info =
+ SELECT COUNT(*) = 1 FROM information_schema.processlist
+ WHERE state = "Table lock" AND info =
"SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1";
--source include/wait_condition.inc
# Make test case independent from earlier grants.
@@ -298,7 +298,7 @@ connection reader;
# Wait till connection writer is blocked
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "alter table t1 auto_increment=0";
+ where state = "Table lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
send
alter table t1 auto_increment=0;
@@ -306,7 +306,7 @@ connection locker;
# Wait till connection reader is blocked
let $wait_condition=
select count(*) = 2 from information_schema.processlist
- where state = "Locked" and info = "alter table t1 auto_increment=0";
+ where state = "Table lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
unlock tables;
connection writer;
@@ -461,16 +461,16 @@ update t1 set i= 10;
connection reader;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "update t1 set i= 10";
+ where state = "Table lock" and info = "update t1 set i= 10";
--source include/wait_condition.inc
send
select * from t1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "select * from t1";
+ where state = "Table lock" and info = "select * from t1";
--source include/wait_condition.inc
-let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`;
+let $ID= `select id from information_schema.processlist where state = "Table lock" and info = "update t1 set i= 10"`;
--replace_result $ID ID
eval kill query $ID;
connection reader;
@@ -613,11 +613,11 @@ lock tables t1 read;
let $tlwa= `show status like 'Table_locks_waited'`;
connect (waiter,localhost,root,,);
connection waiter;
---send insert into t1 values(1);
+send insert into t1 values(1);
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and info = "insert into t1 values(1)";
+ where state = "Table lock" and info = "insert into t1 values(1)";
--source include/wait_condition.inc
let $tlwb= `show status like 'Table_locks_waited'`;
unlock tables;
=== modified file 'mysql-test/t/lock_sync.test'
--- a/mysql-test/t/lock_sync.test 2009-10-27 09:43:40 +0000
+++ b/mysql-test/t/lock_sync.test 2009-12-03 20:08:27 +0000
@@ -71,7 +71,7 @@ set debug_sync= 'now WAIT_FOR parked';
connection default;
--echo # Wait until this LOCK TABLES statement starts waiting for table lock.
let $wait_condition= select count(*)= 1 from information_schema.processlist
- where state= 'Locked' and
+ where state= 'Table lock' and
info='lock table t1 write';
--source include/wait_condition.inc
--echo # Allow SELECT ... FOR UPDATE to resume.
=== modified file 'mysql-test/t/multi_update.test'
--- a/mysql-test/t/multi_update.test 2009-02-09 21:00:15 +0000
+++ b/mysql-test/t/multi_update.test 2009-12-03 20:08:27 +0000
@@ -496,7 +496,7 @@ connection updater;
# Wait till "alter table t1 ..." of session changer is in work.
# = There is one session is in state "Locked".
let $wait_condition= select count(*)= 1 from information_schema.processlist
- where state= 'Locked';
+ where state= 'Table lock';
--source include/wait_condition.inc
send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a;
@@ -507,7 +507,7 @@ connection locker;
# are in work.
# = There are two session is in state "Locked".
let $wait_condition= select count(*)= 2 from information_schema.processlist
- where state= 'Locked';
+ where state= 'Table lock';
--source include/wait_condition.inc
unlock tables;
=== modified file 'mysql-test/t/query_cache_28249.test'
--- a/mysql-test/t/query_cache_28249.test 2009-02-23 08:03:31 +0000
+++ b/mysql-test/t/query_cache_28249.test 2009-12-03 20:08:27 +0000
@@ -58,18 +58,18 @@ connection user3;
# Typical information_schema.processlist content after sufficient sleep time
# ID USER COMMAND TIME STATE INFO
# ....
-# 2 root Query 5 Locked SELECT *, (SELECT COUNT(*) FROM t2) FROM t1
+# 2 root Query 5 Table lock SELECT *, (SELECT COUNT(*) FROM t2) FROM t1
# ....
# XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# The values marked with 'X' must be reached.
--echo # Poll till the select of connection user1 is blocked by the write lock on t1.
let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist
-WHERE state = 'Locked'
+WHERE state = 'Table lock'
AND info = '$select_for_qc';
--source include/wait_condition.inc
eval
SELECT user,command,state,info FROM information_schema.processlist
-WHERE state = 'Locked'
+WHERE state = 'Table lock'
AND info = '$select_for_qc';
INSERT INTO t1 VALUES (4);
=== modified file 'mysql-test/t/sp_notembedded.test'
--- a/mysql-test/t/sp_notembedded.test 2009-10-13 18:21:42 +0000
+++ b/mysql-test/t/sp_notembedded.test 2009-12-03 20:08:27 +0000
@@ -322,7 +322,7 @@ set session low_priority_updates=on;
connection rl_wait;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Locked" and
+ where state = "Table lock" and
info = "update t1 set value='updated' where value='old'";
--source include/wait_condition.inc
=== modified file 'mysql-test/t/status.test'
--- a/mysql-test/t/status.test 2009-11-20 20:30:00 +0000
+++ b/mysql-test/t/status.test 2009-12-03 20:08:27 +0000
@@ -58,7 +58,7 @@ let $ID= `select connection_id()`;
connection con2;
--echo # Switched to connection: con2
# wait for the other query to start executing
-let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Locked";
+let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Table lock";
--source include/wait_condition.inc
unlock tables;
=== modified file 'mysys/my_static.c'
--- a/mysys/my_static.c 2009-10-14 08:25:39 +0000
+++ b/mysys/my_static.c 2009-12-03 18:59:52 +0000
@@ -92,6 +92,19 @@ void (*error_handler_hook)(uint error,co
void (*fatal_error_handler_hook)(uint error,const char *str,myf MyFlags)=
my_message_no_curses;
+static const char *proc_info_dummy(void *a __attribute__((unused)),
+ const char *b __attribute__((unused)),
+ const char *c __attribute__((unused)),
+ const char *d __attribute__((unused)),
+ const unsigned int e __attribute__((unused)))
+{
+ return 0;
+}
+
+/* this is to be able to call set_thd_proc_info from the C code */
+const char *(*proc_info_hook)(void *, const char *, const char *, const char *,
+ const unsigned int)= proc_info_dummy;
+
#if defined(ENABLED_DEBUG_SYNC)
/**
Global pointer to be set if callback function is defined
=== modified file 'mysys/thr_lock.c'
--- a/mysys/thr_lock.c 2009-12-02 23:09:22 +0000
+++ b/mysys/thr_lock.c 2009-12-03 20:08:27 +0000
@@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait,
struct timespec wait_timeout;
enum enum_thr_lock_result result= THR_LOCK_ABORTED;
my_bool can_deadlock= test(data->owner->info->n_cursors);
+ const char *old_proc_info;
DBUG_ENTER("wait_for_lock");
/*
@@ -434,6 +435,9 @@ wait_for_lock(struct st_lock_list *wait,
thread_var->current_cond= cond;
data->cond= cond;
+ old_proc_info= proc_info_hook(NULL, "Table lock",
+ __func__, __FILE__, __LINE__);
+
if (can_deadlock)
set_timespec(wait_timeout, table_lock_wait_timeout);
while (!thread_var->abort || in_wait_list)
@@ -504,6 +508,9 @@ wait_for_lock(struct st_lock_list *wait,
thread_var->current_mutex= 0;
thread_var->current_cond= 0;
pthread_mutex_unlock(&thread_var->mutex);
+
+ proc_info_hook(NULL, old_proc_info, __func__, __FILE__, __LINE__);
+
DBUG_RETURN(result);
}
=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt 2009-12-03 11:37:42 +0000
+++ b/sql/CMakeLists.txt 2009-12-03 18:37:38 +0000
@@ -77,6 +77,7 @@ SET (SQL_SOURCE
sql_connect.cc scheduler.cc
sql_profile.cc event_parse_data.cc
sql_signal.cc rpl_handler.cc mdl.cc
+ transaction.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am 2009-12-03 11:37:42 +0000
+++ b/sql/Makefile.am 2009-12-03 18:37:38 +0000
@@ -113,7 +113,7 @@ noinst_HEADERS = item.h item_func.h item
sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h sql_signal.h records.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 \
item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -159,7 +159,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.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
=== modified file 'sql/handler.cc'
--- a/sql/handler.cc 2009-12-03 11:37:42 +0000
+++ b/sql/handler.cc 2009-12-03 18:37:38 +0000
@@ -27,6 +27,7 @@
#include "rpl_handler.h"
#include "rpl_filter.h"
#include <myisampack.h>
+#include "transaction.h"
#include <errno.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
involve the two-phase commit protocol!
- At the end of a statement, server call
- ha_autocommit_or_rollback() is invoked. This call in turn
- invokes handlerton::prepare() for every involved engine.
- Prepare is followed by a call to handlerton::commit_one_phase()
- If a one-phase commit will suffice, handlerton::prepare() is not
- invoked and the server only calls handlerton::commit_one_phase().
- At statement commit, the statement-related read-write engine
- flag is propagated to the corresponding flag in the normal
- transaction. When the commit is complete, the list of registered
- engines is cleared.
+ At the end of a statement, server call trans_commit_stmt is
+ invoked. This call in turn invokes handlerton::prepare()
+ for every involved engine. Prepare is followed by a call
+ to handlerton::commit_one_phase() If a one-phase commit
+ will suffice, handlerton::prepare() is not invoked and
+ the server only calls handlerton::commit_one_phase().
+ At statement commit, the statement-related read-write
+ engine flag is propagated to the corresponding flag in the
+ normal transaction. When the commit is complete, the list
+ of registered engines is cleared.
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
modify the transaction state. Besides, each DDL in
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.
However, as it has been pointed out with CREATE TABLE .. SELECT,
some DDL statements can start a *new* transaction.
@@ -1370,47 +1371,6 @@ int ha_rollback_trans(THD *thd, bool all
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 {
XID *xid;
@@ -3496,7 +3456,7 @@ int ha_enable_transaction(THD *thd, bool
So, let's commit an open transaction (if any) now.
*/
if (!(error= ha_commit_trans(thd, 0)))
- error= end_trans(thd, COMMIT);
+ error= trans_commit_implicit(thd);
}
DBUG_RETURN(error);
}
=== modified file 'sql/handler.h'
--- a/sql/handler.h 2009-12-01 19:07:18 +0000
+++ b/sql/handler.h 2009-12-03 18:37:38 +0000
@@ -1945,10 +1945,6 @@ extern TYPELIB tx_isolation_typelib;
extern TYPELIB myisam_stats_method_typelib;
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 */
handlerton *ha_default_handlerton(THD *thd);
plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name);
@@ -2025,13 +2021,12 @@ int ha_release_temporary_latches(THD *th
int ha_start_consistent_snapshot(THD *thd);
int ha_commit_or_rollback_by_xid(XID *xid, bool commit);
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_prepare(THD *thd);
int ha_recover(HASH *commit_list);
/* 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);
/* savepoints */
=== modified file 'sql/lock.cc'
--- a/sql/lock.cc 2009-12-02 20:47:23 +0000
+++ b/sql/lock.cc 2009-12-03 20:08:27 +0000
@@ -310,7 +310,6 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
break;
}
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
- thd_proc_info(thd, "Locked");
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc 2009-12-03 11:37:42 +0000
+++ b/sql/log_event.cc 2009-12-03 18:37:38 +0000
@@ -31,6 +31,7 @@
#include "rpl_filter.h"
#include "rpl_utility.h"
#include "rpl_record.h"
+#include "transaction.h"
#include <my_dir.h>
#endif /* MYSQL_CLIENT */
@@ -3231,7 +3232,7 @@ Default database: '%s'. Query: '%s'",
them back here.
*/
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
@@ -3240,7 +3241,8 @@ Default database: '%s'. Query: '%s'",
else if (expected_error && !actual_error &&
(concurrency_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.
*/
@@ -5315,7 +5317,7 @@ int Xid_log_event::do_apply_event(Relay_
/* For a slave Xid_log_event is COMMIT */
general_log_print(thd, COM_QUERY,
"COMMIT /* implicit, from Xid_log_event */");
- return end_trans(thd, COMMIT);
+ return trans_commit(thd);
}
Log_event::enum_skip_reason
@@ -7607,7 +7609,7 @@ static int rows_event_stmt_cleanup(Relay
are involved, commit the transaction and flush the pending event to the
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
=== modified file 'sql/log_event_old.cc'
--- a/sql/log_event_old.cc 2009-12-03 11:37:42 +0000
+++ b/sql/log_event_old.cc 2009-12-03 18:37:38 +0000
@@ -6,6 +6,7 @@
#endif
#include "log_event_old.h"
#include "rpl_record_old.h"
+#include "transaction.h"
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -1795,7 +1796,7 @@ int Old_rows_log_event::do_apply_event(R
are involved, commit the transaction and flush the pending event to the
binlog.
*/
- if ((error= ha_autocommit_or_rollback(thd, 0)))
+ if ((error= trans_commit_stmt(thd)))
rli->report(ERROR_LEVEL, error,
"Error in %s event: commit of row events failed, "
"table `%s`.`%s`",
=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h 2009-12-03 15:47:20 +0000
+++ b/sql/mysql_priv.h 2009-12-03 18:37:38 +0000
@@ -883,15 +883,6 @@ bool parse_sql(THD *thd,
Parser_state *parser_state,
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);
/* log.cc */
=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc 2009-12-03 11:37:42 +0000
+++ b/sql/mysqld.cc 2009-12-03 18:59:52 +0000
@@ -3828,6 +3828,10 @@ static int init_server_components()
}
}
+ proc_info_hook= (const char *(*)(void *, const char *, const char *,
+ const char *, const unsigned int))
+ set_thd_proc_info;
+
if (xid_cache_init())
{
sql_print_error("Out of memory");
=== modified file 'sql/rpl_injector.cc'
--- a/sql/rpl_injector.cc 2008-02-19 11:43:01 +0000
+++ b/sql/rpl_injector.cc 2009-12-03 18:37:38 +0000
@@ -15,6 +15,7 @@
#include "mysql_priv.h"
#include "rpl_injector.h"
+#include "transaction.h"
/*
injector::transaction - member definitions
@@ -35,7 +36,7 @@ injector::transaction::transaction(MYSQL
m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0));
m_start_pos.m_file_pos= log_info.pos;
- begin_trans(m_thd);
+ trans_begin(m_thd);
thd->set_current_stmt_binlog_row_based();
}
@@ -81,8 +82,8 @@ int injector::transaction::commit()
is committed by committing the statement transaction
explicitly.
*/
- ha_autocommit_or_rollback(m_thd, 0);
- end_trans(m_thd, COMMIT);
+ trans_commit_stmt(m_thd);
+ trans_commit(m_thd);
DBUG_RETURN(0);
}
=== modified file 'sql/rpl_rli.cc'
--- a/sql/rpl_rli.cc 2009-12-03 11:37:42 +0000
+++ b/sql/rpl_rli.cc 2009-12-03 18:37:38 +0000
@@ -20,6 +20,7 @@
#include <my_dir.h> // For MY_STAT
#include "sql_repl.h" // For check_binlog_magic
#include "rpl_utility.h"
+#include "transaction.h"
static int count_relay_log_space(Relay_log_info* rli);
@@ -1183,8 +1184,8 @@ void Relay_log_info::cleanup_context(THD
*/
if (error)
{
- ha_autocommit_or_rollback(thd, 1); // if a "statement transaction"
- end_trans(thd, ROLLBACK); // if a "real transaction"
+ trans_rollback_stmt(thd); // if a "statement transaction"
+ trans_rollback(thd); // if a "real transaction"
}
m_table_map.clear_tables();
slave_close_thread_tables(thd);
=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc 2009-12-03 11:37:42 +0000
+++ b/sql/set_var.cc 2009-12-03 18:37:38 +0000
@@ -61,6 +61,7 @@
#include <my_dir.h>
#include "events.h"
+#include "transaction.h"
/* WITH_NDBCLUSTER_STORAGE_ENGINE */
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
@@ -3190,7 +3191,7 @@ static bool set_option_autocommit(THD *t
*/
if (var->save_result.ulong_value != 0 &&
(thd->options & OPTION_NOT_AUTOCOMMIT) &&
- ha_commit(thd))
+ trans_commit(thd))
return 1;
if (var->save_result.ulong_value != 0)
=== modified file 'sql/slave.cc'
--- a/sql/slave.cc 2009-12-01 19:07:18 +0000
+++ b/sql/slave.cc 2009-12-03 18:37:38 +0000
@@ -34,6 +34,7 @@
#include "sql_repl.h"
#include "rpl_filter.h"
#include "repl_failsafe.h"
+#include "transaction.h"
#include <thr_alarm.h>
#include <my_dir.h>
#include <sql_common.h>
@@ -2430,7 +2431,7 @@ static int exec_relay_log_event(THD* thd
else
{
exec_res= 0;
- end_trans(thd, ROLLBACK);
+ trans_rollback(thd);
/* chance for concurrent connection to get more locks */
safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
(CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli);
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc 2009-12-03 11:37:42 +0000
+++ b/sql/sql_base.cc 2009-12-03 18:37:38 +0000
@@ -22,6 +22,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_trigger.h"
+#include "transaction.h"
#include "sql_prepare.h"
#include <m_ctype.h>
#include <my_dir.h>
@@ -1402,7 +1403,7 @@ void close_thread_tables(THD *thd,
if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
{
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;
/*
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2009-12-03 11:37:42 +0000
+++ b/sql/sql_class.cc 2009-12-03 18:59:52 +0000
@@ -42,6 +42,7 @@
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "transaction.h"
#include "debug_sync.h"
/*
@@ -246,13 +247,16 @@ int thd_tablespace_op(const THD *thd)
extern "C"
-const char *set_thd_proc_info(THD *thd, const char *info,
- const char *calling_function,
- const char *calling_file,
+const char *set_thd_proc_info(THD *thd, const char *info,
+ const char *calling_function,
+ const char *calling_file,
const unsigned int calling_line)
{
+ if (!thd)
+ thd= current_thd;
+
const char *old_info= thd->proc_info;
- DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line,
+ DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line,
(info != NULL) ? info : "(null)"));
#if defined(ENABLED_PROFILING)
thd->profiling.status_change(info, calling_function, calling_file, calling_line);
@@ -987,7 +991,8 @@ void THD::cleanup(void)
}
#endif
{
- ha_rollback(this);
+ transaction.xid_state.xa_state= XA_NOTR;
+ trans_rollback(this);
xid_cache_delete(&transaction.xid_state);
}
locked_tables_list.unlock_locked_tables(this);
=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc 2009-12-03 15:47:20 +0000
+++ b/sql/sql_delete.cc 2009-12-03 18:37:38 +0000
@@ -23,6 +23,7 @@
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "transaction.h"
/**
Implement DELETE SQL word.
@@ -1071,8 +1072,8 @@ static bool mysql_truncate_by_delete(THD
if (error)
{
DBUG_ASSERT(thd->stmt_da->is_error());
- ha_autocommit_or_rollback(thd, TRUE);
- end_active_trans(thd);
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
}
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(error);
=== modified file 'sql/sql_do.cc'
--- a/sql/sql_do.cc 2008-02-19 12:45:21 +0000
+++ b/sql/sql_do.cc 2009-12-03 18:37:38 +0000
@@ -17,6 +17,7 @@
/* Execute DO statement */
#include "mysql_priv.h"
+#include "transaction.h"
bool mysql_do(THD *thd, List<Item> &values)
{
@@ -36,7 +37,7 @@ bool mysql_do(THD *thd, List<Item> &valu
will clear the error and the rollback in the end of
dispatch_command() won't work.
*/
- ha_autocommit_or_rollback(thd, thd->is_error());
+ trans_rollback_stmt(thd);
thd->clear_error(); // DO always is OK
}
my_ok(thd);
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc 2009-12-02 15:22:15 +0000
+++ b/sql/sql_insert.cc 2009-12-03 18:37:38 +0000
@@ -61,6 +61,7 @@
#include "sql_show.h"
#include "slave.h"
#include "rpl_mi.h"
+#include "transaction.h"
#ifndef EMBEDDED_LIBRARY
static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
@@ -2539,7 +2540,7 @@ pthread_handler_t handle_delayed_insert(
*/
di->table->file->ha_release_auto_increment();
mysql_unlock_tables(thd, lock);
- ha_autocommit_or_rollback(thd, 0);
+ trans_commit_stmt(thd);
di->group_count=0;
pthread_mutex_lock(&di->mutex);
}
@@ -2559,7 +2560,7 @@ pthread_handler_t handle_delayed_insert(
first call to ha_*_row() instead. Remove code that are used to
cover for the case outlined above.
*/
- ha_autocommit_or_rollback(thd, 1);
+ trans_rollback_stmt(thd);
DBUG_LEAVE;
}
@@ -3863,8 +3864,8 @@ bool select_create::send_eof()
*/
if (!table->s->tmp_table)
{
- ha_autocommit_or_rollback(thd, 0);
- end_active_trans(thd);
+ trans_commit_stmt(thd);
+ trans_commit_implicit(thd);
}
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2009-12-03 15:47:20 +0000
+++ b/sql/sql_parse.cc 2009-12-03 18:37:38 +0000
@@ -28,6 +28,7 @@
#include "sp_cache.h"
#include "events.h"
#include "sql_trigger.h"
+#include "transaction.h"
#include "sql_prepare.h"
#include "probes_mysql.h"
@@ -88,113 +89,6 @@ const char *xa_state_names[]={
"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
/**
@@ -256,9 +150,9 @@ static bool opt_implicit_commit(THD *thd
if (!skip)
{
/* 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. */
- res= end_active_trans(thd);
+ res= trans_commit_implicit(thd);
}
DBUG_RETURN(res);
@@ -691,80 +585,6 @@ void cleanup_items(Item *item)
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
/**
@@ -1346,7 +1166,7 @@ bool dispatch_command(enum enum_server_c
bool not_used;
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
ulong options= (ulong) (uchar) packet[0];
- if (end_active_trans(thd))
+ if (trans_commit_implicit(thd))
break;
if (check_global_access(thd,RELOAD_ACL))
break;
@@ -1374,7 +1194,7 @@ bool dispatch_command(enum enum_server_c
#endif
if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used))
break;
- if (end_active_trans(thd))
+ if (trans_commit_implicit(thd))
break;
my_ok(thd);
break;
@@ -1532,7 +1352,7 @@ bool dispatch_command(enum enum_server_c
/* If commit fails, we should be able to reset the OK status. */
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->transaction.stmt.reset();
@@ -3485,7 +3305,7 @@ end_with_restore_list:
thd->locked_tables_list.unlock_locked_tables(thd);
if (thd->options & OPTION_TABLE_LOCK)
{
- end_active_trans(thd);
+ trans_commit_implicit(thd);
thd->options&= ~(OPTION_TABLE_LOCK);
}
if (thd->global_read_lock)
@@ -3495,7 +3315,7 @@ end_with_restore_list:
case SQLCOM_LOCK_TABLES:
thd->locked_tables_list.unlock_locked_tables(thd);
/* we must end the trasaction first, regardless of anything */
- if (end_active_trans(thd))
+ if (trans_commit_implicit(thd))
goto error;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
FALSE, UINT_MAX, FALSE))
@@ -3524,8 +3344,8 @@ end_with_restore_list:
can free its locks if LOCK TABLES locked some tables before finding
that it can't lock a table in its list
*/
- ha_autocommit_or_rollback(thd, 1);
- end_active_trans(thd);
+ trans_rollback_stmt(thd);
+ trans_commit_implicit(thd);
thd->options&= ~(OPTION_TABLE_LOCK);
}
else
@@ -4007,132 +3827,50 @@ end_with_restore_list:
break;
case SQLCOM_BEGIN:
- 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 (begin_trans(thd))
+ if (trans_begin(thd, lex->start_transaction_opt))
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);
break;
case SQLCOM_COMMIT:
DBUG_ASSERT(thd->lock == NULL ||
thd->locked_tables_mode == LTM_LOCK_TABLES);
- if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
- lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
+ if (trans_commit(thd))
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);
break;
case SQLCOM_ROLLBACK:
DBUG_ASSERT(thd->lock == NULL ||
thd->locked_tables_mode == LTM_LOCK_TABLES);
- if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
- lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
+ if (trans_rollback(thd))
+ 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);
break;
case SQLCOM_RELEASE_SAVEPOINT:
- {
- SAVEPOINT *sv;
- 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)
- {
- 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);
+ if (trans_release_savepoint(thd, lex->ident))
+ goto error;
+ my_ok(thd);
break;
- }
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- {
- SAVEPOINT *sv;
- 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)
- {
- 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);
+ if (trans_rollback_to_savepoint(thd, lex->ident))
+ goto error;
+ my_ok(thd);
break;
- }
case SQLCOM_SAVEPOINT:
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
- thd->in_sub_stmt) || !opt_using_transactions)
- 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);
- }
- }
+ if (trans_savepoint(thd, lex->ident))
+ goto error;
+ my_ok(thd);
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
@@ -4456,7 +4194,7 @@ create_sp_error:
lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
goto error;
- if (end_active_trans(thd))
+ if (trans_commit_implicit(thd))
goto error;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (sp_automatic_privileges && !opt_noacl &&
@@ -4620,185 +4358,29 @@ create_sp_error:
break;
}
case SQLCOM_XA_START:
- if (thd->transaction.xid_state.xa_state == XA_IDLE &&
- thd->lex->xa_opt == XA_RESUME)
- {
- 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;
+ if (trans_xa_start(thd))
+ goto error;
my_ok(thd);
break;
case SQLCOM_XA_END:
- /* fake it */
- if (thd->lex->xa_opt != XA_NONE)
- { // 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;
+ if (trans_xa_end(thd))
+ goto error;
my_ok(thd);
break;
case SQLCOM_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]);
- 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;
+ if (trans_xa_prepare(thd))
+ goto error;
my_ok(thd);
break;
case SQLCOM_XA_COMMIT:
- 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 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;
+ if (trans_xa_commit(thd))
+ goto error;
+ my_ok(thd);
break;
case SQLCOM_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
- {
- 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);
+ if (trans_xa_rollback(thd))
+ goto error;
+ my_ok(thd);
break;
case SQLCOM_XA_RECOVER:
res= mysql_xa_recover(thd);
=== modified file 'sql/sql_partition.cc'
--- a/sql/sql_partition.cc 2009-12-03 11:37:42 +0000
+++ b/sql/sql_partition.cc 2009-12-03 18:37:38 +0000
@@ -50,6 +50,7 @@
#include <errno.h>
#include <m_ctype.h>
#include "my_md5.h"
+#include "transaction.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -4327,8 +4328,8 @@ static int fast_end_partition(THD *thd,
if (!is_empty)
query_cache_invalidate3(thd, table_list, 0);
- error= ha_autocommit_or_rollback(thd, 0);
- if (end_active_trans(thd))
+ error= trans_commit_stmt(thd);
+ if (trans_commit_implicit(thd))
error= 1;
if (error)
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2009-12-03 15:47:20 +0000
+++ b/sql/sql_table.cc 2009-12-03 18:37:38 +0000
@@ -22,6 +22,7 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_show.h"
+#include "transaction.h"
#ifdef __WIN__
#include <io.h>
@@ -4668,8 +4669,8 @@ static bool mysql_admin_table(THD* thd,
DBUG_PRINT("admin", ("calling prepare_func"));
switch ((*prepare_func)(thd, table, check_opt)) {
case 1: // error, message written to net
- ha_autocommit_or_rollback(thd, 1);
- end_trans(thd, ROLLBACK);
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
close_thread_tables(thd);
DBUG_PRINT("admin", ("simple error, admin next table"));
continue;
@@ -4740,8 +4741,8 @@ static bool mysql_admin_table(THD* thd,
length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
table_name);
protocol->store(buff, length, system_charset_info);
- ha_autocommit_or_rollback(thd, 0);
- end_trans(thd, COMMIT);
+ trans_commit_stmt(thd);
+ trans_commit(thd);
close_thread_tables(thd);
lex->reset_query_tables_list(FALSE);
table->table=0; // For query cache
@@ -4790,7 +4791,7 @@ static bool mysql_admin_table(THD* thd,
HA_ADMIN_NEEDS_ALTER))
{
DBUG_PRINT("admin", ("recreating table"));
- ha_autocommit_or_rollback(thd, 1);
+ trans_rollback_stmt(thd);
close_thread_tables(thd);
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
result_code= mysql_recreate_table(thd, table);
@@ -4910,7 +4911,7 @@ send_result_message:
system_charset_info);
if (protocol->write())
goto err;
- ha_autocommit_or_rollback(thd, 0);
+ trans_commit_stmt(thd);
close_thread_tables(thd);
DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
TABLE_LIST *save_next_local= table->next_local,
@@ -4927,7 +4928,7 @@ send_result_message:
*/
if (thd->stmt_da->is_ok())
thd->stmt_da->reset_diagnostics_area();
- ha_autocommit_or_rollback(thd, 0);
+ trans_commit_stmt(thd);
close_thread_tables(thd);
if (!result_code) // recreation went ok
{
@@ -5022,8 +5023,8 @@ send_result_message:
query_cache_invalidate3(thd, table->table, 0);
}
}
- ha_autocommit_or_rollback(thd, 0);
- end_trans(thd, COMMIT);
+ trans_commit_stmt(thd);
+ trans_commit_implicit(thd);
close_thread_tables(thd);
table->table=0; // For query cache
if (protocol->write())
@@ -5034,8 +5035,8 @@ send_result_message:
DBUG_RETURN(FALSE);
err:
- ha_autocommit_or_rollback(thd, 1);
- end_trans(thd, ROLLBACK);
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
close_thread_tables(thd); // Shouldn't be needed
if (table)
table->table=0;
@@ -5550,15 +5551,15 @@ mysql_discard_or_import_tablespace(THD *
query_cache_invalidate3(thd, table_list, 0);
/* The ALTER TABLE is always in its own transaction */
- error = ha_autocommit_or_rollback(thd, 0);
- if (end_active_trans(thd))
+ error= trans_commit_stmt(thd);
+ if (trans_commit_implicit(thd))
error=1;
if (error)
goto err;
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
err:
- ha_autocommit_or_rollback(thd, error);
+ trans_rollback_stmt(thd);
thd->tablespace_op=FALSE;
if (error == 0)
@@ -7208,8 +7209,8 @@ view_err:
thd_proc_info(thd, "manage keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
- error= ha_autocommit_or_rollback(thd, 0);
- if (end_active_trans(thd))
+ error= trans_commit_stmt(thd);
+ if (trans_commit_implicit(thd))
error= 1;
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -7297,7 +7298,7 @@ view_err:
/* Need to commit before a table is unlocked (NDB requirement). */
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;
committed= 1;
}
@@ -7803,9 +7804,9 @@ copy_data_between_tables(TABLE *from,TAB
Ensure that the new table is saved properly to disk so that we
can do a rename
*/
- if (ha_autocommit_or_rollback(thd, 0))
+ if (trans_commit_stmt(thd))
error=1;
- if (end_active_trans(thd))
+ if (trans_commit_implicit(thd))
error=1;
err:
=== added file 'sql/transaction.cc'
--- a/sql/transaction.cc 1970-01-01 00:00:00 +0000
+++ b/sql/transaction.cc 2009-12-03 18:37:38 +0000
@@ -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);
+}
=== added file 'sql/transaction.h'
--- a/sql/transaction.h 1970-01-01 00:00:00 +0000
+++ b/sql/transaction.h 2009-12-03 18:37:38 +0000
@@ -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 */
Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20091203200827-nwrw6lpatzf115q0.bundle
| Thread |
|---|
| • bzr push into mysql-5.6-next-mr branch (kostja:2982 to 2985) Bug#39897 | Konstantin Osipov | 3 Dec |