diff options
author | Sujatha Sivakumar <sujatha.sivakumar@oracle.com> | 2014-04-15 15:17:25 +0530 |
---|---|---|
committer | Sujatha Sivakumar <sujatha.sivakumar@oracle.com> | 2014-04-15 15:17:25 +0530 |
commit | 1b74f2e3da53dd8b965920fad2ff599df2d1fc89 (patch) | |
tree | 23a4a9122231307ef4549e9187e0f1d979933d27 /sql/sql_truncate.cc | |
parent | ea38c2658229c45e48bf417168efc85f4768cef4 (diff) | |
download | mariadb-git-1b74f2e3da53dd8b965920fad2ff599df2d1fc89.tar.gz |
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.
Analysis:
========
Truncate table can happen either through drop and create of
table or by deleting rows. In the second case the existing
code is written in such a way that even if an error occurs
the truncate statement will always be binlogged. Which is not
correct.
Binlogging of TRUNCATE TABLE statement should check whether
truncate is executed "transactionally or not". If the table
is transaction based we log the TRUNCATE TABLE only on
successful completion.
If table is non transactional there are possibilities that on
error we could have partial changes done hence in such cases
we do log in spite of errors as some of the lines might have
been removed, so the statement has to be sent to slave.
Fix:
===
Using table handler whether truncate table is being executed
in transaction based mode or not is identified and statement
is binlogged accordingly.
mysql-test/suite/binlog/r/binlog_truncate_kill.result:
Added test case to test the fix for Bug#17942050.
mysql-test/suite/binlog/t/binlog_truncate_kill.test:
Added test case to test the fix for Bug#17942050.
sql/sql_truncate.cc:
Check if truncation is successful or not and retun appropriate
return values so that binlogging can be done based on that.
sql/sql_truncate.h:
Added a new enum.
Diffstat (limited to 'sql/sql_truncate.cc')
-rw-r--r-- | sql/sql_truncate.cc | 51 |
1 files changed, 38 insertions, 13 deletions
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; } /* |