MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:July 23 2008 4:12pm
Subject:bzr commit into mysql-6.0 branch (davi:2683) Bug#989, WL#4284
View as plain text  
# At a local mysql-6.0 repository of davi

 2683 Davi Arnaut	2008-07-23
      WL#4284: Transactional DDL locking
      Bug#989: If DROP TABLE while there's an active transaction,
               wrong binlog order
      
      Currently the MySQL server does not keep metadata locks on
      schema objects for the duration of a transaction, thus failing
      to guarantee the integrity of the schema objects being used
      during the transaction and to protect then from concurrent
      DDL operations. This also poses a problem for replication as
      a DDL operation might be replicated even thought there are
      active transactions using the object being modified.
      
      The solution is to defer the release of metadata locks until
      a active transaction is either committed or rolled back. This
      prevents other statements from modifying the table for the
      entire duration of the transaction. This provides commitment
      ordering for guaranteeing serializability across multiple
      transactions.
modified:
  mysql-test/r/autocommit_func.result
  mysql-test/r/flush_block_commit.result
  mysql-test/r/flush_block_commit_notembedded.result
  mysql-test/r/implicit_commit.result
  mysql-test/t/autocommit_func.test
  mysql-test/t/flush_block_commit.test
  mysql-test/t/flush_block_commit_notembedded.test
  sql/backup/backup_progress.cc
  sql/backup/be_default.cc
  sql/backup/be_snapshot.cc
  sql/backup/be_thread.cc
  sql/backup/data_backup.cc
  sql/backup/kernel.cc
  sql/event_db_repository.cc
  sql/mysql_priv.h
  sql/si_objects.cc
  sql/sp_head.cc
  sql/sql_acl.cc
  sql/sql_base.cc
  sql/sql_class.cc
  sql/sql_class.h
  sql/sql_insert.cc
  sql/sql_parse.cc
  sql/sql_show.cc
  sql/sql_table.cc
  sql/sql_udf.cc
  sql/table.cc
  sql/transaction.cc
  sql/transaction.h

per-file messages:
  mysql-test/r/autocommit_func.result
    Update test case result.
  mysql-test/r/flush_block_commit.result
    Update test case result.
  mysql-test/r/flush_block_commit_notembedded.result
    Update test case result.
  mysql-test/r/implicit_commit.result
    Flush commands now issue a implicit commit.
  mysql-test/t/autocommit_func.test
    Force the release of locks.
  mysql-test/t/flush_block_commit.test
    Reflect the fact that transactions now hold metadata
    locks for the duration of the transaction.
  mysql-test/t/flush_block_commit_notembedded.test
    Empty commit does not modify the binary log.
  sql/backup/backup_progress.cc
    Close tables and release locks.
  sql/backup/be_default.cc
    Close tables and release locks.
  sql/backup/be_snapshot.cc
    Close tables and release locks.
  sql/backup/be_thread.cc
    Close tables and release locks.
  sql/backup/data_backup.cc
    Close tables and release locks.
  sql/backup/kernel.cc
    Close tables and release locks.
  sql/event_db_repository.cc
    Allocate the locks from the appropriate memory root.
  sql/mysql_priv.h
    Introduce flags to signal whether the metadata locks
    should be released when closing the tables.
  sql/si_objects.cc
    Allocate the locks from the appropriate memory root.
  sql/sp_head.cc
    Allocate the locks from the appropriate memory root.
  sql/sql_acl.cc
    Allocate the locks from the appropriate memory root.
  sql/sql_base.cc
    Defer the release of metadata locks when closing tables
    if not required to. Allocate new metadata lock requests
    upon demand when opening tables. Issue a deadlock error
    if the locking protocol requires that a transaction
    re-acquire its locks.
  sql/sql_class.cc
    Initialize memory root used for allocating metadata locks.
  sql/sql_class.h
    Add memory root for allocating metadata locks. A new memory
    root is used as the duration of metadata locks does not cope
    well with the duration of the transaction memory root, mostly
    due to API abuses.
  sql/sql_insert.cc
    Allocate the locks from the appropriate memory root.
  sql/sql_parse.cc
    Don't allocate metadata locks during parsing as a implicit
    commit might be issued after parsing and before the command
    execution.
    
    Add the commands flush, check and backup commands to the list
    of commands that issue a implicit commit before and after the
    command execution.
  sql/sql_show.cc
    Allocate the locks from the appropriate memory root.
  sql/sql_table.cc
    Allocate the locks from the appropriate memory root.
    Remove unnecessary calls to close_thread_tables.
  sql/sql_udf.cc
    Allocate the locks from the appropriate memory root.
  sql/table.cc
    Force the allocation of a new lock request if the object is
    being cleaned up for re-execution.
  sql/transaction.cc
    Release locks once the transaction is committed or rolled
    back. Add flag to signal that a implicit commit should
    release locks.
  sql/transaction.h
    Add a flags parameter to trans_commit_implicit
=== modified file 'mysql-test/r/autocommit_func.result'
--- a/mysql-test/r/autocommit_func.result	2008-04-10 13:14:28 +0000
+++ b/mysql-test/r/autocommit_func.result	2008-07-23 16:12:09 +0000
@@ -104,6 +104,8 @@ id	name
 2	Record_2
 4	Record_4
 5	Record_5
+## Commit changes
+COMMIT;
 ## Dropping table t1 ##
 DROP table t1;
 ## Disconnecting both connections ##

=== modified file 'mysql-test/r/flush_block_commit.result'
--- a/mysql-test/r/flush_block_commit.result	2006-12-26 16:22:17 +0000
+++ b/mysql-test/r/flush_block_commit.result	2008-07-23 16:12:09 +0000
@@ -2,12 +2,11 @@ drop table if exists t1;
 create table t1 (a int) engine=innodb;
 begin;
 insert into t1 values(1);
-flush tables with read lock;
-select * from t1;
-a
+flush tables with read lock;;
 commit;
 select * from t1;
 a
+1
 unlock tables;
 begin;
 select * from t1 for update;
@@ -19,13 +18,12 @@ flush tables with read lock;
 commit;
 a
 1
+commit;
 unlock tables;
 commit;
 begin;
 insert into t1 values(10);
 flush tables with read lock;
-commit;
-unlock tables;
 flush tables with read lock;
 unlock tables;
 begin;
@@ -36,4 +34,10 @@ a
 show create database test;
 Database	Create Database
 test	CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */
+commit;
+flush tables with read lock;
+begin;
+insert into t1 values (1);;
+unlock tables;
+commit;
 drop table t1;

=== modified file 'mysql-test/r/flush_block_commit_notembedded.result'
--- a/mysql-test/r/flush_block_commit_notembedded.result	2008-02-03 09:00:49 +0000
+++ b/mysql-test/r/flush_block_commit_notembedded.result	2008-07-23 16:12:09 +0000
@@ -1,12 +1,14 @@
 create table t1 (a int) engine=innodb;
 reset master;
 set autocommit=0;
-insert t1 values (1);
+select 1;
+1
+1
 flush tables with read lock;
 show master status;
 File	Position	Binlog_Do_DB	Binlog_Ignore_DB
 master-bin.000001	107		
-commit;
+insert into t1 values (1);
 show master status;
 File	Position	Binlog_Do_DB	Binlog_Ignore_DB
 master-bin.000001	107		

=== modified file 'mysql-test/r/implicit_commit.result'
--- a/mysql-test/r/implicit_commit.result	2008-07-19 01:23:51 +0000
+++ b/mysql-test/r/implicit_commit.result	2008-07-23 16:12:09 +0000
@@ -1102,6 +1102,8 @@ show binlog events from <binlog_start>;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 master-bin.000001	#	Query	#	#	use `db1`; BEGIN
 master-bin.000001	#	Query	#	#	use `db1`; INSERT INTO t1 (a) VALUES (1)
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `db1`; BEGIN
 master-bin.000001	#	Query	#	#	use `db1`; INSERT INTO t1 (a) VALUES (2)
 master-bin.000001	#	Xid	#	#	COMMIT /* XID */
 RESET MASTER;
@@ -1114,7 +1116,9 @@ show binlog events from <binlog_start>;
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 master-bin.000001	#	Query	#	#	use `db1`; BEGIN
 master-bin.000001	#	Query	#	#	use `db1`; INSERT INTO t1 (a) VALUES (1)
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
 master-bin.000001	#	Query	#	#	use `db1`; flush privileges
+master-bin.000001	#	Query	#	#	use `db1`; BEGIN
 master-bin.000001	#	Query	#	#	use `db1`; INSERT INTO t1 (a) VALUES (2)
 master-bin.000001	#	Xid	#	#	COMMIT /* XID */
 #

=== modified file 'mysql-test/t/autocommit_func.test'
--- a/mysql-test/t/autocommit_func.test	2008-04-10 13:14:28 +0000
+++ b/mysql-test/t/autocommit_func.test	2008-07-23 16:12:09 +0000
@@ -153,6 +153,10 @@ SELECT * from t1;
 CONNECTION test_con2;
 SELECT * from t1;
 
+--echo ## Commit changes
+CONNECTION test_con1;
+COMMIT;
+
 --echo ## Dropping table t1 ##
 DROP table t1;
 

=== modified file 'mysql-test/t/flush_block_commit.test'
--- a/mysql-test/t/flush_block_commit.test	2006-12-26 16:22:17 +0000
+++ b/mysql-test/t/flush_block_commit.test	2008-07-23 16:12:09 +0000
@@ -21,16 +21,13 @@ create table t1 (a int) engine=innodb;
 begin;
 insert into t1 values(1);
 connection con2;
-flush tables with read lock;
-select * from t1;
+--send flush tables with read lock;
 connection con1;
-send commit; # blocked by con2
-sleep 1;
+commit;
 connection con2;
-select * from t1; # verify con1 was blocked and data did not move
+--reap
+select * from t1;
 unlock tables;
-connection con1;
-reap;
 
 # No deadlock ?
 
@@ -47,6 +44,7 @@ connection con1;
 commit; # should not be blocked by con3
 connection con2;
 reap;
+commit;
 connection con3;
 reap;
 unlock tables;
@@ -60,8 +58,6 @@ connection con1;
 begin;
 insert into t1 values(10);
 flush tables with read lock;
-commit;
-unlock tables;
 connection con2;
 flush tables with read lock; # bug caused hang here
 unlock tables;
@@ -71,6 +67,24 @@ unlock tables;
 begin;
 select * from t1;
 show create database test;
+commit;
+
+# GLR blocks new transactions
+connection con1;
+flush tables with read lock;
+connection con2;
+begin;
+--send insert into t1 values (1);
+connection con1;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for release of readlock" and
+        info = "insert into t1 values (1)";
+--source include/wait_condition.inc
+unlock tables;
+connection con2;
+--reap
+commit;
 
 drop table t1;
 

=== modified file 'mysql-test/t/flush_block_commit_notembedded.test'
--- a/mysql-test/t/flush_block_commit_notembedded.test	2007-06-15 16:56:11 +0000
+++ b/mysql-test/t/flush_block_commit_notembedded.test	2008-07-23 16:12:09 +0000
@@ -17,12 +17,12 @@ connection con1;
 create table t1 (a int) engine=innodb;
 reset master;
 set autocommit=0;
-insert t1 values (1);
+select 1;
 connection con2;
 flush tables with read lock;
 show master status;
 connection con1;
-send commit;
+send insert into t1 values (1);
 connection con2;
 sleep 1;
 show master status;

=== modified file 'sql/backup/backup_progress.cc'
--- a/sql/backup/backup_progress.cc	2008-06-12 02:23:08 +0000
+++ b/sql/backup/backup_progress.cc	2008-07-23 16:12:09 +0000
@@ -63,7 +63,7 @@ my_bool check_ob_progress_tables(THD *th
     sql_print_error(ER(ER_BACKUP_PROGRESS_TABLES));
     DBUG_RETURN(ret);
   }
-  close_thread_tables(thd);
+  close_thread_tables(thd, CTT_RELEASE_TRANS_LOCKS);
 
   /* Check mysql.online_backup_progress */
   tables.init_one_table("mysql", 5, "online_backup_progress", 13,
@@ -75,7 +75,7 @@ my_bool check_ob_progress_tables(THD *th
     sql_print_error(ER(ER_BACKUP_PROGRESS_TABLES));
     DBUG_RETURN(ret);
   }
-  close_thread_tables(thd);
+  close_thread_tables(thd, CTT_RELEASE_TRANS_LOCKS);
   DBUG_RETURN(ret);
 }
 

=== modified file 'sql/backup/be_default.cc'
--- a/sql/backup/be_default.cc	2008-05-25 17:31:52 +0000
+++ b/sql/backup/be_default.cc	2008-07-23 16:12:09 +0000
@@ -630,7 +630,7 @@ result_t Restore::truncate_table(TABLE *
 result_t Restore::end()
 {
   DBUG_ENTER("Restore::end");
-  close_thread_tables(m_thd);
+  close_thread_tables(m_thd, CTT_RELEASE_TRANS_LOCKS);
   DBUG_RETURN(OK);
 }
 

=== modified file 'sql/backup/be_snapshot.cc'
--- a/sql/backup/be_snapshot.cc	2008-07-19 01:23:51 +0000
+++ b/sql/backup/be_snapshot.cc	2008-07-23 16:12:09 +0000
@@ -122,8 +122,7 @@ result_t Backup::get_data(Buffer &buf)
   {
     locking_thd->lock_state= LOCK_DONE; // set lock done so destructor won't wait
     trans_commit_stmt(locking_thd->m_thd);
-    trans_commit_implicit(locking_thd->m_thd);
-    close_thread_tables(locking_thd->m_thd);
+    trans_commit_implicit(locking_thd->m_thd, TRANS_RELEASE_MDL);
   }
   return(res);
 }

=== modified file 'sql/backup/be_thread.cc'
--- a/sql/backup/be_thread.cc	2008-05-25 17:31:52 +0000
+++ b/sql/backup/be_thread.cc	2008-07-23 16:12:09 +0000
@@ -204,7 +204,7 @@ pthread_handler_t backup_thread_for_lock
     Cleanup and return.
   */
 end:
-  close_thread_tables(thd);
+  close_thread_tables(thd, CTT_RELEASE_TRANS_LOCKS);
 
 end2:
   THD_SET_PROC_INFO(thd, "lock done");

=== modified file 'sql/backup/data_backup.cc'
--- a/sql/backup/data_backup.cc	2008-03-20 14:53:16 +0000
+++ b/sql/backup/data_backup.cc	2008-07-23 16:12:09 +0000
@@ -1585,7 +1585,7 @@ int restore_table_data(THD* thd, Restore
     Close all tables if default or snapshot driver used.
   */
   if (table_list)
-    close_thread_tables(::current_thd);
+    close_thread_tables(::current_thd, CTT_RELEASE_TRANS_LOCKS);
 
   DBUG_RETURN(state == ERROR ? backup::ERROR : 0);
 

=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc	2008-06-20 13:11:20 +0000
+++ b/sql/backup/kernel.cc	2008-07-23 16:12:09 +0000
@@ -1172,7 +1172,7 @@ Backup_info::add_table(Db_item &dbi, con
 
  end:
 
-  ::close_thread_tables(m_thd);
+  ::close_thread_tables(m_thd, CTT_RELEASE_TRANS_LOCKS);
 
   return ti;
 }

=== modified file 'sql/event_db_repository.cc'
--- a/sql/event_db_repository.cc	2008-06-12 02:23:08 +0000
+++ b/sql/event_db_repository.cc	2008-07-23 16:12:09 +0000
@@ -553,7 +553,7 @@ Event_db_repository::open_event_table(TH
   DBUG_ENTER("Event_db_repository::open_event_table");
 
   tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
-  alloc_mdl_locks(&tables, thd->mem_root);
+  alloc_mdl_locks(&tables, thd->lock_data_root());
 
   if (simple_open_n_lock_tables(thd, &tables))
   {
@@ -1096,7 +1096,7 @@ Event_db_repository::check_system_tables
 
   /* Check mysql.db */
   tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ);
-  alloc_mdl_locks(&tables, thd->mem_root);
+  alloc_mdl_locks(&tables, thd->lock_data_root());
 
   if (simple_open_n_lock_tables(thd, &tables))
   {
@@ -1114,7 +1114,7 @@ Event_db_repository::check_system_tables
   }
   /* Check mysql.user */
   tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
-  alloc_mdl_locks(&tables, thd->mem_root);
+  alloc_mdl_locks(&tables, thd->lock_data_root());
 
   if (simple_open_n_lock_tables(thd, &tables))
   {
@@ -1135,7 +1135,7 @@ Event_db_repository::check_system_tables
   }
   /* Check mysql.event */
   tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
-  alloc_mdl_locks(&tables, thd->mem_root);
+  alloc_mdl_locks(&tables, thd->lock_data_root());
 
   if (simple_open_n_lock_tables(thd, &tables))
   {

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2008-07-19 01:23:51 +0000
+++ b/sql/mysql_priv.h	2008-07-23 16:12:09 +0000
@@ -800,7 +800,12 @@ extern my_decimal decimal_zero;
 void free_items(Item *item);
 void cleanup_items(Item *item);
 class THD;
-void close_thread_tables(THD *thd, bool skip_mdl= 0);
+
+/* Options of close_thread_tables */
+#define CTT_SKIP_MDL_REMOVE       (1U << 0)
+#define CTT_RELEASE_TRANS_LOCKS   (2U << 0)
+
+void close_thread_tables(THD *thd, uint flags= 0);
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);

=== modified file 'sql/si_objects.cc'
--- a/sql/si_objects.cc	2008-05-23 13:54:03 +0000
+++ b/sql/si_objects.cc	2008-07-23 16:12:09 +0000
@@ -1928,7 +1928,7 @@ bool TriggerObj::serialize(THD *thd, Str
   if (!lst)
     DBUG_RETURN(FALSE);
 
-  alloc_mdl_locks(lst, thd->mem_root);
+  alloc_mdl_locks(lst, thd->lock_data_root());
 
   if (open_tables(thd, &lst, &num_tables, 0))
     DBUG_RETURN(FALSE);

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2008-07-17 09:15:22 +0000
+++ b/sql/sp_head.cc	2008-07-23 16:12:09 +0000
@@ -3941,9 +3941,7 @@ sp_head::add_used_tables_to_table_list(T
       table->belong_to_view= belong_to_view;
       table->trg_event_map= stab->trg_event_map;
       table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name,
-                                           thd->locked_tables_root ?
-                                           thd->locked_tables_root :
-                                           thd->mem_root);
+                                           thd->lock_data_root());
 
       /* Everyting else should be zeroed */
 
@@ -3988,9 +3986,7 @@ sp_add_to_query_tables(THD *thd, LEX *le
   table->select_lex= lex->current_select;
   table->cacheable_table= 1;
   table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name,
-                                       thd->locked_tables_root ?
-                                       thd->locked_tables_root : thd->mem_root);
-
+                                       thd->lock_data_root());
   lex->add_to_query_tables(table);
   return table;
 }

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2008-06-28 11:00:59 +0000
+++ b/sql/sql_acl.cc	2008-07-23 16:12:09 +0000
@@ -693,7 +693,7 @@ my_bool acl_reload(THD *thd)
   tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
   tables[0].skip_temporary= tables[1].skip_temporary=
     tables[2].skip_temporary= TRUE;
-  alloc_mdl_locks(tables, thd->mem_root);
+  alloc_mdl_locks(tables, thd->lock_data_root());
 
   if (simple_open_n_lock_tables(thd, tables))
   {
@@ -1589,7 +1589,7 @@ bool change_password(THD *thd, const cha
   bzero((char*) &tables, sizeof(tables));
   tables.alias= tables.table_name= (char*) "user";
   tables.db= (char*) "mysql";
-  alloc_mdl_locks(&tables, thd->mem_root);
+  alloc_mdl_locks(&tables, thd->lock_data_root());
 
 #ifdef HAVE_REPLICATION
   /*
@@ -3023,7 +3023,7 @@ int mysql_table_grant(THD *thd, TABLE_LI
 			    ? tables+2 : 0);
   tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
   tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
-  alloc_mdl_locks(tables, thd->mem_root);
+  alloc_mdl_locks(tables, thd->lock_data_root());
 
   /*
     This statement will be replicated as a statement, even when using
@@ -3249,7 +3249,7 @@ bool mysql_routine_grant(THD *thd, TABLE
   tables[0].next_local= tables[0].next_global= tables+1;
   tables[0].lock_type=tables[1].lock_type=TL_WRITE;
   tables[0].db=tables[1].db=(char*) "mysql";
-  alloc_mdl_locks(tables, thd->mem_root);
+  alloc_mdl_locks(tables, thd->lock_data_root());
 
   /*
     This statement will be replicated as a statement, even when using
@@ -3391,7 +3391,7 @@ bool mysql_grant(THD *thd, const char *d
   tables[0].next_local= tables[0].next_global= tables+1;
   tables[0].lock_type=tables[1].lock_type=TL_WRITE;
   tables[0].db=tables[1].db=(char*) "mysql";
-  alloc_mdl_locks(tables, thd->mem_root);
+  alloc_mdl_locks(tables, thd->lock_data_root());
 
   /*
     This statement will be replicated as a statement, even when using
@@ -3725,7 +3725,7 @@ static my_bool grant_reload_procs_priv(T
   table.db= (char *) "mysql";
   table.lock_type= TL_READ;
   table.skip_temporary= 1;
-  alloc_mdl_locks(&table, thd->mem_root);
+  alloc_mdl_locks(&table, thd->lock_data_root());
 
   if (simple_open_n_lock_tables(thd, &table))
   {
@@ -3792,7 +3792,7 @@ my_bool grant_reload(THD *thd)
   tables[0].next_local= tables[0].next_global= tables+1;
   tables[0].lock_type= tables[1].lock_type= TL_READ;
   tables[0].skip_temporary= tables[1].skip_temporary= TRUE;
-  alloc_mdl_locks(tables, thd->mem_root);
+  alloc_mdl_locks(tables, thd->lock_data_root());
 
   /*
     To avoid deadlocks we should obtain table locks before
@@ -5121,7 +5121,7 @@ int open_grant_tables(THD *thd, TABLE_LI
     (tables+4)->lock_type= TL_WRITE;
   tables->db= (tables+1)->db= (tables+2)->db= 
     (tables+3)->db= (tables+4)->db= (char*) "mysql";
-  alloc_mdl_locks(tables, thd->mem_root);
+  alloc_mdl_locks(tables, thd->lock_data_root());
 
 #ifdef HAVE_REPLICATION
   /*

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2008-07-19 01:23:51 +0000
+++ b/sql/sql_base.cc	2008-07-23 16:12:09 +0000
@@ -1286,8 +1286,7 @@ close_all_tables_for_name(THD *thd, TABL
     leave prelocked mode if needed.
 */
 
-void close_thread_tables(THD *thd,
-                         bool skip_mdl)
+void close_thread_tables(THD *thd, uint flags)
 {
   TABLE *table;
   DBUG_ENTER("close_thread_tables");
@@ -1429,10 +1428,20 @@ void close_thread_tables(THD *thd,
   if (thd->open_tables)
     close_open_tables(thd);
 
-  mdl_release_locks(&thd->mdl_context);
-  if (!skip_mdl)
+  /*
+    Defer the release of metadata locks until the current transaction
+    is either committed or rolled back. This prevents other statements
+    from modifying the table for the entire duration of this transaction.
+    This provides commitment ordering for guaranteeing serializability
+    across multiple transactions.
+  */
+  if (! (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) ||
+        (thd->state_flags & Open_tables_state::BACKUPS_AVAIL) ||
+        (flags & CTT_RELEASE_TRANS_LOCKS))
   {
-    mdl_remove_all_locks(&thd->mdl_context);
+    mdl_release_locks(&thd->mdl_context);
+    if (! (flags & CTT_SKIP_MDL_REMOVE))
+      mdl_remove_all_locks(&thd->mdl_context);
   }
   DBUG_VOID_RETURN;
 }
@@ -2514,6 +2523,17 @@ bool open_table(THD *thd, TABLE_LIST *ta
     This is the normal use case.
   */
 
+  /*
+    Allocate a new metadata lock request for the table object that
+    doesn't have one already. This is done at this point because a
+    implicit commit (with locks release and freeing) might have been
+    issued after this object was assembled.
+  */
+  if (! table_list->mdl_lock_data)
+   table_list->mdl_lock_data= mdl_alloc_lock(0, table_list->db,
+                                             table_list->table_name,
+                                             thd->lock_data_root());
+
   mdl_lock_data= table_list->mdl_lock_data;
   if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
   {
@@ -2954,7 +2974,7 @@ Locked_tables_list::unlock_locked_tables
     }
     thd->locked_tables_mode= LTM_NONE;
 
-    close_thread_tables(thd);
+    close_thread_tables(thd, CTT_RELEASE_TRANS_LOCKS);
   }
   /*
     After closing tables we can free memory used for storing lock
@@ -3518,6 +3538,7 @@ int open_tables(THD *thd, TABLE_LIST **s
   /* Also used for indicating that prelocking is need */
   TABLE_LIST **query_tables_last_own;
   bool safe_to_ignore_table;
+  bool has_locks= mdl_has_locks(&thd->mdl_context);
 
   DBUG_ENTER("open_tables");
   /*
@@ -3660,6 +3681,18 @@ int open_tables(THD *thd, TABLE_LIST **s
       if (action)
       {
         /*
+          We have met a exclusive metadata lock or a old version of table and
+          we are inside a transaction that already hold locks. We can't follow
+          the locking protocol in this scenario as it might lead to deadlocks.
+        */
+        if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && has_locks)
+        {
+          my_error(ER_LOCK_DEADLOCK, MYF(0));
+          result= -1;
+          goto err;
+        }
+
+        /*
           We have met exclusive metadata lock or old version of table. Now we
           have to close all tables which are not up to date/release metadata
           locks. We also have to throw away set of prelocked tables (and thus
@@ -4004,7 +4037,8 @@ retry:
       call close_thread_tables() to release metadata locks which
       might have been acquired successfully.
     */
-    close_thread_tables(thd, (action == OT_BACK_OFF_AND_RETRY));
+    uint flags= (action == OT_BACK_OFF_AND_RETRY) ? CTT_SKIP_MDL_REMOVE : 0;
+    close_thread_tables(thd, flags);
     if (recover_from_failed_open_table_attempt(thd, table_list, action))
       break;
   }
@@ -4508,7 +4542,7 @@ void close_tables_for_reopen(THD *thd, T
   sp_remove_not_own_routines(thd->lex);
   for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
     tmp->table= 0;
-  close_thread_tables(thd, skip_mdl);
+  close_thread_tables(thd, skip_mdl ? CTT_SKIP_MDL_REMOVE : 0);
 }
 
 
@@ -7849,7 +7883,7 @@ open_system_table_for_update(THD *thd, T
 {
   DBUG_ENTER("open_system_table_for_update");
 
-  alloc_mdl_locks(one_table, thd->mem_root);
+  alloc_mdl_locks(one_table, thd->lock_data_root());
 
   TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0);
   if (table)

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2008-07-19 01:23:51 +0000
+++ b/sql/sql_class.cc	2008-07-23 16:12:09 +0000
@@ -546,6 +546,7 @@ THD::THD()
     will be re-initialized in init_for_queries().
   */
   init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+  init_sql_alloc(&trans_mdl_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
   stmt_arena= this;
   thread_stack= 0;
   catalog= (char*)"std"; // the only catalog we have for now
@@ -788,6 +789,9 @@ void THD::init_for_queries()
   reset_root_defaults(&transaction.mem_root,
                       variables.trans_alloc_block_size,
                       variables.trans_prealloc_size);
+  reset_root_defaults(&trans_mdl_root,
+                      ALLOC_ROOT_MIN_BLOCK_SIZE,
+                      sizeof(MDL_LOCK_DATA) + MAX_MDLKEY_LENGTH);
   transaction.xid_state.xid.null();
   transaction.xid_state.in_thd=1;
 }
@@ -837,10 +841,9 @@ void THD::cleanup(void)
 #error xid_state in the cache should be replaced by the allocated value
   }
 #endif
-  {
-    trans_rollback(this);
-    xid_cache_delete(&transaction.xid_state);
-  }
+  locked_tables_list.unlock_locked_tables(this);
+  trans_rollback(this);
+  xid_cache_delete(&transaction.xid_state);
   locked_tables_list.unlock_locked_tables(this);
   mysql_ha_cleanup(this);
   delete_dynamic(&user_var_events);
@@ -923,6 +926,7 @@ THD::~THD()
   main_security_ctx.destroy();
   safeFree(db);
   free_root(&warn_root,MYF(0));
+  free_root(&trans_mdl_root,MYF(0));
   free_root(&transaction.mem_root,MYF(0));
   mysys_var=0;					// Safety (shouldn't be needed)
   pthread_mutex_destroy(&LOCK_delete);

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2008-07-19 01:23:51 +0000
+++ b/sql/sql_class.h	2008-07-23 16:12:09 +0000
@@ -793,6 +793,8 @@ struct st_savepoint {
   char                *name;
   uint                 length;
   Ha_trx_info         *ha_list;
+  /** Last acquired lock before this savepoint was set. */
+  MDL_LOCK_DATA       *last_lock_data;
 };
 
 enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED};
@@ -1930,6 +1932,30 @@ public:
 
   Locked_tables_list locked_tables_list;
 
+  /**
+    Memory root with appropriate scope and lifetime from which
+    to allocate metadata locks for tables used in a transaction.
+  */
+  MEM_ROOT trans_mdl_root;
+
+  /**
+    Returns a pointer to the memory root from which to allocate
+    metadata locks. It returns the transactional mdl root if
+    inside a transaction context, otherwise return the suitable
+    memory root for the scope of this statement.
+  */
+  MEM_ROOT *lock_data_root()
+  {
+    MEM_ROOT *root= mem_root;
+
+    if (locked_tables_root)
+      root= locked_tables_root;
+    else if (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+      root= &trans_mdl_root;
+
+    return root;
+  }
+
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   partition_info *work_part_info;
 #endif

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2008-07-19 01:23:51 +0000
+++ b/sql/sql_insert.cc	2008-07-23 16:12:09 +0000
@@ -2350,7 +2350,7 @@ pthread_handler_t handle_delayed_insert(
   thd->lex->set_stmt_unsafe();
   thd->set_current_stmt_binlog_row_based_if_mixed();
 
-  alloc_mdl_locks(&di->table_list, thd->mem_root);
+  alloc_mdl_locks(&di->table_list, thd->lock_data_root());
 
   if (di->open_and_lock_table())
     goto err;

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2008-07-19 01:23:51 +0000
+++ b/sql/sql_parse.cc	2008-07-23 16:12:09 +0000
@@ -123,7 +123,7 @@ static bool opt_implicit_commit(THD *thd
     break;
   }
 
-  DBUG_RETURN(skip ? FALSE : trans_commit_implicit(thd));
+  DBUG_RETURN(skip ? FALSE : trans_commit_implicit(thd, TRANS_RELEASE_MDL));
 }
 
 
@@ -281,7 +281,8 @@ void init_update_queries(void)
                                               CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_OPTIMIZE]=         CF_WRITE_LOGS_COMMAND |
                                               CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_ANALYZE]=          CF_WRITE_LOGS_COMMAND;
+  sql_command_flags[SQLCOM_ANALYZE]=          CF_WRITE_LOGS_COMMAND |
+                                              CF_AUTO_COMMIT_TRANS;
 
   sql_command_flags[SQLCOM_CREATE_USER]=      CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_DROP_USER]=        CF_AUTO_COMMIT_TRANS;
@@ -296,6 +297,12 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_ALTER_FUNCTION]=     CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_PRELOAD_KEYS]=       CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_FLUSH]=              CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_CHECK]=              CF_AUTO_COMMIT_TRANS;
+
+  sql_command_flags[SQLCOM_SHOW_ARCHIVE]=       CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_BACKUP]=             CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_RESTORE]=            CF_AUTO_COMMIT_TRANS;
 }
 
 
@@ -3185,12 +3192,12 @@ end_with_restore_list:
       done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
       false, mysqldump will not work.
     */
-    thd->locked_tables_list.unlock_locked_tables(thd);
     if (thd->options & OPTION_TABLE_LOCK)
     {
-      trans_commit_implicit(thd);
+      trans_commit_implicit(thd, TRANS_RELEASE_MDL);
       thd->options&= ~(OPTION_TABLE_LOCK);
     }
+    thd->locked_tables_list.unlock_locked_tables(thd);
     if (thd->global_read_lock)
       unlock_global_read_lock(thd);
     my_ok(thd);
@@ -3240,11 +3247,13 @@ end_with_restore_list:
     */
     if (check_transactional_lock(thd, all_tables))
       goto error;
-    thd->locked_tables_list.unlock_locked_tables(thd);
+
     /* we must end the trasaction first, regardless of anything */
-    if (trans_commit_implicit(thd))
+    if (trans_commit_implicit(thd, TRANS_RELEASE_MDL))
       goto error;
 
+    thd->locked_tables_list.unlock_locked_tables(thd);
+
     alloc_mdl_locks(all_tables, thd->locked_tables_list.locked_tables_root());
 
     thd->options|= OPTION_TABLE_LOCK;
@@ -3266,7 +3275,7 @@ end_with_restore_list:
         that it can't lock a table in its list
       */
       trans_rollback_stmt(thd);
-      trans_commit_implicit(thd);
+      trans_commit_implicit(thd, TRANS_RELEASE_MDL);
       thd->options&= ~(OPTION_TABLE_LOCK);
     }
     else
@@ -4144,7 +4153,7 @@ create_sp_error:
                                  lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
           goto error;
 
-        if (trans_commit_implicit(thd))
+        if (trans_commit_implicit(thd, TRANS_RELEASE_MDL))
           goto error;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 	if (sp_automatic_privileges && !opt_noacl &&
@@ -5959,9 +5968,6 @@ TABLE_LIST *st_select_lex::add_table_to_
   ptr->next_name_resolution_table= NULL;
   /* Link table in global list (all used tables) */
   lex->add_to_query_tables(ptr);
-  ptr->mdl_lock_data= mdl_alloc_lock(0 , ptr->db, ptr->table_name,
-                                thd->locked_tables_root ?
-                                thd->locked_tables_root : thd->mem_root);
   DBUG_RETURN(ptr);
 }
 
@@ -6496,7 +6502,7 @@ bool reload_acl_and_cache(THD *thd, ulon
         if we have a write locked table as this would lead to a deadlock
         when trying to reopen (and re-lock) the table after the flush.
       */
-      if (thd->locked_tables_mode)
+      if (thd->locked_tables_mode || thd->active_transaction())
       {
         my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
         return 1;

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2008-06-30 09:59:59 +0000
+++ b/sql/sql_show.cc	2008-07-23 16:12:09 +0000
@@ -7350,7 +7350,7 @@ bool show_create_trigger(THD *thd, const
 
   uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
 
-  alloc_mdl_locks(lst, thd->mem_root);
+  alloc_mdl_locks(lst, thd->lock_data_root());
 
   if (open_tables(thd, &lst, &num_tables, 0))
   {

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2008-07-19 01:23:51 +0000
+++ b/sql/sql_table.cc	2008-07-23 16:12:09 +0000
@@ -4155,6 +4155,12 @@ static bool mysql_admin_table(THD* thd, 
   CHARSET_INFO *cs= system_charset_info;
   DBUG_ENTER("mysql_admin_table");
 
+  /*
+    Allocate metadata lock requests on the connection memory root as
+    the admin operations will do implicit commits and re-open the table.
+  */
+  alloc_mdl_locks(tables, thd->mem_root);
+
   field_list.push_back(item = new Item_empty_string("Table",
                                                     NAME_CHAR_LEN * 2,
                                                     cs));
@@ -4219,7 +4225,6 @@ static bool mysql_admin_table(THD* thd, 
       case  1:           // error, message written to net
         trans_rollback_stmt(thd);
         trans_rollback(thd);
-        close_thread_tables(thd);
         DBUG_PRINT("admin", ("simple error, admin next table"));
         continue;
       case -1:           // error, message could be written to net
@@ -4278,7 +4283,6 @@ static bool mysql_admin_table(THD* thd, 
       protocol->store(buff, length, system_charset_info);
       trans_commit_stmt(thd);
       trans_commit(thd);
-      close_thread_tables(thd);
       lex->reset_query_tables_list(FALSE);
       table->table=0;				// For query cache
       if (protocol->write())
@@ -4326,7 +4330,7 @@ static bool mysql_admin_table(THD* thd, 
       {
         DBUG_PRINT("admin", ("recreating table"));
         trans_rollback_stmt(thd);
-        close_thread_tables(thd);
+        trans_commit(thd);
         tmp_disable_binlog(thd); // binlogging is done by caller if wanted
         result_code= mysql_recreate_table(thd, table);
         reenable_binlog(thd);
@@ -4439,7 +4443,7 @@ send_result_message:
         reopen the table and do ha_innobase::analyze() on it.
       */
       trans_commit_stmt(thd);
-      close_thread_tables(thd);
+      trans_commit(thd);
       TABLE_LIST *save_next_local= table->next_local,
                  *save_next_global= table->next_global;
       table->next_local= table->next_global= 0;
@@ -4455,7 +4459,7 @@ send_result_message:
       if (thd->main_da.is_ok())
         thd->main_da.reset_diagnostics_area();
       trans_commit_stmt(thd);
-      close_thread_tables(thd);
+      trans_commit(thd);
       if (!result_code) // recreation went ok
       {
         if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
@@ -4557,7 +4561,6 @@ send_result_message:
 err:
   trans_rollback_stmt(thd);
   trans_rollback(thd);
-  close_thread_tables(thd);			// Shouldn't be needed
   if (table)
     table->table=0;
   DBUG_RETURN(TRUE);

=== modified file 'sql/sql_udf.cc'
--- a/sql/sql_udf.cc	2008-05-23 13:54:03 +0000
+++ b/sql/sql_udf.cc	2008-07-23 16:12:09 +0000
@@ -481,7 +481,7 @@ int mysql_create_function(THD *thd,udf_f
   bzero((char*) &tables,sizeof(tables));
   tables.db= (char*) "mysql";
   tables.table_name= tables.alias= (char*) "func";
-  alloc_mdl_locks(&tables, thd->mem_root);
+  alloc_mdl_locks(&tables, thd->lock_data_root());
   /* Allow creation of functions even if we can't open func table */
   if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
     goto err;

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2008-07-14 12:49:19 +0000
+++ b/sql/table.cc	2008-07-23 16:12:09 +0000
@@ -4626,6 +4626,12 @@ void TABLE_LIST::reinit_before_use(THD *
   table= 0;
   /* Reset is_schema_table_processed value(needed for I_S tables */
   schema_table_state= NOT_PROCESSED;
+  /*
+    Force the allocation of a new metadata lock request for this object
+    as the lock lifetime is usually bound to the lifetime of the transaction
+    that took the lock.
+  */
+  mdl_lock_data= NULL;
 
   TABLE_LIST *embedded; /* The table at the current level of nesting. */
   TABLE_LIST *parent_embedding= this; /* The parent nested table reference. */

=== modified file 'sql/transaction.cc'
--- a/sql/transaction.cc	2008-07-19 01:23:51 +0000
+++ b/sql/transaction.cc	2008-07-23 16:12:09 +0000
@@ -63,7 +63,7 @@ bool trans_begin(THD *thd, uint flags)
 
   thd->locked_tables_list.unlock_locked_tables(thd);
 
-  if (trans_commit_implicit(thd))
+  if (trans_commit_implicit(thd, TRANS_RELEASE_MDL))
     DBUG_RETURN(TRUE);
 
   thd->options|= OPTION_BEGIN;
@@ -77,6 +77,28 @@ bool trans_begin(THD *thd, uint flags)
 
 
 /**
+  Close tables and releases metadata locks that were implicitly or
+  explicitly acquired during the current transaction.
+
+  @note Relies on close_thread_tables to close tables and to release
+        the locks. This is done this way because under some circumstances
+        the locks might not be released when under lock tables mode.
+        There is no problem in not releasing the locks under lock tables
+        as the locks are going to be released once the lock tables mode
+        is finished.
+
+  @param thd     Current thread
+*/
+
+static void trans_release_locks(THD *thd)
+{
+  close_thread_tables(thd, CTT_RELEASE_TRANS_LOCKS);
+  if (! mdl_has_locks(&thd->mdl_context))
+    free_root(&thd->trans_mdl_root, MYF(MY_KEEP_PREALLOC));
+}
+
+
+/**
   Commit the current transaction, making its changes permanent.
 
   @param thd     Current thread
@@ -97,6 +119,7 @@ bool trans_commit(THD *thd)
   res= ha_commit_trans(thd, TRUE);
   thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
   thd->transaction.all.modified_non_trans_table= FALSE;
+  trans_release_locks(thd);
 
   DBUG_RETURN(test(res));
 }
@@ -113,7 +136,7 @@ bool trans_commit(THD *thd)
   @retval TRUE   Failure
 */
 
-bool trans_commit_implicit(THD *thd)
+bool trans_commit_implicit(THD *thd, uint flags)
 {
   bool res= FALSE;
   DBUG_ENTER("trans_commit_implicit");
@@ -132,6 +155,8 @@ bool trans_commit_implicit(THD *thd)
 #ifdef WITH_MARIA_STORAGE_ENGINE
     ha_maria::implicit_commit(thd);
 #endif
+    if (flags & TRANS_RELEASE_MDL)
+      trans_release_locks(thd);
   }
 
   thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
@@ -162,6 +187,7 @@ bool trans_rollback(THD *thd)
   res= ha_rollback_trans(thd, TRUE);
   thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
   thd->transaction.all.modified_non_trans_table= FALSE;
+  trans_release_locks(thd);
 
   DBUG_RETURN(test(res));
 }
@@ -281,11 +307,50 @@ bool trans_savepoint(THD *thd, LEX_STRIN
   newsv->prev= thd->transaction.savepoints;
   thd->transaction.savepoints= newsv;
 
+  /*
+    Remember the last acquired lock before the savepoint was set.
+    This is used as a marker to only release locks acquired after
+    the setting of this savepoint.
+  */
+  newsv->last_lock_data = thd->mdl_context.locks.head();
+
   DBUG_RETURN(FALSE);
 }
 
 
 /**
+  Releases metadata locks that were acquired during this savepoint unit.
+
+  @note Locks taken after this savepoint was set are released.
+  @note It's safe to iterate and unlock any locks after taken after this
+        savepoint because other statements that take other special locks
+        cause a implicit commit (ie LOCK TABLES).
+
+  @param thd    Current thread
+  @param name   Savepoint name
+*/
+
+static void savepoint_release_locks(THD *thd, SAVEPOINT *sv)
+{
+  MDL_LOCK_DATA_iterator it= mdl_get_locks(&thd->mdl_context);
+  MDL_LOCK_DATA *mdl_lock_data;
+
+  DBUG_ASSERT(thd->open_tables == NULL);
+
+  while ((mdl_lock_data= it++))
+  {
+    /* Stop when lock was acquired before this savepoint. */
+    if (mdl_lock_data == sv->last_lock_data)
+      break;
+    /* Unlock the metadata lock on this table. */
+    mdl_release_lock(&thd->mdl_context, mdl_lock_data);
+    /* Remove lock from context. */
+    mdl_remove_lock(&thd->mdl_context, mdl_lock_data);
+  }
+}
+
+
+/**
   Rollback a transaction to the named savepoint.
 
   @note Modifications that the current transaction made to
@@ -316,12 +381,18 @@ bool trans_rollback_to_savepoint(THD *th
 
   if (ha_rollback_to_savepoint(thd, sv))
     res= TRUE;
-  else if (((thd->options & OPTION_KEEP_LOG) ||
-            thd->transaction.all.modified_non_trans_table) &&
-           !thd->slave_thread)
-    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                 ER_WARNING_NOT_COMPLETE_ROLLBACK,
-                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
+  else
+  {
+    /* Release metadata locks that were acquired during this savepoint unit. */
+    savepoint_release_locks(thd, sv);
+    /* Set warning if a non-transactional table was updated. */
+    if (((thd->options & OPTION_KEEP_LOG) ||
+          thd->transaction.all.modified_non_trans_table) &&
+        !thd->slave_thread)
+      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                    ER_WARNING_NOT_COMPLETE_ROLLBACK,
+                    ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
+  }
 
   thd->transaction.savepoints= sv;
 

=== modified file 'sql/transaction.h'
--- a/sql/transaction.h	2008-07-19 01:23:51 +0000
+++ b/sql/transaction.h	2008-07-23 16:12:09 +0000
@@ -23,9 +23,12 @@
 /* Options of START TRANSACTION statement (and later of SET TRANSACTION stmt) */
 #define MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT    (1U << 0)
 
+/* Options of trans_commit_implicit */
+#define TRANS_RELEASE_MDL   (1U << 0)
+
 bool trans_begin(THD *thd, uint flags= 0);
 bool trans_commit(THD *thd);
-bool trans_commit_implicit(THD *thd);
+bool trans_commit_implicit(THD *thd, uint flags= 0);
 bool trans_rollback(THD *thd);
 
 bool trans_commit_stmt(THD *thd);

Thread
bzr commit into mysql-6.0 branch (davi:2683) Bug#989, WL#4284Davi Arnaut23 Jul
  • Re: bzr commit into mysql-6.0 branch (davi:2683) Bug#989, WL#4284Konstantin Osipov24 Jul
    • Re: bzr commit into mysql-6.0 branch (davi:2683) Bug#989, WL#4284Davi Arnaut24 Jul