diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index e40b5b310f3ff36fa26ac44b6e7a54cbd48d3a1d..8e27f10d39a8226f70b57ae67b6eccf77ec42f49 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.97 2004/01/14 03:44:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.98 2004/05/11 17:36:12 tgl Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -48,7 +48,8 @@ static void ExecuteGrantStmt_Namespace(GrantStmt *stmt);
 
 static const char *privilege_to_string(AclMode privilege);
 
-static AclResult aclcheck(Acl *acl, AclId userid, AclMode mode);
+static AclMode aclmask(Acl *acl, AclId userid,
+					   AclMode mask, AclMaskHow how);
 
 
 #ifdef ACLDEBUG
@@ -869,15 +870,33 @@ in_group(AclId uid, AclId gid)
 
 
 /*
- * aclcheck
+ * aclmask --- compute bitmask of all privileges held by userid.
  *
- * Returns ACLCHECK_OK if the 'userid' has ACL entries in 'acl' to
- * satisfy any one of the requirements of 'mode'.  Returns an
- * appropriate ACLCHECK_* error code otherwise.
+ * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
+ * held by the given userid according to the given ACL list, ANDed
+ * with 'mask'.  (The point of passing 'mask' is to let the routine
+ * exit early if all privileges of interest have been found.)
+ *
+ * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
+ * is known true.  (This lets us exit soonest in cases where the
+ * caller is only going to test for zero or nonzero result.)
+ *
+ * Usage patterns:
+ *
+ * To see if any of a set of privileges are held:
+ *		if (aclmask(acl, userid, privs, ACLMASK_ANY) != 0)
+ *
+ * To see if all of a set of privileges are held:
+ *		if (aclmask(acl, userid, privs, ACLMASK_ALL) == privs)
+ *
+ * To determine exactly which of a set of privileges are held:
+ *		heldprivs = aclmask(acl, userid, privs, ACLMASK_ALL);
  */
-static AclResult
-aclcheck(Acl *acl, AclId userid, AclMode mode)
+static AclMode
+aclmask(Acl *acl, AclId userid, AclMode mask, AclMaskHow how)
 {
+	AclMode		result;
+	AclMode		remaining;
 	AclItem    *aidat;
 	int			i,
 				num;
@@ -887,38 +906,55 @@ aclcheck(Acl *acl, AclId userid, AclMode mode)
 	 * appropriate default
 	 */
 	if (acl == NULL)
-	{
 		elog(ERROR, "null ACL");
-		return ACLCHECK_NO_PRIV;
-	}
+
+	/* Quick exit for mask == 0 */
+	if (mask == 0)
+		return 0;
 
 	num = ACL_NUM(acl);
 	aidat = ACL_DAT(acl);
 
+	result = 0;
+
 	/*
-	 * See if privilege is granted directly to user or to public
+	 * Check privileges granted directly to user or to public
 	 */
 	for (i = 0; i < num; i++)
-		if (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_WORLD
-			|| (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_UID
-				&& aidat[i].ai_grantee == userid))
+	{
+		AclItem	   *aidata = &aidat[i];
+
+		if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_WORLD
+			|| (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_UID
+				&& aidata->ai_grantee == userid))
 		{
-			if (aidat[i].ai_privs & mode)
-				return ACLCHECK_OK;
+			result |= (aidata->ai_privs & mask);
+			if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
+				return result;
 		}
+	}
 
 	/*
-	 * See if he has the permission via any group (do this in a separate
-	 * pass to avoid expensive(?) lookups in pg_group)
+	 * Check privileges granted via groups.  We do this in a separate
+	 * pass to minimize expensive lookups in pg_group.
 	 */
+	remaining = (mask & ~result);
 	for (i = 0; i < num; i++)
-		if (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_GID
-			&& aidat[i].ai_privs & mode
-			&& in_group(userid, aidat[i].ai_grantee))
-			return ACLCHECK_OK;
+	{
+		AclItem	   *aidata = &aidat[i];
+
+		if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_GID
+			&& (aidata->ai_privs & remaining)
+			&& in_group(userid, aidata->ai_grantee))
+		{
+			result |= (aidata->ai_privs & mask);
+			if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
+				return result;
+			remaining = (mask & ~result);
+		}
+	}
 
-	/* If here, doesn't have the privilege. */
-	return ACLCHECK_NO_PRIV;
+	return result;
 }
 
 
@@ -1001,17 +1037,20 @@ aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
 
 
 /*
- * Exported routine for checking a user's access privileges to a table
+ * Exported routine for examining a user's privileges for a table
+ *
+ * See aclmask() for a description of the API.
  *
  * Note: we give lookup failure the full ereport treatment because the
  * has_table_privilege() family of functions allow users to pass
  * any random OID to this function.  Likewise for the sibling functions
  * below.
  */
-AclResult
-pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
+AclMode
+pg_class_aclmask(Oid table_oid, AclId userid,
+				 AclMode mask, AclMaskHow how)
 {
-	AclResult	result;
+	AclMode		result;
 	bool		usesuper,
 				usecatupd;
 	HeapTuple	tuple;
@@ -1046,7 +1085,8 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
 	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_TABLE),
-			  errmsg("relation with OID %u does not exist", table_oid)));
+				 errmsg("relation with OID %u does not exist",
+						table_oid)));
 	classForm = (Form_pg_class) GETSTRUCT(tuple);
 
 	/*
@@ -1058,7 +1098,7 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
 	 * be protected in this way.  Assume the view rules can take care
 	 * of themselves.
 	 */
-	if ((mode & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
+	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
 		IsSystemClass(classForm) &&
 		classForm->relkind != RELKIND_VIEW &&
 		!usecatupd &&
@@ -1067,8 +1107,7 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
 #ifdef ACLDEBUG
 		elog(DEBUG2, "permission denied for system catalog update");
 #endif
-		ReleaseSysCache(tuple);
-		return ACLCHECK_NO_PRIV;
+		mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE);
 	}
 
 	/*
@@ -1080,7 +1119,7 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
 		elog(DEBUG2, "%u is superuser, home free", userid);
 #endif
 		ReleaseSysCache(tuple);
-		return ACLCHECK_OK;
+		return mask;
 	}
 
 	/*
@@ -1102,7 +1141,7 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
 		acl = DatumGetAclP(aclDatum);
 	}
 
-	result = aclcheck(acl, userid, mode);
+	result = aclmask(acl, userid, mask, how);
 
 	/* if we have a detoasted copy, free it */
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
@@ -1114,12 +1153,13 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
 }
 
 /*
- * Exported routine for checking a user's access privileges to a database
+ * Exported routine for examining a user's privileges for a database
  */
-AclResult
-pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
+AclMode
+pg_database_aclmask(Oid db_oid, AclId userid,
+					AclMode mask, AclMaskHow how)
 {
-	AclResult	result;
+	AclMode		result;
 	Relation	pg_database;
 	ScanKeyData entry[1];
 	HeapScanDesc scan;
@@ -1130,7 +1170,7 @@ pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
 
 	/* Superusers bypass all permission checking. */
 	if (superuser_arg(userid))
-		return ACLCHECK_OK;
+		return mask;
 
 	/*
 	 * Get the database's ACL from pg_database
@@ -1167,7 +1207,7 @@ pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
 		acl = DatumGetAclP(aclDatum);
 	}
 
-	result = aclcheck(acl, userid, mode);
+	result = aclmask(acl, userid, mask, how);
 
 	/* if we have a detoasted copy, free it */
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
@@ -1180,12 +1220,13 @@ pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
 }
 
 /*
- * Exported routine for checking a user's access privileges to a function
+ * Exported routine for examining a user's privileges for a function
  */
-AclResult
-pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
+AclMode
+pg_proc_aclmask(Oid proc_oid, AclId userid,
+				AclMode mask, AclMaskHow how)
 {
-	AclResult	result;
+	AclMode		result;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1193,7 +1234,7 @@ pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
 
 	/* Superusers bypass all permission checking. */
 	if (superuser_arg(userid))
-		return ACLCHECK_OK;
+		return mask;
 
 	/*
 	 * Get the function's ACL from pg_proc
@@ -1223,7 +1264,7 @@ pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
 		acl = DatumGetAclP(aclDatum);
 	}
 
-	result = aclcheck(acl, userid, mode);
+	result = aclmask(acl, userid, mask, how);
 
 	/* if we have a detoasted copy, free it */
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
@@ -1235,12 +1276,13 @@ pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
 }
 
 /*
- * Exported routine for checking a user's access privileges to a language
+ * Exported routine for examining a user's privileges for a language
  */
-AclResult
-pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
+AclMode
+pg_language_aclmask(Oid lang_oid, AclId userid,
+					AclMode mask, AclMaskHow how)
 {
-	AclResult	result;
+	AclMode		result;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1248,7 +1290,7 @@ pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
 
 	/* Superusers bypass all permission checking. */
 	if (superuser_arg(userid))
-		return ACLCHECK_OK;
+		return mask;
 
 	/*
 	 * Get the language's ACL from pg_language
@@ -1276,7 +1318,7 @@ pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
 		acl = DatumGetAclP(aclDatum);
 	}
 
-	result = aclcheck(acl, userid, mode);
+	result = aclmask(acl, userid, mask, how);
 
 	/* if we have a detoasted copy, free it */
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
@@ -1288,12 +1330,13 @@ pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
 }
 
 /*
- * Exported routine for checking a user's access privileges to a namespace
+ * Exported routine for examining a user's privileges for a namespace
  */
-AclResult
-pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
+AclMode
+pg_namespace_aclmask(Oid nsp_oid, AclId userid,
+					 AclMode mask, AclMaskHow how)
 {
-	AclResult	result;
+	AclMode		result;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1304,11 +1347,11 @@ pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
 	 * we have all grantable privileges on it.
 	 */
 	if (isTempNamespace(nsp_oid))
-		return ACLCHECK_OK;
+		return mask;
 
 	/* Superusers bypass all permission checking. */
 	if (superuser_arg(userid))
-		return ACLCHECK_OK;
+		return mask;
 
 	/*
 	 * Get the schema's ACL from pg_namespace
@@ -1338,7 +1381,7 @@ pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
 		acl = DatumGetAclP(aclDatum);
 	}
 
-	result = aclcheck(acl, userid, mode);
+	result = aclmask(acl, userid, mask, how);
 
 	/* if we have a detoasted copy, free it */
 	if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
@@ -1350,6 +1393,71 @@ pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
 }
 
 
+/*
+ * Exported routine for checking a user's access privileges to a table
+ *
+ * Returns ACLCHECK_OK if the user has any of the privileges identified by
+ * 'mode'; otherwise returns a suitable error code (in practice, always
+ * ACLCHECK_NO_PRIV).
+ */
+AclResult
+pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
+{
+	if (pg_class_aclmask(table_oid, userid, mode, ACLMASK_ANY) != 0)
+		return ACLCHECK_OK;
+	else
+		return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a database
+ */
+AclResult
+pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
+{
+	if (pg_database_aclmask(db_oid, userid, mode, ACLMASK_ANY) != 0)
+		return ACLCHECK_OK;
+	else
+		return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a function
+ */
+AclResult
+pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
+{
+	if (pg_proc_aclmask(proc_oid, userid, mode, ACLMASK_ANY) != 0)
+		return ACLCHECK_OK;
+	else
+		return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a language
+ */
+AclResult
+pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
+{
+	if (pg_language_aclmask(lang_oid, userid, mode, ACLMASK_ANY) != 0)
+		return ACLCHECK_OK;
+	else
+		return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a namespace
+ */
+AclResult
+pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
+{
+	if (pg_namespace_aclmask(nsp_oid, userid, mode, ACLMASK_ANY) != 0)
+		return ACLCHECK_OK;
+	else
+		return ACLCHECK_NO_PRIV;
+}
+
+
 /*
  * Ownership check for a relation (specified by OID).
  */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 13b38d3752994d3dd794625571774b36b0371f12..e8ed4ce5cc46f702a438610d2332d659eb15dc73 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.230 2004/03/23 19:35:16 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.231 2004/05/11 17:36:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -412,28 +412,13 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 	userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
 
 	/*
-	 * For each bit in requiredPerms, apply the required check.  (We can't
-	 * do this in one aclcheck call because aclcheck treats multiple bits
-	 * as OR semantics, when we want AND.)
-	 *
-	 * We use a well-known cute trick for isolating the rightmost one-bit
-	 * in a nonzero word.  See nodes/bitmapset.c for commentary.
+	 * We must have *all* the requiredPerms bits, so use aclmask not
+	 * aclcheck.
 	 */
-#define RIGHTMOST_ONE(x) ((int32) (x) & -((int32) (x)))
-
-	while (requiredPerms != 0)
-	{
-		AclMode		thisPerm;
-		AclResult	aclcheck_result;
-
-		thisPerm = RIGHTMOST_ONE(requiredPerms);
-		requiredPerms &= ~thisPerm;
-
-		aclcheck_result = pg_class_aclcheck(relOid, userid, thisPerm);
-		if (aclcheck_result != ACLCHECK_OK)
-			aclcheck_error(aclcheck_result, ACL_KIND_CLASS,
-						   get_rel_name(relOid));
-	}
+	if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL)
+		!= requiredPerms)
+		aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+					   get_rel_name(relOid));
 }
 
 /*
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 4fcec230ccbe339332f2ac6a4085fb73190b09f1..59131122cbc0d057746dec45d5f07e420dd825d9 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.68 2004/05/02 13:38:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.69 2004/05/11 17:36:13 tgl Exp $
  *
  * NOTES
  *	  An ACL array is simply an array of AclItems, representing the union
@@ -177,6 +177,12 @@ typedef ArrayType IdList;
 #define ACL_ALL_RIGHTS_LANGUAGE		(ACL_USAGE)
 #define ACL_ALL_RIGHTS_NAMESPACE	(ACL_USAGE|ACL_CREATE)
 
+/* operation codes for pg_*_aclmask */
+typedef enum
+{
+	ACLMASK_ALL,				/* normal case: compute all bits */
+	ACLMASK_ANY					/* return when result is known nonzero */
+} AclMaskHow;
 
 /* result codes for pg_*_aclcheck */
 typedef enum
@@ -228,6 +234,17 @@ extern void ExecuteGrantStmt(GrantStmt *stmt);
 extern AclId get_grosysid(char *groname);
 extern char *get_groname(AclId grosysid);
 
+extern AclMode pg_class_aclmask(Oid table_oid, AclId userid,
+								AclMode mask, AclMaskHow how);
+extern AclMode pg_database_aclmask(Oid db_oid, AclId userid,
+								   AclMode mask, AclMaskHow how);
+extern AclMode pg_proc_aclmask(Oid proc_oid, AclId userid,
+							   AclMode mask, AclMaskHow how);
+extern AclMode pg_language_aclmask(Oid lang_oid, AclId userid,
+								   AclMode mask, AclMaskHow how);
+extern AclMode pg_namespace_aclmask(Oid nsp_oid, AclId userid,
+									AclMode mask, AclMaskHow how);
+
 extern AclResult pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode);
 extern AclResult pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode);
 extern AclResult pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode);