from http://mysql.bkbits.net:8080/mysql-5.0-community/?PAGE=gnupatch&REV=1.2410.5.12
---
 mysql-test/r/grant.result |   75 ++++++++++++++++++++
 mysql-test/t/grant.test   |  144 +++++++++++++++++++++++++++++++++++++++
 sql/mysql_priv.h          |    2 
 sql/sql_db.cc             |   43 +++++------
 sql/sql_parse.cc          |  167 +++++++++++++++++++++++++++++++++-------------
 sql/sql_show.cc           |    2 
 6 files changed, 362 insertions(+), 71 deletions(-)

--- mysql-test/r/grant.result.orig
+++ mysql-test/r/grant.result
@@ -1000,4 +1000,79 @@ f1	f2
 DROP DATABASE db27878;
 use test;
 DROP TABLE t1;
+DROP DATABASE IF EXISTS mysqltest1;
+DROP DATABASE IF EXISTS mysqltest2;
+CREATE DATABASE mysqltest1;
+CREATE DATABASE mysqltest2;
+GRANT ALL PRIVILEGES ON mysqltest1.* TO mysqltest_1@localhost;
+GRANT SELECT ON mysqltest2.* TO mysqltest_1@localhost;
+CREATE PROCEDURE mysqltest1.p1() SQL SECURITY INVOKER
+SELECT 1;
+
+---> connection: bug27337_con1
+CREATE TABLE t1(c INT);
+ERROR 42000: CREATE command denied to user 'mysqltest_1'@'localhost' for table 't1'
+CALL mysqltest1.p1();
+1
+1
+CREATE TABLE t1(c INT);
+ERROR 42000: CREATE command denied to user 'mysqltest_1'@'localhost' for table 't1'
+
+---> connection: bug27337_con2
+CREATE TABLE t1(c INT);
+ERROR 42000: CREATE command denied to user 'mysqltest_1'@'localhost' for table 't1'
+SHOW TABLES;
+Tables_in_mysqltest2
+
+---> connection: default
+DROP DATABASE mysqltest1;
+DROP DATABASE mysqltest2;
+DROP USER mysqltest_1@localhost;
+DROP DATABASE IF EXISTS mysqltest1;
+DROP DATABASE IF EXISTS mysqltest2;
+CREATE DATABASE mysqltest1;
+CREATE DATABASE mysqltest2;
+CREATE TABLE mysqltest1.t1(c INT);
+CREATE TABLE mysqltest2.t2(c INT);
+GRANT SELECT ON mysqltest1.t1 TO mysqltest_1@localhost;
+GRANT SELECT ON mysqltest2.t2 TO mysqltest_2@localhost;
+
+---> connection: bug27337_con1
+SHOW TABLES FROM mysqltest1;
+Tables_in_mysqltest1
+t1
+PREPARE stmt1 FROM 'SHOW TABLES FROM mysqltest1';
+EXECUTE stmt1;
+Tables_in_mysqltest1
+t1
+
+---> connection: bug27337_con2
+SHOW COLUMNS FROM mysqltest2.t2;
+Field	Type	Null	Key	Default	Extra
+c	int(11)	YES		NULL	
+PREPARE stmt2 FROM 'SHOW COLUMNS FROM mysqltest2.t2';
+EXECUTE stmt2;
+Field	Type	Null	Key	Default	Extra
+c	int(11)	YES		NULL	
+
+---> connection: default
+REVOKE SELECT ON mysqltest1.t1 FROM mysqltest_1@localhost;
+REVOKE SELECT ON mysqltest2.t2 FROM mysqltest_2@localhost;
+
+---> connection: bug27337_con1
+SHOW TABLES FROM mysqltest1;
+ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest1'
+EXECUTE stmt1;
+ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest1'
+
+---> connection: bug27337_con2
+SHOW COLUMNS FROM mysqltest2.t2;
+ERROR 42000: SELECT command denied to user 'mysqltest_2'@'localhost' for table 't2'
+EXECUTE stmt2;
+ERROR 42000: SELECT command denied to user 'mysqltest_2'@'localhost' for table 't2'
+
+---> connection: default
+DROP DATABASE mysqltest1;
+DROP DATABASE mysqltest2;
+DROP USER mysqltest_1@localhost;
 End of 5.0 tests
--- mysql-test/t/grant.test.orig
+++ mysql-test/t/grant.test
@@ -911,4 +911,148 @@ DROP DATABASE db27878;
 use test;
 DROP TABLE t1;
 
+#
+# BUG#27337: Privileges are not restored properly.
+#
+# Actually, the patch for this bugs fixes two problems. So, here are two test
+# cases.
+
+# Test case 1: privileges are not restored properly after calling a stored
+# routine defined with SQL SECURITY INVOKER clause.
+
+# Prepare.
+
+--disable_warnings
+DROP DATABASE IF EXISTS mysqltest1;
+DROP DATABASE IF EXISTS mysqltest2;
+--enable_warnings
+
+CREATE DATABASE mysqltest1;
+CREATE DATABASE mysqltest2;
+
+GRANT ALL PRIVILEGES ON mysqltest1.* TO mysqltest_1@localhost;
+GRANT SELECT ON mysqltest2.* TO mysqltest_1@localhost;
+
+CREATE PROCEDURE mysqltest1.p1() SQL SECURITY INVOKER
+  SELECT 1;
+
+# Test.
+
+--connect (bug27337_con1,localhost,mysqltest_1,,mysqltest2)
+--echo
+--echo ---> connection: bug27337_con1
+
+--error ER_TABLEACCESS_DENIED_ERROR
+CREATE TABLE t1(c INT);
+
+CALL mysqltest1.p1();
+
+--error ER_TABLEACCESS_DENIED_ERROR
+CREATE TABLE t1(c INT);
+
+--disconnect bug27337_con1
+
+--connect (bug27337_con2,localhost,mysqltest_1,,mysqltest2)
+--echo
+--echo ---> connection: bug27337_con2
+
+--error ER_TABLEACCESS_DENIED_ERROR
+CREATE TABLE t1(c INT);
+
+SHOW TABLES;
+
+# Cleanup.
+
+--connection default
+--echo
+--echo ---> connection: default
+
+--disconnect bug27337_con2
+
+DROP DATABASE mysqltest1;
+DROP DATABASE mysqltest2;
+
+DROP USER mysqltest_1@localhost;
+
+# Test case 2: priveleges are not checked properly for prepared statements.
+
+# Prepare.
+
+--disable_warnings
+DROP DATABASE IF EXISTS mysqltest1;
+DROP DATABASE IF EXISTS mysqltest2;
+--enable_warnings
+
+CREATE DATABASE mysqltest1;
+CREATE DATABASE mysqltest2;
+
+CREATE TABLE mysqltest1.t1(c INT);
+CREATE TABLE mysqltest2.t2(c INT);
+
+GRANT SELECT ON mysqltest1.t1 TO mysqltest_1@localhost;
+GRANT SELECT ON mysqltest2.t2 TO mysqltest_2@localhost;
+
+# Test.
+
+--connect (bug27337_con1,localhost,mysqltest_1,,mysqltest1)
+--echo
+--echo ---> connection: bug27337_con1
+
+SHOW TABLES FROM mysqltest1;
+
+PREPARE stmt1 FROM 'SHOW TABLES FROM mysqltest1';
+
+EXECUTE stmt1;
+
+--connect (bug27337_con2,localhost,mysqltest_2,,mysqltest2)
+--echo
+--echo ---> connection: bug27337_con2
+
+SHOW COLUMNS FROM mysqltest2.t2;
+
+PREPARE stmt2 FROM 'SHOW COLUMNS FROM mysqltest2.t2';
+
+EXECUTE stmt2;
+
+--connection default
+--echo
+--echo ---> connection: default
+
+REVOKE SELECT ON mysqltest1.t1 FROM mysqltest_1@localhost;
+REVOKE SELECT ON mysqltest2.t2 FROM mysqltest_2@localhost;
+
+--connection bug27337_con1
+--echo
+--echo ---> connection: bug27337_con1
+
+--error ER_DBACCESS_DENIED_ERROR
+SHOW TABLES FROM mysqltest1;
+
+--error ER_DBACCESS_DENIED_ERROR
+EXECUTE stmt1;
+
+--connection bug27337_con2
+--echo
+--echo ---> connection: bug27337_con2
+
+--error ER_TABLEACCESS_DENIED_ERROR
+SHOW COLUMNS FROM mysqltest2.t2;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+EXECUTE stmt2;
+
+# Cleanup.
+
+--connection default
+--echo
+--echo ---> connection: default
+
+--disconnect bug27337_con2
+
+DROP DATABASE mysqltest1;
+DROP DATABASE mysqltest2;
+
+DROP USER mysqltest_1@localhost;
+
+
 --echo End of 5.0 tests
--- sql/mysql_priv.h.orig
+++ sql/mysql_priv.h
@@ -908,6 +908,8 @@ int fill_schema_schema_privileges(THD *t
 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
 bool get_schema_tables_result(JOIN *join);
+enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
+
 #define is_schema_db(X) \
   !my_strcasecmp(system_charset_info, information_schema_name.str, (X))
 
--- sql/sql_db.cc.orig
+++ sql/sql_db.cc
@@ -1304,30 +1304,27 @@ bool mysql_change_db(THD *thd, const LEX
   DBUG_PRINT("info",("Use database: %s", new_db_file_name.str));
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-  if (!force_switch) /* FIXME: this is BUG#27337. */
+  db_access=
+    test_all_bits(sctx->master_access, DB_ACLS) ?
+    DB_ACLS :
+    acl_get(sctx->host,
+            sctx->ip,
+            sctx->priv_user,
+            new_db_file_name.str,
+            FALSE) | sctx->master_access;
+
+  if (!force_switch &&
+      !(db_access & DB_ACLS) &&
+      (!grant_option || check_grant_db(thd, new_db_file_name.str)))
   {
-    db_access=
-      test_all_bits(sctx->master_access, DB_ACLS) ?
-      DB_ACLS :
-      acl_get(sctx->host,
-              sctx->ip,
-              sctx->priv_user,
-              new_db_file_name.str,
-              FALSE) | sctx->master_access;
-
-    if (!force_switch &&
-        !(db_access & DB_ACLS) &&
-        (!grant_option || check_grant_db(thd, new_db_file_name.str)))
-    {
-      my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
-               sctx->priv_user,
-               sctx->priv_host,
-               new_db_file_name.str);
-      mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR),
-                      sctx->priv_user, sctx->priv_host, new_db_file_name.str);
-      my_free(new_db_file_name.str, MYF(0));
-      DBUG_RETURN(TRUE);
-    }
+    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+             sctx->priv_user,
+             sctx->priv_host,
+             new_db_file_name.str);
+    mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR),
+                    sctx->priv_user, sctx->priv_host, new_db_file_name.str);
+    my_free(new_db_file_name.str, MYF(0));
+    DBUG_RETURN(TRUE);
   }
 #endif
 
--- sql/sql_parse.cc.orig
+++ sql/sql_parse.cc
@@ -2210,7 +2210,8 @@ int prepare_schema_table(THD *thd, LEX *
                          enum enum_schema_tables schema_table_idx)
 {
   DBUG_ENTER("prepare_schema_table");
-  SELECT_LEX *sel= 0;
+  SELECT_LEX *schema_select_lex= NULL;
+
   switch (schema_table_idx) {
   case SCH_SCHEMATA:
 #if defined(DONT_ALLOW_SHOW_COMMANDS)
@@ -2218,11 +2219,9 @@ int prepare_schema_table(THD *thd, LEX *
                ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
     DBUG_RETURN(1);
 #else
-    if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
-	check_global_access(thd, SHOW_DB_ACL))
-      DBUG_RETURN(1);
     break;
 #endif
+
   case SCH_TABLE_NAMES:
   case SCH_TABLES:
   case SCH_VIEWS:
@@ -2232,32 +2231,25 @@ int prepare_schema_table(THD *thd, LEX *
                ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
     DBUG_RETURN(1);
 #else
+    if (lex->select_lex.db == NULL &&
+        thd->copy_db_to(&lex->select_lex.db, NULL))
     {
-      char *db;
-      if (lex->select_lex.db == NULL &&
-          thd->copy_db_to(&lex->select_lex.db, 0))
-      {
-        DBUG_RETURN(1);
-      }
-      db= lex->select_lex.db;
-      remove_escape(db);				// Fix escaped '_'
-      if (check_db_name(db))
-      {
-        my_error(ER_WRONG_DB_NAME, MYF(0), db);
-        DBUG_RETURN(1);
-      }
-      if (check_access(thd, SELECT_ACL, db, &thd->col_access, 0, 0,
-                       is_schema_db(db)))
-        DBUG_RETURN(1);			        /* purecov: inspected */
-      if (!thd->col_access && check_grant_db(thd,db))
-      {
-	my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
-                 thd->security_ctx->priv_user, thd->security_ctx->priv_host,
-                 db);
-	DBUG_RETURN(1);
-      }
-      break;
+      DBUG_RETURN(1);
+    }
+
+    schema_select_lex= new SELECT_LEX();
+    schema_select_lex->db= lex->select_lex.db;
+    schema_select_lex->table_list.first= NULL;
+    remove_escape(schema_select_lex->db); // Fix escaped '_'
+
+    if (check_db_name(schema_select_lex->db))
+    {
+      my_error(ER_WRONG_DB_NAME, MYF(0), schema_select_lex->db);
+      DBUG_RETURN(1);
     }
+
+
+    break;
 #endif
   case SCH_COLUMNS:
   case SCH_STATISTICS:
@@ -2266,28 +2258,23 @@ int prepare_schema_table(THD *thd, LEX *
                ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
     DBUG_RETURN(1);
 #else
-    if (table_ident)
     {
+      DBUG_ASSERT(table_ident);
+
       TABLE_LIST **query_tables_last= lex->query_tables_last;
-      sel= new SELECT_LEX();
+      schema_select_lex= new SELECT_LEX();
       /* 'parent_lex' is used in init_query() so it must be before it. */
-      sel->parent_lex= lex;
-      sel->init_query();
-      if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, 
-                                 (List<String> *) 0, (List<String> *) 0))
+      schema_select_lex->parent_lex= lex;
+      schema_select_lex->init_query();
+      if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
+                                  (List<String> *) 0, (List<String> *) 0))
         DBUG_RETURN(1);
       lex->query_tables_last= query_tables_last;
-      TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
-      char *db= table_list->db;
-      remove_escape(db);			// Fix escaped '_'
-      remove_escape(table_list->table_name);
-      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
-                       &table_list->grant.privilege, 0, 0,
-                       test(table_list->schema_table)))
-        DBUG_RETURN(1);				/* purecov: inspected */
-      if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
-                                      UINT_MAX, 0))
-        DBUG_RETURN(1);
+
+      TABLE_LIST *dst_table= (TABLE_LIST*) schema_select_lex->table_list.first;
+      remove_escape(dst_table->db);			// Fix escaped '_'
+      remove_escape(dst_table->table_name);
+
       break;
     }
 #endif
@@ -2314,7 +2301,7 @@ int prepare_schema_table(THD *thd, LEX *
     DBUG_RETURN(1);
   }
   TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
-  table_list->schema_select_lex= sel;
+  table_list->schema_select_lex= schema_select_lex;
   table_list->schema_table_reformed= 1;
   statistic_increment(thd->status_var.com_stat[lex->orig_sql_command],
                       &LOCK_status);
@@ -5318,6 +5305,83 @@ bool check_global_access(THD *thd, ulong
 }
 
 
+static bool check_show_access(THD *thd, TABLE_LIST *table)
+{
+  switch (get_schema_table_idx(table->schema_table))
+  {
+  case SCH_SCHEMATA:
+    return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
+           check_global_access(thd, SHOW_DB_ACL);
+
+  case SCH_TABLE_NAMES:
+  case SCH_TABLES:
+  case SCH_VIEWS:
+  case SCH_TRIGGERS:
+    {
+      const char *dst_db_name= table->schema_select_lex->db;
+
+      DBUG_ASSERT(dst_db_name);
+
+      if (check_access(thd, SELECT_ACL, dst_db_name,
+                       &thd->col_access, FALSE, FALSE,
+                       is_schema_db(dst_db_name)))
+      {
+        return TRUE;
+      }
+
+      if (!thd->col_access && check_grant_db(thd, dst_db_name))
+      {
+        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+                 thd->security_ctx->priv_user,
+                 thd->security_ctx->priv_host,
+                 dst_db_name);
+        return TRUE;
+      }
+
+      return FALSE;
+    }
+
+  case SCH_COLUMNS:
+  case SCH_STATISTICS:
+    {
+      TABLE_LIST *dst_table=
+        (TABLE_LIST *) table->schema_select_lex->table_list.first;
+
+      DBUG_ASSERT(dst_table);
+
+      if (check_access(thd, SELECT_ACL | EXTRA_ACL,
+                       dst_table->db,
+                       &dst_table->grant.privilege,
+                       FALSE, FALSE,
+                       test(dst_table->schema_table)))
+      {
+        return FALSE;
+      }
+
+      return grant_option &&
+             check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE);
+    }
+
+  case SCH_OPEN_TABLES:
+  case SCH_VARIABLES:
+  case SCH_STATUS:
+  case SCH_PROCEDURES:
+  case SCH_CHARSETS:
+  case SCH_COLLATIONS:
+  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
+  case SCH_USER_PRIVILEGES:
+  case SCH_SCHEMA_PRIVILEGES:
+  case SCH_TABLE_PRIVILEGES:
+  case SCH_COLUMN_PRIVILEGES:
+  case SCH_TABLE_CONSTRAINTS:
+  case SCH_KEY_COLUMN_USAGE:
+    break;
+  }
+
+  return FALSE;
+}
+
+
 /*
   Check the privilege for all used tables.
 
@@ -5376,7 +5440,16 @@ check_table_access(THD *thd, ulong want_
        Remove SHOW_VIEW_ACL, because it will be checked during making view
      */
     tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
-    if (tables->derived || tables->schema_table ||
+
+    if (tables->schema_table_reformed)
+    {
+      if (check_show_access(thd, tables))
+        goto deny;
+
+      continue;
+    }
+
+    if (tables->derived ||
         (tables->table && (int)tables->table->s->tmp_table) ||
         my_tz_check_n_skip_implicit_tables(&tables,
                                            thd->lex->time_zone_tables_used))
--- sql/sql_show.cc.orig
+++ sql/sql_show.cc
@@ -2140,7 +2140,7 @@ int get_all_tables(THD *thd, TABLE_LIST 
   */
   thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
 
-  if (lsel)
+  if (lsel && lsel->table_list.first)
   {
     TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
     bool res;
