diff options
-rw-r--r-- | mysql-test/suite/binlog/r/binlog_truncate_kill.result | 33 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/binlog_truncate_kill.test | 56 | ||||
-rw-r--r-- | sql/sql_truncate.cc | 51 | ||||
-rw-r--r-- | sql/sql_truncate.h | 10 |
4 files changed, 135 insertions, 15 deletions
diff --git a/mysql-test/suite/binlog/r/binlog_truncate_kill.result b/mysql-test/suite/binlog/r/binlog_truncate_kill.result new file mode 100644 index 00000000000..c2ea2e8d404 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_truncate_kill.result @@ -0,0 +1,33 @@ +RESET MASTER; +connection default; +CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY, a INT, b INT) ENGINE=INNODB; +INSERT INTO t1(a, b) VALUES(1,2),(2,4),(3,6),(4,8),(5,10); +SET DEBUG_SYNC = "open_and_process_table signal truncate_before_lock wait_for forever"; +TRUNCATE t1; +connect con1,localhost,root,,; +SET DEBUG_SYNC = "now wait_for truncate_before_lock"; +SELECT ((@id := id) - id) FROM information_schema.processlist WHERE processlist.info LIKE '%TRUNCATE t1%' AND state LIKE '%open_and_process_table%'; +((@id := id) - id) +0 +KILL QUERY @id; +connection default; +ERROR 70100: Query execution was interrupted +connection con1; +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(id INT AUTO_INCREMENT PRIMARY KEY, a INT, b INT) ENGINE=INNODB +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Intvar # # INSERT_ID=1 +master-bin.000001 # Query # # use `test`; INSERT INTO t1(a, b) VALUES(1,2),(2,4),(3,6),(4,8),(5,10) +master-bin.000001 # Xid # # COMMIT /* XID */ +disconnect con1; +connection default; +SELECT * FROM t1; +id a b +1 1 2 +2 2 4 +3 3 6 +4 4 8 +5 5 10 +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/suite/binlog/t/binlog_truncate_kill.test b/mysql-test/suite/binlog/t/binlog_truncate_kill.test new file mode 100644 index 00000000000..ffbccfa536f --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_truncate_kill.test @@ -0,0 +1,56 @@ +############################################################################### +# Bug#17942050:KILL OF TRUNCATE TABLE WILL LEAD TO BINARY LOG WRITTEN WHILE +# ROWS REMAINS +# +# Problem: +# ======== +# When truncate table fails while using transactional based engines even +# though the operation errors out we still continue and log it to binlog. +# Because of this master has data but the truncate will be written to binary +# log which will cause inconsistency. +# +# Test: +# ===== +# Make master to wait in "open_table" call during the execution of truncate +# table command and kill the truncate table from other connection. This causes +# open table to return an error saying truncate failed during open table. This +# statement should not be binlogged. +############################################################################### +--source include/have_debug_sync.inc +--source include/have_binlog_format_statement.inc +RESET MASTER; +--enable_connect_log +--connection default +CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY, a INT, b INT) ENGINE=INNODB; +INSERT INTO t1(a, b) VALUES(1,2),(2,4),(3,6),(4,8),(5,10); +SET DEBUG_SYNC = "open_and_process_table signal truncate_before_lock wait_for forever"; +--send TRUNCATE t1 + +connect(con1,localhost,root,,); +SET DEBUG_SYNC = "now wait_for truncate_before_lock"; +# Wait for one connection to reach open_and_process_table. +--let $show_statement= SHOW PROCESSLIST +--let $field= State +--let $condition= 'debug sync point: open_and_process_table'; +--source include/wait_show_condition.inc + +SELECT ((@id := id) - id) FROM information_schema.processlist WHERE processlist.info LIKE '%TRUNCATE t1%' AND state LIKE '%open_and_process_table%'; +# Test killing from mysql server +KILL QUERY @id; + +connection default; +--ERROR ER_QUERY_INTERRUPTED +--reap + +connection con1; +--source include/show_binlog_events.inc + +disconnect con1; +--source include/wait_until_disconnected.inc +connection default; + +SELECT * FROM t1; + +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; +--disable_connect_log diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index 2285131fade..07bf145c847 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -181,12 +181,19 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table) @param table_ref Table list element for the table to be truncated. @param is_tmp_table True if element refers to a temp table. - @retval 0 Success. - @retval > 0 Error code. + @retval TRUNCATE_OK Truncate was successful and statement can be safely + binlogged. + @retval TRUNCATE_FAILED_BUT_BINLOG Truncate failed but still go ahead with + binlogging as in case of non transactional tables + partial truncation is possible. + + @retval TRUNCATE_FAILED_SKIP_BINLOG Truncate was not successful hence donot + binlong the statement. */ -int Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref, - bool is_tmp_table) +enum Truncate_statement::truncate_result +Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref, + bool is_tmp_table) { int error= 0; uint flags; @@ -228,16 +235,30 @@ int Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref, /* Open the table as it will handle some required preparations. */ if (open_and_lock_tables(thd, table_ref, FALSE, flags)) - DBUG_RETURN(1); + DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG); /* Whether to truncate regardless of foreign keys. */ if (! (thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)) - error= fk_truncate_illegal_if_parent(thd, table_ref->table); + if (fk_truncate_illegal_if_parent(thd, table_ref->table)) + DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG); - if (!error && (error= table_ref->table->file->ha_truncate())) + error= table_ref->table->file->ha_truncate(); + if (error) + { table_ref->table->file->print_error(error, MYF(0)); - - DBUG_RETURN(error); + /* + If truncate method is not implemented then we don't binlog the + statement. If truncation has failed in a transactional engine then also we + donot binlog the statment. Only in non transactional engine we binlog + inspite of errors. + */ + if (error == HA_ERR_WRONG_COMMAND || + table_ref->table->file->has_transactions()) + DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG); + else + DBUG_RETURN(TRUNCATE_FAILED_BUT_BINLOG); + } + DBUG_RETURN(TRUNCATE_OK); } @@ -470,10 +491,14 @@ bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref) /* All effects of a TRUNCATE TABLE operation are committed even if - truncation fails. Thus, the query must be written to the binary - log. The only exception is a unimplemented truncate method. + truncation fails in the case of non transactional tables. Thus, the + query must be written to the binary log. The only exception is a + unimplemented truncate method. */ - binlog_stmt= !error || error != HA_ERR_WRONG_COMMAND; + if (error == TRUNCATE_OK || error == TRUNCATE_FAILED_BUT_BINLOG) + binlog_stmt= true; + else + binlog_stmt= false; } /* diff --git a/sql/sql_truncate.h b/sql/sql_truncate.h index 95a2f35df4f..0280ecc4932 100644 --- a/sql/sql_truncate.h +++ b/sql/sql_truncate.h @@ -1,6 +1,6 @@ #ifndef SQL_TRUNCATE_INCLUDED #define SQL_TRUNCATE_INCLUDED -/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,11 +47,17 @@ public: bool execute(THD *thd); protected: + enum truncate_result{ + TRUNCATE_OK=0, + TRUNCATE_FAILED_BUT_BINLOG, + TRUNCATE_FAILED_SKIP_BINLOG + }; + /** Handle locking a base table for truncate. */ bool lock_table(THD *, TABLE_LIST *, bool *); /** Truncate table via the handler method. */ - int handler_truncate(THD *, TABLE_LIST *, bool); + enum truncate_result handler_truncate(THD *, TABLE_LIST *, bool); /** Optimized delete of all rows by doing a full regenerate of the table. |