Commit 326c4e90 authored by unknown's avatar unknown

A fix for Bug#34643: TRUNCATE crash if trigger and foreign key.

  
In cases when TRUNCATE was executed by invoking mysql_delete() rather
than by table recreation (for example, when TRUNCATE was issued on
InnoDB table with is referenced by foreign key) triggers were invoked.
In debug builds this also led to crash because of an assertion, which
assumes that some preliminary actions take place before trigger 
invocation, which doesn't happen in case of TRUNCATE.

The fix is not to execute triggers in mysql_delete() when this
function is used by TRUNCATE.


mysql-test/r/trigger-trans.result:
  Update result file.
mysql-test/t/trigger-trans.test:
  A test case for Bug#34643: TRUNCATE crash if trigger and foreign key.
sql/sql_delete.cc:
  Do not process triggers in TRUNCATE.
parent aafb492d
...@@ -140,4 +140,23 @@ select * from t3; ...@@ -140,4 +140,23 @@ select * from t3;
c c
1 1
drop table t1, t2, t3; drop table t1, t2, t3;
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=innodb;
CREATE TABLE t2(b INT, FOREIGN KEY(b) REFERENCES t1(a)) ENGINE=innodb;
INSERT INTO t1 VALUES (1);
CREATE TRIGGER t1_bd BEFORE DELETE ON t1 FOR EACH ROW SET @a = 1;
CREATE TRIGGER t1_ad AFTER DELETE ON t1 FOR EACH ROW SET @b = 1;
SET @a = 0;
SET @b = 0;
TRUNCATE t1;
SELECT @a, @b;
@a @b
0 0
INSERT INTO t1 VALUES (1);
DELETE FROM t1;
SELECT @a, @b;
@a @b
1 1
DROP TABLE t2, t1;
End of 5.0 tests End of 5.0 tests
...@@ -128,5 +128,37 @@ drop table t1, t2, t3; ...@@ -128,5 +128,37 @@ drop table t1, t2, t3;
disconnect connection_update; disconnect connection_update;
disconnect connection_aux; disconnect connection_aux;
#
# Bug#34643: TRUNCATE crash if trigger and foreign key.
#
--disable_warnings
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
--enable_warnings
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=innodb;
CREATE TABLE t2(b INT, FOREIGN KEY(b) REFERENCES t1(a)) ENGINE=innodb;
INSERT INTO t1 VALUES (1);
CREATE TRIGGER t1_bd BEFORE DELETE ON t1 FOR EACH ROW SET @a = 1;
CREATE TRIGGER t1_ad AFTER DELETE ON t1 FOR EACH ROW SET @b = 1;
SET @a = 0;
SET @b = 0;
TRUNCATE t1;
SELECT @a, @b;
INSERT INTO t1 VALUES (1);
DELETE FROM t1;
SELECT @a, @b;
DROP TABLE t2, t1;
--echo End of 5.0 tests --echo End of 5.0 tests
...@@ -35,6 +35,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -35,6 +35,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
READ_RECORD info; READ_RECORD info;
bool using_limit=limit != HA_POS_ERROR; bool using_limit=limit != HA_POS_ERROR;
bool transactional_table, safe_update, const_cond; bool transactional_table, safe_update, const_cond;
bool triggers_applicable;
ha_rows deleted; ha_rows deleted;
uint usable_index= MAX_KEY; uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex; SELECT_LEX *select_lex= &thd->lex->select_lex;
...@@ -93,6 +94,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -93,6 +94,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
select_lex->no_error= thd->lex->ignore; select_lex->no_error= thd->lex->ignore;
/* NOTE: TRUNCATE must not invoke triggers. */
triggers_applicable= table->triggers &&
thd->lex->sql_command != SQLCOM_TRUNCATE;
/* /*
Test if the user wants to delete all rows and deletion doesn't have Test if the user wants to delete all rows and deletion doesn't have
any side-effects (because of triggers), so we can use optimized any side-effects (because of triggers), so we can use optimized
...@@ -102,8 +108,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -102,8 +108,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
*/ */
if (!using_limit && const_cond && (!conds || conds->val_int()) && if (!using_limit && const_cond && (!conds || conds->val_int()) &&
!(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
(thd->lex->sql_command == SQLCOM_TRUNCATE || !(triggers_applicable && table->triggers->has_delete_triggers())
!(table->triggers && table->triggers->has_delete_triggers()))
) )
{ {
deleted= table->file->records; deleted= table->file->records;
...@@ -217,7 +222,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -217,7 +222,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
init_ftfuncs(thd, select_lex, 1); init_ftfuncs(thd, select_lex, 1);
thd->proc_info="updating"; thd->proc_info="updating";
if (table->triggers) if (triggers_applicable)
{ {
table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE); table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
if (table->triggers->has_triggers(TRG_EVENT_DELETE, if (table->triggers->has_triggers(TRG_EVENT_DELETE,
...@@ -239,7 +244,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -239,7 +244,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(select && select->skip_record())&& !thd->net.report_error ) if (!(select && select->skip_record())&& !thd->net.report_error )
{ {
if (table->triggers && if (triggers_applicable &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE, table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE)) TRG_ACTION_BEFORE, FALSE))
{ {
...@@ -250,7 +255,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -250,7 +255,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(error=table->file->delete_row(table->record[0]))) if (!(error=table->file->delete_row(table->record[0])))
{ {
deleted++; deleted++;
if (table->triggers && if (triggers_applicable &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE, table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE)) TRG_ACTION_AFTER, FALSE))
{ {
......
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