diff options
author | unknown <guilhem@mysql.com> | 2003-08-22 15:39:24 +0200 |
---|---|---|
committer | unknown <guilhem@mysql.com> | 2003-08-22 15:39:24 +0200 |
commit | fdfb10f2fb9855d92be2ad9133a950a94606030f (patch) | |
tree | 2a1056a34974412902221ff829b9363a53e05fc9 /sql/handler.cc | |
parent | fd9b1775642e4da09697fd46a7bbb98d59b9843f (diff) | |
download | mariadb-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).
Diffstat (limited to 'sql/handler.cc')
-rw-r--r-- | sql/handler.cc | 69 |
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); |