summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <guilhem@mysql.com>2003-08-22 15:39:24 +0200
committerunknown <guilhem@mysql.com>2003-08-22 15:39:24 +0200
commitfdfb10f2fb9855d92be2ad9133a950a94606030f (patch)
tree2a1056a34974412902221ff829b9363a53e05fc9
parentfd9b1775642e4da09697fd46a7bbb98d59b9843f (diff)
downloadmariadb-git-fdfb10f2fb9855d92be2ad9133a950a94606030f.tar.gz
2 minor edits, plus
fix for BUG#1113 "INSERT into non-trans table SELECT ; ROLLBACK" does not send warning" and fix for BUG#873 "In transaction, INSERT to non-trans table is written too early to binlog". Now we don't always write the non-trans update immediately to the binlog; if there is something in the binlog cache we write it to the binlog cache (because the non-trans update could depend on a trans table which was modified earlier in the transaction); then in case of ROLLBACK, we write the binlog cache to the binlog, wrapped with BEGIN/ROLLBACK. This guarantees that the slave does the same updates. For ROLLBACK TO SAVEPOINT: when we execute a SAVEPOINT command we write it to the binlog cache. At ROLLBACK TO SAVEPOINT, if some non-trans table was updated, we write ROLLBACK TO SAVEPOINT to the binlog cache; when the transaction terminates (COMMIT/ROLLBACK), the binlog cache will be flushed to the binlog (because of the non-trans update) so we'll have SAVEPOINT and ROLLBACK TO SAVEPOINT in the binlog. Apart from this rare case of updates of mixed table types in transaction, the usual way is still clear the binlog cache at ROLLBACK, or chop it at ROLLBACK TO SAVEPOINT (meaning the SAVEPOINT command is also chopped, which is fine). Note that BUG#873 encompasses subbugs 1) and 2) of BUG#333 "3 binlogging bugs when doing INSERT with mixed InnoDB/MyISAM". client/mysqldump.c: Minor edit: one CHANGE MASTER with 2 arguments instead of 2 CHANGE MASTER with one argument each. mysql-test/r/rpl_loaddata.result: result update mysql-test/t/rpl_loaddata.test: minor edit: simplifying the test. sql/handler.cc: Fix for BUG#873. See comments in code, and the description of the changeset. sql/log.cc: * Previously, if a query updated a non-transactional table we wrote it immediately to the real binlog. This causes a bug when the update is done inside a transaction and uses the content of an updated transactional table (because this makes a wrong order of queries in the binlog). So if the binlog cache is not empty, we write the query to the binlog cache; otherwise we can write it to the binlog. * Previously, when we flushed the binlog cache to the binlog, we wrapped it with BEGIN/COMMIT. Now it's also possible to wrap it with BEGIN/ROLLBACK, to handle transactions which update both transactional and non-transactional tables. sql/log_event.cc: The slave thread can leave a transaction if COMMIT or if ROLLBACK. sql/sql_class.h: prototype sql/sql_insert.cc: Fix for BUG#1113: this was because the INSERT SELECT code did not set OPTION_STATUS_NO_TRANS_UPDATE. sql/sql_parse.cc: Don't send ER_WARNING_NOT_COMPLETE_ROLLBACK if this is the SQL slave thread (see comments).
-rw-r--r--client/mysqldump.c5
-rw-r--r--mysql-test/r/mix_innodb_myisam_binlog.result180
-rw-r--r--mysql-test/r/rpl_loaddata.result6
-rw-r--r--mysql-test/t/mix_innodb_myisam_binlog.test175
-rw-r--r--mysql-test/t/rpl_loaddata.test14
-rw-r--r--sql/handler.cc69
-rw-r--r--sql/log.cc31
-rw-r--r--sql/log_event.cc2
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_parse.cc13
11 files changed, 471 insertions, 30 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c
index 459cb9fda31..f1425faf0ed 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -1459,9 +1459,8 @@ int main(int argc, char **argv)
fprintf(md_result_file,
"\n--\n-- Position to start replication from\n--\n\n");
fprintf(md_result_file,
- "CHANGE MASTER TO MASTER_LOG_FILE='%s' ;\n", row[0]);
- fprintf(md_result_file, "CHANGE MASTER TO MASTER_LOG_POS=%s ;\n",
- row[1]);
+ "CHANGE MASTER TO MASTER_LOG_FILE='%s', \
+MASTER_LOG_POS=%s ;\n",row[0],row[1]);
}
mysql_free_result(master);
}
diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result
new file mode 100644
index 00000000000..8a3415a81d0
--- /dev/null
+++ b/mysql-test/r/mix_innodb_myisam_binlog.result
@@ -0,0 +1,180 @@
+drop table if exists ti, tm;
+create table ti (a int) type=innodb;
+create table tm (a int) type=myisam;
+reset master;
+begin;
+insert into ti values(1);
+insert into tm select * from ti;
+commit;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(1)
+master-bin.001 178 Query 1 79 use test; insert into tm select * from ti
+master-bin.001 244 Query 1 244 use test; COMMIT
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(2);
+insert into tm select * from ti;
+rollback;
+Warning: Some non-transactional changed tables couldn't be rolled back
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(2)
+master-bin.001 178 Query 1 79 use test; insert into tm select * from ti
+master-bin.001 244 Query 1 244 use test; ROLLBACK
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(3);
+savepoint my_savepoint;
+insert into ti values(4);
+insert into tm select * from ti;
+rollback to savepoint my_savepoint;
+Warning: Some non-transactional changed tables couldn't be rolled back
+commit;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(3)
+master-bin.001 178 Query 1 79 use test; savepoint my_savepoint
+master-bin.001 235 Query 1 79 use test; insert into ti values(4)
+master-bin.001 294 Query 1 79 use test; insert into tm select * from ti
+master-bin.001 360 Query 1 79 use test; rollback to savepoint my_savepoint
+master-bin.001 429 Query 1 429 use test; COMMIT
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(5);
+savepoint my_savepoint;
+insert into ti values(6);
+insert into tm select * from ti;
+rollback to savepoint my_savepoint;
+Warning: Some non-transactional changed tables couldn't be rolled back
+insert into ti values(7);
+commit;
+select a from ti order by a;
+a
+5
+7
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(5)
+master-bin.001 178 Query 1 79 use test; savepoint my_savepoint
+master-bin.001 235 Query 1 79 use test; insert into ti values(6)
+master-bin.001 294 Query 1 79 use test; insert into tm select * from ti
+master-bin.001 360 Query 1 79 use test; rollback to savepoint my_savepoint
+master-bin.001 429 Query 1 79 use test; insert into ti values(7)
+master-bin.001 488 Query 1 488 use test; COMMIT
+delete from ti;
+delete from tm;
+reset master;
+select get_lock("a",10);
+get_lock("a",10)
+1
+begin;
+insert into ti values(8);
+insert into tm select * from ti;
+select get_lock("a",10);
+get_lock("a",10)
+1
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(8)
+master-bin.001 178 Query 1 79 use test; insert into tm select * from ti
+master-bin.001 244 Query 1 244 use test; ROLLBACK
+delete from ti;
+delete from tm;
+reset master;
+insert into ti values(9);
+insert into tm select * from ti;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; insert into ti values(9)
+master-bin.001 138 Query 1 138 use test; insert into tm select * from ti
+delete from ti;
+delete from tm;
+reset master;
+insert into ti values(10);
+begin;
+insert into tm select * from ti;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; insert into ti values(10)
+master-bin.001 139 Query 1 139 use test; insert into tm select * from ti
+insert into ti values(11);
+commit;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; insert into ti values(10)
+master-bin.001 139 Query 1 139 use test; insert into tm select * from ti
+master-bin.001 205 Query 1 205 use test; BEGIN
+master-bin.001 245 Query 1 205 use test; insert into ti values(11)
+master-bin.001 305 Query 1 305 use test; COMMIT
+alter table tm type=INNODB;
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(12);
+insert into tm select * from ti;
+commit;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(12)
+master-bin.001 179 Query 1 79 use test; insert into tm select * from ti
+master-bin.001 245 Query 1 245 use test; COMMIT
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(13);
+insert into tm select * from ti;
+rollback;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(14);
+savepoint my_savepoint;
+insert into ti values(15);
+insert into tm select * from ti;
+rollback to savepoint my_savepoint;
+commit;
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(14)
+master-bin.001 179 Query 1 179 use test; COMMIT
+delete from ti;
+delete from tm;
+reset master;
+begin;
+insert into ti values(16);
+savepoint my_savepoint;
+insert into ti values(17);
+insert into tm select * from ti;
+rollback to savepoint my_savepoint;
+insert into ti values(18);
+commit;
+select a from ti order by a;
+a
+16
+18
+show binlog events from 79;
+Log_name Pos Event_type Server_id Orig_log_pos Info
+master-bin.001 79 Query 1 79 use test; BEGIN
+master-bin.001 119 Query 1 79 use test; insert into ti values(16)
+master-bin.001 179 Query 1 79 use test; insert into ti values(18)
+master-bin.001 239 Query 1 239 use test; COMMIT
+drop table ti,tm;
diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result
index e93f8487c32..0302381c119 100644
--- a/mysql-test/r/rpl_loaddata.result
+++ b/mysql-test/r/rpl_loaddata.result
@@ -20,9 +20,9 @@ day id category name
2003-02-22 2461 b a a a @ %  ' " a
2003-03-22 2161 c asdf
2003-04-22 2416 a bbbbb
-show binlog events from 898;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-slave-bin.001 898 Query 1 898 use test; insert into t3 select * from t2
+show master status;
+File Position Binlog_do_db Binlog_ignore_db
+slave-bin.001 964
drop table t1;
drop table t2;
drop table t3;
diff --git a/mysql-test/t/mix_innodb_myisam_binlog.test b/mysql-test/t/mix_innodb_myisam_binlog.test
new file mode 100644
index 00000000000..240aaefb349
--- /dev/null
+++ b/mysql-test/t/mix_innodb_myisam_binlog.test
@@ -0,0 +1,175 @@
+# Check that binlog is ok when a transaction mixes updates to InnoDB and
+# MyISAM. It would be nice to make this a replication test, but in 4.0 the slave
+# is always with --skip-innodb in the testsuite. I (Guilhem) however did some
+# tests manually on a slave; tables are replicated fine and Exec_master_log_pos
+# advances as expected.
+
+-- source include/have_innodb.inc
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+
+connection con1;
+
+drop table if exists ti, tm;
+create table ti (a int) type=innodb;
+create table tm (a int) type=myisam;
+
+reset master;
+
+begin;
+insert into ti values(1);
+insert into tm select * from ti;
+commit;
+
+show binlog events from 79;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(2);
+insert into tm select * from ti;
+# should say some changes to non-transactional tables couldn't be rolled back
+--error 1196
+rollback;
+
+show binlog events from 79;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(3);
+savepoint my_savepoint;
+insert into ti values(4);
+insert into tm select * from ti;
+--error 1196
+rollback to savepoint my_savepoint;
+commit;
+
+show binlog events from 79;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(5);
+savepoint my_savepoint;
+insert into ti values(6);
+insert into tm select * from ti;
+--error 1196
+rollback to savepoint my_savepoint;
+insert into ti values(7);
+commit;
+select a from ti order by a; # check that savepoints work :)
+
+show binlog events from 79;
+
+# and when ROLLBACK is not explicit?
+delete from ti;
+delete from tm;
+reset master;
+
+select get_lock("a",10);
+begin;
+insert into ti values(8);
+insert into tm select * from ti;
+disconnect con1;
+
+connection con2;
+# We want to SHOW BINLOG EVENTS, to know what was logged. But there is no
+# guarantee that logging of the terminated con1 has been done yet (it may not
+# even be started, so con1 may have not even attempted to lock the binlog yet;
+# so SHOW BINLOG EVENTS may come before con1 does the loggin. To be sure that
+# logging has been done, we use a user lock.
+select get_lock("a",10);
+show binlog events from 79;
+
+# and when not in a transaction?
+delete from ti;
+delete from tm;
+reset master;
+
+insert into ti values(9);
+insert into tm select * from ti;
+
+show binlog events from 79;
+
+# Check that when the query updating the MyISAM table is the first in the
+# transaction, we log it immediately.
+delete from ti;
+delete from tm;
+reset master;
+
+insert into ti values(10); # first make ti non-empty
+begin;
+insert into tm select * from ti;
+show binlog events from 79;
+insert into ti values(11);
+commit;
+
+show binlog events from 79;
+
+
+# Check that things work like before this BEGIN/ROLLBACK code was added, when tm
+# is INNODB
+
+alter table tm type=INNODB;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(12);
+insert into tm select * from ti;
+commit;
+
+show binlog events from 79;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(13);
+insert into tm select * from ti;
+rollback;
+
+show binlog events from 79;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(14);
+savepoint my_savepoint;
+insert into ti values(15);
+insert into tm select * from ti;
+rollback to savepoint my_savepoint;
+commit;
+
+show binlog events from 79;
+
+delete from ti;
+delete from tm;
+reset master;
+
+begin;
+insert into ti values(16);
+savepoint my_savepoint;
+insert into ti values(17);
+insert into tm select * from ti;
+rollback to savepoint my_savepoint;
+insert into ti values(18);
+commit;
+select a from ti order by a; # check that savepoints work :)
+
+show binlog events from 79;
+
+drop table ti,tm;
diff --git a/mysql-test/t/rpl_loaddata.test b/mysql-test/t/rpl_loaddata.test
index 65e07aaa823..4c4ff6a093e 100644
--- a/mysql-test/t/rpl_loaddata.test
+++ b/mysql-test/t/rpl_loaddata.test
@@ -32,15 +32,13 @@ sync_with_master;
select * from t1;
select * from t3;
# We want to be sure that LOAD DATA is in the slave's binlog.
-# But we can't simply read this binlog, because the file_id is uncertain (would
-# cause test failures). So instead, we test if the binlog looks long enough to
+# But we can't simply read this binlog, because as the slave has not been
+# restarted for this test, the file_id is uncertain (would cause test
+# failures). So instead, we test if the binlog looks long enough to
# contain LOAD DATA. That is, I (Guilhem) have done SHOW BINLOG EVENTS on my
-# machine, saw that the last event is 'create table t3' and is at position 898
-# when things go fine. If LOAD DATA was not logged, the binlog would be shorter
-# than 898 bytes and there would be an error in SHOW BINLOG EVENTS. Of course,
-# if someone changes the content of '../../std_data/rpl_loaddata2.dat', 898 will
-# have to be changed too.
-show binlog events from 898;
+# machine, saw that the binlog is of size 964 when things go fine.
+# If LOAD DATA was not logged, the binlog would be shorter.
+show master status;
connection master;
diff --git a/sql/handler.cc b/sql/handler.cc
index 96611301bfa..0d29dbeaa31 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -349,7 +349,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans)
if (trans == &thd->transaction.all && mysql_bin_log.is_open() &&
my_b_tell(&thd->transaction.trans_log))
{
- mysql_bin_log.write(thd, &thd->transaction.trans_log);
+ mysql_bin_log.write(thd, &thd->transaction.trans_log, 1);
reinit_io_cache(&thd->transaction.trans_log,
WRITE_CACHE, (my_off_t) 0, 0, 1);
thd->transaction.trans_log.end_of_file= max_binlog_cache_size;
@@ -432,9 +432,21 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
}
#endif
if (trans == &thd->transaction.all)
+ {
+ /*
+ Update the binary log with a BEGIN/ROLLBACK block if we have cached some
+ queries and we updated some non-transactional table. Such cases should
+ be rare (updating a non-transactional table inside a transaction...).
+ */
+ if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
+ mysql_bin_log.is_open() &&
+ my_b_tell(&thd->transaction.trans_log)))
+ mysql_bin_log.write(thd, &thd->transaction.trans_log, 0);
+ /* Flushed or not, empty the binlog cache */
reinit_io_cache(&thd->transaction.trans_log,
- WRITE_CACHE, (my_off_t) 0, 0, 1);
- thd->transaction.trans_log.end_of_file= max_binlog_cache_size;
+ WRITE_CACHE, (my_off_t) 0, 0, 1);
+ thd->transaction.trans_log.end_of_file= max_binlog_cache_size;
+ }
thd->variables.tx_isolation=thd->session_tx_isolation;
if (operation_done)
{
@@ -448,9 +460,27 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
/*
-Rolls the current transaction back to a savepoint.
-Return value: 0 if success, 1 if there was not a savepoint of the given
-name.
+ Rolls the current transaction back to a savepoint.
+ Return value: 0 if success, 1 if there was not a savepoint of the given
+ name.
+ NOTE: how do we handle this (unlikely but legal) case:
+ [transaction] + [update to non-trans table] + [rollback to savepoint] ?
+ The problem occurs when a savepoint is before the update to the
+ non-transactional table. Then when there's a rollback to the savepoint, if we
+ simply truncate the binlog cache, we lose the part of the binlog cache where
+ the update is. If we want to not lose it, we need to write the SAVEPOINT
+ command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
+ is easy: it's just write at the end of the binlog cache, but the former should
+ be *inserted* to the place where the user called SAVEPOINT. The solution is
+ that when the user calls SAVEPOINT, we write it to the binlog cache (so no
+ need to later insert it). As transactions are never intermixed in the binary log
+ (i.e. they are serialized), we won't have conflicts with savepoint names when
+ using mysqlbinlog or in the slave SQL thread.
+ Then when ROLLBACK TO SAVEPOINT is called, if we updated some
+ non-transactional table, we don't truncate the binlog cache but instead write
+ ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
+ will chop the SAVEPOINT command from the binlog cache, which is good as in
+ that case there is no need to have it in the binlog).
*/
int ha_rollback_to_savepoint(THD *thd, char *savepoint_name)
@@ -475,8 +505,24 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name)
error=1;
}
else
- reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE,
- binlog_cache_pos, 0, 0);
+ {
+ /*
+ Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
+ non-transactional table. Otherwise, truncate the binlog cache starting
+ from the SAVEPOINT command.
+ */
+ if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
+ mysql_bin_log.is_open() &&
+ my_b_tell(&thd->transaction.trans_log)))
+ {
+ Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE);
+ if (mysql_bin_log.write(&qinfo))
+ error= 1;
+ }
+ else
+ reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE,
+ binlog_cache_pos, 0, 0);
+ }
operation_done=1;
#endif
if (operation_done)
@@ -505,6 +551,13 @@ int ha_savepoint(THD *thd, char *savepoint_name)
#ifdef HAVE_INNOBASE_DB
innobase_savepoint(thd,savepoint_name, binlog_cache_pos);
#endif
+ /* Write it to the binary log (see comments of ha_rollback_to_savepoint). */
+ if (mysql_bin_log.is_open())
+ {
+ Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE);
+ if (mysql_bin_log.write(&qinfo))
+ error= 1;
+ }
}
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
diff --git a/sql/log.cc b/sql/log.cc
index ee774ea3700..6e9fa38c407 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1062,7 +1062,17 @@ bool MYSQL_LOG::write(Log_event* event_info)
bool should_rotate = 0;
const char *local_db = event_info->get_db();
#ifdef USING_TRANSACTIONS
- IO_CACHE *file = ((event_info->get_cache_stmt()) ?
+ /*
+ Should we write to the binlog cache or to the binlog on disk?
+ Write to the binlog cache if:
+ - it is already not empty (meaning we're in a transaction; note that the
+ present event could be about a non-transactional table, but still we need
+ to write to the binlog cache in that case to handle updates to mixed
+ trans/non-trans table types the best possible in binlogging)
+ - or if the event asks for it (cache_stmt == true).
+ */
+ IO_CACHE *file = ((event_info->get_cache_stmt() ||
+ my_b_tell(&thd->transaction.trans_log)) ?
&thd->transaction.trans_log :
&log_file);
#else
@@ -1258,6 +1268,13 @@ uint MYSQL_LOG::next_file_id()
/*
Write a cached log entry to the binary log
+ SYNOPSIS
+ write()
+ thd
+ cache The cache to copy to the binlog
+ commit_or_rollback If true, will write "COMMIT" in the end, if false will
+ write "ROLLBACK".
+
NOTE
- We only come here if there is something in the cache.
- The thing in the cache is always a complete transaction
@@ -1265,10 +1282,13 @@ uint MYSQL_LOG::next_file_id()
IMPLEMENTATION
- To support transaction over replication, we wrap the transaction
- with BEGIN/COMMIT in the binary log.
+ with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
+ We want to write a BEGIN/ROLLBACK block when a non-transactional table was
+ updated in a transaction which was rolled back. This is to ensure that the
+ same updates are run on the slave.
*/
-bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache)
+bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
{
VOID(pthread_mutex_lock(&LOCK_log));
DBUG_ENTER("MYSQL_LOG::write(cache");
@@ -1322,7 +1342,10 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache)
*/
{
- Query_log_event qinfo(thd, "COMMIT", 6, TRUE);
+ Query_log_event qinfo(thd,
+ commit_or_rollback ? "COMMIT" : "ROLLBACK",
+ commit_or_rollback ? 6 : 8,
+ TRUE);
qinfo.set_log_pos(this);
if (qinfo.write(&log_file) || flush_io_cache(&log_file))
goto err;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 9e7546dd635..54e4d34f77e 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1803,7 +1803,7 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli)
*/
if (!strcmp(thd->query,"BEGIN"))
rli->inside_transaction= opt_using_transactions;
- else if (!strcmp(thd->query,"COMMIT"))
+ else if (!(strcmp(thd->query,"COMMIT") && strcmp(thd->query,"ROLLBACK")))
rli->inside_transaction=0;
/*
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 49a364856eb..b30faa3e9d9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -140,7 +140,7 @@ public:
bool write(THD *thd, const char *query, uint query_length,
time_t query_start=0);
bool write(Log_event* event_info); // binary log write
- bool write(THD *thd, IO_CACHE *cache);
+ bool write(THD *thd, IO_CACHE *cache, bool commit_or_rollback);
/*
v stands for vector
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 167ccf974c7..0a83358e8c6 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1367,6 +1367,8 @@ void select_insert::send_error(uint errcode,const char *err)
table->file->has_transactions());
mysql_bin_log.write(&qinfo);
}
+ if (!table->tmp_table)
+ thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
}
ha_rollback_stmt(thd);
if (info.copied || info.deleted)
@@ -1398,6 +1400,8 @@ bool select_insert::send_eof()
if (info.copied || info.deleted)
{
query_cache_invalidate3(thd, table, 1);
+ if (!(table->file->has_transactions() || table->tmp_table))
+ thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
}
if (error)
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1479a611b5a..548714345f8 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2553,7 +2553,16 @@ mysql_execute_command(void)
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
if (!ha_rollback(thd))
{
- if (thd->options & OPTION_STATUS_NO_TRANS_UPDATE)
+ /*
+ If a non-transactional table was updated, warn; don't warn if this is a
+ slave thread (because when a slave thread executes a ROLLBACK, it has
+ been read from the binary log, so it's 100% sure and normal to produce
+ error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the
+ slave SQL thread, it would not stop the thread but just be printed in
+ the error log; but we don't want users to wonder why they have this
+ message in the error log, so we don't send it.
+ */
+ if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
send_warning(&thd->net,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
else
send_ok(&thd->net);
@@ -2565,7 +2574,7 @@ mysql_execute_command(void)
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
{
- if (thd->options & OPTION_STATUS_NO_TRANS_UPDATE)
+ if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
send_warning(&thd->net,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
else
send_ok(&thd->net);