Commit 48d65a64 authored by Luis Soares's avatar Luis Soares

BUG#51251: Wrong binlogging in case of TRUNCATE <temporary InnoDB table>

For temporary tables that are created with an engine that does
not provide the HTON_CAN_RECREATE, the truncate operation is
performed resorting to the optimized handler::ha_delete_all_rows
method. However, this means that the truncate will share
execution path, from mysql_delete, with truncate on regular
tables and other delete operations. As a consequence the truncate
operation, for the temporary table is logged, even if in row mode
because there is no distinction between this and the other delete
operations at binlogging time.

We fix this by checking if: (i) the binlog format, when the
truncate operation was issued, is ROW; (ii) if the operation is a
truncate; and (iii) if the table is a temporary table; before
writing to the binary log. If all three conditions are met, we
skip writing to the binlog. A side effect of this fix is that we
limit the scope of setting and resetting the
current_stmt_binlog_row_based. Now we just set and reset it
inside mysql_delete in the boundaries of the
handler::ha_write_row loop. This way we have access to
thd->current_stmt_binlog_row_based real value inside
mysql_delete.

mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result:
  Updated result for spurious truncate table.
mysql-test/suite/binlog/t/binlog_row_innodb_truncate.test:
  Test case.
sql/sql_delete.cc:
  Added check in mysql_delete before writing the TRUNCATE statement
  to the binary log. Additionally, removed the set/reset of
  current_stmt_binlog_row_based so that it happens just in the 
  boundaries of the handler::ha_write_row loop inside mysql_delete.
parent 32058ba9
CREATE TABLE t1 ( c1 int , primary key (c1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (2), (3);
CREATE TEMPORARY TABLE IF NOT EXISTS t2 LIKE t1;
TRUNCATE TABLE t2;
DROP TABLE t1;
###############################################
### assertion: No event for 'TRUNCATE TABLE t2'
###############################################
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 ( c1 int , primary key (c1)) ENGINE=InnoDB
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (test.t1)
master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Query # # use `test`; DROP TABLE t1
###############################################
RESET MASTER;
CREATE TEMPORARY TABLE t1 (c1 int) Engine=InnoDB;
INSERT INTO t1 VALUES (1), (2), (3);
TRUNCATE t1;
DROP TEMPORARY TABLE t1;
###############################################
### assertion: No event for 'TRUNCATE TABLE t1'
###############################################
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
###############################################
...@@ -413,7 +413,6 @@ master-bin.000001 # Query # # BEGIN ...@@ -413,7 +413,6 @@ master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Table_map # # table_id: # (test.t1)
master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; TRUNCATE table t2
master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Table_map # # table_id: # (test.t1)
master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
......
-- source include/have_binlog_format_row.inc
-- source include/have_innodb.inc
#
# BUG#51251
#
# The test case checks if truncating a temporary table created with
# engine InnoDB will not cause the truncate statement to be binlogged.
# Before patch for BUG#51251, the TRUNCATE statements below would be
# binlogged, which would cause the slave to fail with "table does not
# exist".
CREATE TABLE t1 ( c1 int , primary key (c1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (2), (3);
CREATE TEMPORARY TABLE IF NOT EXISTS t2 LIKE t1;
TRUNCATE TABLE t2;
DROP TABLE t1;
-- echo ###############################################
-- echo ### assertion: No event for 'TRUNCATE TABLE t2'
-- echo ###############################################
-- source include/show_binlog_events.inc
-- echo ###############################################
RESET MASTER;
CREATE TEMPORARY TABLE t1 (c1 int) Engine=InnoDB;
INSERT INTO t1 VALUES (1), (2), (3);
TRUNCATE t1;
DROP TEMPORARY TABLE t1;
-- echo ###############################################
-- echo ### assertion: No event for 'TRUNCATE TABLE t1'
-- echo ###############################################
-- source include/show_binlog_events.inc
-- echo ###############################################
...@@ -50,6 +50,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -50,6 +50,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SELECT_LEX *select_lex= &thd->lex->select_lex; SELECT_LEX *select_lex= &thd->lex->select_lex;
THD::killed_state killed_status= THD::NOT_KILLED; THD::killed_state killed_status= THD::NOT_KILLED;
DBUG_ENTER("mysql_delete"); DBUG_ENTER("mysql_delete");
bool save_binlog_row_based;
THD::enum_binlog_query_type query_type= THD::enum_binlog_query_type query_type=
thd->lex->sql_command == SQLCOM_TRUNCATE ? thd->lex->sql_command == SQLCOM_TRUNCATE ?
...@@ -293,6 +294,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -293,6 +294,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->mark_columns_needed_for_delete(); table->mark_columns_needed_for_delete();
save_binlog_row_based= thd->current_stmt_binlog_row_based;
if (thd->lex->sql_command == SQLCOM_TRUNCATE &&
thd->current_stmt_binlog_row_based)
thd->clear_current_stmt_binlog_row_based();
while (!(error=info.read_record(&info)) && !thd->killed && while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error()) ! thd->is_error())
{ {
...@@ -342,6 +348,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -342,6 +348,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
else else
table->file->unlock_row(); // Row failed selection, release lock on it table->file->unlock_row(); // Row failed selection, release lock on it
} }
thd->current_stmt_binlog_row_based= save_binlog_row_based;
killed_status= thd->killed; killed_status= thd->killed;
if (killed_status != THD::NOT_KILLED || thd->is_error()) if (killed_status != THD::NOT_KILLED || thd->is_error())
error= 1; // Aborted error= 1; // Aborted
...@@ -390,7 +397,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -390,7 +397,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
/* See similar binlogging code in sql_update.cc, for comments */ /* See similar binlogging code in sql_update.cc, for comments */
if ((error < 0) || thd->transaction.stmt.modified_non_trans_table) if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
{ {
if (mysql_bin_log.is_open()) if (mysql_bin_log.is_open() &&
!(thd->lex->sql_command == SQLCOM_TRUNCATE &&
thd->current_stmt_binlog_row_based &&
find_temporary_table(thd, table_list)))
{ {
bool const is_trans= bool const is_trans=
thd->lex->sql_command == SQLCOM_TRUNCATE ? thd->lex->sql_command == SQLCOM_TRUNCATE ?
...@@ -1059,15 +1069,13 @@ bool multi_delete::send_eof() ...@@ -1059,15 +1069,13 @@ bool multi_delete::send_eof()
static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
{ {
bool error, save_binlog_row_based= thd->current_stmt_binlog_row_based; bool error;
DBUG_ENTER("mysql_truncate_by_delete"); DBUG_ENTER("mysql_truncate_by_delete");
table_list->lock_type= TL_WRITE; table_list->lock_type= TL_WRITE;
mysql_init_select(thd->lex); mysql_init_select(thd->lex);
thd->clear_current_stmt_binlog_row_based();
error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE); error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE);
ha_autocommit_or_rollback(thd, error); ha_autocommit_or_rollback(thd, error);
end_trans(thd, error ? ROLLBACK : COMMIT); end_trans(thd, error ? ROLLBACK : COMMIT);
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment