diff --git a/mysql-test/r/rpl_ddl.result b/mysql-test/r/rpl_ddl.result
index 3737883b694e61759e1b655c5d5b19d005c81661..9f90001ab67475dc1e8befd079caf97b4a29f3d2 100644
--- a/mysql-test/r/rpl_ddl.result
+++ b/mysql-test/r/rpl_ddl.result
@@ -1072,6 +1072,188 @@ Database (mysqltest3)
 mysqltest3
 
 -------- switch to master -------
+
+######## CREATE PROCEDURE p1() READS SQL DATA SELECT "this is p1"  ########
+
+-------- switch to master -------
+INSERT INTO t1 SET f1= 15 + 1;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+16
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+15
+
+-------- switch to master -------
+CREATE PROCEDURE p1() READS SQL DATA SELECT "this is p1";
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+16
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+16
+
+-------- switch to master -------
+ROLLBACK;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+16
+
+TEST-INFO: MASTER: The INSERT is committed (Succeeded)
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+16
+
+TEST-INFO: SLAVE:  The INSERT is committed (Succeeded)
+
+-------- switch to master -------
+flush logs;
+
+-------- switch to slave --------
+flush logs;
+
+-------- switch to master -------
+SHOW PROCEDURE STATUS LIKE 'p1';
+Db	mysqltest1
+Name	p1
+Type	PROCEDURE
+Definer	root@localhost
+Modified	#
+Created	#
+Security_type	DEFINER
+Comment	
+	-------- switch to slave -------
+SHOW PROCEDURE STATUS LIKE 'p1';
+Db	mysqltest1
+Name	p1
+Type	PROCEDURE
+Definer	@
+Modified	#
+Created	#
+Security_type	DEFINER
+Comment	
+
+######## ALTER PROCEDURE p1 COMMENT "I have been altered"  ########
+
+-------- switch to master -------
+INSERT INTO t1 SET f1= 16 + 1;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+17
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+16
+
+-------- switch to master -------
+ALTER PROCEDURE p1 COMMENT "I have been altered";
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+17
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+17
+
+-------- switch to master -------
+ROLLBACK;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+17
+
+TEST-INFO: MASTER: The INSERT is committed (Succeeded)
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+17
+
+TEST-INFO: SLAVE:  The INSERT is committed (Succeeded)
+
+-------- switch to master -------
+flush logs;
+
+-------- switch to slave --------
+flush logs;
+
+-------- switch to master -------
+SHOW PROCEDURE STATUS LIKE 'p1';
+Db	mysqltest1
+Name	p1
+Type	PROCEDURE
+Definer	root@localhost
+Modified	#
+Created	#
+Security_type	DEFINER
+Comment	I have been altered
+	-------- switch to slave -------
+SHOW PROCEDURE STATUS LIKE 'p1';
+Db	mysqltest1
+Name	p1
+Type	PROCEDURE
+Definer	@
+Modified	#
+Created	#
+Security_type	DEFINER
+Comment	I have been altered
+
+######## DROP PROCEDURE p1  ########
+
+-------- switch to master -------
+INSERT INTO t1 SET f1= 17 + 1;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+18
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+17
+
+-------- switch to master -------
+DROP PROCEDURE p1;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+18
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+18
+
+-------- switch to master -------
+ROLLBACK;
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+18
+
+TEST-INFO: MASTER: The INSERT is committed (Succeeded)
+
+-------- switch to slave --------
+SELECT MAX(f1) FROM t1;
+MAX(f1)
+18
+
+TEST-INFO: SLAVE:  The INSERT is committed (Succeeded)
+
+-------- switch to master -------
+flush logs;
+
+-------- switch to slave --------
+flush logs;
+
+-------- switch to master -------
+SHOW PROCEDURE STATUS LIKE 'p1';
+	-------- switch to slave -------
+SHOW PROCEDURE STATUS LIKE 'p1';
 DROP DATABASE IF EXISTS mysqltest1;
 DROP DATABASE IF EXISTS mysqltest2;
 DROP DATABASE IF EXISTS mysqltest3;
diff --git a/mysql-test/r/rpl_view.result b/mysql-test/r/rpl_view.result
index 0b8f6fdd7e88898be1e3e16817fddff709dea737..cf4c161b29643923e545ccf9318dd99ef66677a6 100644
--- a/mysql-test/r/rpl_view.result
+++ b/mysql-test/r/rpl_view.result
@@ -43,9 +43,8 @@ drop view v1;
 select * from v1 order by a;
 ERROR 42S02: Table 'test.v1' doesn't exist
 drop table t1;
-show binlog events;
+show binlog events limit 1,100;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
-slave-bin.000001	#	Format_desc	2	#	Server ver: 5.0.13-beta-debug-log, Binlog ver: 4
 slave-bin.000001	#	Query	1	#	use `test`; create table t1 (a int)
 slave-bin.000001	#	Query	1	#	use `test`; insert into t1 values (1)
 slave-bin.000001	#	Query	1	#	use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=root@localhost SQL SECURITY DEFINER VIEW v1 AS select a from t1
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index 1bf974210600397adc19c671df841aadb500ce9a..4cb3db2e4aa9b8027f7ab8c26796703efa6d24fb 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -2210,15 +2210,15 @@ INSERT INTO  t1 VALUES ('2005-09-06');
 CREATE VIEW v1 AS SELECT DAYNAME(date) FROM t1;
 SHOW CREATE VIEW v1;
 View	Create View
-v1	CREATE ALGORITHM=UNDEFINED VIEW `v1` AS select dayname(`t1`.`date`) AS `DAYNAME(date)` from `t1`
+v1	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select dayname(`t1`.`date`) AS `DAYNAME(date)` from `t1`
 CREATE VIEW v2 AS SELECT DAYOFWEEK(date) FROM t1;
 SHOW CREATE VIEW v2;
 View	Create View
-v2	CREATE ALGORITHM=UNDEFINED VIEW `v2` AS select dayofweek(`t1`.`date`) AS `DAYOFWEEK(date)` from `t1`
+v2	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select dayofweek(`t1`.`date`) AS `DAYOFWEEK(date)` from `t1`
 CREATE VIEW v3 AS SELECT WEEKDAY(date) FROM t1;
 SHOW CREATE VIEW v3;
 View	Create View
-v3	CREATE ALGORITHM=UNDEFINED VIEW `v3` AS select weekday(`t1`.`date`) AS `WEEKDAY(date)` from `t1`
+v3	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select weekday(`t1`.`date`) AS `WEEKDAY(date)` from `t1`
 SELECT DAYNAME('2005-09-06');
 DAYNAME('2005-09-06')
 Tuesday
diff --git a/mysql-test/t/rpl_ddl.test b/mysql-test/t/rpl_ddl.test
index 9521ba3d4c1a664390ea9465b0317005bc303633..60a00a7b1b4b2b4f3d236240d1e05d049a595d1f 100644
--- a/mysql-test/t/rpl_ddl.test
+++ b/mysql-test/t/rpl_ddl.test
@@ -340,6 +340,57 @@ connection master;
 SELECT '-------- switch to master -------' as "";
 --enable_query_log
 
+# End of 4.1 tests
+
+###############################################################
+# Cases with stored procedures
+###############################################################
+let $my_stmt= CREATE PROCEDURE p1() READS SQL DATA SELECT "this is p1";
+let $my_master_commit= true;
+let $my_slave_commit= true;
+--source include/rpl_stmt_seq.inc
+--vertical_results
+--replace_column 5 # 6 #
+SHOW PROCEDURE STATUS LIKE 'p1';
+--disable_query_log
+SELECT '-------- switch to slave -------' as "";
+--enable_query_log
+connection slave;
+--replace_column 5 # 6 #
+SHOW PROCEDURE STATUS LIKE 'p1';
+connection master;
+--horizontal_results
+
+let $my_stmt= ALTER PROCEDURE p1 COMMENT "I have been altered";
+let $my_master_commit= true;
+let $my_slave_commit= true;
+--source include/rpl_stmt_seq.inc
+--vertical_results
+--replace_column 5 # 6 #
+SHOW PROCEDURE STATUS LIKE 'p1';
+--disable_query_log
+SELECT '-------- switch to slave -------' as "";
+--enable_query_log
+connection slave;
+--replace_column 5 # 6 #
+SHOW PROCEDURE STATUS LIKE 'p1';
+connection master;
+--horizontal_results
+
+let $my_stmt= DROP PROCEDURE p1;
+let $my_master_commit= true;
+let $my_slave_commit= true;
+--source include/rpl_stmt_seq.inc
+--vertical_results
+SHOW PROCEDURE STATUS LIKE 'p1';
+--disable_query_log
+SELECT '-------- switch to slave -------' as "";
+--enable_query_log
+connection slave;
+SHOW PROCEDURE STATUS LIKE 'p1';
+connection master;
+--horizontal_results
+
 ###############################################################
 # Cleanup
 ###############################################################
@@ -349,4 +400,4 @@ DROP DATABASE IF EXISTS mysqltest2;
 DROP DATABASE IF EXISTS mysqltest3;
 --enable_warnings
 
-# End of 4.1 tests
+
diff --git a/mysql-test/t/rpl_view.test b/mysql-test/t/rpl_view.test
index 7910aa822046da66880118c381b3672f262ef765..0a0c6a6dddbcc9a0619469671745d0e03eaefe13 100644
--- a/mysql-test/t/rpl_view.test
+++ b/mysql-test/t/rpl_view.test
@@ -44,4 +44,4 @@ connection master;
 drop table t1;
 sync_slave_with_master;
 --replace_column 2 # 5 #
-show binlog events;
+show binlog events limit 1,100;
diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test
index a770782e5db551b2b8725ce88c02a144d3ed08ad..cd79eb82ace21063bcc43feb9d9ab05675b647b5 100644
--- a/mysql-test/t/trigger.test
+++ b/mysql-test/t/trigger.test
@@ -10,6 +10,11 @@ drop function if exists f1;
 drop procedure if exists p1;
 --enable_warnings
 
+# Create additional connections used through test
+connect (addconroot1, localhost, root,,);
+connect (addconroot2, localhost, root,,);
+connection default;
+
 create table t1 (i int);
 
 # let us test some very simple trigger
@@ -680,12 +685,10 @@ end|
 delimiter ;|
 update t1 set data = 1;
 
-connect (addconroot, localhost, root,,);
-connection addconroot;
+connection addconroot1;
 update t1 set data = 2;
 
 connection default;
-disconnect addconroot;
 drop table t1;
 
 #
@@ -765,3 +768,110 @@ insert into t1 values (3);
 select * from t1;
 drop trigger t1_bi;
 drop tables t1, t2;
+
+# Tests for bug #12704 "Server crashes during trigger execution".
+# If we run DML statements and CREATE TRIGGER statements concurrently
+# it may happen that trigger will be created while DML statement is
+# waiting for table lock. In this case we have to reopen tables and
+# recalculate prelocking set.
+# Unfortunately these tests rely on the order in which tables are locked
+# by statement so they are non determenistic and are disabled.
+--disable_parsing
+create table t1 (id int);
+create table t2 (id int);
+create table t3 (id int);
+create function f1() returns int return (select max(id)+2 from t2);
+create view v1 as select f1() as f;
+
+# Let us check that we notice trigger at all
+connection addconroot1;
+lock tables t2 write;
+connection default;
+send insert into t1 values ((select max(id) from t2)), (2);
+--sleep 1
+connection addconroot2;
+create trigger t1_trg before insert on t1 for each row set NEW.id:= 1;
+connection addconroot1;
+unlock tables;
+connection default;
+reap;
+select * from t1;
+
+# Check that we properly calculate new prelocking set
+insert into t2 values (3);
+connection addconroot1;
+lock tables t2 write;
+connection default;
+send insert into t1 values ((select max(id) from t2)), (4);
+--sleep 1
+connection addconroot2;
+drop trigger t1_trg;
+create trigger t1_trg before insert on t1 for each row
+  insert into t3 values (new.id);
+connection addconroot1;
+unlock tables;
+connection default;
+reap;
+select * from t1;
+select * from t3;
+
+# We should be able to do this even if fancy views are involved
+connection addconroot1;
+lock tables t2 write;
+connection default;
+send insert into t1 values ((select max(f) from v1)), (6);
+--sleep 1
+connection addconroot2;
+drop trigger t1_trg;
+create trigger t1_trg before insert on t1 for each row
+  insert into t3 values (new.id + 100);
+connection addconroot1;
+unlock tables;
+connection default;
+reap;
+select * from t1;
+select * from t3;
+
+# This also should work for multi-update
+# Let us drop trigger to demonstrate that prelocking set is really
+# rebuilt
+drop trigger t1_trg;
+connection addconroot1;
+lock tables t2 write;
+connection default;
+send update t1, t2 set t1.id=10 where t1.id=t2.id;
+--sleep 1
+connection addconroot2;
+create trigger t1_trg before update on t1 for each row
+  insert into t3 values (new.id);
+connection addconroot1;
+unlock tables;
+connection default;
+reap;
+select * from t1;
+select * from t3;
+
+# And even for multi-update converted from ordinary update thanks to view
+drop view v1;
+drop trigger t1_trg;
+create view v1 as select t1.id as id1 from t1, t2 where t1.id= t2.id;
+insert into t2 values (10);
+connection addconroot1;
+lock tables t2 write;
+connection default;
+send update v1 set id1= 11;
+--sleep 1
+connection addconroot2;
+create trigger t1_trg before update on t1 for each row
+  insert into t3 values (new.id + 100);
+connection addconroot1;
+unlock tables;
+connection default;
+reap;
+select * from t1;
+select * from t3;
+
+drop function f1;
+drop view v1;
+drop table t1, t2, t3;
+--enable_parsing
diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc
index 832246498428f3e2fcf2b184c34bea5e6c7e29a1..2590e7881a4ac86bbc81a2fdb43335a4cf136c77 100644
--- a/sql/ha_federated.cc
+++ b/sql/ha_federated.cc
@@ -522,7 +522,8 @@ static int check_foreign_data_source(
 
 static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num)
 {
-  char buf[table->s->connect_string.length+1];
+  char buf[FEDERATED_QUERY_BUFFER_SIZE];
+  int buf_len;
   DBUG_ENTER("ha_federated parse_url_error");
   if (share->scheme)
   {
@@ -532,9 +533,11 @@ static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num)
     my_free((gptr) share->scheme, MYF(0));
     share->scheme= 0;
   }
-
-  strnmov(buf, table->s->connect_string.str, table->s->connect_string.length+1);
-  buf[table->s->connect_string.length]= '\0';
+  buf_len= (table->s->connect_string.length > (FEDERATED_QUERY_BUFFER_SIZE - 1)) 
+    ? FEDERATED_QUERY_BUFFER_SIZE - 1 : table->s->connect_string.length;
+  
+  strnmov(buf, table->s->connect_string.str, buf_len);
+  buf[buf_len]= '\0';
   my_error(error_num, MYF(0), buf);
   DBUG_RETURN(error_num);
 }
@@ -743,13 +746,12 @@ ha_federated::ha_federated(TABLE *table_arg)
 
 uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row)
 {
-  uint num_fields;
   ulong *lengths;
   Field **field;
 
   DBUG_ENTER("ha_federated::convert_row_to_internal_format");
 
-  num_fields= mysql_num_fields(stored_result);
+  // num_fields= mysql_num_fields(stored_result);
   lengths= mysql_fetch_lengths(stored_result);
 
   memset(record, 0, table->s->null_bytes);
@@ -1115,12 +1117,9 @@ bool ha_federated::create_where_from_key(String *to,
   for (int i= 0; i <= 1; i++)
   {
     bool needs_quotes;
-    uint loop_counter= 0;
     KEY_PART_INFO *key_part;
     if (ranges[i] == NULL)
       continue;
-    const byte *key= ranges[i]->key;
-    uint key_length= ranges[i]->length;
 
     if (both_not_null)
     {
@@ -1435,7 +1434,6 @@ const char **ha_federated::bas_ext() const
 
 int ha_federated::open(const char *name, int mode, uint test_if_locked)
 {
-  int rc;
   DBUG_ENTER("ha_federated::open");
 
   if (!(share= get_share(name, table)))
@@ -1778,7 +1776,6 @@ int ha_federated::update_row(const byte *old_data, byte *new_data)
   /* 
     buffers for following strings
   */
-  char error_buffer[FEDERATED_QUERY_BUFFER_SIZE];
   char old_field_value_buffer[STRING_BUFFER_USUAL_SIZE];
   char new_field_value_buffer[STRING_BUFFER_USUAL_SIZE];
   char update_buffer[FEDERATED_QUERY_BUFFER_SIZE];
@@ -1848,10 +1845,8 @@ int ha_federated::update_row(const byte *old_data, byte *new_data)
       where_string.append(FEDERATED_ISNULL);
     else
     {
-      uint o_len;
       (*field)->val_str(&old_field_value,
                         (char*) (old_data + (*field)->offset()));
-      o_len= (*field)->pack_length();
       (*field)->quote_data(&old_field_value);
       where_string.append(old_field_value);
     }
@@ -1989,8 +1984,6 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key,
   int retval;
   char error_buffer[FEDERATED_QUERY_BUFFER_SIZE];
   char index_value[STRING_BUFFER_USUAL_SIZE];
-  char key_value[STRING_BUFFER_USUAL_SIZE];
-  char test_value[STRING_BUFFER_USUAL_SIZE];
   char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
   String index_string(index_value, 
                       sizeof(index_value),
@@ -2071,7 +2064,6 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key,
 /* Initialized at each key walk (called multiple times unlike rnd_init()) */
 int ha_federated::index_init(uint keynr)
 {
-  int error;
   DBUG_ENTER("ha_federated::index_init");
   DBUG_PRINT("info",
              ("table: '%s'  key: %d", table->s->table_name, keynr));
@@ -2178,10 +2170,6 @@ int ha_federated::index_next(byte *buf)
 
 int ha_federated::rnd_init(bool scan)
 {
-  int num_fields, rows;
-  int retval;
-  char error_buffer[FEDERATED_QUERY_BUFFER_SIZE];
-
   DBUG_ENTER("ha_federated::rnd_init");
   /*
     The use of the 'scan' flag is incredibly important for this handler
@@ -2472,7 +2460,6 @@ void ha_federated::info(uint flag)
     }
     if (flag & HA_STATUS_CONST)
     {
-      TABLE_SHARE *share= table->s;
       block_size= 4096;
     }
   }
diff --git a/sql/lock.cc b/sql/lock.cc
index 941d7baa76e8a18fba5424b2cb21645717c0f4b3..f4c4a781e459442c1ceafc118ddeea19c562fbac 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -93,23 +93,33 @@ static void print_lock_error(int error, const char *);
     flags                       Options:
       MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      Ignore a global read lock
       MYSQL_LOCK_IGNORE_FLUSH                 Ignore a flush tables.
+      MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        Instead of reopening altered
+                                              or dropped tables by itself,
+                                              mysql_lock_tables() should
+                                              notify upper level and rely
+                                              on caller doing this.
+    need_reopen                 Out parameter, TRUE if some tables were altered
+                                or deleted and should be reopened by caller.
 
   RETURN
     A lock structure pointer on success.
-    NULL on error.
+    NULL on error or if some tables should be reopen.
 */
 
+/* Map the return value of thr_lock to an error from errmsg.txt */
 static int thr_lock_errno_to_mysql[]=
 { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
 
-MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
+                              uint flags, bool *need_reopen)
 {
   MYSQL_LOCK *sql_lock;
   TABLE *write_lock_used;
   int rc;
-  /* Map the return value of thr_lock to an error from errmsg.txt */
   DBUG_ENTER("mysql_lock_tables");
 
+  *need_reopen= FALSE;
+
   for (;;)
   {
     if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
@@ -178,6 +188,11 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
     thd->locked=0;
 retry:
     sql_lock=0;
+    if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
+    {
+      *need_reopen= TRUE;
+      break;
+    }
     if (wait_for_tables(thd))
       break;					// Couldn't open tables
   }
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index d585abc803b09c00aaf9996844f0c5812f09a35c..2d45360ab644e3290ccd52f855b90fc0457b54da 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -952,7 +952,7 @@ int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
 int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
 bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
 bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
-int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
+int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
 TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
 			    const char *table_name, bool link_in_list);
 bool rm_temporary_table(enum db_type base, char *path);
@@ -960,6 +960,7 @@ void free_io_cache(TABLE *entry);
 void intern_close_table(TABLE *entry);
 bool close_thread_table(THD *thd, TABLE **table_ptr);
 void close_temporary_tables(THD *thd);
+void close_tables_for_reopen(THD *thd, TABLE_LIST *tables);
 TABLE_LIST *find_table_in_list(TABLE_LIST *table,
                                uint offset_to_list,
                                const char *db_name,
@@ -1238,10 +1239,12 @@ extern pthread_t signal_thread;
 extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd;
 #endif /* HAVE_OPENSSL */
 
-MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
+                              uint flags, bool *need_reopen);
 /* mysql_lock_tables() flags bits */
 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
 #define MYSQL_LOCK_IGNORE_FLUSH                 0x0002
+#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        0x0004
 
 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
 void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
diff --git a/sql/sp.cc b/sql/sp.cc
index 271a5d3a4f831a3474891d1aabd643ffbc36106d..80b18571d7d71318b9fff2844015b9472eb84383 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -107,7 +107,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
 {
   TABLE_LIST tables;
   TABLE *table;
-  bool refresh;
+  bool not_used;
   DBUG_ENTER("open_proc_table");
 
   /*
@@ -122,7 +122,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
   bzero((char*) &tables, sizeof(tables));
   tables.db= (char*) "mysql";
   tables.table_name= tables.alias= (char*)"proc";
-  if (!(table= open_table(thd, &tables, thd->mem_root, &refresh,
+  if (!(table= open_table(thd, &tables, thd->mem_root, &not_used,
                           MYSQL_LOCK_IGNORE_FLUSH)))
   {
     thd->restore_backup_open_tables_state(backup);
@@ -138,7 +138,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
     could lead to a deadlock if we have other tables opened.
   */
   if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
-                                     MYSQL_LOCK_IGNORE_FLUSH)))
+                                     MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
   {
     close_proc_table(thd, backup);
     DBUG_RETURN(0);
@@ -1265,7 +1265,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
 
 
 /*
-  Add routine to the set of stored routines used by statement.
+  Add routine which is explicitly used by statement to the set of stored
+  routines used by this statement.
 
   SYNOPSIS
     sp_add_used_routine()
@@ -1276,7 +1277,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
       rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
 
   NOTES
-    Will also add element to end of 'LEX::sroutines_list' list.
+    Will also add element to end of 'LEX::sroutines_list' list (and will
+    take into account that this is explicitly used routine).
 
     To be friendly towards prepared statements one should pass
     persistent arena as second argument.
@@ -1287,6 +1289,37 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
 {
   rt->set_routine_type(rt_type);
   (void)add_used_routine(lex, arena, &rt->m_sroutines_key);
+  lex->sroutines_list_own_last= lex->sroutines_list.next;
+  lex->sroutines_list_own_elements= lex->sroutines_list.elements;
+}
+
+
+/*
+  Remove routines which are only indirectly used by statement from
+  the set of routines used by this statement.
+
+  SYNOPSIS
+    sp_remove_not_own_routines()
+      lex  LEX representing statement
+*/
+
+void sp_remove_not_own_routines(LEX *lex)
+{
+  Sroutine_hash_entry *not_own_rt, *next_rt;
+  for (not_own_rt= *(Sroutine_hash_entry **)lex->sroutines_list_own_last;
+       not_own_rt; not_own_rt= next_rt)
+  {
+    /*
+      It is safe to obtain not_own_rt->next after calling hash_delete() now
+      but we want to be more future-proof.
+    */
+    next_rt= not_own_rt->next;
+    hash_delete(&lex->sroutines, (byte *)not_own_rt);
+  }
+
+  *(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL;
+  lex->sroutines_list.next= lex->sroutines_list_own_last;
+  lex->sroutines_list.elements= lex->sroutines_list_own_elements;
 }
 
 
@@ -1343,6 +1376,28 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
 }
 
 
+/*
+  Add contents of list representing set of routines to the set of
+  routines used by statement.
+
+  SYNOPSIS
+    sp_update_stmt_used_routines()
+      thd  Thread context
+      lex  LEX representing statement
+      src  List representing set from which routines will be added
+
+  NOTE
+    It will also add elements to end of 'LEX::sroutines_list' list.
+*/
+
+static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src)
+{
+  for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
+       rt; rt= rt->next)
+    (void)add_used_routine(lex, thd->stmt_arena, &rt->key);
+}
+
+
 /*
   Cache sub-set of routines used by statement, add tables used by these
   routines to statement table list. Do the same for all routines used
@@ -1463,7 +1518,7 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
 {
   Sroutine_hash_entry **last_cached_routine_ptr=
                           (Sroutine_hash_entry **)lex->sroutines_list.next;
-  sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines);
+  sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines_list);
   (void)sp_cache_routines_and_add_tables_aux(thd, lex, 
                                              *last_cached_routine_ptr, FALSE);
 }
diff --git a/sql/sp.h b/sql/sp.h
index c278da863e0e6b9a6f738b48b82d1ec2f2556604..933e5793e4c0c7e5fd052a71bdf7f28658c0a6f4 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -84,6 +84,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
                             bool *first_no_prelocking);
 void sp_add_used_routine(LEX *lex, Query_arena *arena,
                          sp_name *rt, char rt_type);
+void sp_remove_not_own_routines(LEX *lex);
 void sp_update_sp_used_routines(HASH *dst, HASH *src);
 bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex, 
                                       bool first_no_prelock);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index a11907373cd8394a7ccc3d1e50e4c86f607722fe..1a7599d7bbcd970dc2f2b9baf70a6a5562a34317 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1909,10 +1909,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
       attached it above in this function).
       Now we'll save the 'tail', and detach it.
     */
-    DBUG_ASSERT(!lex_query_tables_own_last ||
-                lex_query_tables_own_last == m_lex->query_tables_own_last &&
-                prelocking_tables == *(m_lex->query_tables_own_last));
-
     lex_query_tables_own_last= m_lex->query_tables_own_last;
     prelocking_tables= *lex_query_tables_own_last;
     *lex_query_tables_own_last= NULL;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index ae50001882250f84ae414207f5d09d0f5fe150ff..fb23093731aee78b6fdc21cb9ff0cb7eda3ff93e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1399,7 +1399,6 @@ bool reopen_table(TABLE *table,bool locked)
   tmp.status=		table->status;
   tmp.keys_in_use_for_query= tmp.s->keys_in_use;
   tmp.used_keys= 	tmp.s->keys_for_keyread;
-  tmp.force_index=	tmp.force_index;
 
   /* Get state */
   tmp.s->key_length=	table->s->key_length;
@@ -1430,6 +1429,9 @@ bool reopen_table(TABLE *table,bool locked)
   for (key=0 ; key < table->s->keys ; key++)
     for (part=0 ; part < table->key_info[key].usable_key_parts ; part++)
       table->key_info[key].key_part[part].field->table= table;
+  if (table->triggers)
+    table->triggers->set_table(table);
+
   VOID(pthread_cond_broadcast(&COND_refresh));
   error=0;
 
@@ -1478,7 +1480,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
 
   TABLE *table,*next,**prev;
   TABLE **tables,**tables_ptr;			// For locks
-  bool error=0;
+  bool error=0, not_used;
   if (get_locks)
   {
     /* The ptr is checked later */
@@ -1519,7 +1521,8 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
     MYSQL_LOCK *lock;
     /* We should always get these locks */
     thd->some_tables_deleted=0;
-    if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), 0)))
+    if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables),
+                                 0, &not_used)))
     {
       thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
     }
@@ -1969,9 +1972,15 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
     /*
       Ignore placeholders for derived tables. After derived tables
       processing, link to created temporary table will be put here.
+      If this is derived table for view then we still want to process
+      routines used by this view.
      */
     if (tables->derived)
+    {
+      if (tables->view)
+        goto process_view_routines;
       continue;
+    }
     if (tables->schema_table)
     {
       if (!mysql_schema_table(thd, thd->lex, tables))
@@ -2003,23 +2012,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
         if (query_tables_last_own == &(tables->next_global) &&
             tables->view->query_tables)
           query_tables_last_own= tables->view->query_tables_last;
-        
         /*
-          Again if needed we have to get cache all routines used by this view
-          and add tables used by them to table list.
+          Let us free memory used by 'sroutines' hash here since we never
+          call destructor for this LEX.
         */
-        if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
-            tables->view->sroutines.records)
-        {
-          /* We have at least one table in TL here */
-          if (!query_tables_last_own)
-            query_tables_last_own= thd->lex->query_tables_last;
-          sp_cache_routines_and_add_tables_for_view(thd, thd->lex,
-                                                    tables->view);
-        }
-        /* Cleanup hashes because destructo for this LEX is never called */
         hash_free(&tables->view->sroutines);
-	continue;
+	goto process_view_routines;
       }
 
       if (refresh)				// Refresh in progress
@@ -2031,11 +2029,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
 	thd->version=refresh_version;
 	TABLE **prev_table= &thd->open_tables;
 	bool found=0;
-        /*
-          QQ: What we should do if we have started building of table list
-          for prelocking ??? Probably throw it away ? But before we should
-          mark all temporary tables as free? How about locked ?
-        */
 	for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global)
 	{
 	  /* Close normal (not temporary) changed tables */
@@ -2059,6 +2052,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
 	pthread_mutex_unlock(&LOCK_open);
 	if (found)
 	  VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+        /*
+          Let us prepare for recalculation of set of prelocked tables.
+          First we pretend that we have finished calculation which we
+          were doing currently. Then we restore list of tables to be
+          opened and set of used routines to the state in which they were
+          before first open_tables() call for this statement (i.e. before
+          we have calculated current set of tables for prelocking).
+        */
+        if (query_tables_last_own)
+          thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
+        thd->lex->chop_off_not_own_tables();
+        sp_remove_not_own_routines(thd->lex);
 	goto restart;
       }
       result= -1;				// Fatal error
@@ -2089,6 +2094,21 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
     if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
       tables->table->reginfo.lock_type=tables->lock_type;
     tables->table->grant= tables->grant;
+
+process_view_routines:
+    /*
+      Again we may need cache all routines used by this view and add
+      tables used by them to table list.
+    */
+    if (tables->view && !thd->prelocked_mode &&
+        !thd->lex->requires_prelocking() &&
+        tables->view->sroutines_list.elements)
+    {
+      /* We have at least one table in TL here. */
+      if (!query_tables_last_own)
+        query_tables_last_own= thd->lex->query_tables_last;
+      sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables->view);
+    }
   }
   thd->proc_info=0;
   free_root(&new_frm_mem, MYF(0));              // Free pre-alloced block
@@ -2193,7 +2213,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
     {
       DBUG_ASSERT(thd->lock == 0);	// You must lock everything at once
       if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
-	if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0)))
+	if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0,
+                                            &refresh)))
 	  table= 0;
     }
   }
@@ -2221,11 +2242,20 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
 
 int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
 {
-  DBUG_ENTER("simple_open_n_lock_tables");
   uint counter;
-  if (open_tables(thd, &tables, &counter, 0) ||
-      lock_tables(thd, tables, counter))
-    DBUG_RETURN(-1);				/* purecov: inspected */
+  bool need_reopen;
+  DBUG_ENTER("simple_open_n_lock_tables");
+
+  for ( ; ; ) 
+  {
+    if (open_tables(thd, &tables, &counter, 0))
+      DBUG_RETURN(-1);
+    if (!lock_tables(thd, tables, counter, &need_reopen))
+      break;
+    if (!need_reopen)
+      DBUG_RETURN(-1);
+    close_tables_for_reopen(thd, tables);
+  }
   DBUG_RETURN(0);
 }
 
@@ -2250,10 +2280,20 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
 bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
 {
   uint counter;
+  bool need_reopen;
   DBUG_ENTER("open_and_lock_tables");
-  if (open_tables(thd, &tables, &counter, 0) ||
-      lock_tables(thd, tables, counter) ||
-      mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
+
+  for ( ; ; ) 
+  {
+    if (open_tables(thd, &tables, &counter, 0))
+      DBUG_RETURN(-1);
+    if (!lock_tables(thd, tables, counter, &need_reopen))
+      break;
+    if (!need_reopen)
+      DBUG_RETURN(-1);
+    close_tables_for_reopen(thd, tables);
+  }
+  if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
       (thd->fill_derived_tables() &&
        mysql_handle_derived(thd->lex, &mysql_derived_filling)))
     DBUG_RETURN(TRUE); /* purecov: inspected */
@@ -2321,7 +2361,12 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
     lock_tables()
     thd			Thread handler
     tables		Tables to lock
-    count		umber of opened tables
+    count		Number of opened tables
+    need_reopen         Out parameter which if TRUE indicates that some
+                        tables were dropped or altered during this call
+                        and therefore invoker should reopen tables and
+                        try to lock them once again (in this case
+                        lock_tables() will also return error).
 
   NOTES
     You can't call lock_tables twice, as this would break the dead-lock-free
@@ -2337,7 +2382,7 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
    -1	Error
 */
 
-int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
+int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
 {
   TABLE_LIST *table;
 
@@ -2353,6 +2398,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
   */
   DBUG_ASSERT(!thd->lex->requires_prelocking() || tables);
 
+  *need_reopen= FALSE;
+
   if (!tables)
     DBUG_RETURN(0);
 
@@ -2385,7 +2432,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
       thd->options|= OPTION_TABLE_LOCK;
     }
 
-    if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), 0)))
+    if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
+                                        MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN,
+                                        need_reopen)))
     {
       if (thd->lex->requires_prelocking())
       {
@@ -2464,6 +2513,28 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
 }
 
 
+/*
+  Prepare statement for reopening of tables and recalculation of set of
+  prelocked tables.
+
+  SYNOPSIS
+    close_tables_for_reopen()
+      thd     Thread context
+      tables  List of tables which we were trying to open and lock
+
+*/
+
+void close_tables_for_reopen(THD *thd, TABLE_LIST *tables)
+{
+  thd->lex->chop_off_not_own_tables();
+  sp_remove_not_own_routines(thd->lex);
+  for (TABLE_LIST *tmp= tables; tmp; tmp= tmp->next_global)
+    if (tmp->table && !tmp->table->s->tmp_table)
+      tmp->table= 0;
+  close_thread_tables(thd);
+}
+
+
 /*
   Open a single table without table caching and don't set it in open_list
   Used by alter_table to open a temporary table and when creating
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 169132e2185159ca48d890497fff370cc1a7527d..cc45a7001cd17d422d75ab7bbe7f88b6397ceb0b 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -346,6 +346,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
   uint          num_rows;
   byte		*key;
   uint		key_len;
+  bool          not_used;
   DBUG_ENTER("mysql_ha_read");
   DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
                       tables->db, tables->table_name, tables->alias));
@@ -431,7 +432,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
   protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
 
   HANDLER_TABLES_HACK(thd);
-  lock= mysql_lock_tables(thd, &tables->table, 1, 0);
+  lock= mysql_lock_tables(thd, &tables->table, 1, 0, &not_used);
   HANDLER_TABLES_HACK(thd);
 
   if (!lock)
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 9421cc4bb6b0896909abe8716d83fa25d02eec87..f548a917bf84a96f5bab2da40e920d26651934ac 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1826,6 +1826,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
 
     if (di->tables_in_use && ! thd->lock)
     {
+      bool not_used;
       /*
         Request for new delayed insert.
         Lock the table, but avoid to be blocked by a global read lock.
@@ -1837,7 +1838,8 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
         inserts are done.
       */
       if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1,
-                                          MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK)))
+                                          MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK,
+                                          &not_used)))
       {
 	/* Fatal error */
 	di->dead= 1;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index e579ee9f8bdbd123f4c383014db8030d119de9a9..b7a2b6b0624d0dc5192800e044d4fa51e1334bee 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -179,6 +179,8 @@ void lex_start(THD *thd, uchar *buf,uint length)
   if (lex->sroutines.records)
     my_hash_reset(&lex->sroutines);
   lex->sroutines_list.empty();
+  lex->sroutines_list_own_last= lex->sroutines_list.next;
+  lex->sroutines_list_own_elements= 0;
   DBUG_VOID_RETURN;
 }
 
@@ -1613,6 +1615,8 @@ st_lex::st_lex()
 {
   hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
   sroutines_list.empty();
+  sroutines_list_own_last= sroutines_list.next;
+  sroutines_list_own_elements= 0;
 }
 
 
@@ -2025,6 +2029,8 @@ void st_lex::cleanup_after_one_table_open()
   if (sroutines.records)
     my_hash_reset(&sroutines);
   sroutines_list.empty();
+  sroutines_list_own_last= sroutines_list.next;
+  sroutines_list_own_elements= 0;
 }
 
 
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index b3a0d6d2eea0b5cee5a9366780e4581d5f1d2049..76ea7f5c27093894ddbff9dd33aad7962c3374ff 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -845,8 +845,15 @@ typedef struct st_lex
   /*
     List linking elements of 'sroutines' set. Allows you to add new elements
     to this set as you iterate through the list of existing elements.
+    'sroutines_list_own_last' is pointer to ::next member of last element of
+    this list which represents routine which is explicitly used by query.
+    'sroutines_list_own_elements' number of explicitly used routines.
+    We use these two members for restoring of 'sroutines_list' to the state
+    in which it was right after query parsing.
   */
   SQL_LIST sroutines_list;
+  byte     **sroutines_list_own_last;
+  uint     sroutines_list_own_elements;
 
   st_sp_chistics sp_chistics;
   bool only_view;       /* used for SHOW CREATE TABLE/VIEW */
@@ -962,6 +969,15 @@ typedef struct st_lex
   {
     return ( query_tables_own_last ? *query_tables_own_last : 0);
   }
+  void chop_off_not_own_tables()
+  {
+    if (query_tables_own_last)
+    {
+      *query_tables_own_last= 0;
+      query_tables_last= query_tables_own_last;
+      query_tables_own_last= 0;
+    }
+  }
   void cleanup_after_one_table_open();
 
   void push_context(Name_resolution_context *context)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 331d6f773a4dad79cb1eaf1ff1cd45efdeb771bb..e83530b7239f86dc79676cbb1ca3c16ab60f0c87 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4029,6 +4029,9 @@ mysql_execute_command(THD *thd)
       goto error;
     }
 
+    if (end_active_trans(thd)) 
+      goto error;
+
     if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0])
     {
       lex->sphead->m_db.length= strlen(thd->db);
@@ -4283,6 +4286,9 @@ mysql_execute_command(THD *thd)
 				 sp->m_name.str,
                                  lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
 	  goto error;
+
+        if (end_active_trans(thd)) 
+          goto error;
 	memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
         if (!trust_routine_creators &&  mysql_bin_log.is_open() &&
             !sp->m_chistics->detistic &&
@@ -4342,6 +4348,9 @@ mysql_execute_command(THD *thd)
 	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
                                  lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
           goto error;
+
+        if (end_active_trans(thd)) 
+          goto error;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 	if (sp_automatic_privileges && !opt_noacl &&
 	    sp_revoke_privileges(thd, db, name, 
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 92af61e2524b560f1cb456d0ea49c1ed922927fc..6eea101de8f0afbb2a8d4a9d986b3b5de35c7e8b 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1103,30 +1103,39 @@ static int mysql_test_update(Prepared_statement *stmt,
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   uint          want_privilege;
 #endif
+  bool need_reopen;
   DBUG_ENTER("mysql_test_update");
 
   if (update_precheck(thd, table_list))
     goto error;
 
-  if (open_tables(thd, &table_list, &table_count, 0))
-    goto error;
-
-  if (table_list->multitable_view)
+  for ( ; ; )
   {
-    DBUG_ASSERT(table_list->view != 0);
-    DBUG_PRINT("info", ("Switch to multi-update"));
-    /* pass counter value */
-    thd->lex->table_count= table_count;
-    /* convert to multiupdate */
-    DBUG_RETURN(2);
+    if (open_tables(thd, &table_list, &table_count, 0))
+      goto error;
+
+    if (table_list->multitable_view)
+    {
+      DBUG_ASSERT(table_list->view != 0);
+      DBUG_PRINT("info", ("Switch to multi-update"));
+      /* pass counter value */
+      thd->lex->table_count= table_count;
+      /* convert to multiupdate */
+      DBUG_RETURN(2);
+    }
+
+    if (!lock_tables(thd, table_list, table_count, &need_reopen))
+      break;
+    if (!need_reopen)
+      goto error;
+    close_tables_for_reopen(thd, table_list);
   }
 
   /*
     thd->fill_derived_tables() is false here for sure (because it is
     preparation of PS, so we even do not check it).
   */
-  if (lock_tables(thd, table_list, table_count) ||
-      mysql_handle_derived(thd->lex, &mysql_derived_prepare))
+  if (mysql_handle_derived(thd->lex, &mysql_derived_prepare))
     goto error;
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1b97de441e7c7b35892c54279be8b991b9e9c3a5..635b512fe23a25616b3d1511f17797a22b7e7160 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1709,6 +1709,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
   List_iterator_fast<Item> it(*items);
   Item *item;
   Field *tmp_field;
+  bool not_used;
   DBUG_ENTER("create_table_from_items");
 
   tmp_table.alias= 0;
@@ -1767,8 +1768,15 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
       DBUG_RETURN(0);
   }
 
+  /*
+    FIXME: What happens if trigger manages to be created while we are
+           obtaining this lock ? May be it is sensible just to disable
+           trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
+           save us from that ?
+  */
   table->reginfo.lock_type=TL_WRITE;
-  if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH)))
+  if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
+                                    MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
   {
     VOID(pthread_mutex_lock(&LOCK_open));
     hash_delete(&open_cache,(byte*) table);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 053dfdfc99029191237e8d1b4981944f68fb8da1..7342c14604523a094b65993d3a39d59f7ebe7b66 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -510,6 +510,25 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table)
 }
 
 
+/*
+  Adjust Table_triggers_list with new TABLE pointer.
+
+  SYNOPSIS
+    set_table()
+      new_table - new pointer to TABLE instance
+*/
+
+void Table_triggers_list::set_table(TABLE *new_table)
+{
+  table= new_table;
+  for (Field **field= table->triggers->record1_field ; *field ; field++)
+  {
+    (*field)->table= (*field)->orig_table= new_table;
+    (*field)->table_name= &new_table->alias;
+  }
+}
+
+
 /*
   Check whenever .TRG file for table exist and load all triggers it contains.
 
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index d9b39cc30342918f95df681c73098e80bdb57f27..c1d1f8d0e9e07d46231440bc9958c64cd1cbd5d9 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -98,6 +98,8 @@ class Table_triggers_list: public Sql_alloc
     return test(bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE]);
   }
 
+  void set_table(TABLE *new_table);
+
   friend class Item_trigger_field;
   friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
                 Table_triggers_list *triggers);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index e02bebfaded3c4920dfb4adf3e4158ec7c9aefe7..a75eef9324c68de670ea44726f38fccfacb09f66 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -134,25 +134,33 @@ int mysql_update(THD *thd,
   SQL_SELECT	*select;
   READ_RECORD	info;
   SELECT_LEX    *select_lex= &thd->lex->select_lex;
+  bool need_reopen;
   DBUG_ENTER("mysql_update");
 
   LINT_INIT(timestamp_query_id);
 
-  if (open_tables(thd, &table_list, &table_count, 0))
-    DBUG_RETURN(1);
-
-  if (table_list->multitable_view)
+  for ( ; ; )
   {
-    DBUG_ASSERT(table_list->view != 0);
-    DBUG_PRINT("info", ("Switch to multi-update"));
-    /* pass counter value */
-    thd->lex->table_count= table_count;
-    /* convert to multiupdate */
-    return 2;
+    if (open_tables(thd, &table_list, &table_count, 0))
+      DBUG_RETURN(1);
+
+    if (table_list->multitable_view)
+    {
+      DBUG_ASSERT(table_list->view != 0);
+      DBUG_PRINT("info", ("Switch to multi-update"));
+      /* pass counter value */
+      thd->lex->table_count= table_count;
+      /* convert to multiupdate */
+      DBUG_RETURN(2);
+    }
+    if (!lock_tables(thd, table_list, table_count, &need_reopen))
+      break;
+    if (!need_reopen)
+      DBUG_RETURN(1);
+    close_tables_for_reopen(thd, table_list);
   }
 
-  if (lock_tables(thd, table_list, table_count) ||
-      mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
+  if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
       (thd->fill_derived_tables() &&
        mysql_handle_derived(thd->lex, &mysql_derived_filling)))
     DBUG_RETURN(1);
@@ -616,7 +624,6 @@ static table_map get_table_map(List<Item> *items)
 bool mysql_multi_update_prepare(THD *thd)
 {
   LEX *lex= thd->lex;
-  ulong opened_tables;
   TABLE_LIST *table_list= lex->query_tables;
   TABLE_LIST *tl, *leaves;
   List<Item> *fields= &lex->select_lex.item_list;
@@ -630,13 +637,16 @@ bool mysql_multi_update_prepare(THD *thd)
   uint  table_count= lex->table_count;
   const bool using_lock_tables= thd->locked_tables != 0;
   bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI);
+  bool need_reopen= FALSE;
   DBUG_ENTER("mysql_multi_update_prepare");
 
   /* following need for prepared statements, to run next time multi-update */
   thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
 
+reopen_tables:
+
   /* open tables and create derived ones, but do not lock and fill them */
-  if ((original_multiupdate &&
+  if (((original_multiupdate || need_reopen) &&
        open_tables(thd, &table_list, &table_count, 0)) ||
       mysql_handle_derived(lex, &mysql_derived_prepare))
     DBUG_RETURN(TRUE);
@@ -742,20 +752,17 @@ bool mysql_multi_update_prepare(THD *thd)
     }
   }
 
-  opened_tables= thd->status_var.opened_tables;
   /* now lock and fill tables */
-  if (lock_tables(thd, table_list, table_count))
-    DBUG_RETURN(TRUE);
-
-  /*
-    we have to re-call fixfields for fixed items, because lock maybe
-    reopened tables
-  */
-  if (opened_tables != thd->status_var.opened_tables)
+  if (lock_tables(thd, table_list, table_count, &need_reopen))
   {
+    if (!need_reopen)
+      DBUG_RETURN(TRUE);
+
     /*
-      Fields items cleanup(). There are only Item_fields in the list, so we
-      do not do Item tree walking
+      We have to reopen tables since some of them were altered or dropped
+      during lock_tables() or something was done with their triggers.
+      Let us do some cleanups to be able do setup_table() and setup_fields()
+      once again.
     */
     List_iterator_fast<Item> it(*fields);
     Item *item;
@@ -766,12 +773,8 @@ bool mysql_multi_update_prepare(THD *thd)
     for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global)
       tbl->cleanup_items();
 
-    if (setup_tables(thd, &lex->select_lex.context,
-                     &lex->select_lex.top_join_list,
-                     table_list, &lex->select_lex.where,
-                     &lex->select_lex.leaf_tables, FALSE) ||
-        setup_fields_with_no_wrap(thd, 0, *fields, 1, 0, 0))
-      DBUG_RETURN(TRUE);
+    close_tables_for_reopen(thd, table_list);
+    goto reopen_tables;
   }
 
   /*