diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
index 64d32ba334c8b98b9efd6d5b2cb4ed922edffd58..287ccd3e0486b606d2cbd4500958cbfc438e51f9 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -1437,3 +1437,51 @@ set GLOBAL query_cache_type=default;
 set GLOBAL query_cache_limit=default;
 set GLOBAL query_cache_min_res_unit=default;
 set GLOBAL query_cache_size= default;
+drop database if exists db1;
+drop database if exists db2;
+set GLOBAL query_cache_size=15*1024*1024;
+create database db1;
+use db1;
+create table t1(c1 int)engine=myisam;
+insert into t1(c1) values (1);
+select * from db1.t1 f;
+c1
+1
+show status like 'Qcache_queries_in_cache';
+Variable_name	Value
+Qcache_queries_in_cache	1
+rename schema db1 to db2;
+show status like 'Qcache_queries_in_cache';
+Variable_name	Value
+Qcache_queries_in_cache	0
+drop database db2;
+set global query_cache_size=default;
+drop database if exists db1;
+drop database if exists db3;
+set GLOBAL query_cache_size=15*1024*1024;
+create database db1;
+create database db3;
+use db1;
+create table t1(c1 int) engine=myisam;
+use db3;
+create table t1(c1 int) engine=myisam;
+use db1;
+insert into t1(c1) values (1);
+use mysql;
+select * from db1.t1;
+c1
+1
+select c1+1 from db1.t1;
+c1+1
+2
+select * from db3.t1;
+c1
+show status like 'Qcache_queries_in_cache';
+Variable_name	Value
+Qcache_queries_in_cache	3
+rename schema db1 to db2;
+show status like 'Qcache_queries_in_cache';
+Variable_name	Value
+Qcache_queries_in_cache	1
+drop database db2;
+drop database db3;
diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
index b878c1415466d8454e0b1cdd778b69e834d3e812..b00fb0fe03546986cf8df46dd2522dfac9091785 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -1000,3 +1000,48 @@ set GLOBAL query_cache_min_res_unit=default;
 set GLOBAL query_cache_size= default;
 
 # End of 5.0 tests
+
+
+#
+# Bug #28211 RENAME DATABASE and query cache don't play nicely together
+#
+--disable_warnings
+drop database if exists db1;
+drop database if exists db2;
+--enable_warnings
+set GLOBAL query_cache_size=15*1024*1024;
+create database db1;
+use db1;
+create table t1(c1 int)engine=myisam;
+insert into t1(c1) values (1);
+select * from db1.t1 f;
+show status like 'Qcache_queries_in_cache';
+rename schema db1 to db2;
+show status like 'Qcache_queries_in_cache';
+drop database db2;
+set global query_cache_size=default;
+
+--disable_warnings
+drop database if exists db1;
+drop database if exists db3;
+--enable_wearnings
+set GLOBAL query_cache_size=15*1024*1024;
+create database db1;
+create database db3;
+use db1;
+create table t1(c1 int) engine=myisam;
+use db3;
+create table t1(c1 int) engine=myisam;
+use db1;
+insert into t1(c1) values (1);
+use mysql;
+select * from db1.t1;
+select c1+1 from db1.t1;
+select * from db3.t1;
+show status like 'Qcache_queries_in_cache';
+rename schema db1 to db2;
+show status like 'Qcache_queries_in_cache';
+drop database db2;
+drop database db3;
+
+# End of 5.1 tests
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index bcc4f6a71d3d80da58fad8cb3ce91435eebc8650..068dc6e0cd9686cd082a07c0a6d94a06223e0528 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1445,40 +1445,62 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32  key_length,
   DBUG_VOID_RETURN;
 }
 
-/*
-  Remove all cached queries that uses the given database
+/**
+   @brief Remove all cached queries that uses the given database
 */
-
 void Query_cache::invalidate(char *db)
 {
+  bool restart= FALSE;
   DBUG_ENTER("Query_cache::invalidate (db)");
   STRUCT_LOCK(&structure_guard_mutex);
   if (query_cache_size > 0 && !flush_in_progress)
   {
-    DUMP(this);
-  restart_search:
     if (tables_blocks)
     {
-      Query_cache_block *curr= tables_blocks;
-      Query_cache_block *next;
-      do
-      {
-        next= curr->next;
-        if (strcmp(db, (char*)(curr->table()->db())) == 0)
-          invalidate_table(curr);
+      Query_cache_block *table_block = tables_blocks;
+      do {
+        restart= FALSE;
+        do
+        {
+          Query_cache_block *next= table_block->next;
+          Query_cache_table *table = table_block->table();
+          if (strcmp(table->db(),db) == 0)
+            invalidate_table(table_block);
+
+          table_block= next;
+
+          /*
+            If our root node to used tables became null then the last element
+            in the table list was removed when a query was invalidated;
+            Terminate the search.
+          */
+          if (tables_blocks == 0)
+          {
+            table_block= tables_blocks;
+          }
+          /*
+            If the iterated list has changed underlying structure;
+            we need to restart the search.
+          */
+          else if (table_block->type == Query_cache_block::FREE)
+          {
+            restart= TRUE;
+            table_block= tables_blocks;
+          }
+          /* 
+            The used tables are linked in a circular list;
+            loop until we return to the begining.
+          */
+        } while (table_block != tables_blocks);
         /*
-          invalidate_table can freed block on which point 'next' (if
-          table of this block used only in queries which was deleted
-          by invalidate_table). As far as we do not allocate new blocks
-          and mark all headers of freed blocks as 'FREE' (even if they are
-          merged with other blocks) we can just test type of block
-          to be sure that block is not deleted
+           Invalidating a table will also mean that all cached queries using
+           this table also will be invalidated. This will in turn change the
+           list of tables associated with these queries and the linked list of
+           used table will be changed. Because of this we might need to restart
+           the search when a table has been invalidated.
         */
-        if (next->type == Query_cache_block::FREE)
-          goto restart_search;
-        curr= next;
-      } while (curr != tables_blocks);
-    }
+      } while (restart);
+    } // end if( tables_blocks )
   }
   STRUCT_UNLOCK(&structure_guard_mutex);
 
@@ -2412,6 +2434,7 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
                   (ulong) tables_used->table,
                   tables_used->table->s->table_cache_key.length,
                   (ulong) tables_used->table->s->table_cache_key.str));
+
       if (!insert_table(tables_used->table->s->table_cache_key.length,
                         tables_used->table->s->table_cache_key.str,
                         block_table,
@@ -2478,9 +2501,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
 
   n= register_tables_from_list(tables_used, 0, block_table);
 
-  if (n)
+  if (n==0)
   {
-    DBUG_PRINT("qcache", ("failed at table %d", (int) n));
     /* Unlink the tables we allocated above */
     for (Query_cache_block_table *tmp = block->table(0) ;
 	 tmp != block_table;
@@ -2970,8 +2992,11 @@ Query_cache::double_linked_list_exclude(Query_cache_block *point,
   {
     point->next->prev = point->prev;
     point->prev->next = point->next;
+    /*
+       If the root is removed; select a new root
+    */
     if (point == *list_pointer)
-      *list_pointer = point->next;
+      *list_pointer= point->next;
   }
   DBUG_VOID_RETURN;
 }
@@ -3769,7 +3794,7 @@ void Query_cache::tables_dump()
       Query_cache_table *table = table_block->table();
       DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table()));
       table_block = table_block->next;
-    } while ( table_block != tables_blocks);
+    } while (table_block != tables_blocks);
   }
   else
     DBUG_PRINT("qcache", ("no tables in list"));