diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result
index d7078d5087de410ca861b2613dae4a900719fb8b..ee72fde7324732903b0a7381fcf153b5071b0f64 100644
--- a/mysql-test/r/sp-security.result
+++ b/mysql-test/r/sp-security.result
@@ -23,10 +23,10 @@ root@localhost	1
 select db();
 db()
 db1_secret
-grant execute on db1_secret.stamp to user1@'%';
-grant execute on db1_secret.db to user1@'%';
-grant execute on db1_secret.stamp to ''@'%';
-grant execute on db1_secret.db to ''@'%';
+grant execute on procedure db1_secret.stamp to user1@'%';
+grant execute on function db1_secret.db to user1@'%';
+grant execute on procedure db1_secret.stamp to ''@'%';
+grant execute on function db1_secret.db to ''@'%';
 call db1_secret.stamp(2);
 select db1_secret.db();
 db1_secret.db()
@@ -105,8 +105,8 @@ select * from t2;
 s1
 0
 2
-grant usage on db2.q to user2@localhost with grant option;
-grant execute on db2.q to user1@localhost;
+grant usage on procedure db2.q to user2@localhost with grant option;
+grant execute on procedure db2.q to user1@localhost;
 use db2;
 call q();
 select * from t2;
@@ -117,9 +117,9 @@ s1
 alter procedure p modifies sql data;
 drop procedure p;
 alter procedure q modifies sql data;
-ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q'
+ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db2.q'
 drop procedure q;
-ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q'
+ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db2.q'
 use db2;
 alter procedure q modifies sql data;
 drop procedure q;
@@ -141,52 +141,52 @@ create database sptest;
 create table t1 ( u varchar(64), i int );
 create procedure sptest.p1(i int) insert into test.t1 values (user(), i);
 grant insert on t1 to usera@localhost;
-grant execute on sptest.p1 to usera@localhost;
+grant execute on procedure sptest.p1 to usera@localhost;
 show grants for usera@localhost;
 Grants for usera@localhost
 GRANT USAGE ON *.* TO 'usera'@'localhost'
 GRANT INSERT ON `test`.`t1` TO 'usera'@'localhost'
-GRANT EXECUTE ON `sptest`.`p1` TO 'usera'@'localhost'
-grant execute on sptest.p1 to userc@localhost with grant option;
+GRANT EXECUTE ON PROCEDURE `sptest`.`p1` TO 'usera'@'localhost'
+grant execute on procedure sptest.p1 to userc@localhost with grant option;
 show grants for userc@localhost;
 Grants for userc@localhost
 GRANT USAGE ON *.* TO 'userc'@'localhost'
-GRANT EXECUTE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION
+GRANT EXECUTE ON PROCEDURE `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION
 call sptest.p1(1);
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 ERROR 42000: grant command denied to user 'usera'@'localhost' for routine 'sptest.p1'
 drop procedure sptest.p1;
-ERROR 42000: alter procedure command denied to user 'usera'@'localhost' for routine 'sptest.p1'
+ERROR 42000: alter routine command denied to user 'usera'@'localhost' for routine 'sptest.p1'
 call sptest.p1(2);
 ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1'
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1'
 drop procedure sptest.p1;
-ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+ERROR 42000: alter routine command denied to user 'userb'@'localhost' for routine 'sptest.p1'
 call sptest.p1(3);
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 drop procedure sptest.p1;
-ERROR 42000: alter procedure command denied to user 'userc'@'localhost' for routine 'sptest.p1'
+ERROR 42000: alter routine command denied to user 'userc'@'localhost' for routine 'sptest.p1'
 call sptest.p1(4);
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 ERROR 42000: grant command denied to user 'userb'@'localhost' for routine 'sptest.p1'
 drop procedure sptest.p1;
-ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+ERROR 42000: alter routine command denied to user 'userb'@'localhost' for routine 'sptest.p1'
 select * from t1;
 u	i
 usera@localhost	1
 userc@localhost	3
 userb@localhost	4
-grant all privileges on sptest.p1 to userc@localhost;
+grant all privileges on procedure sptest.p1 to userc@localhost;
 show grants for userc@localhost;
 Grants for userc@localhost
 GRANT USAGE ON *.* TO 'userc'@'localhost'
-GRANT EXECUTE, ALTER ROUTINE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION
+GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION
 show grants for userb@localhost;
 Grants for userb@localhost
 GRANT USAGE ON *.* TO 'userb'@'localhost'
-GRANT EXECUTE ON `sptest`.`p1` TO 'userb'@'localhost'
-revoke all privileges on sptest.p1 from userb@localhost;
+GRANT EXECUTE ON PROCEDURE `sptest`.`p1` TO 'userb'@'localhost'
+revoke all privileges on procedure sptest.p1 from userb@localhost;
 show grants for userb@localhost;
 Grants for userb@localhost
 GRANT USAGE ON *.* TO 'userb'@'localhost'
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result
index f3f019e43bad0a998ccc8327e10dd23689eb5a71..159e9d15f290e50d583cb04c68493bd2ad8e089a 100644
--- a/mysql-test/r/system_mysql_db.result
+++ b/mysql-test/r/system_mysql_db.result
@@ -152,10 +152,11 @@ procs_priv	CREATE TABLE `procs_priv` (
   `Db` char(64) collate utf8_bin NOT NULL default '',
   `User` char(16) collate utf8_bin NOT NULL default '',
   `Routine_name` char(64) collate utf8_bin NOT NULL default '',
+  `Routine_type` enum('FUNCTION','PROCEDURE') collate utf8_bin NOT NULL default 'FUNCTION',
   `Grantor` char(77) collate utf8_bin NOT NULL default '',
-  `Timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
   `Proc_priv` set('Execute','Alter Routine','Grant') character set utf8 NOT NULL default '',
-  PRIMARY KEY  (`Host`,`Db`,`User`,`Routine_name`),
+  `Timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
+  PRIMARY KEY  (`Host`,`Db`,`User`,`Routine_name`,`Routine_type`),
   KEY `Grantor` (`Grantor`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges'
 show create table proc;
diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test
index 5a8dfc54920821107a7e4fa268cbedd691d39c77..e1d8043ccdafcb0a80199d7c001bf10c884bff6a 100644
--- a/mysql-test/t/sp-security.test
+++ b/mysql-test/t/sp-security.test
@@ -43,10 +43,10 @@ call stamp(1);
 select * from t1;
 select db();
 
-grant execute on db1_secret.stamp to user1@'%';
-grant execute on db1_secret.db to user1@'%';
-grant execute on db1_secret.stamp to ''@'%';
-grant execute on db1_secret.db to ''@'%';
+grant execute on procedure db1_secret.stamp to user1@'%';
+grant execute on function db1_secret.db to user1@'%';
+grant execute on procedure db1_secret.stamp to ''@'%';
+grant execute on function db1_secret.db to ''@'%';
 
 connect (con2user1,localhost,user1,,);
 connect (con3anon,localhost,anon,,);
@@ -183,10 +183,10 @@ call q();
 select * from t2;
 
 connection con1root;
-grant usage on db2.q to user2@localhost with grant option;
+grant usage on procedure db2.q to user2@localhost with grant option;
 
 connection con4user2;
-grant execute on db2.q to user1@localhost;
+grant execute on procedure db2.q to user1@localhost;
 
 connection con2user1;
 use db2;
@@ -245,9 +245,9 @@ create database sptest;
 create table t1 ( u varchar(64), i int );
 create procedure sptest.p1(i int) insert into test.t1 values (user(), i);
 grant insert on t1 to usera@localhost;
-grant execute on sptest.p1 to usera@localhost;
+grant execute on procedure sptest.p1 to usera@localhost;
 show grants for usera@localhost;
-grant execute on sptest.p1 to userc@localhost with grant option;
+grant execute on procedure sptest.p1 to userc@localhost with grant option;
 show grants for userc@localhost;
 
 connect (con2usera,localhost,usera,,);
@@ -257,7 +257,7 @@ connect (con4userc,localhost,userc,,);
 connection con2usera;
 call sptest.p1(1);
 --error 1370
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 --error 1370
 drop procedure sptest.p1;
 
@@ -265,32 +265,32 @@ connection con3userb;
 --error 1370
 call sptest.p1(2);
 --error 1370
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 --error 1370
 drop procedure sptest.p1;
 
 connection con4userc;
 call sptest.p1(3);
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 --error 1370
 drop procedure sptest.p1;
 
 connection con3userb;
 call sptest.p1(4);
 --error 1370
-grant execute on sptest.p1 to userb@localhost;
+grant execute on procedure sptest.p1 to userb@localhost;
 --error 1370
 drop procedure sptest.p1;
 
 connection con1root;
 select * from t1;
 
-grant all privileges on sptest.p1 to userc@localhost;
+grant all privileges on procedure sptest.p1 to userc@localhost;
 show grants for userc@localhost;
 show grants for userb@localhost;
 
 connection con4userc;
-revoke all privileges on sptest.p1 from userb@localhost;
+revoke all privileges on procedure sptest.p1 from userb@localhost;
 
 connection con1root;
 show grants for userb@localhost;
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh
index 0eb14cd5e6574e15362a00925b1a2dbc497a14dd..a8f6c02b05751118cd00e77e6b1f05ea695afc2c 100644
--- a/scripts/mysql_create_system_tables.sh
+++ b/scripts/mysql_create_system_tables.sh
@@ -255,10 +255,11 @@ then
   c_pp="$c_pp   Db char(64) binary DEFAULT '' NOT NULL,"
   c_pp="$c_pp   User char(16) binary DEFAULT '' NOT NULL,"
   c_pp="$c_pp   Routine_name char(64) binary DEFAULT '' NOT NULL,"
+  c_pp="$c_pp   Routine_type enum('FUNCTION','PROCEDURE') NOT NULL,"
   c_pp="$c_pp   Grantor char(77) DEFAULT '' NOT NULL,"
-  c_pp="$c_pp   Timestamp timestamp(14),"
   c_pp="$c_pp   Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL,"
-  c_pp="$c_pp   PRIMARY KEY (Host,Db,User,Routine_name),"
+  c_pp="$c_pp   Timestamp timestamp(14),"
+  c_pp="$c_pp   PRIMARY KEY (Host,Db,User,Routine_name,Routine_type),"
   c_pp="$c_pp   KEY Grantor (Grantor)"
   c_pp="$c_pp ) engine=MyISAM"
   c_pp="$c_pp CHARACTER SET utf8 COLLATE utf8_bin"
diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql
index 292720371c8bfcc7b74cf0bac0f1fcc55da9bf4f..68b31cf1519b26d39a27d19aa0888cdac1a8c457 100644
--- a/scripts/mysql_fix_privilege_tables.sql
+++ b/scripts/mysql_fix_privilege_tables.sql
@@ -67,6 +67,10 @@ ALTER TABLE tables_priv
 ALTER TABLE procs_priv ENGINE=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;
 ALTER TABLE procs_priv
   modify Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL;
+ALTER TABLE procs_priv
+  add Routine_type enum('FUNCTION','PROCEDURE') COLLATE utf8_general_ci NOT NULL AFTER Routine_name;
+ALTER TABLE procs_priv
+  modify Timestamp timestamp(14) AFTER Proc_priv;
 
 CREATE TABLE IF NOT EXISTS columns_priv (
   Host char(60) DEFAULT '' NOT NULL,
@@ -316,10 +320,11 @@ Host char(60) binary DEFAULT '' NOT NULL,
 Db char(64) binary DEFAULT '' NOT NULL,
 User char(16) binary DEFAULT '' NOT NULL,
 Routine_name char(64) binary DEFAULT '' NOT NULL,
+Routine_type enum('FUNCTION','PROCEDURE') NOT NULL,
 Grantor char(77) DEFAULT '' NOT NULL,
-Timestamp timestamp(14),
 Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL,
-PRIMARY KEY (Host,Db,User,Routine_name),
+Timestamp timestamp(14),
+PRIMARY KEY (Host,Db,User,Routine_name,Routine_type),
 KEY Grantor (Grantor)
 ) CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';
 
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 2d537b1cccfbecb9f2873af8ad6cec6131befc14..0934f7844d5ca2306049c0a0d64350d8004f1e9d 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -4771,13 +4771,13 @@ Item_func_sp::execute(Item **itp)
 #endif
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-  if (check_procedure_access(thd, EXECUTE_ACL, 
-			     m_sp->m_db.str, m_sp->m_name.str, 0))
+  if (check_routine_access(thd, EXECUTE_ACL, 
+			   m_sp->m_db.str, m_sp->m_name.str, 0, 0))
       DBUG_RETURN(-1);
   sp_change_security_context(thd, m_sp, &save_ctx);
   if (save_ctx.changed && 
-      check_procedure_access(thd, EXECUTE_ACL, 
-			     m_sp->m_db.str, m_sp->m_name.str, 0))
+      check_routine_access(thd, EXECUTE_ACL, 
+			   m_sp->m_db.str, m_sp->m_name.str, 0, 0))
   {
     sp_restore_security_context(thd, m_sp, &save_ctx);
     thd->client_capabilities|= old_client_capabilites &  CLIENT_MULTI_RESULTS;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 25490b04ab30a50d2ba604a2d621b2efa7699f27..5a4bed1abcc782c560880289732e63995e20c72c 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -473,12 +473,12 @@ void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0,
                          TABLE *stopper= 0);
 bool check_one_table_access(THD *thd, ulong privilege,
 			   TABLE_LIST *tables);
-bool check_procedure_access(THD *thd,ulong want_access,char *db,char *name,
-			    bool no_errors);
+bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
+			  bool is_proc, bool no_errors);
 bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
 bool check_merge_table_access(THD *thd, char *db,
 			      TABLE_LIST *table_list);
-bool check_some_routine_access(THD *thd, const char *db, const char *name);
+bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
 bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
 bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count);
 bool mysql_multi_update_prepare(THD *thd);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 2ffcd45b1a7c07be433a96ac1cb53249949849a9..cfd50feb1a7680d193cbedf030ecd063db70603f 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1111,7 +1111,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
                  (!strcmp(sp->m_definer_user.str, thd->priv_user) &&
                   !strcmp(sp->m_definer_host.str, thd->priv_host)));
   if (!*full_access)
-    return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str);
+    return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str,
+                                     sp->m_type == TYPE_ENUM_PROCEDURE);
   return 0;
 }
 
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index ca9ba7611e69327a558b1524579a600fb34968b5..92a482193f57d90c3320849853b4c45ad0131ed8 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -60,7 +60,7 @@ static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
 static MEM_ROOT mem, memex;
 static bool initialized=0;
 static bool allow_all_hosts=1;
-static HASH acl_check_hosts, column_priv_hash, proc_priv_hash;
+static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
 static DYNAMIC_ARRAY acl_wild_hosts;
 static hash_filo *acl_cache;
 static uint grant_version=0; /* Version of priv tables. incremented by acl_init */
@@ -2136,11 +2136,12 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
 
 
 inline GRANT_NAME *
-proc_hash_search(const char *host, const char *ip, const char *db,
-                 const char *user, const char *tname, bool exact)
+routine_hash_search(const char *host, const char *ip, const char *db,
+                 const char *user, const char *tname, bool proc, bool exact)
 {
-  return (GRANT_TABLE*) name_hash_search(&proc_priv_hash, host, ip, db,
-					 user, tname, exact);
+  return (GRANT_TABLE*)
+    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
+		     host, ip, db, user, tname, exact);
 }
 
 
@@ -2466,16 +2467,17 @@ table_error:
 }
 
 
-static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
+static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
 			      TABLE *table, const LEX_USER &combo,
-			      const char *db, const char *proc_name,
-			      ulong rights, bool revoke_grant)
+			      const char *db, const char *routine_name,
+			      bool is_proc, ulong rights, bool revoke_grant)
 {
   char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
   int old_row_exists= 1;
   int error=0;
   ulong store_proc_rights;
-  DBUG_ENTER("replace_proc_table");
+  byte *key;
+  DBUG_ENTER("replace_routine_table");
 
   if (!initialized)
   {
@@ -2499,7 +2501,10 @@ static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
   table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
   table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
   table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
-  table->field[3]->store(proc_name,(uint) strlen(proc_name), &my_charset_latin1);
+  table->field[3]->store(routine_name,(uint) strlen(routine_name),
+                         &my_charset_latin1);
+  table->field[4]->store((longlong)(is_proc ? 
+                         TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION));
   store_record(table,record[1]);			// store at pos 1
 
   if (table->file->index_read_idx(table->record[0],0,
@@ -2514,7 +2519,7 @@ static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
     if (revoke_grant)
     { // no row, no revoke
       my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
-               combo.user.str, combo.host.str, proc_name);
+               combo.user.str, combo.host.str, routine_name);
       DBUG_RETURN(-1);
     }
     old_row_exists= 0;
@@ -2539,7 +2544,7 @@ static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
     }
   }
 
-  table->field[4]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
+  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
   table->field[6]->store((longlong) store_proc_rights);
   rights=fix_rights_for_procedure(store_proc_rights);
 
@@ -2566,7 +2571,7 @@ static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
   }
   else
   {
-    hash_delete(&proc_priv_hash,(byte*) grant_name);
+    hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
   }
   DBUG_RETURN(0);
 
@@ -2841,12 +2846,13 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
 
 
 /*
-  Store procedure level grants in the privilege tables
+  Store routine level grants in the privilege tables
 
   SYNOPSIS
-    mysql_procedure_grant()
+    mysql_routine_grant()
     thd			Thread handle
-    table_list		List of procedures to give grant
+    table_list		List of routines to give grant
+    is_proc             true indicates routine list are procedures
     user_list		List of users to give grant
     rights		Table level grant
     revoke_grant	Set to 1 if this is a REVOKE command
@@ -2856,16 +2862,16 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
     1	error
 */
 
-bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list,
-			   List <LEX_USER> &user_list, ulong rights,
-			   bool revoke_grant, bool no_error)
+bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
+			 List <LEX_USER> &user_list, ulong rights,
+			 bool revoke_grant, bool no_error)
 {
   List_iterator <LEX_USER> str_list (user_list);
   LEX_USER *Str;
   TABLE_LIST tables[2];
   bool create_new_users=0, result=0;
   char *db_name, *table_name;
-  DBUG_ENTER("mysql_procedure_grant");
+  DBUG_ENTER("mysql_routine_grant");
 
   if (!initialized)
   {
@@ -2884,7 +2890,7 @@ bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list,
 
   if (!revoke_grant)
   {
-    if (sp_exists_routine(thd, table_list, 0, no_error)<0)
+    if (sp_exists_routine(thd, table_list, is_proc, no_error)<0)
       DBUG_RETURN(TRUE);
   }
 
@@ -2957,8 +2963,8 @@ bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list,
     db_name= table_list->db;
     table_name= table_list->table_name;
 
-    grant_name= proc_hash_search(Str->host.str, NullS, db_name,
-                                 Str->user.str, table_name, 1);
+    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
+                                    Str->user.str, table_name, is_proc, 1);
     if (!grant_name)
     {
       if (revoke_grant)
@@ -2977,11 +2983,11 @@ bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list,
         result= TRUE;
 	continue;
       }
-      my_hash_insert(&proc_priv_hash,(byte*) grant_name);
+      my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
     }
 
-    if (replace_proc_table(thd, grant_name, tables[1].table, *Str,
-			   db_name, table_name, rights, revoke_grant))
+    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
+			   db_name, table_name, is_proc, rights, revoke_grant))
     {
       result= TRUE;
       continue;
@@ -3133,6 +3139,9 @@ my_bool grant_init(THD *org_thd)
   (void) hash_init(&proc_priv_hash,system_charset_info,
 		   0,0,0, (hash_get_key) get_grant_table,
 		   0,0);
+  (void) hash_init(&func_priv_hash,system_charset_info,
+		   0,0,0, (hash_get_key) get_grant_table,
+		   0,0);
   init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
 
   /* Don't do anything if running with --skip-grant */
@@ -3206,6 +3215,8 @@ my_bool grant_init(THD *org_thd)
     do
     {
       GRANT_NAME *mem_check;
+      longlong proc_type;
+      HASH *hash;
       if (!(mem_check=new GRANT_NAME(p_table)))
       {
 	/* This could only happen if we are out memory */
@@ -3224,11 +3235,27 @@ my_bool grant_init(THD *org_thd)
 	  continue;
 	}
       }
+      if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
+      {
+        hash= &proc_priv_hash;
+      }
+      else
+      if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
+      {
+        hash= &func_priv_hash;
+      }
+      else
+      {
+        sql_print_warning("'procs_priv' entry '%s' "
+                          "ignored, bad routine type",
+                          mem_check->tname);
+	continue;
+      }
 
       mem_check->privs= fix_rights_for_procedure(mem_check->privs);
       if (! mem_check->ok())
 	delete mem_check;
-      else if (my_hash_insert(&proc_priv_hash,(byte*) mem_check))
+      else if (my_hash_insert(hash, (byte*) mem_check))
       {
 	delete mem_check;
 	grant_option= FALSE;
@@ -3272,7 +3299,7 @@ end:
 
 void grant_reload(THD *thd)
 {
-  HASH old_column_priv_hash, old_proc_priv_hash;
+  HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
   bool old_grant_option;
   MEM_ROOT old_mem;
   DBUG_ENTER("grant_reload");
@@ -3281,6 +3308,7 @@ void grant_reload(THD *thd)
   grant_version++;
   old_column_priv_hash= column_priv_hash;
   old_proc_priv_hash= proc_priv_hash;
+  old_func_priv_hash= func_priv_hash;
   old_grant_option= grant_option;
   old_mem= memex;
 
@@ -3290,6 +3318,7 @@ void grant_reload(THD *thd)
     grant_free();				/* purecov: deadcode */
     column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
     proc_priv_hash= old_proc_priv_hash;
+    func_priv_hash= old_func_priv_hash;
     grant_option= old_grant_option;		/* purecov: deadcode */
     memex= old_mem;				/* purecov: deadcode */
   }
@@ -3297,6 +3326,7 @@ void grant_reload(THD *thd)
   {
     hash_free(&old_column_priv_hash);
     hash_free(&old_proc_priv_hash);
+    hash_free(&old_func_priv_hash);
     free_root(&old_mem,MYF(0));
   }
   rw_unlock(&LOCK_grant);
@@ -3540,13 +3570,14 @@ bool check_grant_db(THD *thd,const char *db)
 
 
 /****************************************************************************
-  Check procedure level grants
+  Check routine level grants
 
   SYNPOSIS
-   bool check_grant_procedure()
+   bool check_grant_routine()
    thd		Thread handler
    want_access  Bits of privileges user needs to have
-   procs	List of procedures to check. The user should have 'want_access'
+   procs	List of routines to check. The user should have 'want_access'
+   is_proc	True if the list is all procedures, else functions
    no_errors	If 0 then we write an error. The error is sent directly to
 		the client
 
@@ -3555,13 +3586,13 @@ bool check_grant_db(THD *thd,const char *db)
      1  Error: User did not have the requested privielges
 ****************************************************************************/
 
-bool check_grant_procedure(THD *thd, ulong want_access, 
-			   TABLE_LIST *procs, bool no_errors)
+bool check_grant_routine(THD *thd, ulong want_access, 
+			 TABLE_LIST *procs, bool is_proc, bool no_errors)
 {
   TABLE_LIST *table;
   char *user= thd->priv_user;
   char *host= thd->priv_host;
-  DBUG_ENTER("check_grant_procedure");
+  DBUG_ENTER("check_grant_routine");
 
   want_access&= ~thd->master_access;
   if (!want_access)
@@ -3571,8 +3602,8 @@ bool check_grant_procedure(THD *thd, ulong want_access,
   for (table= procs; table; table= table->next_global)
   {
     GRANT_NAME *grant_proc;
-    if ((grant_proc= proc_hash_search(host,thd->ip, 
-				      table->db, user, table->table_name, 0)))
+    if ((grant_proc= routine_hash_search(host,thd->ip, table->db, user,
+					 table->table_name, is_proc, 0)))
       table->grant.privilege|= grant_proc->privs;
 
     if (want_access & ~table->grant.privilege)
@@ -3594,7 +3625,7 @@ err:
     if (want_access & EXECUTE_ACL)
       command= "execute";
     else if (want_access & ALTER_PROC_ACL)
-      command= "alter procedure";
+      command= "alter routine";
     else if (want_access & GRANT_ACL)
       command= "grant";
     my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
@@ -3606,7 +3637,7 @@ err:
 
 /*
   Check if routine has any of the 
-  procedure level grants
+  routine level grants
   
   SYNPOSIS
    bool    check_routine_level_acl()
@@ -3619,15 +3650,15 @@ err:
    1            error
 */
 
-bool check_routine_level_acl(THD *thd, const char *db, const char *name)
+bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool is_proc)
 {
   bool no_routine_acl= 1;
   if (grant_option)
   {
     GRANT_NAME *grant_proc;
     rw_rdlock(&LOCK_grant);
-    if ((grant_proc= proc_hash_search(thd->priv_host, thd->ip, db,
-                                      thd->priv_user, name, 0)))
+    if ((grant_proc= routine_hash_search(thd->priv_host, thd->ip, db,
+                                         thd->priv_user, name, is_proc, 0)))
       no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
     rw_unlock(&LOCK_grant);
   }
@@ -3730,6 +3761,11 @@ static uint command_lengths[]=
 };
 
 
+static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
+                               const char *type, int typelen,
+                               char *buff, int buffsize);
+
+
 /*
   SHOW GRANTS;  Send grants for a user to the client
 
@@ -4076,12 +4112,40 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
     }
   }
 
-  /* Add procedure access */
-  for (index=0 ; index < proc_priv_hash.records ; index++)
+  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
+                          "PROCEDURE", 9, buff, sizeof(buff)))
+  {
+    error= -1;
+    goto end;
+  }
+
+  if (show_routine_grants(thd, lex_user, &func_priv_hash,
+                          "FUNCTION", 8, buff, sizeof(buff)))
+  {
+    error= -1;
+    goto end;
+  }
+
+end:
+  VOID(pthread_mutex_unlock(&acl_cache->lock));
+  rw_unlock(&LOCK_grant);
+
+  send_eof(thd);
+  DBUG_RETURN(error);
+}
+
+static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
+                               const char *type, int typelen,
+                               char *buff, int buffsize)
+{
+  uint counter, index;
+  int error= 0;
+  Protocol *protocol= thd->protocol;
+  /* Add routine access */
+  for (index=0 ; index < hash->records ; index++)
   {
     const char *user;
-    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
-						       index);
+    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
 
     if (!(user=grant_proc->user))
       user= "";
@@ -4093,7 +4157,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
       ulong proc_access= grant_proc->privs;
       if (proc_access != 0)
       {
-	String global(buff, sizeof(buff), system_charset_info);
+	String global(buff, buffsize, system_charset_info);
 	ulong test_access= proc_access & ~GRANT_ACL;
 
 	global.length(0);
@@ -4119,6 +4183,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
 	  }
 	}
 	global.append(" ON ",4);
+        global.append(type,typelen);
+        global.append(' ');
 	append_identifier(thd, &global, grant_proc->db,
 			  strlen(grant_proc->db));
 	global.append('.');
@@ -4143,15 +4209,9 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
       }
     }
   }
-end:
-  VOID(pthread_mutex_unlock(&acl_cache->lock));
-  rw_unlock(&LOCK_grant);
-
-  send_eof(thd);
-  DBUG_RETURN(error);
+  return error;
 }
 
-
 /*
   Make a clear-text version of the requested privilege.
 */
@@ -4977,7 +5037,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
 
 bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
 {
-  uint counter, revoked;
+  uint counter, revoked, is_proc;
   int result;
   ACL_DB *acl_db;
   TABLE_LIST tables[GRANT_TABLES];
@@ -5092,12 +5152,12 @@ bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
     } while (revoked);
 
     /* Remove procedure access */
-    do {
-      for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; )
+    for (is_proc=0; is_proc<2; is_proc++) do {
+      HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+      for (counter= 0, revoked= 0 ; counter < hash->records ; )
       {
 	const char *user,*host;
-	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
-							   counter);
+	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
 	if (!(user=grant_proc->user))
 	  user= "";
 	if (!(host=grant_proc->host.hostname))
@@ -5106,9 +5166,10 @@ bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
 	if (!strcmp(lex_user->user.str,user) &&
 	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
 	{
-	  if (!replace_proc_table(thd,grant_proc,tables[4].table,*lex_user,
+	  if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
 				  grant_proc->db,
 				  grant_proc->tname,
+                                  is_proc,
 				  ~0, 1))
 	  {
 	    revoked= 1;
@@ -5146,11 +5207,13 @@ bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
     < 0         Error. Error message not yet sent.
 */
 
-bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name)
+bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
+                          bool is_proc)
 {
   uint counter, revoked;
   int result;
   TABLE_LIST tables[GRANT_TABLES];
+  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
   DBUG_ENTER("sp_revoke_privileges");
 
   if ((result= open_grant_tables(thd, tables)))
@@ -5162,10 +5225,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name)
   /* Remove procedure access */
   do
   {
-    for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; )
+    for (counter= 0, revoked= 0 ; counter < hash->records ; )
     {
-      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
-							 counter);
+      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
       if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) &&
 	  !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
       {
@@ -5174,8 +5236,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name)
 	lex_user.user.length= strlen(grant_proc->user);
 	lex_user.host.str= grant_proc->host.hostname;
 	lex_user.host.length= strlen(grant_proc->host.hostname);
-	if (!replace_proc_table(thd,grant_proc,tables[4].table,lex_user,
-				grant_proc->db, grant_proc->tname, ~0, 1))
+	if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
+				   grant_proc->db, grant_proc->tname,
+                                   is_proc, ~0, 1))
 	{
 	  revoked= 1;
 	  continue;
@@ -5211,7 +5274,8 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name)
     < 0         Error. Error message not yet sent.
 */
 
-bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name)
+bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
+                         bool is_proc)
 {
   LEX_USER *combo;
   TABLE_LIST tables[1];
@@ -5249,7 +5313,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name)
   thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
   bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
 
-  result= mysql_procedure_grant(thd, tables, user_list,
+  result= mysql_routine_grant(thd, tables, is_proc, user_list,
   				DEFAULT_CREATE_PROC_ACLS, 0, 1);
   DBUG_RETURN(result);
 }
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 18eb123d4023fae82a413117398209d88a16d0db..f2896889669aaf7b1e3003b2753b20461f36e446 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -185,9 +185,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
 bool mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
                        List <LEX_COLUMN> &column_list, ulong rights,
                        bool revoke);
-bool mysql_procedure_grant(THD *thd, TABLE_LIST *table, 
-			   List <LEX_USER> &user_list, ulong rights,
-			   bool revoke, bool no_error);
+bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc,
+			 List <LEX_USER> &user_list, ulong rights,
+			 bool revoke, bool no_error);
 ACL_USER *check_acl_user(LEX_USER *user_name, uint *acl_acl_userdx);
 my_bool grant_init(THD *thd);
 void grant_free(void);
@@ -200,8 +200,8 @@ bool check_grant_column (THD *thd, GRANT_INFO *grant,
 bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
                              const char* db_name, const char *table_name,
                              Field_iterator *fields);
-bool check_grant_procedure(THD *thd, ulong want_access, 
-			   TABLE_LIST *procs, bool no_error);
+bool check_grant_routine(THD *thd, ulong want_access,
+			 TABLE_LIST *procs, bool is_proc, bool no_error);
 bool check_grant_db(THD *thd,const char *db);
 ulong get_table_grant(THD *thd, TABLE_LIST *table);
 ulong get_column_grant(THD *thd, GRANT_INFO *grant,
@@ -216,9 +216,12 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list);
 bool mysql_revoke_all(THD *thd, List <LEX_USER> &list);
 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
                                      const char *db, const char *table);
-bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name);
-bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name);
-bool check_routine_level_acl(THD *thd, const char *db, const char *name);
+bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
+                          bool is_proc);
+bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
+                         bool is_proc);
+bool check_routine_level_acl(THD *thd, const char *db, const char *name,
+                             bool is_proc);
 
 #ifdef NO_EMBEDDED_ACCESS_CHECKS
 #define check_grant(A,B,C,D,E,F) 0
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 4a8303695e9072a6dda5a52cdb9fdec33a05bb0a..29d9a7bf9c45c0f957a7466adac910f430065453 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3307,7 +3307,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
         0)
     {
       my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(),
-	       table->real_name);
+	       table->s->table_name);
       map->set_all();
       return 1;
     }
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 47ac8d3afc117ef0222d7e10425cbca77b3b6458..a9e8138a2e002e8d43fdae4be2c0f69adf9ffbd9 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3669,17 +3669,20 @@ unsent_create_error:
     }
     if (first_table)
     {
-      if (!lex->columns.elements && 
-          sp_exists_routine(thd, all_tables, 1, 1))
+      if (lex->type == TYPE_ENUM_PROCEDURE ||
+          lex->type == TYPE_ENUM_FUNCTION)
       {
         uint grants= lex->all_privileges 
 		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
 		   : lex->grant;
         if (grant_option && 
-	    check_grant_procedure(thd, grants | GRANT_ACL, all_tables, 0))
+	    check_grant_routine(thd, grants | GRANT_ACL, all_tables,
+                                lex->type == TYPE_ENUM_PROCEDURE, 0))
 	  goto error;
-        res= mysql_procedure_grant(thd, all_tables, lex->users_list,
-				   grants, lex->sql_command == SQLCOM_REVOKE,0);
+        res= mysql_routine_grant(thd, all_tables,
+                                 lex->type == TYPE_ENUM_PROCEDURE, 
+                                 lex->users_list, grants,
+                                 lex->sql_command == SQLCOM_REVOKE, 0);
       }
       else
       {
@@ -3701,7 +3704,7 @@ unsent_create_error:
     }
     else
     {
-      if (lex->columns.elements)
+      if (lex->columns.elements || lex->type)
       {
 	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                    MYF(0));
@@ -3983,11 +3986,13 @@ unsent_create_error:
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
       /* only add privileges if really neccessary */
       if (sp_automatic_privileges &&
-          check_procedure_access(thd, DEFAULT_CREATE_PROC_ACLS,
-      				 db, name, 1))
+          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
+      			       db, name,
+                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
       {
         close_thread_tables(thd);
-        if (sp_grant_privileges(thd, db, name))
+        if (sp_grant_privileges(thd, db, name, 
+                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
           push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
 	  	       ER_PROC_AUTO_GRANT_FAIL,
 		       ER(ER_PROC_AUTO_GRANT_FAIL));
@@ -4072,8 +4077,8 @@ unsent_create_error:
 	}
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-	if (check_procedure_access(thd, EXECUTE_ACL, 
-				   sp->m_db.str, sp->m_name.str, 0))
+	if (check_routine_access(thd, EXECUTE_ACL, 
+				 sp->m_db.str, sp->m_name.str, TRUE, 0))
 	{
 #ifndef EMBEDDED_LIBRARY
 	  thd->net.no_send_ok= nsok;
@@ -4082,8 +4087,8 @@ unsent_create_error:
 	}
 	sp_change_security_context(thd, sp, &save_ctx);
 	if (save_ctx.changed && 
-	    check_procedure_access(thd, EXECUTE_ACL, 
-				   sp->m_db.str, sp->m_name.str, 0))
+	    check_routine_access(thd, EXECUTE_ACL, 
+				   sp->m_db.str, sp->m_name.str, TRUE, 0))
 	{
 #ifndef EMBEDDED_LIBRARY
 	  thd->net.no_send_ok= nsok;
@@ -4185,8 +4190,9 @@ unsent_create_error:
       }
       else
       {
-        if (check_procedure_access(thd, ALTER_PROC_ACL, sp->m_db.str, 
-				  sp->m_name.str, 0))
+        if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str, 
+				 sp->m_name.str,
+                                 lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
 	  goto error;
 	memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
         if (!trust_routine_creators &&  mysql_bin_log.is_open() &&
@@ -4244,11 +4250,13 @@ unsent_create_error:
       {
         db= thd->strdup(sp->m_db.str);
 	name= thd->strdup(sp->m_name.str);
-	if (check_procedure_access(thd, ALTER_PROC_ACL, db, name, 0))
+	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
+                                 lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
           goto error;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 	if (sp_automatic_privileges &&
-	    sp_revoke_privileges(thd, db, name))
+	    sp_revoke_privileges(thd, db, name, 
+                                 lex->sql_command == SQLCOM_DROP_PROCEDURE))
 	{
 	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
 		       ER_PROC_AUTO_REVOKE_FAIL,
@@ -4832,8 +4840,8 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
 
 
 bool
-check_procedure_access(THD *thd, ulong want_access,char *db, char *name,
-		       bool no_errors)
+check_routine_access(THD *thd, ulong want_access,char *db, char *name,
+		     bool is_proc, bool no_errors)
 {
   TABLE_LIST tables[1];
   
@@ -4849,7 +4857,7 @@ check_procedure_access(THD *thd, ulong want_access,char *db, char *name,
   
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   if (grant_option)
-    return check_grant_procedure(thd, want_access, tables, no_errors);
+    return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
 #endif
 
   return FALSE;
@@ -4870,7 +4878,8 @@ check_procedure_access(THD *thd, ulong want_access,char *db, char *name,
     1            error
 */
 
-bool check_some_routine_access(THD *thd, const char *db, const char *name)
+bool check_some_routine_access(THD *thd, const char *db, const char *name,
+                               bool is_proc)
 {
   ulong save_priv;
   if (thd->master_access & SHOW_PROC_ACLS)
@@ -4878,7 +4887,7 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name)
   if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1) ||
       (save_priv & SHOW_PROC_ACLS))
     return FALSE;
-  return check_routine_level_acl(thd, db, name);
+  return check_routine_level_acl(thd, db, name, is_proc);
 }
 
 
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 686060d17409471b7af12e3eacf04c43bc0ea0ba..1160267732b84fe9da8380a94b60110a97a9820e 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2625,7 +2625,8 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
   definer= get_field(thd->mem_root, proc_table->field[11]);
   if (!full_access)
     full_access= !strcmp(sp_user, definer);
-  if (!full_access && check_some_routine_access(thd, sp_db, sp_name))
+  if (!full_access && check_some_routine_access(thd, sp_db, sp_name,
+			proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE))
     return 0;
 
   if (lex->orig_sql_command == SQLCOM_SHOW_STATUS_PROC &&
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 9328d7345c3704b8b6cedc8eb5766b9592458360..719d9d6609b8115583050b00cecd04f1bb0cfd51 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -804,7 +804,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 	opt_delete_options opt_delete_option varchar nchar nvarchar
 	opt_outer table_list table_name opt_option opt_place
 	opt_attribute opt_attribute_list attribute column_list column_list_id
-	opt_column_list grant_privileges opt_table grant_list grant_option
+	opt_column_list grant_privileges grant_ident grant_list grant_option
 	object_privilege object_privilege_list user_list rename_list
 	clear_privileges flush_options flush_option
 	equal optional_braces opt_key_definition key_usage_list2
@@ -3994,6 +3994,7 @@ select_options:
               YYABORT;
 	    }
           }
+          ;
 
 select_option_list:
 	select_option_list select_option
@@ -7906,9 +7907,36 @@ revoke:
         ;
 
 revoke_command:
-	grant_privileges ON opt_table FROM grant_list
+	grant_privileges ON opt_table grant_ident FROM grant_list
 	{
-	  Lex->sql_command = SQLCOM_REVOKE;
+          LEX *lex= Lex;
+	  lex->sql_command= SQLCOM_REVOKE;
+          lex->type= 0;
+        }
+        |
+        grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list
+        {
+          LEX *lex= Lex;
+          if (lex->columns.elements)
+          {
+            yyerror(ER(ER_SYNTAX_ERROR));
+	    YYABORT;
+          }
+	  lex->sql_command= SQLCOM_REVOKE;
+          lex->type= TYPE_ENUM_FUNCTION;
+          
+        }
+	|
+        grant_privileges ON PROCEDURE grant_ident FROM grant_list
+        {
+          LEX *lex= Lex;
+          if (lex->columns.elements)
+          {
+            yyerror(ER(ER_SYNTAX_ERROR));
+	    YYABORT;
+          }
+	  lex->sql_command= SQLCOM_REVOKE;
+          lex->type= TYPE_ENUM_PROCEDURE;
         }
 	|
 	ALL opt_privileges ',' GRANT OPTION FROM grant_list
@@ -7918,11 +7946,50 @@ revoke_command:
 	;
 
 grant:
-	GRANT clear_privileges grant_privileges ON opt_table TO_SYM grant_list
+	GRANT clear_privileges grant_command
+	{}
+        ;
+
+grant_command:
+	grant_privileges ON opt_table grant_ident TO_SYM grant_list
 	require_clause grant_options
-	{ Lex->sql_command= SQLCOM_GRANT; }
-	;
+	{
+          LEX *lex= Lex;
+          lex->sql_command= SQLCOM_GRANT;
+          lex->type= 0;
+        }
+        |
+	grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
+	require_clause grant_options
+	{
+          LEX *lex= Lex;
+          if (lex->columns.elements)
+          {
+            yyerror(ER(ER_SYNTAX_ERROR));
+	    YYABORT;
+          }
+          lex->sql_command= SQLCOM_GRANT;
+          lex->type= TYPE_ENUM_FUNCTION;
+        }
+        |
+	grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list
+	require_clause grant_options
+	{
+          LEX *lex= Lex;
+          if (lex->columns.elements)
+          {
+            yyerror(ER(ER_SYNTAX_ERROR));
+	    YYABORT;
+          }
+          lex->sql_command= SQLCOM_GRANT;
+          lex->type= TYPE_ENUM_PROCEDURE;
+        }
+        ;
 
+opt_table:
+	/* Empty */
+	| TABLE_SYM ;
+        
 grant_privileges:
 	object_privilege_list { }
 	| ALL opt_privileges
@@ -8015,7 +8082,7 @@ require_list_element:
 	}
 	;
 
-opt_table:
+grant_ident:
 	'*'
 	  {
 	    LEX *lex= Lex;