List:Commits« Previous MessageNext Message »
From:dlenev Date:September 4 2007 9:32am
Subject:bk commit into 5.1 tree (dlenev:1.2578) BUG#25144
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of dlenev. When dlenev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-09-04 13:32:01+04:00, dlenev@stripped +13 -0
  Tentative fix for bug#25144 "replication / binlog with view breaks".
  
  The fact that CREATE/ALTER/DROP VIEW statements on a view were not
  waiting for statements that were using this view to complete led
  to incorrect sequence of statements in the binary log in cases
  when one used statement based logging.
  
  This patch solves the problem by changing implementation of
  CREATE/ALTER and DROP VIEW statements in such way that they
  take exclusive meta-data lock on a view to be changed. Since
  statements that use views already take shared meta-data lock
  on them this ensures that statements that modify the view
  and statements that use it are properly isolated from each
  other and appear in correct order in binary log.
  
  Note that this patch is based on the series of previous patches
  that significantly change code responsible for meta-data locking.
  
  As always questions for reviewer are marked by QQ.

  mysql-test/r/lock.result@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +2 -1
    One is no longer allowed to drop, create or later view under LOCK TABLES.

  mysql-test/r/view.result@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +1 -0
    One is no longer allowed to drop, create or later view under LOCK TABLES.

  mysql-test/r/view_multi.result@stripped, 2007-09-04 13:31:59+04:00, dlenev@stripped +48 -0
    New BitKeeper file ``mysql-test/r/view_multi.result''

  mysql-test/r/view_multi.result@stripped, 2007-09-04 13:31:59+04:00, dlenev@stripped +0 -0

  mysql-test/t/lock.test@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +2 -1
    One is no longer allowed to drop, create or later view under LOCK TABLES.

  mysql-test/t/view.test@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +1 -0
    One is no longer allowed to drop, create or later view under LOCK TABLES.

  mysql-test/t/view_multi.test@stripped, 2007-09-04 13:31:59+04:00, dlenev@stripped +110 -0
    New BitKeeper file ``mysql-test/t/view_multi.test''

  mysql-test/t/view_multi.test@stripped, 2007-09-04 13:31:59+04:00, dlenev@stripped +0 -0

  sql/meta_lock.cc@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +9 -1
    Now meta-data locking sybsystem allows to take shared lock on the object
    in cases when current locker already has exclusive lock on it.

  sql/mysql_priv.h@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +3 -0
    Made open_view_entry() function accessible outside of sql_base.cc.

  sql/sql_base.cc@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +21 -18
    open_view_entry():
     Made it accessible from outside of sql_base.cc
     Added argument for flags to be used when opening the view.
     Finally we no longer rely on caller to lock LOCK_open mutex
     as now this is done inside this function.
    open_table():
     Added support for acquiring exclusive meta-data lock on the
     table instead of acquiring shared lock and opening it (we 
     use this in CREATE/ALTER VIEW implementation).

  sql/sql_parse.cc@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +1 -1
    Replaced boolean TABLE_LIST::create member with enum open_table_type member.
    This allows easily handle situation in which instead of opening the table
    we want only to take exclusive meta-data lock on it.

  sql/sql_prepare.cc@stripped, 2007-09-04 13:31:58+04:00, dlenev@stripped +1 -1
    Replaced boolean TABLE_LIST::create member with enum open_table_type member.
    This allows easily handle situation in which instead of opening the table
    we want only to take exclusive meta-data lock on it.

  sql/sql_view.cc@stripped, 2007-09-04 13:31:59+04:00, dlenev@stripped +110 -64
    Added proper meta-data locking to implementations of CREATE/ALTER/DROP VIEW
    statements. Now we obtain exclusive meta-data lock on a view before creating/
    changing/dropping it. This ensures that all concurrent statements that use
    this view will finish before our statement will proceed and therefore we
    will get correct order of statements in the binary log.

  sql/table.h@stripped, 2007-09-04 13:31:59+04:00, dlenev@stripped +6 -4
    Replaced boolean TABLE_LIST::create member with enum open_table_type member.
    This allows easily handle situation in which instead of opening the table
    we want only to take exclusive meta-data lock on it (we need this in order
    to handle ALTER VIEW and CREATE VIEW statements).

diff -Nrup a/mysql-test/r/lock.result b/mysql-test/r/lock.result
--- a/mysql-test/r/lock.result	2007-08-09 12:28:17 +04:00
+++ b/mysql-test/r/lock.result	2007-09-04 13:31:58 +04:00
@@ -123,13 +123,14 @@ select * from v_bug5719;
 1
 1
 drop view v_bug5719;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 
 sic: did not left LOCK TABLES mode automatically
 
 select * from t1;
 ERROR HY000: Table 't1' was not locked with LOCK TABLES
 unlock tables;
-create view v_bug5719 as select * from t1;
+create or replace view v_bug5719 as select * from t1;
 lock tables v_bug5719 write;
 select * from v_bug5719;
 a
diff -Nrup a/mysql-test/r/view.result b/mysql-test/r/view.result
--- a/mysql-test/r/view.result	2007-07-07 19:06:38 +04:00
+++ b/mysql-test/r/view.result	2007-09-04 13:31:58 +04:00
@@ -1102,6 +1102,7 @@ select * from v1;
 a
 select * from t2;
 ERROR HY000: Table 't2' was not locked with LOCK TABLES
+unlock tables;
 drop view v1;
 drop table t1, t2;
 create table t1 (a int);
diff -Nrup a/mysql-test/r/view_multi.result b/mysql-test/r/view_multi.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/view_multi.result	2007-09-04 13:31:59 +04:00
@@ -0,0 +1,48 @@
+reset master;
+create table t1 (i int);
+create table t2 (i int);
+create view v1 as select * from t1;
+select get_lock("lock_bg25144", 1);
+get_lock("lock_bg25144", 1)
+1
+insert into v1 values (get_lock("lock_bg25144", 100));;
+drop view v1;;
+select release_lock("lock_bg25144");
+release_lock("lock_bg25144")
+1
+select release_lock("lock_bg25144");
+release_lock("lock_bg25144")
+1
+select * from t1;
+i
+1
+create view v1 as select * from t1;
+select get_lock("lock_bg25144", 1);
+get_lock("lock_bg25144", 1)
+1
+insert into v1 values (get_lock("lock_bg25144", 100));;
+alter view v1 as select * from t2;;
+select release_lock("lock_bg25144");
+release_lock("lock_bg25144")
+1
+select release_lock("lock_bg25144");
+release_lock("lock_bg25144")
+1
+select * from t1;
+i
+1
+1
+select * from t2;
+i
+show binlog events in 'master-bin.000001' from 106;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	1	#	use `test`; create table t1 (i int)
+master-bin.000001	#	Query	1	#	use `test`; create table t2 (i int)
+master-bin.000001	#	Query	1	#	use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1
+master-bin.000001	#	Query	1	#	use `test`; insert into v1 values (get_lock("lock_bg25144", 100))
+master-bin.000001	#	Query	1	#	use `test`; drop view v1
+master-bin.000001	#	Query	1	#	use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1
+master-bin.000001	#	Query	1	#	use `test`; insert into v1 values (get_lock("lock_bg25144", 100))
+master-bin.000001	#	Query	1	#	use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t2
+drop table t1, t2;
+drop view v1;
diff -Nrup a/mysql-test/t/lock.test b/mysql-test/t/lock.test
--- a/mysql-test/t/lock.test	2007-08-09 12:28:17 +04:00
+++ b/mysql-test/t/lock.test	2007-09-04 13:31:58 +04:00
@@ -173,6 +173,7 @@ select * from t2;
 --error ER_TABLE_NOT_LOCKED
 select * from t3;
 select * from v_bug5719;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop view v_bug5719;
 --echo
 --echo sic: did not left LOCK TABLES mode automatically
@@ -180,7 +181,7 @@ drop view v_bug5719;
 --error ER_TABLE_NOT_LOCKED
 select * from t1;
 unlock tables;
-create view v_bug5719 as select * from t1;
+create or replace view v_bug5719 as select * from t1;
 lock tables v_bug5719 write;
 select * from v_bug5719;
 --echo
diff -Nrup a/mysql-test/t/view.test b/mysql-test/t/view.test
--- a/mysql-test/t/view.test	2007-07-07 19:14:04 +04:00
+++ b/mysql-test/t/view.test	2007-09-04 13:31:58 +04:00
@@ -1016,6 +1016,7 @@ lock tables t1 read, v1 read;
 select * from v1;
 -- error 1100
 select * from t2;
+unlock tables;
 drop view v1;
 drop table t1, t2;
 
diff -Nrup a/mysql-test/t/view_multi.test b/mysql-test/t/view_multi.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/view_multi.test	2007-09-04 13:31:59 +04:00
@@ -0,0 +1,110 @@
+#
+# QQ: Should we find a better place for this test?
+#     May be binlog or rpl suites ?
+#
+--source include/have_log_bin.inc
+--source include/have_binlog_format_mixed_or_statement.inc
+
+#
+# Bug #25144 "replication / binlog with view breaks".
+# Statements that used views didn't ensure that view were not modified
+# during their execution. Indeed this led to incorrect binary log with
+# statement based logging.
+#
+--disable_parsing
+drop table if not exists t1, t2;
+drop view if exists v1;
+--enable_parsing
+
+# We are going to use binary log later to check that statements are
+# logged in proper order, so it is good idea to reset it here.
+reset master;
+
+connect (addconn1,localhost,root,,);
+connect (addconn2,localhost,root,,);
+connection default;
+
+create table t1 (i int);
+create table t2 (i int);
+create view v1 as select * from t1;
+
+# First we try to concurrently execute statement that uses view
+# and statement that drops it. We use "user" locks as means to
+# suspend execution of first statement once it opens our view.
+select get_lock("lock_bg25144", 1);
+
+connection addconn1;
+--send insert into v1 values (get_lock("lock_bg25144", 100));
+
+connection addconn2;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "User lock" and info like "insert into v1 %lock_bg25144%";
+--source include/wait_condition.inc
+--send drop view v1;
+
+connection default;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state is null and info = "drop view v1";
+--source include/wait_condition.inc
+
+select release_lock("lock_bg25144");
+
+connection addconn1;
+--reap
+select release_lock("lock_bg25144");
+
+connection addconn2;
+--reap
+
+connection default;
+# Check that insertion through view did happen.
+select * from t1;
+# At the end of test we will check that statements were
+# logged in proper order.
+
+# Now we will repeat the test by trying concurrently execute
+# statement that uses a view and statement that alters it.
+create view v1 as select * from t1;
+
+select get_lock("lock_bg25144", 1);
+
+connection addconn1;
+--send insert into v1 values (get_lock("lock_bg25144", 100));
+
+connection addconn2;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "User lock" and info like "insert into v1 %lock_bg25144%";
+--source include/wait_condition.inc
+--send alter view v1 as select * from t2;
+
+connection default;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Opening tables" and
+  info = "alter view v1 as select * from t2";
+--source include/wait_condition.inc
+
+select release_lock("lock_bg25144");
+
+connection addconn1;
+--reap
+select release_lock("lock_bg25144");
+
+connection addconn2;
+--reap
+
+connection default;
+
+# Second insertion should go to t1 as well.
+select * from t1;
+select * from t2;
+
+# Now let us check that statements were logged in proper order
+--replace_column 2 # 5 #
+show binlog events in 'master-bin.000001' from 106;
+
+drop table t1, t2;
+drop view v1;
diff -Nrup a/sql/meta_lock.cc b/sql/meta_lock.cc
--- a/sql/meta_lock.cc	2007-08-29 23:30:00 +04:00
+++ b/sql/meta_lock.cc	2007-09-04 13:31:58 +04:00
@@ -248,8 +248,16 @@ bool mdl_acquire_lock(MDL_EL *l)
   else
   {
     if (!(lock->active_writers || lock->waiting_writers ||
-          lock->active_readers_waiting_upgrade))
+          lock->active_readers_waiting_upgrade) ||
+        (lock->active_writers && lock->active_writers->ctx == l->ctx))
     {
+      /*
+        When writers come from the same context we can satisfy our shared
+        lock. This is required for CREATE TABLE ... SELECT ... and
+        ALTER VIEW ... AS ....
+        QQ: May be it is better always return error in this specific case
+            (as later we always discover that there is an error) ?
+      */
       l->next_lock= lock->active_readers;
       lock->active_readers= l;
       lock->users++;
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h	2007-08-29 23:30:00 +04:00
+++ b/sql/mysql_priv.h	2007-09-04 13:31:58 +04:00
@@ -1128,6 +1128,9 @@ enum enum_open_table_action {OT_NO_ACTIO
                              OT_DISCOVER, OT_REPAIR};
 TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
 		  enum_open_table_action *action, uint flags);
+bool open_view_entry(THD *thd, TABLE_LIST *table_list, const char *alias,
+                     char *cache_key, uint cache_key_length,
+                     MEM_ROOT *mem_root, uint flags);
 bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list);
 TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
 bool reopen_table(TABLE *table);
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-08-29 23:30:00 +04:00
+++ b/sql/sql_base.cc	2007-09-04 13:31:58 +04:00
@@ -101,9 +101,6 @@ static bool reopen_table_entry(THD *thd,
                                const char *alias, char *cache_key,
                                uint cache_key_length);
 static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry);
-static bool open_view_entry(THD *thd, TABLE_LIST *table_list, const char *alias,
-                            char *cache_key, uint cache_key_length,
-                            MEM_ROOT *mem_root);
 static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
 static void free_cache_entry(TABLE *entry);
 static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
@@ -2337,15 +2334,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *
                            table_list->db, table_list->table_name, reg_ext, 0);
       if (mysql_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
       {
-        VOID(pthread_mutex_lock(&LOCK_open));
         if (!open_view_entry(thd, table_list, alias, key, key_length,
-                             mem_root))
+                             mem_root, 0))
         {
           DBUG_ASSERT(table_list->view != 0);
-          VOID(pthread_mutex_unlock(&LOCK_open));
           DBUG_RETURN(0); // VIEW
         }
-        VOID(pthread_mutex_unlock(&LOCK_open));
       }
     }
     /*
@@ -2379,9 +2373,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *
   */
 
   mdl= table_list->mdl;
-  mdl_add_lock(&thd->mdl_context, mdl, table_list->create);
+  mdl_add_lock(&thd->mdl_context, mdl, table_list->open_table_type);
 
-  if (table_list->create)
+  if (table_list->open_table_type)
   {
     // This case can be significantly optimized
     mdl_acquire_exclusive_locks(&thd->mdl_context);
@@ -2422,7 +2416,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *
   if (thd->handler_tables)
     mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
 
-  if (table_list->create)
+  if (table_list->open_table_type == TABLE_LIST::OPEN_OR_CREATE)
   {
     bool exists;
 
@@ -2436,6 +2430,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *
     }
     /* Table exists. Let us try to open it. */
   }
+  else if (table_list->open_table_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL)
+  {
+    VOID(pthread_mutex_unlock(&LOCK_open));
+    DBUG_RETURN(0);
+  }
 
   /* QQ: Does it makes sense to move code below to separate function? */
   if (!(share= get_table_share_with_create(thd, table_list, key,
@@ -2568,7 +2567,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *
   VOID(pthread_mutex_unlock(&LOCK_open));
 
   // Table existed
-  if (table_list->create)
+  if (table_list->open_table_type == TABLE_LIST::OPEN_OR_CREATE)
     mdl_downgrade_exclusive_locks(&thd->mdl_context);
 
   table->mdl_lock= mdl;
@@ -3144,6 +3143,7 @@ void assign_new_table_id(TABLE_SHARE *sh
    @param cache_key         Key for table definition cache
    @param cache_key_length  Length of cache_key
    @param mem_root          Memory to be used for .frm parsing.
+   @param flags             Flags which modify how we open the view
 
    @todo This function is needed for special handling of views under
          LOCK TABLES. We probably should get rid of it in long term.
@@ -3153,35 +3153,38 @@ void assign_new_table_id(TABLE_SHARE *sh
    QQ: How about better name for this function ?
 */
 
-static bool open_view_entry(THD *thd, TABLE_LIST *table_list,
-                            const char *alias, char *cache_key,
-                            uint cache_key_length, MEM_ROOT *mem_root)
+bool open_view_entry(THD *thd, TABLE_LIST *table_list, const char *alias,
+                     char *cache_key, uint cache_key_length,
+                     MEM_ROOT *mem_root, uint flags)
 {
   TABLE not_used;
   int error;
   TABLE_SHARE *share;
 
-  safe_mutex_assert_owner(&LOCK_open);
+  VOID(pthread_mutex_lock(&LOCK_open));
 
   if (!(share= get_table_share_with_create(thd, table_list, cache_key,
                                            cache_key_length, 
                                            OPEN_VIEW, &error)))
-    return TRUE;
+    goto err;
 
   if (share->is_view &&
       !open_new_frm(thd, share, alias,
                     (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
                             HA_GET_INDEX | HA_TRY_READ_ONLY),
                     READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
-                    0, thd->open_options, &not_used, table_list,
+                    flags, thd->open_options, &not_used, table_list,
                     mem_root))
   {
     release_table_share(share, RELEASE_NORMAL);
+    VOID(pthread_mutex_unlock(&LOCK_open));
     return FALSE;
   }
 
   my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "BASE TABLE");
   release_table_share(share, RELEASE_NORMAL);
+err:
+  VOID(pthread_mutex_unlock(&LOCK_open));
   return TRUE;
 }
 
@@ -3667,7 +3670,7 @@ int open_tables(THD *thd, TABLE_LIST **s
         table and successful table creation.
         ...
       */
-      if (tables->create)
+      if (tables->open_table_type)
         continue;
 
       if (action)
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-08-22 00:43:30 +04:00
+++ b/sql/sql_parse.cc	2007-09-04 13:31:58 +04:00
@@ -2206,7 +2206,7 @@ mysql_execute_command(THD *thd)
       if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
       {
         lex->link_first_table_back(create_table, link_to_local);
-        create_table->create= TRUE;
+        create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE;
       }
 
       if (!(res= open_and_lock_tables(thd, lex->query_tables)))
diff -Nrup a/sql/sql_prepare.cc b/sql/sql_prepare.cc
--- a/sql/sql_prepare.cc	2007-07-16 23:31:33 +04:00
+++ b/sql/sql_prepare.cc	2007-09-04 13:31:58 +04:00
@@ -1493,7 +1493,7 @@ static bool mysql_test_create_table(Prep
     if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
     {
       lex->link_first_table_back(create_table, link_to_local);
-      create_table->create= TRUE;
+      create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE;
     }
 
     if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))
diff -Nrup a/sql/sql_view.cc b/sql/sql_view.cc
--- a/sql/sql_view.cc	2007-08-29 23:30:00 +04:00
+++ b/sql/sql_view.cc	2007-09-04 13:31:59 +04:00
@@ -177,17 +177,18 @@ err:
 static bool
 fill_defined_view_parts (THD *thd, TABLE_LIST *view)
 {
+  char key[MAX_DBKEY_LENGTH];
+  uint key_length;
   LEX *lex= thd->lex;
   enum_open_table_action not_used;
   TABLE_LIST decoy;
 
   memcpy (&decoy, view, sizeof (TABLE_LIST));
-  if (!open_table(thd, &decoy, thd->mem_root, &not_used, OPEN_VIEW_NO_PARSE) &&
-      !decoy.view)
-  {
-    /* It's a table */
+  key_length= create_table_def_key(thd, key, view, 0);
+
+  if (open_view_entry(thd, &decoy, decoy.alias, key, key_length,
+                      thd->mem_root, OPEN_VIEW_NO_PARSE))
     return TRUE;
-  }
 
   if (!lex->definer)
   {
@@ -242,70 +243,26 @@ bool mysql_create_view(THD *thd, TABLE_L
   DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
               !lex->param_list.elements && !lex->derived_tables);
 
-  if (mode != VIEW_CREATE_NEW)
-  {
-    if (mode == VIEW_ALTER &&
-        fill_defined_view_parts(thd, view))
-    {
-      res= TRUE;
-      goto err;
-    }
-    sp_cache_invalidate();
-  }
+  /*
+    We can't allow taking exclusive meta-data locks of unlocked view under
+    LOCK TABLES since this might lead to deadlock. Since at the moment we
+    can't really lock view with LOCK TABLES we simply prohibit creation/
+    alteration of views under LOCK TABLES.
+
+    QQ: Theoretically this breaks backward compatibility. So may be it is
+        better to use some alternative approach (e.g. similar to CREATE
+        TABLE which uses trylock?).
+  */
 
-  if (!lex->definer)
+  if (thd->locked_tables)
   {
-    /*
-      DEFINER-clause is missing; we have to create default definer in
-      persistent arena to be PS/SP friendly.
-      If this is an ALTER VIEW then the current user should be set as
-      the definer.
-    */
-    Query_arena original_arena;
-    Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
-
-    if (!(lex->definer= create_default_definer(thd)))
-      res= TRUE;
-
-    if (ps_arena)
-      thd->restore_active_arena(ps_arena, &original_arena);
-
-    if (res)
-      goto err;
+    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+    res= TRUE;
+    goto err;
   }
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   /*
-    check definer of view:
-      - same as current user
-      - current user has SUPER_ACL
-  */
-  if (definer_check_is_needed &&
-      (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 ||
-       my_strcasecmp(system_charset_info,
-                     lex->definer->host.str,
-                     thd->security_ctx->priv_host) != 0))
-  {
-    if (!(thd->security_ctx->master_access & SUPER_ACL))
-    {
-      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
-      res= TRUE;
-      goto err;
-    }
-    else
-    {
-      if (!is_acl_user(lex->definer->host.str,
-                       lex->definer->user.str))
-      {
-        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
-                            ER_NO_SUCH_USER,
-                            ER(ER_NO_SUCH_USER),
-                            lex->definer->user.str,
-                            lex->definer->host.str);
-      }
-    }
-  }
-  /*
     Privilege check for view creation:
     - user has CREATE VIEW privilege on view table
     - user has DROP privilege in case of ALTER VIEW or CREATE OR REPLACE
@@ -401,13 +358,83 @@ bool mysql_create_view(THD *thd, TABLE_L
     }
   }
 #endif
+  
+  lex->link_first_table_back(view, link_to_local);
+  view->open_table_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL;
 
-  if (open_and_lock_tables(thd, tables))
+  if (open_and_lock_tables(thd, lex->query_tables))
   {
+    view= lex->unlink_first_table(&link_to_local);
     res= TRUE;
     goto err;
   }
 
+  view= lex->unlink_first_table(&link_to_local);
+
+  if (mode != VIEW_CREATE_NEW)
+  {
+    if (mode == VIEW_ALTER &&
+        fill_defined_view_parts(thd, view))
+    {
+      res= TRUE;
+      goto err;
+    }
+    sp_cache_invalidate();
+  }
+
+  if (!lex->definer)
+  {
+    /*
+      DEFINER-clause is missing; we have to create default definer in
+      persistent arena to be PS/SP friendly.
+      If this is an ALTER VIEW then the current user should be set as
+      the definer.
+    */
+    Query_arena original_arena;
+    Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
+
+    if (!(lex->definer= create_default_definer(thd)))
+      res= TRUE;
+
+    if (ps_arena)
+      thd->restore_active_arena(ps_arena, &original_arena);
+
+    if (res)
+      goto err;
+  }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  /*
+    check definer of view:
+      - same as current user
+      - current user has SUPER_ACL
+  */
+  if (definer_check_is_needed &&
+      (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 ||
+       my_strcasecmp(system_charset_info,
+                     lex->definer->host.str,
+                     thd->security_ctx->priv_host) != 0))
+  {
+    if (!(thd->security_ctx->master_access & SUPER_ACL))
+    {
+      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+      res= TRUE;
+      goto err;
+    }
+    else
+    {
+      if (!is_acl_user(lex->definer->host.str,
+                       lex->definer->user.str))
+      {
+        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+                            ER_NO_SUCH_USER,
+                            ER(ER_NO_SUCH_USER),
+                            lex->definer->user.str,
+                            lex->definer->host.str);
+      }
+    }
+  }
+#endif
   /*
     check that tables are not temporary  and this VIEW do not used in query
     (it is possible with ALTERing VIEW).
@@ -539,6 +566,10 @@ bool mysql_create_view(THD *thd, TABLE_L
   }
 #endif
 
+  /*
+    QQ: May be it is better to move this before obtaining exclusive
+        MDL/opening tables ?
+  */
   if (wait_if_global_read_lock(thd, 0, 0))
   {
     res= TRUE;
@@ -1443,6 +1474,21 @@ bool mysql_drop_view(THD *thd, TABLE_LIS
   bool error= FALSE;
   enum legacy_db_type not_used;
   DBUG_ENTER("mysql_drop_view");
+
+  /*
+    We can't allow dropping of unlocked view under LOCK TABLES since this
+    might lead to deadlock. But since we can't really lock view with LOCK
+    TABLES we have to simply prohibit dropping of views.
+  */
+
+  if (thd->locked_tables)
+  {
+    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+    DBUG_RETURN(TRUE);
+  }
+
+  if (lock_table_names(thd, views))
+    DBUG_RETURN(TRUE);
 
   VOID(pthread_mutex_lock(&LOCK_open));
   for (view= views; view; view= view->next_local)
diff -Nrup a/sql/table.h b/sql/table.h
--- a/sql/table.h	2007-08-29 23:30:00 +04:00
+++ b/sql/table.h	2007-09-04 13:31:59 +04:00
@@ -1056,11 +1056,13 @@ struct TABLE_LIST
   */
   bool          prelocking_placeholder;
   /*
-    This TABLE_LIST object corresponds to the table to be created
-    so it is possible that it does not exist (used in CREATE TABLE
-    ... SELECT implementation).
+    This TABLE_LIST object corresponds to the table/view which requires
+    special handling/meta-data locking. For example this is a target
+    table in CREATE TABLE ... SELECT so it is possible that it does not
+    exist and we should take exclusive meta-data lock on it in this
+    case.
   */
-  bool          create;
+  enum {NORMAL_OPEN= 0, OPEN_OR_CREATE, TAKE_EXCLUSIVE_MDL} open_table_type;
 
 
   /* View creation context. */
Thread
bk commit into 5.1 tree (dlenev:1.2578) BUG#25144dlenev4 Sep