Commit 577e390e authored by Davi Arnaut's avatar Davi Arnaut

Bug#37016: TRUNCATE TABLE removes some rows but not all

The special TRUNCATE TABLE (DDL) transaction wasn't being properly
rolled back if a error occurred during row by row deletion. The
error can be caused by a foreign key restriction imposed by InnoDB
SE and would cause the server to erroneously issue a implicit
commit.

The solution is to rollback the transaction if a truncation via row
by row deletion fails, otherwise commit. All effects of a TRUNCATE 
ABLE operation are rolled back if a row by row deletion fails.
parent 2ce96d24
...@@ -617,10 +617,10 @@ call p_verify_status_increment(0, 0, 0, 0); ...@@ -617,10 +617,10 @@ call p_verify_status_increment(0, 0, 0, 0);
--echo --echo
--echo # No test because of Bug#8729 "rename table fails on temporary table" --echo # No test because of Bug#8729 "rename table fails on temporary table"
--echo # 24. DDL: TRUNCATE TEMPORARY TABLE, does not start a transaction --echo # 24. DDL: TRUNCATE TEMPORARY TABLE
--echo --echo
truncate table t2; truncate table t2;
call p_verify_status_increment(2, 0, 2, 0); call p_verify_status_increment(4, 0, 4, 0);
commit; commit;
--echo # There is nothing left to commit --echo # There is nothing left to commit
call p_verify_status_increment(0, 0, 0, 0); call p_verify_status_increment(0, 0, 0, 0);
...@@ -733,7 +733,7 @@ call p_verify_status_increment(1, 0, 1, 0); ...@@ -733,7 +733,7 @@ call p_verify_status_increment(1, 0, 1, 0);
rename table t4 to t3; rename table t4 to t3;
call p_verify_status_increment(1, 0, 1, 0); call p_verify_status_increment(1, 0, 1, 0);
truncate table t3; truncate table t3;
call p_verify_status_increment(2, 2, 2, 2); call p_verify_status_increment(4, 4, 2, 2);
create view v1 as select * from t2; create view v1 as select * from t2;
call p_verify_status_increment(1, 0, 1, 0); call p_verify_status_increment(1, 0, 1, 0);
check table t1; check table t1;
......
...@@ -683,10 +683,10 @@ SUCCESS ...@@ -683,10 +683,10 @@ SUCCESS
# 23. DDL: RENAME TEMPORARY TABLE, does not start a transaction # 23. DDL: RENAME TEMPORARY TABLE, does not start a transaction
# No test because of Bug#8729 "rename table fails on temporary table" # No test because of Bug#8729 "rename table fails on temporary table"
# 24. DDL: TRUNCATE TEMPORARY TABLE, does not start a transaction # 24. DDL: TRUNCATE TEMPORARY TABLE
truncate table t2; truncate table t2;
call p_verify_status_increment(2, 0, 2, 0); call p_verify_status_increment(4, 0, 4, 0);
SUCCESS SUCCESS
commit; commit;
...@@ -853,7 +853,7 @@ call p_verify_status_increment(1, 0, 1, 0); ...@@ -853,7 +853,7 @@ call p_verify_status_increment(1, 0, 1, 0);
SUCCESS SUCCESS
truncate table t3; truncate table t3;
call p_verify_status_increment(2, 2, 2, 2); call p_verify_status_increment(4, 4, 2, 2);
SUCCESS SUCCESS
create view v1 as select * from t2; create view v1 as select * from t2;
......
...@@ -1788,4 +1788,62 @@ DEALLOCATE PREPARE stmt1; ...@@ -1788,4 +1788,62 @@ DEALLOCATE PREPARE stmt1;
DEALLOCATE PREPARE stmt3; DEALLOCATE PREPARE stmt3;
DROP TABLE t1,t3,t2; DROP TABLE t1,t3,t2;
DROP FUNCTION f1; DROP FUNCTION f1;
DROP TABLE IF EXISTS t1,t2;
CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE t2 (id INT PRIMARY KEY,
t1_id INT, INDEX par_ind (t1_id),
FOREIGN KEY (t1_id) REFERENCES t1(id)) ENGINE=INNODB;
INSERT INTO t1 VALUES (1),(2);
INSERT INTO t2 VALUES (3,2);
SET AUTOCOMMIT = 0;
START TRANSACTION;
TRUNCATE TABLE t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))
SELECT * FROM t1;
id
1
2
COMMIT;
SELECT * FROM t1;
id
1
2
START TRANSACTION;
TRUNCATE TABLE t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))
SELECT * FROM t1;
id
1
2
ROLLBACK;
SELECT * FROM t1;
id
1
2
SET AUTOCOMMIT = 1;
START TRANSACTION;
SELECT * FROM t1;
id
1
2
COMMIT;
TRUNCATE TABLE t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))
SELECT * FROM t1;
id
1
2
DELETE FROM t2 WHERE id = 3;
START TRANSACTION;
SELECT * FROM t1;
id
1
2
TRUNCATE TABLE t1;
ROLLBACK;
SELECT * FROM t1;
id
TRUNCATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
End of 5.1 tests End of 5.1 tests
...@@ -132,4 +132,56 @@ DEALLOCATE PREPARE stmt3; ...@@ -132,4 +132,56 @@ DEALLOCATE PREPARE stmt3;
DROP TABLE t1,t3,t2; DROP TABLE t1,t3,t2;
DROP FUNCTION f1; DROP FUNCTION f1;
#
# Bug#37016: TRUNCATE TABLE removes some rows but not all
#
--disable_warnings
DROP TABLE IF EXISTS t1,t2;
--enable_warnings
CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE t2 (id INT PRIMARY KEY,
t1_id INT, INDEX par_ind (t1_id),
FOREIGN KEY (t1_id) REFERENCES t1(id)) ENGINE=INNODB;
INSERT INTO t1 VALUES (1),(2);
INSERT INTO t2 VALUES (3,2);
SET AUTOCOMMIT = 0;
START TRANSACTION;
--error ER_ROW_IS_REFERENCED_2
TRUNCATE TABLE t1;
SELECT * FROM t1;
COMMIT;
SELECT * FROM t1;
START TRANSACTION;
--error ER_ROW_IS_REFERENCED_2
TRUNCATE TABLE t1;
SELECT * FROM t1;
ROLLBACK;
SELECT * FROM t1;
SET AUTOCOMMIT = 1;
START TRANSACTION;
SELECT * FROM t1;
COMMIT;
--error ER_ROW_IS_REFERENCED_2
TRUNCATE TABLE t1;
SELECT * FROM t1;
DELETE FROM t2 WHERE id = 3;
START TRANSACTION;
SELECT * FROM t1;
TRUNCATE TABLE t1;
ROLLBACK;
SELECT * FROM t1;
TRUNCATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -950,6 +950,26 @@ bool multi_delete::send_eof() ...@@ -950,6 +950,26 @@ bool multi_delete::send_eof()
TRUNCATE TABLE TRUNCATE TABLE
****************************************************************************/ ****************************************************************************/
/*
Row-by-row truncation if the engine does not support table recreation.
Probably a InnoDB table.
*/
static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
{
bool error, save_binlog_row_based= thd->current_stmt_binlog_row_based;
DBUG_ENTER("mysql_truncate_by_delete");
table_list->lock_type= TL_WRITE;
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);
ha_autocommit_or_rollback(thd, error);
end_trans(thd, error ? ROLLBACK : COMMIT);
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(error);
}
/* /*
Optimize delete of all rows by doing a full generate of the table Optimize delete of all rows by doing a full generate of the table
This will work even if the .ISM and .ISD tables are destroyed This will work even if the .ISM and .ISD tables are destroyed
...@@ -1055,24 +1075,6 @@ end: ...@@ -1055,24 +1075,6 @@ end:
DBUG_RETURN(error); DBUG_RETURN(error);
trunc_by_del: trunc_by_del:
/* Probably InnoDB table */ error= mysql_truncate_by_delete(thd, table_list);
ulonglong save_options= thd->options;
table_list->lock_type= TL_WRITE;
thd->options&= ~(OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
ha_enable_transaction(thd, FALSE);
mysql_init_select(thd->lex);
bool save_binlog_row_based= thd->current_stmt_binlog_row_based;
thd->clear_current_stmt_binlog_row_based();
error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
HA_POS_ERROR, LL(0), TRUE);
ha_enable_transaction(thd, TRUE);
/*
Safety, in case the engine ignored ha_enable_transaction(FALSE)
above. Also clears thd->transaction.*.
*/
error= ha_autocommit_or_rollback(thd, error);
ha_commit(thd);
thd->options= save_options;
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