mirror of
https://github.com/MariaDB/server.git
synced 2026-05-06 15:15:34 +02:00
BUG#53560 CREATE TEMP./DROP TEMP. are not binglogged correctly after a failed statement
This patch fixes two problems described as follows:
1 - If there is an on-going transaction and a temporary table is created or
dropped, any failed statement that follows the "create" or "drop commands"
triggers a rollback and by consequence the slave will go out sync because
the binary log will have a wrong sequence of events.
To fix the problem, we changed the expression that evaluates when the
cache should be flushed after either the rollback of a statment or
transaction.
2 - When a "CREATE TEMPORARY TABLE SELECT * FROM" was executed the
OPTION_KEEP_LOG was not set into the thd->options. For that reason, if
the transaction had updated only transactional engines and was rolled
back at the end (.e.g due to a deadlock) the changes were not written
to the binary log, including the creation of the temporary table.
To fix the problem, we have set the OPTION_KEEP_LOG into the thd->options
when a "CREATE TEMPORARY TABLE SELECT * FROM" is executed.
sql/log.cc:
Reorganized the code based on the following functions:
- bool ending_trans(const THD* thd, const bool all);
- bool trans_has_updated_non_trans_table(const THD* thd);
- bool trans_has_no_stmt_committed(const THD* thd, const bool all);
- bool stmt_has_updated_non_trans_table(const THD* thd);
sql/log.h:
Added functions to organize the code in log.cc.
sql/log_event.cc:
Removed the OPTION_KEEP_LOG since it must be used only when
creating and dropping temporary tables.
sql/log_event_old.cc:
Removed the OPTION_KEEP_LOG since it must be used only when
creating and dropping temporary tables.
sql/sql_parse.cc:
When a "CREATE TEMPORARY TABLE SELECT * FROM" was executed the
OPTION_KEEP_LOG was not set into the thd->options.
To fix the problem, we have set the OPTION_KEEP_LOG into the
thd->options when a "CREATE TEMPORARY TABLE SELECT * FROM"
is executed.
This commit is contained in:
parent
59b4dfccb5
commit
3f8bde448c
8 changed files with 190 additions and 41 deletions
106
sql/log.cc
106
sql/log.cc
|
|
@ -1510,27 +1510,23 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
|
|||
}
|
||||
|
||||
/*
|
||||
We commit the transaction if:
|
||||
We flush the cache if:
|
||||
|
||||
- We are not in a transaction and committing a statement, or
|
||||
|
||||
- We are in a transaction and a full transaction is committed
|
||||
- we are committing a transaction;
|
||||
- and no statement was committed before and just non-transactional
|
||||
tables were updated.
|
||||
|
||||
Otherwise, we accumulate the statement
|
||||
*/
|
||||
ulonglong const in_transaction=
|
||||
thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
|
||||
DBUG_PRINT("debug",
|
||||
("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
|
||||
("all: %d, empty: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
|
||||
all,
|
||||
YESNO(trx_data->empty()),
|
||||
YESNO(in_transaction),
|
||||
YESNO(thd->transaction.all.modified_non_trans_table),
|
||||
YESNO(thd->transaction.stmt.modified_non_trans_table)));
|
||||
if (!in_transaction || all ||
|
||||
(!all && !trx_data->at_least_one_stmt_committed &&
|
||||
!stmt_has_updated_trans_table(thd) &&
|
||||
thd->transaction.stmt.modified_non_trans_table))
|
||||
if (ending_trans(thd, all) ||
|
||||
(trans_has_no_stmt_committed(thd, all) &&
|
||||
!stmt_has_updated_trans_table(thd) && stmt_has_updated_non_trans_table(thd)))
|
||||
{
|
||||
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, TRUE, 0);
|
||||
error= binlog_end_trans(thd, trx_data, &qev, all);
|
||||
|
|
@ -1593,7 +1589,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
|
|||
On the other hand, if a statement is transactional, we just safely roll it
|
||||
back.
|
||||
*/
|
||||
if ((thd->transaction.stmt.modified_non_trans_table ||
|
||||
if ((stmt_has_updated_non_trans_table(thd) ||
|
||||
(thd->options & OPTION_KEEP_LOG)) &&
|
||||
mysql_bin_log.check_write_error(thd))
|
||||
trx_data->set_incident();
|
||||
|
|
@ -1603,19 +1599,18 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
|
|||
{
|
||||
/*
|
||||
We flush the cache with a rollback, wrapped in a beging/rollback if:
|
||||
. aborting a transaction that modified a non-transactional table;
|
||||
. aborting a transaction that modified a non-transactional table or
|
||||
the OPTION_KEEP_LOG is activate.
|
||||
. aborting a statement that modified both transactional and
|
||||
non-transactional tables but which is not in the boundaries of any
|
||||
transaction or there was no early change;
|
||||
. the OPTION_KEEP_LOG is activate.
|
||||
*/
|
||||
if ((all && thd->transaction.all.modified_non_trans_table) ||
|
||||
(!all && thd->transaction.stmt.modified_non_trans_table &&
|
||||
!(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) ||
|
||||
(!all && thd->transaction.stmt.modified_non_trans_table &&
|
||||
!trx_data->at_least_one_stmt_committed &&
|
||||
thd->current_stmt_binlog_row_based) ||
|
||||
((thd->options & OPTION_KEEP_LOG)))
|
||||
if ((ending_trans(thd, all) &&
|
||||
(trans_has_updated_non_trans_table(thd) ||
|
||||
(thd->options & OPTION_KEEP_LOG))) ||
|
||||
(trans_has_no_stmt_committed(thd, all) &&
|
||||
stmt_has_updated_non_trans_table(thd) &&
|
||||
thd->current_stmt_binlog_row_based))
|
||||
{
|
||||
Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0);
|
||||
error= binlog_end_trans(thd, trx_data, &qev, all);
|
||||
|
|
@ -1624,8 +1619,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
|
|||
Otherwise, we simply truncate the cache as there is no change on
|
||||
non-transactional tables as follows.
|
||||
*/
|
||||
else if ((all && !thd->transaction.all.modified_non_trans_table) ||
|
||||
(!all && !thd->transaction.stmt.modified_non_trans_table))
|
||||
else if (ending_trans(thd, all) ||
|
||||
(!(thd->options & OPTION_KEEP_LOG) && !stmt_has_updated_non_trans_table(thd)))
|
||||
error= binlog_end_trans(thd, trx_data, 0, all);
|
||||
}
|
||||
if (!all)
|
||||
|
|
@ -1721,7 +1716,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
|
|||
non-transactional table. Otherwise, truncate the binlog cache starting
|
||||
from the SAVEPOINT command.
|
||||
*/
|
||||
if (unlikely(thd->transaction.all.modified_non_trans_table ||
|
||||
if (unlikely(trans_has_updated_non_trans_table(thd) ||
|
||||
(thd->options & OPTION_KEEP_LOG)))
|
||||
{
|
||||
String log_query;
|
||||
|
|
@ -3934,6 +3929,67 @@ bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
|
|||
query_id_param >= thd->binlog_evt_union.first_query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
This function checks if a transaction, either a multi-statement
|
||||
or a single statement transaction is about to commit or not.
|
||||
|
||||
@param thd The client thread that executed the current statement.
|
||||
@param all Committing a transaction (i.e. TRUE) or a statement
|
||||
(i.e. FALSE).
|
||||
@return
|
||||
@c true if committing a transaction, otherwise @c false.
|
||||
*/
|
||||
bool ending_trans(const THD* thd, const bool all)
|
||||
{
|
||||
return (all || (!all && !(thd->options &
|
||||
(OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))));
|
||||
}
|
||||
|
||||
/**
|
||||
This function checks if a non-transactional table was updated by
|
||||
the current transaction.
|
||||
|
||||
@param thd The client thread that executed the current statement.
|
||||
@return
|
||||
@c true if a non-transactional table was updated, @c false
|
||||
otherwise.
|
||||
*/
|
||||
bool trans_has_updated_non_trans_table(const THD* thd)
|
||||
{
|
||||
return (thd->transaction.all.modified_non_trans_table ||
|
||||
thd->transaction.stmt.modified_non_trans_table);
|
||||
}
|
||||
|
||||
/**
|
||||
This function checks if any statement was committed and cached.
|
||||
|
||||
@param thd The client thread that executed the current statement.
|
||||
@param all Committing a transaction (i.e. TRUE) or a statement
|
||||
(i.e. FALSE).
|
||||
@return
|
||||
@c true if at a statement was committed and cached, @c false
|
||||
otherwise.
|
||||
*/
|
||||
bool trans_has_no_stmt_committed(const THD* thd, bool all)
|
||||
{
|
||||
binlog_trx_data *const trx_data=
|
||||
(binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
|
||||
|
||||
return (!all && !trx_data->at_least_one_stmt_committed);
|
||||
}
|
||||
|
||||
/**
|
||||
This function checks if a non-transactional table was updated by the
|
||||
current statement.
|
||||
|
||||
@param thd The client thread that executed the current statement.
|
||||
@return
|
||||
@c true if a non-transactional table was updated, @c false otherwise.
|
||||
*/
|
||||
bool stmt_has_updated_non_trans_table(const THD* thd)
|
||||
{
|
||||
return (thd->transaction.stmt.modified_non_trans_table);
|
||||
}
|
||||
|
||||
/*
|
||||
These functions are placed in this file since they need access to
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue