mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
BUG#51894 Replication failure with SBR on DROP TEMPORARY TABLE inside a
transaction BUG#52616 Temp table prevents switch binlog format from STATEMENT to ROW Before the WL#2687 and BUG#46364, every non-transactional change that happened after a transactional change was written to trx-cache and flushed upon committing the transaction. WL#2687 and BUG#46364 changed this behavior and non-transactional changes are now written to the binary log upon committing the statement. A binary log event is identified as transactional or non-transactional through a flag in the Log_event which is set taking into account the underlie storage engine on what it is stems from. In the current bug, this flag was not being set properly when the DROP TEMPORARY TABLE was executed. However, while fixing this bug we figured out that changes to temporary tables should be always written to the trx-cache if there is an on-going transaction. Otherwise, binlog events in the reversed order would be produced. Regarding concurrency, keeping changes to temporary tables in the trx-cache is also safe as temporary tables are only visible to the owner connection. In this patch, we classify the following statements as unsafe: 1 - INSERT INTO t_myisam SELECT * FROM t_myisam_temp 2 - INSERT INTO t_myisam_temp SELECT * FROM t_myisam 3 - CREATE TEMPORARY TABLE t_myisam_temp SELECT * FROM t_myisam On the other hand, the following statements are classified as safe: 1 - INSERT INTO t_innodb SELECT * FROM t_myisam_temp 2 - INSERT INTO t_myisam_temp SELECT * FROM t_innodb The patch also guarantees that transactions that have a DROP TEMPORARY are always written to the binary log regardless of the mode and the outcome: commit or rollback. In particular, the DROP TEMPORARY is extended with the IF EXISTS clause when the current statement logging format is set to row. Finally, the patch allows to switch from STATEMENT to MIXED/ROW when there are temporary tables but the contrary is not possible.
This commit is contained in:
@ -1998,8 +1998,13 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
case 0:
|
||||
// removed temporary table
|
||||
tmp_table_deleted= 1;
|
||||
if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
|
||||
thd->is_current_stmt_binlog_format_row())
|
||||
/*
|
||||
One needs to always log any temporary table drop if the current
|
||||
statement logging format is set to row. This happens because one
|
||||
might have created a temporary table while the statement logging
|
||||
format was statement and then switched to mixed or row format.
|
||||
*/
|
||||
if (thd->is_current_stmt_binlog_format_row())
|
||||
{
|
||||
if (built_tmp_query.is_empty())
|
||||
{
|
||||
@ -2191,7 +2196,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
deleted one or more non-temporary tables (and no temporary
|
||||
tables). In this case, we can write the original query into
|
||||
the binary log.
|
||||
*/
|
||||
*/
|
||||
error |= write_bin_log(thd, !error, thd->query(), thd->query_length());
|
||||
}
|
||||
else if (thd->is_current_stmt_binlog_format_row() &&
|
||||
@ -2218,11 +2223,12 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
}
|
||||
|
||||
/*
|
||||
One needs to always log any temporary table drop, if:
|
||||
1. thread logging format is mixed mode; AND
|
||||
2. current statement logging format is set to row.
|
||||
One needs to always log any temporary table drop if the current
|
||||
statement logging format is set to row. This happens because one
|
||||
might have created a temporary table while the statement logging
|
||||
format was statement and then switched to mixed or row format.
|
||||
*/
|
||||
if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED)
|
||||
if (thd->is_current_stmt_binlog_format_row())
|
||||
{
|
||||
/*
|
||||
In this case we have deleted some temporary tables but we are using
|
||||
@ -2233,8 +2239,14 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
*/
|
||||
built_tmp_query.chop(); // Chop of the last comma
|
||||
built_tmp_query.append(" /* generated by server */");
|
||||
error|= write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length(),
|
||||
thd->in_multi_stmt_transaction());
|
||||
/*
|
||||
We cannot call the write_bin_log as we do not care about any errors
|
||||
in the master as the statement is always DROP TEMPORARY TABLE IF EXISTS
|
||||
and as such there will be no errors in the slave.
|
||||
*/
|
||||
error|= thd->binlog_query(THD::STMT_QUERY_TYPE, built_tmp_query.ptr(),
|
||||
built_tmp_query.length(), FALSE, FALSE, FALSE,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3749,43 +3761,6 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field)
|
||||
(void) prepare_blob_field(thd, sql_field);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Write CREATE TABLE binlog
|
||||
|
||||
SYNOPSIS
|
||||
write_create_table_bin_log()
|
||||
thd Thread object
|
||||
create_info Create information
|
||||
internal_tmp_table Set to 1 if this is an internal temporary table
|
||||
|
||||
DESCRIPTION
|
||||
This function only is called in mysql_create_table_no_lock and
|
||||
mysql_create_table
|
||||
|
||||
RETURN VALUES
|
||||
NONE
|
||||
*/
|
||||
static inline int write_create_table_bin_log(THD *thd,
|
||||
const HA_CREATE_INFO *create_info,
|
||||
bool internal_tmp_table)
|
||||
{
|
||||
/*
|
||||
Don't write statement if:
|
||||
- It is an internal temporary table,
|
||||
- Row-based logging is used and it we are creating a temporary table, or
|
||||
- The binary log is not open.
|
||||
Otherwise, the statement shall be binlogged.
|
||||
*/
|
||||
if (!internal_tmp_table &&
|
||||
(!thd->is_current_stmt_binlog_format_row() ||
|
||||
(thd->is_current_stmt_binlog_format_row() &&
|
||||
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
|
||||
return write_bin_log(thd, TRUE, thd->query(), thd->query_length());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create a table
|
||||
|
||||
|
Reference in New Issue
Block a user