diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result
index 1b720be940302abcbdf1eb2a736808afb4e9c0f6..4df8086c84eeec46fcf6b1c9699a022fccd11e28 100644
--- a/mysql-test/r/sp-destruct.result
+++ b/mysql-test/r/sp-destruct.result
@@ -72,6 +72,12 @@ drop trigger t1_ai;
 create trigger t1_ai after insert on t1 for each row call bug14233_3();
 insert into t1 values (0);
 ERROR HY000: Failed to load routine test.bug14233_3. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
-delete from mysql.proc where name like 'bug14233%';
 drop trigger t1_ai;
 drop table t1;
+drop function bug14233_1;
+drop function bug14233_2;
+drop procedure bug14233_3;
+show procedure status;
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+show function status;
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
diff --git a/mysql-test/t/sp-destruct.test b/mysql-test/t/sp-destruct.test
index ac74a1be9b07e6a95ad71cb79a4ea5b13da780d2..fb368a1e621a1b0b3713b8e120f60760f82f50ce 100644
--- a/mysql-test/t/sp-destruct.test
+++ b/mysql-test/t/sp-destruct.test
@@ -119,6 +119,15 @@ create trigger t1_ai after insert on t1 for each row call bug14233_3();
 insert into t1 values (0);
 
 # Clean-up
-delete from mysql.proc where name like 'bug14233%';
 drop trigger t1_ai;
 drop table t1;
+
+#
+# BUG#16303: erroneus stored procedures and functions should be droppable
+#
+drop function bug14233_1;
+drop function bug14233_2;
+drop procedure bug14233_3;
+# Assert: These should show nothing.
+show procedure status;
+show function status;
diff --git a/sql/sp.cc b/sql/sp.cc
index 37a9c02124e345e794c87c978ad48c92075b30cf..fe249141feafca30d75ef8fcef5585ca5760d425 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1002,22 +1002,26 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
 }
 
 
+/*
+  This is used by sql_acl.cc:mysql_routine_grant() and is used to find
+  the routines in 'routines'.
+*/
 
 int
-sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
+sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error)
 {
-  TABLE_LIST *table;
+  TABLE_LIST *routine;
   bool result= 0;
   DBUG_ENTER("sp_exists_routine");
-  for (table= tables; table; table= table->next_global)
+  for (routine= routines; routine; routine= routine->next_global)
   {
     sp_name *name;
     LEX_STRING lex_db;
     LEX_STRING lex_name;
-    lex_db.length= strlen(table->db);
-    lex_name.length= strlen(table->table_name);
-    lex_db.str= thd->strmake(table->db, lex_db.length);
-    lex_name.str= thd->strmake(table->table_name, lex_name.length);
+    lex_db.length= strlen(routine->db);
+    lex_name.length= strlen(routine->table_name);
+    lex_db.str= thd->strmake(routine->db, lex_db.length);
+    lex_name.str= thd->strmake(routine->table_name, lex_name.length);
     name= new sp_name(lex_db, lex_name);
     name->init_qname(thd);
     if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
@@ -1034,7 +1038,7 @@ sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
       if (!no_error)
       {
 	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE", 
-		 table->table_name);
+		 routine->table_name);
 	DBUG_RETURN(-1);
       }
       DBUG_RETURN(0);
@@ -1044,6 +1048,39 @@ sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
 }
 
 
+/*
+  Check if a routine exists in the mysql.proc table, without actually
+  parsing the definition. (Used for dropping)
+
+  SYNOPSIS
+    sp_routine_exists_in_table()
+      thd        - thread context
+      name       - name of procedure
+
+  RETURN VALUE
+    0     - Success
+    non-0 - Error;  SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND
+*/
+
+int
+sp_routine_exists_in_table(THD *thd, int type, sp_name *name)
+{
+  TABLE *table;
+  int ret;
+  Open_tables_state open_tables_state_backup;
+
+  if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
+    ret= SP_OPEN_TABLE_FAILED;
+  else
+  {
+    if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
+      ret= SP_KEY_NOT_FOUND;
+    close_proc_table(thd, &open_tables_state_backup);
+  }
+  return ret;
+}
+
+
 int
 sp_create_procedure(THD *thd, sp_head *sp)
 {
diff --git a/sql/sp.h b/sql/sp.h
index 53343e0fb2596ac21b23f4c4e9a12efb3e055df1..2587a9b115ac4d10b9f1f506b8fe9762bf5ed539 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -40,7 +40,10 @@ sp_find_routine(THD *thd, int type, sp_name *name,
                 sp_cache **cp, bool cache_only);
 
 int
-sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
+sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
+
+int
+sp_routine_exists_in_table(THD *thd, int type, sp_name *name);
 
 int
 sp_create_procedure(THD *thd, sp_head *sp);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 7bc5aac270b1b2de6ff779868530d19750e5b0c1..c67ce383398c1ac451e86908aa5c37056078e1b9 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -3030,7 +3030,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
 
   if (!revoke_grant)
   {
-    if (sp_exists_routine(thd, table_list, is_proc, no_error)<0)
+    if (sp_exist_routines(thd, table_list, is_proc, no_error)<0)
       DBUG_RETURN(TRUE);
   }
 
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a61815c82021742398f38b36d5624c08ea32abeb..978cab704c01c54a1ba10946eefcb31406ad807a 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4445,21 +4445,17 @@ mysql_execute_command(THD *thd)
   case SQLCOM_DROP_PROCEDURE:
   case SQLCOM_DROP_FUNCTION:
     {
-      sp_head *sp;
       int result;
-      char *db, *name;
+      int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
+                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
 
-      if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
-        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
-                            &thd->sp_proc_cache, FALSE);
-      else
-        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
-                            &thd->sp_func_cache, FALSE);
+      result= sp_routine_exists_in_table(thd, type, lex->spname);
       mysql_reset_errors(thd, 0);
-      if (sp)
+      if (result == SP_OK)
       {
-        db= thd->strdup(sp->m_db.str);
-	name= thd->strdup(sp->m_name.str);
+        char *db= lex->spname->m_db.str;
+	char *name= lex->spname->m_name.str;
+
 	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
                                  lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
           goto error;