Commit d6f6db6f authored by Dmitry Shulga's avatar Dmitry Shulga

Fixed bug #55421 - Protocol::end_statement(): Assertion `0' on

multi-table UPDATE IGNORE.
The problem was that if there was an active SELECT statement
during trigger execution, an error risen during the execution
may cause a crash. The fix is to temporary reset LEX::current_select
before trigger execution and restore it afterwards. This way
errors risen during the trigger execution are processed as
if there was no active SELECT.

mysql-test/r/trigger_notembedded.result:
  added test case result for bug #55421.
mysql-test/t/trigger_notembedded.test:
  added test case for bug #55421.
sql/sql_trigger.cc:
  Reset thd->lex->current_select before start trigger execution
  and restore its original value after execution is finished.
  This is neccessery in order to set error status in 
  diagnostic_area in case of trigger execution failure.
parent 446cc653
...@@ -474,4 +474,25 @@ SHOW CREATE TRIGGER db1.trg; ...@@ -474,4 +474,25 @@ SHOW CREATE TRIGGER db1.trg;
ERROR 42000: Access denied; you need the TRIGGER privilege for this operation ERROR 42000: Access denied; you need the TRIGGER privilege for this operation
DROP USER 'no_rights'@'localhost'; DROP USER 'no_rights'@'localhost';
DROP DATABASE db1; DROP DATABASE db1;
DROP DATABASE IF EXISTS mysqltest_db1;
CREATE DATABASE mysqltest_db1;
USE mysqltest_db1;
GRANT ALL ON mysqltest_db1.* TO mysqltest_u1@localhost;
CREATE TABLE t1 (
a1 int,
a2 int
);
INSERT INTO t1 VALUES (1, 20);
CREATE TRIGGER mysqltest_db1.upd_t1
BEFORE UPDATE ON t1 FOR EACH ROW SET new.a2 = 200;
CREATE TABLE t2 (
a1 int
);
INSERT INTO t2 VALUES (2);
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_u1@localhost;
UPDATE IGNORE t1, t2 SET t1.a1 = 2, t2.a1 = 3 WHERE t1.a1 = 1 AND t2.a1 = 2;
ERROR 42000: TRIGGER command denied to user 'mysqltest_u1'@'localhost' for table 't1'
DROP DATABASE mysqltest_db1;
DROP USER mysqltest_u1@localhost;
USE test;
End of 5.1 tests. End of 5.1 tests.
...@@ -932,4 +932,52 @@ disconnect con1; ...@@ -932,4 +932,52 @@ disconnect con1;
DROP USER 'no_rights'@'localhost'; DROP USER 'no_rights'@'localhost';
DROP DATABASE db1; DROP DATABASE db1;
#
# Bug#55421 Protocol::end_statement(): Assertion `0' on multi-table UPDATE IGNORE
# To reproduce a crash we need to provoke a trigger execution with
# the following conditions:
# - active SELECT statement during trigger execution
# (i.e. LEX::current_select != NULL);
# - IGNORE option (i.e. LEX::current_select->no_error == TRUE);
--disable_warnings
DROP DATABASE IF EXISTS mysqltest_db1;
--enable_warnings
CREATE DATABASE mysqltest_db1;
USE mysqltest_db1;
GRANT ALL ON mysqltest_db1.* TO mysqltest_u1@localhost;
--connect(con1,localhost,mysqltest_u1,,mysqltest_db1)
CREATE TABLE t1 (
a1 int,
a2 int
);
INSERT INTO t1 VALUES (1, 20);
CREATE TRIGGER mysqltest_db1.upd_t1
BEFORE UPDATE ON t1 FOR EACH ROW SET new.a2 = 200;
CREATE TABLE t2 (
a1 int
);
INSERT INTO t2 VALUES (2);
--connection default
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_u1@localhost;
--error ER_TABLEACCESS_DENIED_ERROR
UPDATE IGNORE t1, t2 SET t1.a1 = 2, t2.a1 = 3 WHERE t1.a1 = 1 AND t2.a1 = 2;
# Cleanup
DROP DATABASE mysqltest_db1;
DROP USER mysqltest_u1@localhost;
--disconnect con1
--connection default
USE test;
--echo End of 5.1 tests. --echo End of 5.1 tests.
...@@ -1989,6 +1989,7 @@ bool Table_triggers_list::process_triggers(THD *thd, ...@@ -1989,6 +1989,7 @@ bool Table_triggers_list::process_triggers(THD *thd,
bool err_status; bool err_status;
Sub_statement_state statement_state; Sub_statement_state statement_state;
sp_head *sp_trigger= bodies[event][time_type]; sp_head *sp_trigger= bodies[event][time_type];
SELECT_LEX *save_current_select;
if (sp_trigger == NULL) if (sp_trigger == NULL)
return FALSE; return FALSE;
...@@ -2012,11 +2013,19 @@ bool Table_triggers_list::process_triggers(THD *thd, ...@@ -2012,11 +2013,19 @@ bool Table_triggers_list::process_triggers(THD *thd,
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
/*
Reset current_select before call execute_trigger() and
restore it after return from one. This way error is set
in case of failure during trigger execution.
*/
save_current_select= thd->lex->current_select;
thd->lex->current_select= NULL;
err_status= err_status=
sp_trigger->execute_trigger(thd, sp_trigger->execute_trigger(thd,
&trigger_table->s->db, &trigger_table->s->db,
&trigger_table->s->table_name, &trigger_table->s->table_name,
&subject_table_grants[event][time_type]); &subject_table_grants[event][time_type]);
thd->lex->current_select= save_current_select;
thd->restore_sub_statement_state(&statement_state); thd->restore_sub_statement_state(&statement_state);
......
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