diff --git a/mysql-test/r/trigger-trans.result b/mysql-test/r/trigger-trans.result
index b56abf1f59a5d8fc02cfd8b43d589a23989d2db1..cd5f629564f0f128785975ab4a12eaa4ec9b8959 100644
--- a/mysql-test/r/trigger-trans.result
+++ b/mysql-test/r/trigger-trans.result
@@ -82,3 +82,62 @@ ALICE	33	1	0
 THE CROWN	43	1	0
 THE PIE	53	1	1
 drop table t1;
+
+Bug#26141 mixing table types in trigger causes full
+table lock on innodb table
+
+Ensure we do not open and lock tables for the triggers we do not
+fire.
+
+drop table if exists t1, t2, t3;
+drop trigger if exists trg_bug26141_au;
+drop trigger if exists trg_bug26141_ai;
+create table t1 (c int primary key) engine=innodb;
+create table t2 (c int) engine=myisam;
+create table t3 (c int) engine=myisam;
+insert into t1 (c) values (1);
+create trigger trg_bug26141_ai after insert on t1
+for each row
+begin
+insert into t2 (c) values (1);
+# We need the 'sync' lock to synchronously wait in connection 2 till 
+# the moment when the trigger acquired all the locks.
+select release_lock("lock_bug26141_sync") into @a;
+# 1000 is time in seconds of lock wait timeout -- this is a way
+# to cause a manageable sleep up to 1000 seconds
+select get_lock("lock_bug26141_wait", 1000) into @a;
+end|
+create trigger trg_bug26141_au after update on t1
+for each row
+begin
+insert into t3 (c) values (1);
+end|
+select get_lock("lock_bug26141_wait", 0);
+get_lock("lock_bug26141_wait", 0)
+1
+select get_lock("lock_bug26141_sync", /* must not be priorly locked */ 0);
+get_lock("lock_bug26141_sync", /* must not be priorly locked */ 0)
+1
+insert into t1 (c) values (2);
+select get_lock("lock_bug26141_sync", 1000);
+get_lock("lock_bug26141_sync", 1000)
+1
+update t1 set c=3 where c=1;
+select release_lock("lock_bug26141_sync");
+release_lock("lock_bug26141_sync")
+1
+select release_lock("lock_bug26141_wait");
+release_lock("lock_bug26141_wait")
+1
+select * from t1;
+c
+2
+3
+select * from t2;
+c
+1
+select * from t3;
+c
+1
+drop table t1, t2, t3;
+End of 5.0 tests
diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result
index 290929d476ddc8e9aa56f0564a71b7bd406ecc93..4b18e525e62bfd6f0cf569ebfac3ede7c083b5e6 100644
--- a/mysql-test/r/trigger.result
+++ b/mysql-test/r/trigger.result
@@ -1476,4 +1476,461 @@ DROP TRIGGER t1_test;
 DROP TABLE t1,t2;
 SET SESSION LOW_PRIORITY_UPDATES=DEFAULT;
 SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT;
+
+Bug#28502 Triggers that update another innodb table will block
+on X lock unnecessarily
+
+Ensure we do not open and lock tables for triggers we do not fire.
+
+drop table if exists t1, t2;
+drop trigger if exists trg_bug28502_au;
+create table t1 (id int, count int);
+create table t2 (id int);
+create trigger trg_bug28502_au before update on t2
+for each row
+begin
+if (new.id is not null) then
+update t1 set count= count + 1 where id = old.id;
+end if;
+end|
+insert into t1 (id, count) values (1, 0);
+lock table t1 write;
+insert into t2 set id=1;
+unlock tables;
+update t2 set id=1 where id=1;
+select * from t1;
+id	count
+1	1
+select * from t2;
+id
+1
+drop table t1, t2;
+
+Additionally, provide test coverage for triggers and 
+all MySQL data changing commands.
+
+drop table if exists t1, t2, t1_op_log;
+drop view if exists v1;
+drop trigger if exists trg_bug28502_bi;
+drop trigger if exists trg_bug28502_ai;
+drop trigger if exists trg_bug28502_bu;
+drop trigger if exists trg_bug28502_au;
+drop trigger if exists trg_bug28502_bd;
+drop trigger if exists trg_bug28502_ad;
+create table t1 (id int primary key auto_increment, operation varchar(255));
+create table t2 (id int primary key);
+create table t1_op_log(operation varchar(255));
+create view v1 as select * from t1;
+create trigger trg_bug28502_bi before insert on t1
+for each row
+insert into t1_op_log (operation)
+values (concat("Before INSERT, new=", new.operation));
+create trigger trg_bug28502_ai after insert on t1
+for each row
+insert into t1_op_log (operation)
+values (concat("After INSERT, new=", new.operation));
+create trigger trg_bug28502_bu before update on t1
+for each row
+insert into t1_op_log (operation)
+values (concat("Before UPDATE, new=", new.operation,
+", old=", old.operation));
+create trigger trg_bug28502_au after update on t1
+for each row
+insert into t1_op_log (operation)
+values (concat("After UPDATE, new=", new.operation,
+", old=", old.operation));
+create trigger trg_bug28502_bd before delete on t1
+for each row
+insert into t1_op_log (operation)
+values (concat("Before DELETE, old=", old.operation));
+create trigger trg_bug28502_ad after delete on t1
+for each row
+insert into t1_op_log (operation)
+values (concat("After DELETE, old=", old.operation));
+insert into t1 (operation) values ("INSERT");
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	INSERT
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT
+After INSERT, new=INSERT
+truncate t1_op_log;
+update t1 set operation="UPDATE" where id=@id;
+select * from t1;
+id	operation
+1	UPDATE
+select * from t1_op_log;
+operation
+Before UPDATE, new=UPDATE, old=INSERT
+After UPDATE, new=UPDATE, old=INSERT
+truncate t1_op_log;
+delete from t1 where id=@id;
+select * from t1;
+id	operation
+select * from t1_op_log;
+operation
+Before DELETE, old=UPDATE
+After DELETE, old=UPDATE
+truncate t1;
+truncate t1_op_log;
+insert into t1 (id, operation) values
+(NULL, "INSERT ON DUPLICATE KEY UPDATE, inserting a new key")
+on duplicate key update id=NULL, operation="Should never happen";
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+After INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+truncate t1_op_log;
+insert into t1 (id, operation) values
+(@id, "INSERT ON DUPLICATE KEY UPDATE, the key value is the same")
+on duplicate key update id=NULL,
+operation="INSERT ON DUPLICATE KEY UPDATE, updating the duplicate";
+select * from t1;
+id	operation
+0	INSERT ON DUPLICATE KEY UPDATE, updating the duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, the key value is the same
+Before UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+After UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+truncate t1;
+truncate t1_op_log;
+replace into t1 values (NULL, "REPLACE, inserting a new key");
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	REPLACE, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE, inserting a new key
+After INSERT, new=REPLACE, inserting a new key
+truncate t1_op_log;
+replace into t1 values (@id, "REPLACE, deleting the duplicate");
+select * from t1;
+id	operation
+1	REPLACE, deleting the duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE, deleting the duplicate
+Before DELETE, old=REPLACE, inserting a new key
+After DELETE, old=REPLACE, inserting a new key
+After INSERT, new=REPLACE, deleting the duplicate
+truncate t1;
+truncate t1_op_log;
+create table if not exists t1
+select NULL, "CREATE TABLE ... SELECT, inserting a new key";
+Warnings:
+Note	1050	Table 't1' already exists
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	CREATE TABLE ... SELECT, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=CREATE TABLE ... SELECT, inserting a new key
+After INSERT, new=CREATE TABLE ... SELECT, inserting a new key
+truncate t1_op_log;
+create table if not exists t1 replace
+select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
+Warnings:
+Note	1050	Table 't1' already exists
+select * from t1;
+id	operation
+1	CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
+select * from t1_op_log;
+operation
+Before INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
+Before DELETE, old=CREATE TABLE ... SELECT, inserting a new key
+After DELETE, old=CREATE TABLE ... SELECT, inserting a new key
+After INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
+truncate t1;
+truncate t1_op_log;
+insert into t1 (id, operation)
+select NULL, "INSERT ... SELECT, inserting a new key";
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	INSERT ... SELECT, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ... SELECT, inserting a new key
+After INSERT, new=INSERT ... SELECT, inserting a new key
+truncate t1_op_log;
+insert into t1 (id, operation)
+select @id,
+"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
+on duplicate key update id=NULL,
+operation="INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate";
+select * from t1;
+id	operation
+0	INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
+Before UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
+After UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
+truncate t1;
+truncate t1_op_log;
+replace into t1 (id, operation)
+select NULL, "REPLACE ... SELECT, inserting a new key";
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	REPLACE ... SELECT, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE ... SELECT, inserting a new key
+After INSERT, new=REPLACE ... SELECT, inserting a new key
+truncate t1_op_log;
+replace into t1 (id, operation)
+select @id, "REPLACE ... SELECT, deleting a duplicate";
+select * from t1;
+id	operation
+1	REPLACE ... SELECT, deleting a duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE ... SELECT, deleting a duplicate
+Before DELETE, old=REPLACE ... SELECT, inserting a new key
+After DELETE, old=REPLACE ... SELECT, inserting a new key
+After INSERT, new=REPLACE ... SELECT, deleting a duplicate
+truncate t1;
+truncate t1_op_log;
+insert into t1 (id, operation) values (1, "INSERT for multi-DELETE");
+insert into t2 (id) values (1);
+delete t1.*, t2.* from t1, t2 where t1.id=1;
+select * from t1;
+id	operation
+select * from t2;
+id
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT for multi-DELETE
+After INSERT, new=INSERT for multi-DELETE
+Before DELETE, old=INSERT for multi-DELETE
+After DELETE, old=INSERT for multi-DELETE
+truncate t1;
+truncate t2;
+truncate t1_op_log;
+insert into t1 (id, operation) values (1, "INSERT for multi-UPDATE");
+insert into t2 (id) values (1);
+update t1, t2 set t1.id=2, operation="multi-UPDATE" where t1.id=1;
+update t1, t2
+set t2.id=3, operation="multi-UPDATE, SET for t2, but the trigger is fired" where t1.id=2;
+select * from t1;
+id	operation
+2	multi-UPDATE, SET for t2, but the trigger is fired
+select * from t2;
+id
+3
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT for multi-UPDATE
+After INSERT, new=INSERT for multi-UPDATE
+Before UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
+After UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
+Before UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
+After UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
+truncate table t1;
+truncate table t2;
+truncate table t1_op_log;
+
+Now do the same but use a view instead of the base table.
+
+insert into v1 (operation) values ("INSERT");
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	INSERT
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT
+After INSERT, new=INSERT
+truncate t1_op_log;
+update v1 set operation="UPDATE" where id=@id;
+select * from t1;
+id	operation
+1	UPDATE
+select * from t1_op_log;
+operation
+Before UPDATE, new=UPDATE, old=INSERT
+After UPDATE, new=UPDATE, old=INSERT
+truncate t1_op_log;
+delete from v1 where id=@id;
+select * from t1;
+id	operation
+select * from t1_op_log;
+operation
+Before DELETE, old=UPDATE
+After DELETE, old=UPDATE
+truncate t1;
+truncate t1_op_log;
+insert into v1 (id, operation) values
+(NULL, "INSERT ON DUPLICATE KEY UPDATE, inserting a new key")
+on duplicate key update id=NULL, operation="Should never happen";
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+After INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+truncate t1_op_log;
+insert into v1 (id, operation) values
+(@id, "INSERT ON DUPLICATE KEY UPDATE, the key value is the same")
+on duplicate key update id=NULL,
+operation="INSERT ON DUPLICATE KEY UPDATE, updating the duplicate";
+select * from t1;
+id	operation
+0	INSERT ON DUPLICATE KEY UPDATE, updating the duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, the key value is the same
+Before UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+After UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
+truncate t1;
+truncate t1_op_log;
+replace into v1 values (NULL, "REPLACE, inserting a new key");
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	REPLACE, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE, inserting a new key
+After INSERT, new=REPLACE, inserting a new key
+truncate t1_op_log;
+replace into v1 values (@id, "REPLACE, deleting the duplicate");
+select * from t1;
+id	operation
+1	REPLACE, deleting the duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE, deleting the duplicate
+Before DELETE, old=REPLACE, inserting a new key
+After DELETE, old=REPLACE, inserting a new key
+After INSERT, new=REPLACE, deleting the duplicate
+truncate t1;
+truncate t1_op_log;
+create table if not exists v1
+select NULL, "CREATE TABLE ... SELECT, inserting a new key";
+Warnings:
+Note	1050	Table 'v1' already exists
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	CREATE TABLE ... SELECT, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=CREATE TABLE ... SELECT, inserting a new key
+After INSERT, new=CREATE TABLE ... SELECT, inserting a new key
+truncate t1_op_log;
+create table if not exists v1 replace
+select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
+Warnings:
+Note	1050	Table 'v1' already exists
+select * from t1;
+id	operation
+1	CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
+select * from t1_op_log;
+operation
+Before INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
+Before DELETE, old=CREATE TABLE ... SELECT, inserting a new key
+After DELETE, old=CREATE TABLE ... SELECT, inserting a new key
+After INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
+truncate t1;
+truncate t1_op_log;
+insert into v1 (id, operation)
+select NULL, "INSERT ... SELECT, inserting a new key";
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	INSERT ... SELECT, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ... SELECT, inserting a new key
+After INSERT, new=INSERT ... SELECT, inserting a new key
+truncate t1_op_log;
+insert into v1 (id, operation)
+select @id,
+"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
+on duplicate key update id=NULL,
+operation="INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate";
+select * from t1;
+id	operation
+0	INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
+Before UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
+After UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
+truncate t1;
+truncate t1_op_log;
+replace into v1 (id, operation)
+select NULL, "REPLACE ... SELECT, inserting a new key";
+set @id=last_insert_id();
+select * from t1;
+id	operation
+1	REPLACE ... SELECT, inserting a new key
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE ... SELECT, inserting a new key
+After INSERT, new=REPLACE ... SELECT, inserting a new key
+truncate t1_op_log;
+replace into v1 (id, operation)
+select @id, "REPLACE ... SELECT, deleting a duplicate";
+select * from t1;
+id	operation
+1	REPLACE ... SELECT, deleting a duplicate
+select * from t1_op_log;
+operation
+Before INSERT, new=REPLACE ... SELECT, deleting a duplicate
+Before DELETE, old=REPLACE ... SELECT, inserting a new key
+After DELETE, old=REPLACE ... SELECT, inserting a new key
+After INSERT, new=REPLACE ... SELECT, deleting a duplicate
+truncate t1;
+truncate t1_op_log;
+insert into v1 (id, operation) values (1, "INSERT for multi-DELETE");
+insert into t2 (id) values (1);
+delete v1.*, t2.* from v1, t2 where v1.id=1;
+select * from t1;
+id	operation
+select * from t2;
+id
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT for multi-DELETE
+After INSERT, new=INSERT for multi-DELETE
+Before DELETE, old=INSERT for multi-DELETE
+After DELETE, old=INSERT for multi-DELETE
+truncate t1;
+truncate t2;
+truncate t1_op_log;
+insert into v1 (id, operation) values (1, "INSERT for multi-UPDATE");
+insert into t2 (id) values (1);
+update v1, t2 set v1.id=2, operation="multi-UPDATE" where v1.id=1;
+update v1, t2
+set t2.id=3, operation="multi-UPDATE, SET for t2, but the trigger is fired" where v1.id=2;
+select * from t1;
+id	operation
+2	multi-UPDATE, SET for t2, but the trigger is fired
+select * from t2;
+id
+3
+select * from t1_op_log;
+operation
+Before INSERT, new=INSERT for multi-UPDATE
+After INSERT, new=INSERT for multi-UPDATE
+Before UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
+After UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
+Before UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
+After UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
+drop view v1;
+drop table t1, t2, t1_op_log;
 End of 5.0 tests
diff --git a/mysql-test/t/trigger-trans.test b/mysql-test/t/trigger-trans.test
index 5c135d988781833da578bfbb0fa662fb7c301155..8103a1ba0b16d99a07ad79892d06231a07e6e985 100644
--- a/mysql-test/t/trigger-trans.test
+++ b/mysql-test/t/trigger-trans.test
@@ -49,4 +49,84 @@ insert into t1 values ('The Pie', 50, 1, 1);
 select * from t1;
 drop table t1;
 
-# End of 5.0 tests
+--echo
+--echo Bug#26141 mixing table types in trigger causes full
+--echo table lock on innodb table
+--echo
+--echo Ensure we do not open and lock tables for the triggers we do not
+--echo fire.
+--echo
+--disable_warnings
+drop table if exists t1, t2, t3;
+drop trigger if exists trg_bug26141_au;
+drop trigger if exists trg_bug26141_ai;
+--enable_warnings
+# Note, for InnoDB to allow concurrent UPDATE and INSERT the
+# table must have a unique key.
+create table t1 (c int primary key) engine=innodb;
+create table t2 (c int) engine=myisam;
+create table t3 (c int) engine=myisam;
+insert into t1 (c) values (1);
+delimiter |;
+
+create trigger trg_bug26141_ai after insert on t1
+for each row
+begin
+  insert into t2 (c) values (1);
+# We need the 'sync' lock to synchronously wait in connection 2 till 
+# the moment when the trigger acquired all the locks.
+  select release_lock("lock_bug26141_sync") into @a;
+# 1000 is time in seconds of lock wait timeout -- this is a way
+# to cause a manageable sleep up to 1000 seconds
+  select get_lock("lock_bug26141_wait", 1000) into @a;
+end|
+
+create trigger trg_bug26141_au after update on t1
+for each row
+begin
+  insert into t3 (c) values (1);
+end|
+delimiter ;|
+
+# Establish an alternative connection.
+--connect (connection_aux,localhost,root,,test,,)
+--connect (connection_update,localhost,root,,test,,)
+
+connection connection_aux;
+# Lock the wait lock, it must not be locked, so specify zero timeout.
+select get_lock("lock_bug26141_wait", 0);
+
+#
+connection default;
+#
+# Run the trigger synchronously 
+#
+select get_lock("lock_bug26141_sync", /* must not be priorly locked */ 0);
+# Will acquire the table level locks, perform the insert into t2,
+# release the sync lock and block on the wait lock.
+send insert into t1 (c) values (2);
+
+connection connection_update;
+# Wait for the trigger to acquire its locks and unlock the sync lock.
+select get_lock("lock_bug26141_sync", 1000); 
+#
+# This must continue: after the fix for the bug, we do not
+# open tables for t2, and with c=4 innobase allows the update
+# to run concurrently with insert.
+update t1 set c=3 where c=1;
+select release_lock("lock_bug26141_sync"); 
+connection connection_aux;
+select release_lock("lock_bug26141_wait");
+connection default;
+reap;
+select * from t1;
+select * from t2;
+select * from t3;
+
+# Drops the trigger as well.
+drop table t1, t2, t3;
+disconnect connection_update;
+disconnect connection_aux;
+
+
+--echo End of 5.0 tests
diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test
index 0fa92f33de2c6a7db48cc8156c3c02127691eae1..a6390036322e8c29c83bd497b3ca6c4bc825acda 100644
--- a/mysql-test/t/trigger.test
+++ b/mysql-test/t/trigger.test
@@ -1828,5 +1828,370 @@ DROP TRIGGER t1_test;
 DROP TABLE t1,t2;
 SET SESSION LOW_PRIORITY_UPDATES=DEFAULT;
 SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT;
+--echo
+--echo Bug#28502 Triggers that update another innodb table will block
+--echo on X lock unnecessarily
+--echo
+--echo Ensure we do not open and lock tables for triggers we do not fire.
+--echo
+--disable_warnings
+drop table if exists t1, t2;
+drop trigger if exists trg_bug28502_au;
+--enable_warnings
+
+create table t1 (id int, count int);
+create table t2 (id int);
+delimiter |;
+
+create trigger trg_bug28502_au before update on t2
+for each row
+begin
+  if (new.id is not null) then
+    update t1 set count= count + 1 where id = old.id;
+  end if;
+end|
+
+delimiter ;|
+insert into t1 (id, count) values (1, 0);
+
+lock table t1 write;
+
+--connect (connection_insert, localhost, root, , test, , )
+connection connection_insert;
+# Is expected to pass.
+insert into t2 set id=1;
+connection default;
+unlock tables;
+update t2 set id=1 where id=1;
+select * from t1;
+select * from t2;
+# Will drop the trigger
+drop table t1, t2;
+disconnect connection_insert;
+--echo
+--echo Additionally, provide test coverage for triggers and 
+--echo all MySQL data changing commands.
+--echo
+--disable_warnings
+drop table if exists t1, t2, t1_op_log;
+drop view if exists v1;
+drop trigger if exists trg_bug28502_bi;
+drop trigger if exists trg_bug28502_ai;
+drop trigger if exists trg_bug28502_bu;
+drop trigger if exists trg_bug28502_au;
+drop trigger if exists trg_bug28502_bd;
+drop trigger if exists trg_bug28502_ad;
+--enable_warnings
+create table t1 (id int primary key auto_increment, operation varchar(255));
+create table t2 (id int primary key);
+create table t1_op_log(operation varchar(255));
+create view v1 as select * from t1;
+create trigger trg_bug28502_bi before insert on t1
+for each row
+  insert into t1_op_log (operation)
+  values (concat("Before INSERT, new=", new.operation));
+
+create trigger trg_bug28502_ai after insert on t1
+for each row
+  insert into t1_op_log (operation)
+  values (concat("After INSERT, new=", new.operation));
+
+create trigger trg_bug28502_bu before update on t1
+for each row
+  insert into t1_op_log (operation)
+  values (concat("Before UPDATE, new=", new.operation,
+                 ", old=", old.operation));
+
+create trigger trg_bug28502_au after update on t1
+for each row
+  insert into t1_op_log (operation)
+  values (concat("After UPDATE, new=", new.operation,
+                 ", old=", old.operation));
+
+create trigger trg_bug28502_bd before delete on t1
+for each row
+  insert into t1_op_log (operation)
+  values (concat("Before DELETE, old=", old.operation));
+
+create trigger trg_bug28502_ad after delete on t1
+for each row
+  insert into t1_op_log (operation)
+  values (concat("After DELETE, old=", old.operation));
+
+insert into t1 (operation) values ("INSERT");
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+update t1 set operation="UPDATE" where id=@id;
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+delete from t1 where id=@id;
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+insert into t1 (id, operation) values
+(NULL, "INSERT ON DUPLICATE KEY UPDATE, inserting a new key")
+on duplicate key update id=NULL, operation="Should never happen";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+insert into t1 (id, operation) values
+(@id, "INSERT ON DUPLICATE KEY UPDATE, the key value is the same")
+on duplicate key update id=NULL,
+operation="INSERT ON DUPLICATE KEY UPDATE, updating the duplicate";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+replace into t1 values (NULL, "REPLACE, inserting a new key");
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+replace into t1 values (@id, "REPLACE, deleting the duplicate");
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+create table if not exists t1
+select NULL, "CREATE TABLE ... SELECT, inserting a new key";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+create table if not exists t1 replace
+select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+insert into t1 (id, operation)
+select NULL, "INSERT ... SELECT, inserting a new key";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+insert into t1 (id, operation)
+select @id,
+"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
+on duplicate key update id=NULL,
+operation="INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+replace into t1 (id, operation)
+select NULL, "REPLACE ... SELECT, inserting a new key";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+replace into t1 (id, operation)
+select @id, "REPLACE ... SELECT, deleting a duplicate";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+insert into t1 (id, operation) values (1, "INSERT for multi-DELETE");
+insert into t2 (id) values (1);
 
+delete t1.*, t2.* from t1, t2 where t1.id=1;
+
+select * from t1;
+select * from t2;
+select * from t1_op_log;
+truncate t1;
+truncate t2;
+truncate t1_op_log;
+
+insert into t1 (id, operation) values (1, "INSERT for multi-UPDATE");
+insert into t2 (id) values (1);
+update t1, t2 set t1.id=2, operation="multi-UPDATE" where t1.id=1;
+update t1, t2
+set t2.id=3, operation="multi-UPDATE, SET for t2, but the trigger is fired" where t1.id=2;
+
+select * from t1;
+select * from t2;
+select * from t1_op_log;
+truncate table t1;
+truncate table t2;
+truncate table t1_op_log;
+
+--echo
+--echo Now do the same but use a view instead of the base table.
+--echo
+
+insert into v1 (operation) values ("INSERT");
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+update v1 set operation="UPDATE" where id=@id;
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+delete from v1 where id=@id;
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+insert into v1 (id, operation) values
+(NULL, "INSERT ON DUPLICATE KEY UPDATE, inserting a new key")
+on duplicate key update id=NULL, operation="Should never happen";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+insert into v1 (id, operation) values
+(@id, "INSERT ON DUPLICATE KEY UPDATE, the key value is the same")
+on duplicate key update id=NULL,
+operation="INSERT ON DUPLICATE KEY UPDATE, updating the duplicate";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+replace into v1 values (NULL, "REPLACE, inserting a new key");
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+replace into v1 values (@id, "REPLACE, deleting the duplicate");
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+create table if not exists v1
+select NULL, "CREATE TABLE ... SELECT, inserting a new key";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+create table if not exists v1 replace
+select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+insert into v1 (id, operation)
+select NULL, "INSERT ... SELECT, inserting a new key";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+insert into v1 (id, operation)
+select @id,
+"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
+on duplicate key update id=NULL,
+operation="INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+replace into v1 (id, operation)
+select NULL, "REPLACE ... SELECT, inserting a new key";
+
+set @id=last_insert_id();
+
+select * from t1;
+select * from t1_op_log;
+truncate t1_op_log;
+
+replace into v1 (id, operation)
+select @id, "REPLACE ... SELECT, deleting a duplicate";
+
+select * from t1;
+select * from t1_op_log;
+truncate t1;
+truncate t1_op_log;
+
+insert into v1 (id, operation) values (1, "INSERT for multi-DELETE");
+insert into t2 (id) values (1);
+
+delete v1.*, t2.* from v1, t2 where v1.id=1;
+
+select * from t1;
+select * from t2;
+select * from t1_op_log;
+truncate t1;
+truncate t2;
+truncate t1_op_log;
+
+insert into v1 (id, operation) values (1, "INSERT for multi-UPDATE");
+insert into t2 (id) values (1);
+update v1, t2 set v1.id=2, operation="multi-UPDATE" where v1.id=1;
+update v1, t2
+set t2.id=3, operation="multi-UPDATE, SET for t2, but the trigger is fired" where v1.id=2;
+
+select * from t1;
+select * from t2;
+select * from t1_op_log;
+
+drop view v1;
+drop table t1, t2, t1_op_log;
+
+#
+# TODO: test LOAD DATA INFILE
 --echo End of 5.0 tests
diff --git a/sql/item.h b/sql/item.h
index 58e3ec439b463173836df9cede641dbc7b6ebcd0..07bf7fec0ea5aefde072272dd027ae1f419595a7 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2309,14 +2309,6 @@ enum trg_action_time_type
   TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
 };
 
-/*
-  Event on which trigger is invoked.
-*/
-enum trg_event_type
-{
-  TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2, TRG_EVENT_MAX
-};
-
 class Table_triggers_list;
 
 /*
diff --git a/sql/sp.cc b/sql/sp.cc
index 3c8ebed4ae6a9cfeba925c58c49d0b26847b56b0..c0e7d5e22713e8790e75799563d6a997078300b5 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -440,6 +440,19 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
     lex_start(thd);
     thd->spcont= NULL;
     ret= MYSQLparse(thd);
+
+    if (ret == 0)
+    {
+      /*
+        Not strictly necessary to invoke this method here, since we know
+        that we've parsed CREATE PROCEDURE/FUNCTION and not an
+        UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+        maintain the invariant that this method is called for each
+        distinct statement, in case its logic is extended with other
+        types of analyses in future.
+      */
+      newlex.set_trg_event_type_for_tables();
+    }
   }
 
   if (ret || thd->is_fatal_error || newlex.sphead == NULL)
@@ -1742,31 +1755,39 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
                                               TABLE_LIST *table)
 {
   int ret= 0;
-  Table_triggers_list *triggers= table->table->triggers;
-  if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key,
-                       table->belong_to_view))
+
+  Sroutine_hash_entry **last_cached_routine_ptr=
+    (Sroutine_hash_entry **)lex->sroutines_list.next;
+
+  if (static_cast<int>(table->lock_type) >=
+      static_cast<int>(TL_WRITE_ALLOW_WRITE))
   {
-    Sroutine_hash_entry **last_cached_routine_ptr=
-                            (Sroutine_hash_entry **)lex->sroutines_list.next;
     for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
     {
-      for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
+      if (table->trg_event_map &
+          static_cast<uint8>(1 << static_cast<int>(i)))
       {
-        if (triggers->bodies[i][j])
+        for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
         {
-          (void)triggers->bodies[i][j]->
-                add_used_tables_to_table_list(thd, &lex->query_tables_last,
-                                              table->belong_to_view);
-          sp_update_stmt_used_routines(thd, lex,
-                                       &triggers->bodies[i][j]->m_sroutines,
-                                       table->belong_to_view);
+          /* We can have only one trigger per action type currently */
+          sp_head *trigger= table->table->triggers->bodies[i][j];
+          if (trigger &&
+              add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key,
+                               table->belong_to_view))
+          {
+            trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last,
+                                                   table->belong_to_view);
+            sp_update_stmt_used_routines(thd, lex,
+                                         &trigger->m_sroutines,
+                                         table->belong_to_view);
+          }
         }
       }
     }
-    ret= sp_cache_routines_and_add_tables_aux(thd, lex,
-                                              *last_cached_routine_ptr, 
-                                              FALSE, NULL);
   }
+  ret= sp_cache_routines_and_add_tables_aux(thd, lex,
+                                            *last_cached_routine_ptr,
+                                            FALSE, NULL);
   return ret;
 }
 
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index d939fd20b9b5202af1e471078d074e337ce82af0..0ac1db336d0d4b14c541f22a6311476ac44c48c3 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -478,12 +478,35 @@ sp_head::init(LEX *lex)
   */
   lex->trg_table_fields.empty();
   my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
-  m_param_begin= m_param_end= m_body_begin= 0;
-  m_qname.str= m_db.str= m_name.str= m_params.str=
-    m_body.str= m_defstr.str= 0;
-  m_qname.length= m_db.length= m_name.length= m_params.length=
-    m_body.length= m_defstr.length= 0;
+
+  m_param_begin= NULL;
+  m_param_end= NULL;
+
+  m_body_begin= NULL ;
+
+  m_qname.str= NULL;
+  m_qname.length= 0;
+
+  m_db.str= NULL;
+  m_db.length= 0;
+
+  m_name.str= NULL;
+  m_name.length= 0;
+
+  m_params.str= NULL;
+  m_params.length= 0;
+
+  m_body.str= NULL;
+  m_body.length= 0;
+
+  m_defstr.str= NULL;
+  m_defstr.length= 0;
+
+  m_sroutines_key.str= NULL;
+  m_sroutines_key.length= 0;
+
   m_return_field_def.charset= NULL;
+
   DBUG_VOID_RETURN;
 }
 
@@ -509,9 +532,14 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
   if (spname->m_qname.length == 0)
     spname->init_qname(thd);
 
-  m_qname.length= spname->m_qname.length;
-  m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str,
-                            m_qname.length);
+  m_sroutines_key.length= spname->m_sroutines_key.length;
+  m_sroutines_key.str= memdup_root(thd->mem_root,
+                                   spname->m_sroutines_key.str,
+                                   spname->m_sroutines_key.length + 1);
+  m_sroutines_key.str[0]= static_cast<char>(m_type);
+
+  m_qname.length= m_sroutines_key.length - 1;
+  m_qname.str= m_sroutines_key.str + 1;
 
   DBUG_VOID_RETURN;
 }
@@ -1796,8 +1824,11 @@ sp_head::restore_lex(THD *thd)
 {
   DBUG_ENTER("sp_head::restore_lex");
   LEX *sublex= thd->lex;
-  LEX *oldlex= (LEX *)m_lex.pop();
+  LEX *oldlex;
+
+  sublex->set_trg_event_type_for_tables();
 
+  oldlex= (LEX *)m_lex.pop();
   if (! oldlex)
     return;			// Nothing to restore
 
@@ -3429,6 +3460,7 @@ typedef struct st_sp_table
   thr_lock_type lock_type; /* lock type used for prelocking */
   uint lock_count;
   uint query_lock_count;
+  uint8 trg_event_map;
 } SP_TABLE;
 
 byte *
@@ -3515,6 +3547,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
         tab->query_lock_count++;
         if (tab->query_lock_count > tab->lock_count)
           tab->lock_count++;
+        tab->trg_event_map|= table->trg_event_map;
       }
       else
       {
@@ -3536,6 +3569,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
         tab->db_length= table->db_length;
         tab->lock_type= table->lock_type;
         tab->lock_count= tab->query_lock_count= 1;
+        tab->trg_event_map= table->trg_event_map;
 	my_hash_insert(&m_sptabs, (byte *)tab);
       }
     }
@@ -3613,6 +3647,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
       table->cacheable_table= 1;
       table->prelocking_placeholder= 1;
       table->belong_to_view= belong_to_view;
+      table->trg_event_map= stab->trg_event_map;
 
       /* Everyting else should be zeroed */
 
diff --git a/sql/sp_head.h b/sql/sp_head.h
index ed99885ae9ae8ee1198b9599ada7674ddadba9a5..ebe40ce9c87210e0a47aeaeee19f7ec3ceeb3f64 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -130,6 +130,12 @@ class sp_head :private Query_arena
   st_sp_chistics *m_chistics;
   ulong m_sql_mode;		// For SHOW CREATE and execution
   LEX_STRING m_qname;		// db.name
+  /**
+    Key representing routine in the set of stored routines used by statement.
+    [routine_type]db.name\0
+    @sa sp_name::m_sroutines_key
+  */
+  LEX_STRING m_sroutines_key;
   LEX_STRING m_db;
   LEX_STRING m_name;
   LEX_STRING m_params;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index dbce1e381399d03e814029469aa14f6978b62e5d..c37d77345b65bfb001020c054b8f45fc2a8ab4db 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2034,6 +2034,27 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl)
 }
 
 
+/**
+  Update the parsed tree with information about triggers that
+  may be fired when executing this statement.
+*/
+
+void st_lex::set_trg_event_type_for_tables()
+{
+  /*
+    Do not iterate over sub-selects, only the tables in the outermost
+    SELECT_LEX can be modified, if any.
+  */
+  TABLE_LIST *tables= select_lex.get_table_list();
+
+  while (tables)
+  {
+    tables->set_trg_event_type(this);
+    tables= tables->next_local;
+  }
+}
+
+
 /*
   Unlink the first table from the global table list and the first table from
   outer select (lex->select_lex) local list
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 25a6c31e21cb4c2b615de17c41af2ee0f538c658..bfa6c05974f49b0d18f3b8102cc91d2247e32875 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1188,6 +1188,8 @@ typedef struct st_lex : public Query_tables_list
       un->uncacheable|= cause;
     }
   }
+  void set_trg_event_type_for_tables();
+
   TABLE_LIST *unlink_first_table(bool *link_to_local);
   void link_first_table_back(TABLE_LIST *first, bool link_to_local);
   void first_lists_tables_same();
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 124fcff951768d1835a832ea0e9e2c8f36840134..91c51641fc0d4f08de81003d736fe76a0395feec 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6080,8 +6080,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
               (thd->query_length= (ulong)(lip.found_semicolon - thd->query)))
             thd->query_length--;
           /* Actually execute the query */
-	  mysql_execute_command(thd);
-	  query_cache_end_of_result(thd);
+          lex->set_trg_event_type_for_tables();
+          mysql_execute_command(thd);
+          query_cache_end_of_result(thd);
 	}
       }
     }
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 567f92b55ba625d1dbb85d788027b767134c1d2d..c993ce32e50ec3e2c1d6c5bb5cd16deaf45062c3 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2826,6 +2826,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
   lex_start(thd);
   lex->safe_to_cache_query= FALSE;
   int err= MYSQLparse((void *)thd);
+  lex->set_trg_event_type_for_tables();
 
   error= err || thd->is_fatal_error ||
       thd->net.report_error || init_param_array(this);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 5762614e47fd2ca436c56dfbd6b81f6f29709394..6e4b5defb970aadfdc9b683f844b56d13b3e5b3e 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -942,17 +942,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
 
       table->triggers= triggers;
 
-      /*
-        Construct key that will represent triggers for this table in the set
-        of routines used by statement.
-      */
-      triggers->sroutines_key.length= 1+strlen(db)+1+strlen(table_name)+1;
-      if (!(triggers->sroutines_key.str=
-              alloc_root(&table->mem_root, triggers->sroutines_key.length)))
-        DBUG_RETURN(1);
-      triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER;
-      strxmov(triggers->sroutines_key.str+1, db, ".", table_name, NullS);
-
       /*
         TODO: This could be avoided if there is no triggers
               for UPDATE and DELETE.
@@ -991,6 +980,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
           DBUG_ASSERT(lex.sphead == 0);
           goto err_with_lex_cleanup;
         }
+        /*
+          Not strictly necessary to invoke this method here, since we know
+          that we've parsed CREATE TRIGGER and not an
+          UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+          maintain the invariant that this method is called for each
+          distinct statement, in case its logic is extended with other
+          types of analyses in future.
+        */
+        lex.set_trg_event_type_for_tables();
 
         lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
 
@@ -1550,6 +1548,12 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
       new_field= record1_field;
       old_field= trigger_table->field;
     }
+    /*
+      This trigger must have been processed by the pre-locking
+      algorithm.
+    */
+    DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
+                static_cast<uint>(1 << static_cast<int>(event)));
 
     thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
     err_status= sp_trigger->execute_trigger
@@ -1568,7 +1572,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
   SYNOPSIS
     mark_fields_used()
       thd    Current thread context
-      event  Type of event triggers for which we are going to inspect
+      event  Type of event triggers for which we are going to ins
 
   DESCRIPTION
     This method marks fields of subject table which are read/set in its
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index b029a70ca2008e9bda45f84e357f34c2220c6ff6..1dc573995f12d80d5d19be47397ecf2394d94357 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -56,14 +56,6 @@ class Table_triggers_list: public Sql_alloc
     updating trigger definitions during RENAME TABLE.
   */
   List<LEX_STRING>  on_table_names_list;
-  /*
-    Key representing triggers for this table in set of all stored
-    routines used by statement.
-    TODO: We won't need this member once triggers namespace will be
-    database-wide instead of table-wide because then we will be able
-    to use key based on sp_name as for other stored routines.
-  */
-  LEX_STRING        sroutines_key;
 
   /*
     Grant information for each trigger (pair: subject table, trigger definer).
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 6c94d388d0ee0a3fb4bbf120f4ac85bc6f40e2bf..7857ba267c5bfeb85db1078bc6db2c9f574c1dce 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1153,7 +1153,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
       */
       for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
         tbl->lock_type= table->lock_type;
+      /*
+        If the view is mergeable, we might want to
+        INSERT/UPDATE/DELETE into tables of this view. Preserve the
+        original sql command and 'duplicates' of the outer lex.
+        This is used later in set_trg_event_type_for_command.
+      */
+      lex->sql_command= old_lex->sql_command;
+      lex->duplicates= old_lex->duplicates;
     }
+    /*
+      This method has a dependency on the proper lock type being set,
+      so in case of views should be called here.
+    */
+    lex->set_trg_event_type_for_tables();
 
     /*
       If we are opening this view as part of implicit LOCK TABLES, then
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 949f3ed4161398a68246b297a7b27078cefabc23..8cc6642ae7ec605e45f6e3180fb17cdfbaa52609 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -9644,13 +9644,13 @@ trigger_tail:
 	    MYSQL_YYABORT;
 	  sp->reset_thd_mem_root(thd);
 	  sp->init(lex);
+	  sp->m_type= TYPE_ENUM_TRIGGER;
           sp->init_sp_name(thd, $3);
 	
 	  lex->stmt_definition_begin= $2;
           lex->ident.str= $7;
           lex->ident.length= $10 - $7;
 
-	  sp->m_type= TYPE_ENUM_TRIGGER;
 	  lex->sphead= sp;
 	  lex->spname= $3;
 	  /*
@@ -9728,9 +9728,9 @@ sp_tail:
 	  sp= new sp_head();
 	  sp->reset_thd_mem_root(YYTHD);
 	  sp->init(lex);
+	  sp->m_type= TYPE_ENUM_PROCEDURE;
           sp->init_sp_name(YYTHD, $3);
 
-	  sp->m_type= TYPE_ENUM_PROCEDURE;
 	  lex->sphead= sp;
 	  /*
 	   * We have to turn of CLIENT_MULTI_QUERIES while parsing a
diff --git a/sql/table.cc b/sql/table.cc
index 899d0ab2ed051cb8cffa2ac7f72037004bd46d24..9c3e7618aa03d4fb0d333588656abf6e201cf6e6 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1776,6 +1776,135 @@ void st_table::reset_item_list(List<Item> *item_list) const
   }
 }
 
+
+/**
+  Set the initial purpose of this TABLE_LIST object in the list of
+  used tables. We need to track this information on table-by-
+  table basis, since when this table becomes an element of the
+  pre-locked list, it's impossible to identify which SQL
+  sub-statement it has been originally used in.
+
+  E.g.:
+
+  User request:                 SELECT * FROM t1 WHERE f1();
+  FUNCTION f1():                DELETE FROM t2; RETURN 1;
+  BEFORE DELETE trigger on t2:  INSERT INTO t3 VALUES (old.a);
+
+  For this user request, the pre-locked list will contain t1, t2, t3
+  table elements, each needed for different DML.
+
+  This method is called immediately after parsing for tables
+  of the table list of the top-level select lex.
+
+  The trigger event map is updated to reflect INSERT, UPDATE, DELETE,
+  REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE ..
+  REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE
+  clause.
+*/
+
+void
+TABLE_LIST::set_trg_event_type(const st_lex *lex)
+{
+  enum trg_event_type trg_event;
+
+  /*
+    Some auxiliary operations
+    (e.g. GRANT processing) create TABLE_LIST instances outside
+    the parser. Additionally, some commands (e.g. OPTIMIZE) change
+    the lock type for a table only after parsing is done. Luckily,
+    these do not fire triggers and do not need to pre-load them.
+    For these TABLE_LISTs set_trg_event_type is never called, and
+    trg_event_map is always empty. That means that the pre-locking
+    algorithm will ignore triggers defined on these tables, if
+    any, and the execution will either fail with an assert in
+    sql_trigger.cc or with an error that a used table was not
+    pre-locked, in case of a production build.
+
+    TODO: this usage pattern creates unnecessary module dependencies
+    and should be rewritten to go through the parser.
+    Table list instances created outside the parser in most cases
+    refer to mysql.* system tables. It is not allowed to have
+    a trigger on a system table, but keeping track of
+    initialization provides extra safety in case this limitation
+    is circumvented.
+  */
+
+  /*
+    This is a fast check to filter out statements that do
+    not change data, or tables  on the right side, in case of
+    INSERT .. SELECT, CREATE TABLE .. SELECT and so on.
+    Here we also filter out OPTIMIZE statement and non-updateable
+    views, for which lock_type is TL_UNLOCK or TL_READ after
+    parsing.
+  */
+  if (static_cast<int>(lock_type) < static_cast<int>(TL_WRITE_ALLOW_WRITE))
+    return;
+
+  switch (lex->sql_command) {
+  /*
+    Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE
+    clause, it will be handled later in this method.
+  */
+  case SQLCOM_INSERT:                           /* fall through */
+  case SQLCOM_INSERT_SELECT:
+  /*
+    LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT
+    triggers.
+    If the statement also has REPLACE clause, it will be
+    handled later in this method.
+  */
+  case SQLCOM_LOAD:                             /* fall through */
+  /*
+    REPLACE is semantically equivalent to INSERT. In case
+    of a primary or unique key conflict, it deletes the old
+    record and inserts a new one. So we also may need to
+    fire ON DELETE triggers. This functionality is handled
+    later in this method.
+  */
+  case SQLCOM_REPLACE:                          /* fall through */
+  case SQLCOM_REPLACE_SELECT:
+  /*
+    CREATE TABLE ... SELECT defaults to INSERT if the table or
+    view already exists. REPLACE option of CREATE TABLE ...
+    REPLACE SELECT is handled later in this method.
+  */
+  case SQLCOM_CREATE_TABLE:
+    trg_event= TRG_EVENT_INSERT;
+    break;
+  /* Basic update and multi-update */
+  case SQLCOM_UPDATE:                           /* fall through */
+  case SQLCOM_UPDATE_MULTI:
+    trg_event= TRG_EVENT_UPDATE;
+    break;
+  /* Basic delete and multi-delete */
+  case SQLCOM_DELETE:                           /* fall through */
+  case SQLCOM_DELETE_MULTI:
+    trg_event= TRG_EVENT_DELETE;
+    break;
+  default:
+    /*
+      OK to return, since value of 'duplicates' is irrelevant
+      for non-updating commands.
+    */
+    return;
+  }
+  trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event));
+
+  switch (lex->duplicates) {
+  case DUP_UPDATE:
+    trg_event= TRG_EVENT_UPDATE;
+    break;
+  case DUP_REPLACE:
+    trg_event= TRG_EVENT_DELETE;
+    break;
+  case DUP_ERROR:
+  default:
+    return;
+  }
+  trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event));
+}
+
+
 /*
   calculate md5 of query
 
diff --git a/sql/table.h b/sql/table.h
index b29ef8c6566104242e6e3048c54afdb7a2d40978..f8f7d7f06b75f848ce56e90a884367b532836aea 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -59,6 +59,17 @@ enum tmp_table_type {NO_TMP_TABLE=0,
                      NON_TRANSACTIONAL_TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2,
                      SYSTEM_TMP_TABLE=3};
 
+
+/** Event on which trigger is invoked. */
+enum trg_event_type
+{
+  TRG_EVENT_INSERT= 0,
+  TRG_EVENT_UPDATE= 1,
+  TRG_EVENT_DELETE= 2,
+  TRG_EVENT_MAX
+};
+
+
 enum frm_type_enum
 {
   FRMTYPE_ERROR= 0,
@@ -702,6 +713,13 @@ struct TABLE_LIST
   */
   bool          create;
 
+  /**
+    Indicates what triggers we need to pre-load for this TABLE_LIST
+    when opening an associated TABLE. This is filled after
+    the parsed tree is created.
+  */
+  uint8 trg_event_map;
+
   enum enum_schema_table_state schema_table_state;
   void calc_md5(char *buffer);
   void set_underlying_merge();
@@ -752,6 +770,7 @@ struct TABLE_LIST
   void reinit_before_use(THD *thd);
   Item_subselect *containing_subselect();
 
+  void set_trg_event_type(const st_lex *lex);
 private:
   bool prep_check_option(THD *thd, uint8 check_opt_type);
   bool prep_where(THD *thd, Item **conds, bool no_where_clause);