diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index c1ca85ce8d7dc836354eadcc41ff1cae0f378777..224e61f0686bb8e84aede500c0618e33dda2e756 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -4,7 +4,7 @@
  *
  * Copyright 2002, PostgreSQL Global Development Group
  *
- * $Id: information_schema.sql,v 1.7 2003/06/05 16:08:47 petere Exp $
+ * $Id: information_schema.sql,v 1.8 2003/06/11 09:23:55 petere Exp $
  */
 
 
@@ -604,7 +604,7 @@ GRANT SELECT ON referential_constraints TO PUBLIC;
  */
 
 CREATE VIEW routine_privileges AS
-    SELECT CAST(u_owner.usename AS sql_identifier) AS grantor,
+    SELECT CAST(u_grantor.usename AS sql_identifier) AS grantor,
            CAST(u_grantee.usename AS sql_identifier) AS grantee,
            CAST(current_database() AS sql_identifier) AS specific_catalog,
            CAST(n.nspname AS sql_identifier) AS specific_schema,
@@ -613,17 +613,22 @@ CREATE VIEW routine_privileges AS
            CAST(n.nspname AS sql_identifier) AS routine_schema,
            CAST(p.proname AS sql_identifier) AS routine_name,
            CAST('EXECUTE' AS character_data) AS privilege_type,
-           CAST('NO' AS character_data) AS is_grantable
+           CAST(
+             CASE WHEN aclcontains(p.proacl,
+                                   makeaclitem(u_grantee.usesysid, 0, u_grantor.usesysid, 'EXECUTE', true))
+                  THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
 
-    FROM pg_user u_owner,
-         pg_user u_grantee,
+    FROM pg_proc p,
          pg_namespace n,
-         pg_proc p
+         pg_user u_grantor,
+         (SELECT usesysid, usename FROM pg_user UNION SELECT 0, 'PUBLIC') AS u_grantee
 
-    WHERE u_owner.usesysid = p.proowner
-          AND p.pronamespace = n.oid
-          AND has_function_privilege(u_grantee.usename, p.oid, 'EXECUTE')
-          AND (u_owner.usename = current_user OR u_grantee.usename = current_user);
+    WHERE p.pronamespace = n.oid
+          AND aclcontains(p.proacl,
+                          makeaclitem(u_grantee.usesysid, 0, u_grantor.usesysid, 'EXECUTE', false))
+          AND (u_grantor.usename = current_user
+               OR u_grantee.usename = current_user
+               OR u_grantee.usename = 'PUBLIC');
 
 GRANT SELECT ON routine_privileges TO PUBLIC;
 
@@ -940,27 +945,31 @@ GRANT SELECT ON table_constraints TO PUBLIC;
  */
 
 CREATE VIEW table_privileges AS
-    SELECT CAST(u_owner.usename AS sql_identifier) AS grantor,
+    SELECT CAST(u_grantor.usename AS sql_identifier) AS grantor,
            CAST(u_grantee.usename AS sql_identifier) AS grantee,
            CAST(current_database() AS sql_identifier) AS table_catalog,
            CAST(nc.nspname AS sql_identifier) AS table_schema,
            CAST(c.relname AS sql_identifier) AS table_name,
            CAST(pr.type AS character_data) AS privilege_type,
-           CAST('NO' AS character_data) AS is_grantable,
+           CAST(
+             CASE WHEN aclcontains(c.relacl,
+                                   makeaclitem(u_grantee.usesysid, 0, u_grantor.usesysid, pr.type, true))
+                  THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable,
            CAST('NO' AS character_data) AS with_hierarchy
 
-    FROM pg_user u_owner,
-         pg_user u_grantee,
+    FROM pg_class c,
          pg_namespace nc,
-         pg_class c,
+         pg_user u_grantor,
+         (SELECT usesysid, usename FROM pg_user UNION SELECT 0, 'PUBLIC') AS u_grantee,
          (SELECT 'SELECT' UNION SELECT 'DELETE' UNION SELECT 'INSERT' UNION SELECT 'UPDATE'
           UNION SELECT 'REFERENCES' UNION SELECT 'TRIGGER') AS pr (type)
 
-    WHERE u_owner.usesysid = c.relowner
-          AND c.relnamespace = nc.oid
-          AND has_table_privilege(u_grantee.usename, c.oid, pr.type)
-
-          AND (u_owner.usename = current_user OR u_grantee.usename = current_user);
+    WHERE c.relnamespace = nc.oid
+          AND aclcontains(c.relacl,
+                          makeaclitem(u_grantee.usesysid, 0, u_grantor.usesysid, pr.type, false))
+          AND (u_grantor.usename = current_user
+               OR u_grantee.usename = current_user
+               OR u_grantee.usename = 'PUBLIC');
 
 GRANT SELECT ON table_privileges TO PUBLIC;
 
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 84abec04894cba06166ebba9b3349b0a03b961b9..2870b5d0d6538680c51712f52b6a00ee87d1d75a 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.87 2003/06/02 19:00:29 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.88 2003/06/11 09:23:55 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,12 +32,14 @@
 
 static const char *getid(const char *s, char *n);
 static void putid(char *p, const char *s);
-static Acl *makeacl(int n);
+static Acl *allocacl(int n);
 static const char *aclparse(const char *s, AclItem *aip);
 static bool aclitemeq(const AclItem *a1, const AclItem *a2);
 static Acl *recursive_revoke(Acl *acl, AclId grantee,
 							 AclMode revoke_privs, DropBehavior behavior);
 
+static AclMode convert_priv_string(text *priv_type_text);
+
 static Oid	convert_table_name(text *tablename);
 static AclMode convert_table_priv_string(text *priv_type_text);
 static Oid	convert_database_name(text *databasename);
@@ -265,20 +267,20 @@ aclparse(const char *s, AclItem *aip)
 }
 
 /*
- * makeacl
+ * allocacl
  *		Allocates storage for a new Acl with 'n' entries.
  *
  * RETURNS:
  *		the new Acl
  */
 static Acl *
-makeacl(int n)
+allocacl(int n)
 {
 	Acl		   *new_acl;
 	Size		size;
 
 	if (n < 0)
-		elog(ERROR, "makeacl: invalid size: %d", n);
+		elog(ERROR, "allocacl: invalid size: %d", n);
 	size = ACL_N_SIZE(n);
 	new_acl = (Acl *) palloc0(size);
 	new_acl->size = size;
@@ -471,7 +473,7 @@ acldefault(GrantObjectType objtype, AclId ownerid)
 			break;
 	}
 
-	acl = makeacl((world_default != ACL_NO_RIGHTS ? 1 : 0)
+	acl = allocacl((world_default != ACL_NO_RIGHTS ? 1 : 0)
 				  + (ownerid ? 1 : 0));
 	aip = ACL_DAT(acl);
 
@@ -513,10 +515,10 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
 
 	/* These checks for null input are probably dead code, but... */
 	if (!old_acl || ACL_NUM(old_acl) < 1)
-		old_acl = makeacl(1);
+		old_acl = allocacl(1);
 	if (!mod_aip)
 	{
-		new_acl = makeacl(ACL_NUM(old_acl));
+		new_acl = allocacl(ACL_NUM(old_acl));
 		memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
 		return new_acl;
 	}
@@ -536,7 +538,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
 		if (aclitemeq(mod_aip, old_aip + dst))
 		{
 			/* found a match, so modify existing item */
-			new_acl = makeacl(num);
+			new_acl = allocacl(num);
 			new_aip = ACL_DAT(new_acl);
 			memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
 			break;
@@ -546,7 +548,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
 	if (dst == num)
 	{
 		/* need to append a new item */
-		new_acl = makeacl(num + 1);
+		new_acl = allocacl(num + 1);
 		new_aip = ACL_DAT(new_acl);
 		memcpy(new_aip, old_aip, num * sizeof(AclItem));
 
@@ -671,10 +673,10 @@ aclremove(PG_FUNCTION_ARGS)
 
 	/* These checks for null input should be dead code, but... */
 	if (!old_acl || ACL_NUM(old_acl) < 1)
-		old_acl = makeacl(1);
+		old_acl = allocacl(1);
 	if (!mod_aip)
 	{
-		new_acl = makeacl(ACL_NUM(old_acl));
+		new_acl = allocacl(ACL_NUM(old_acl));
 		memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
 		PG_RETURN_ACL_P(new_acl);
 	}
@@ -689,13 +691,13 @@ aclremove(PG_FUNCTION_ARGS)
 	if (dst >= old_num)
 	{
 		/* Not found, so return copy of source ACL */
-		new_acl = makeacl(old_num);
+		new_acl = allocacl(old_num);
 		memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
 	}
 	else
 	{
 		new_num = old_num - 1;
-		new_acl = makeacl(new_num);
+		new_acl = allocacl(new_num);
 		new_aip = ACL_DAT(new_acl);
 		if (dst == 0)
 		{						/* start */
@@ -734,13 +736,97 @@ aclcontains(PG_FUNCTION_ARGS)
 	aidat = ACL_DAT(acl);
 	for (i = 0; i < num; ++i)
 	{
-		if (aip->ai_grantee == aidat[i].ai_grantee &&
-			aip->ai_privs == aidat[i].ai_privs)
+		if (aip->ai_grantee == aidat[i].ai_grantee
+			&& ACLITEM_GET_IDTYPE(*aip) == ACLITEM_GET_IDTYPE(aidat[i])
+			&& aip->ai_grantor == aidat[i].ai_grantor
+			&& (ACLITEM_GET_PRIVS(*aip) & ACLITEM_GET_PRIVS(aidat[i])) == ACLITEM_GET_PRIVS(*aip)
+			&& (ACLITEM_GET_GOPTIONS(*aip) & ACLITEM_GET_GOPTIONS(aidat[i])) == ACLITEM_GET_GOPTIONS(*aip))
 			PG_RETURN_BOOL(true);
 	}
 	PG_RETURN_BOOL(false);
 }
 
+Datum
+makeaclitem(PG_FUNCTION_ARGS)
+{
+	int32		u_grantee = PG_GETARG_INT32(0);
+	int32		g_grantee = PG_GETARG_INT32(1);
+	int32		grantor = PG_GETARG_INT32(2);
+	text	   *privtext = PG_GETARG_TEXT_P(3);
+	bool		goption = PG_GETARG_BOOL(4);
+	AclItem	   *aclitem;
+	AclMode		priv;
+
+	priv = convert_priv_string(privtext);
+
+	aclitem = (AclItem *) palloc(sizeof(*aclitem));
+	if (u_grantee == 0 && g_grantee == 0)
+	{
+		aclitem->ai_grantee = 0;
+		ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_WORLD);
+	}
+	else if (u_grantee != 0 && g_grantee != 0)
+	{
+		elog(ERROR, "cannot specify both user and group");
+	}
+	else if (u_grantee != 0)
+	{
+		aclitem->ai_grantee = u_grantee;
+		ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_UID);
+	}
+	else if (g_grantee != 0)
+	{
+		aclitem->ai_grantee = g_grantee;
+		ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_GID);
+	}
+
+	aclitem->ai_grantor = grantor;
+	ACLITEM_SET_PRIVS(*aclitem, priv);
+	if (goption)
+		ACLITEM_SET_GOPTIONS(*aclitem, priv);
+	else
+		ACLITEM_SET_GOPTIONS(*aclitem, ACL_NO_RIGHTS);
+
+	PG_RETURN_ACLITEM_P(aclitem);
+}
+
+static AclMode
+convert_priv_string(text *priv_type_text)
+{
+	char	   *priv_type;
+
+	priv_type = DatumGetCString(DirectFunctionCall1(textout,
+													PointerGetDatum(priv_type_text)));
+
+	if (strcasecmp(priv_type, "SELECT") == 0)
+		return ACL_SELECT;
+	if (strcasecmp(priv_type, "INSERT") == 0)
+		return ACL_INSERT;
+	if (strcasecmp(priv_type, "UPDATE") == 0)
+		return ACL_UPDATE;
+	if (strcasecmp(priv_type, "DELETE") == 0)
+		return ACL_DELETE;
+	if (strcasecmp(priv_type, "RULE") == 0)
+		return ACL_RULE;
+	if (strcasecmp(priv_type, "REFERENCES") == 0)
+		return ACL_REFERENCES;
+	if (strcasecmp(priv_type, "TRIGGER") == 0)
+		return ACL_TRIGGER;
+	if (strcasecmp(priv_type, "EXECUTE") == 0)
+		return ACL_EXECUTE;
+	if (strcasecmp(priv_type, "USAGE") == 0)
+		return ACL_USAGE;
+	if (strcasecmp(priv_type, "CREATE") == 0)
+		return ACL_CREATE;
+	if (strcasecmp(priv_type, "TEMP") == 0)
+		return ACL_CREATE_TEMP;
+	if (strcasecmp(priv_type, "TEMPORARY") == 0)
+		return ACL_CREATE_TEMP;
+
+	elog(ERROR, "invalid privilege type %s", priv_type);
+	return ACL_NO_RIGHTS;		/* keep compiler quiet */
+}
+
 
 /*
  * has_table_privilege variants
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 7d6ff867962ac7f5e08d096d13ba5cdbb03a6876..d8ce41c6a8e661b9c1b754e9ff1e87658002fdfc 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.302 2003/05/26 00:11:27 tgl Exp $
+ * $Id: pg_proc.h,v 1.303 2003/06/11 09:23:55 petere Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -1318,6 +1318,8 @@ DATA(insert OID = 1036 (  aclremove		   PGNSP PGUID 12 f f t f s 2 1034 "1034 10
 DESCR("remove ACL item");
 DATA(insert OID = 1037 (  aclcontains	   PGNSP PGUID 12 f f t f s 2 16 "1034 1033"	aclcontains - _null_ ));
 DESCR("does ACL contain item?");
+DATA(insert OID = 1365 (  makeaclitem	   PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"	makeaclitem - _null_ ));
+DESCR("make ACL item");
 DATA(insert OID = 1038 (  seteval		   PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
 DESCR("internal function supporting PostQuel-style sets");
 DATA(insert OID = 1044 (  bpcharin		   PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 871ed680f502722f21f087503b03c96a61bf939d..1a54042e5e9f6913fbbfe0b1c3022c0d84ffd2b3 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: acl.h,v 1.51 2003/01/23 23:39:07 petere Exp $
+ * $Id: acl.h,v 1.52 2003/06/11 09:23:55 petere Exp $
  *
  * NOTES
  *	  For backward-compatibility purposes we have to allow there
@@ -70,6 +70,9 @@ typedef struct AclItem
   ((item).ai_privs = (ACLITEM_GET_IDTYPE(item)<<30) | (ACLITEM_GET_GOPTIONS(item)<<15) | ((privs) & 0x7FFF))
 #define ACLITEM_SET_GOPTIONS(item,goptions) \
   ((item).ai_privs = (ACLITEM_GET_IDTYPE(item)<<30) | (((goptions) & 0x7FFF) << 15) | ACLITEM_GET_PRIVS(item))
+#define ACLITEM_SET_IDTYPE(item,idtype) \
+  ((item).ai_privs = ((idtype)<<30) | (ACLITEM_GET_GOPTIONS(item)<<15) | ACLITEM_GET_PRIVS(item))
+
 #define ACLITEM_SET_PRIVS_IDTYPE(item,privs,goption,idtype) \
   ((item).ai_privs = ((privs) & 0x7FFF) |(((goption) & 0x7FFF) << 15) | ((idtype) << 30))
 
@@ -188,6 +191,7 @@ extern Datum aclitemout(PG_FUNCTION_ARGS);
 extern Datum aclinsert(PG_FUNCTION_ARGS);
 extern Datum aclremove(PG_FUNCTION_ARGS);
 extern Datum aclcontains(PG_FUNCTION_ARGS);
+extern Datum makeaclitem(PG_FUNCTION_ARGS);
 
 /*
  * prototypes for functions in aclchk.c