summaryrefslogtreecommitdiff
path: root/sql/handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/handler.cc')
-rw-r--r--sql/handler.cc69
1 files changed, 61 insertions, 8 deletions
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);