diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 2a56a6573a4e117383d1459797d0af940ca8128b..6a2bd7dc9323e01a2b6cf5855674cf7107e062fc 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.133 2001/06/12 05:55:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.134 2001/06/14 01:09:22 tgl Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -19,40 +19,36 @@
  */
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/tuptoaster.h"
 #include "catalog/catalog.h"
 #include "catalog/catname.h"
+#include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_attrdef.h"
+#include "catalog/pg_index.h"
 #include "catalog/pg_opclass.h"
-#include "catalog/pg_rewrite.h"
+#include "catalog/pg_type.h"
 #include "commands/command.h"
-#include "executor/spi.h"
-#include "catalog/heap.h"
+#include "commands/trigger.h"
+#include "executor/execdefs.h"
+#include "executor/executor.h"
 #include "miscadmin.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
 #include "optimizer/prep.h"
-#include "utils/acl.h"
-#include "utils/fmgroids.h"
-#include "commands/trigger.h"
-
+#include "parser/parse.h"
 #include "parser/parse_expr.h"
-#include "parser/parse_clause.h"
-#include "parser/parse_relation.h"
 #include "parser/parse_oper.h"
-#include "nodes/makefuncs.h"
-#include "optimizer/planmain.h"
-#include "optimizer/clauses.h"
-#include "rewrite/rewriteSupport.h"
-#include "commands/view.h"
-#include "utils/temprel.h"
-#include "executor/spi_priv.h"
-#include "catalog/pg_index.h"
-#include "catalog/pg_shadow.h"
+#include "parser/parse_relation.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/relcache.h"
-
-#include "parser/parse.h"
-#include "access/genam.h"
+#include "utils/temprel.h"
 
 
 static void drop_default(Oid relid, int16 attnum);
@@ -1689,13 +1685,7 @@ AlterTableOwner(const char *relationName, const char *newOwnerName)
 	/*
 	 * look up the new owner in pg_shadow and get the sysid
 	 */
-	tuple = SearchSysCache(SHADOWNAME,
-						   PointerGetDatum(newOwnerName),
-						   0, 0, 0);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "ALTER TABLE: user \"%s\" not found", newOwnerName);
-	newOwnerSysid = ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
-	ReleaseSysCache(tuple);
+	newOwnerSysid = get_usesysid(newOwnerName);
 
 	/*
 	 * find the table's entry in pg_class and make a modifiable copy
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 74990ce1b92c1c54eeb8481e7253801b9dfacc50..3d003616c09bb2ab137a9044b4b1e07f91de9a1c 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.76 2001/06/12 05:55:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.77 2001/06/14 01:09:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -728,16 +729,9 @@ CreateGroup(CreateGroupStmt *stmt)
 		const char *groupuser = strVal(lfirst(item));
 		Value	   *v;
 
-		tuple = SearchSysCache(SHADOWNAME,
-							   PointerGetDatum(groupuser),
-							   0, 0, 0);
-		if (!HeapTupleIsValid(tuple))
-			elog(ERROR, "CREATE GROUP: user \"%s\" does not exist", groupuser);
-
-		v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
+		v = makeInteger(get_usesysid(groupuser));
 		if (!member(v, newlist))
 			newlist = lcons(v, newlist);
-		ReleaseSysCache(tuple);
 	}
 
 	/* build an array to insert */
@@ -879,18 +873,10 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 			if (strcmp(tag, "ALTER GROUP") == 0)
 			{
 				/* Get the uid of the proposed user to add. */
-				tuple = SearchSysCache(SHADOWNAME,
-								   PointerGetDatum(strVal(lfirst(item))),
-									   0, 0, 0);
-				if (!HeapTupleIsValid(tuple))
-					elog(ERROR, "%s: user \"%s\" does not exist",
-						 tag, strVal(lfirst(item)));
-				v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
-				ReleaseSysCache(tuple);
+				v = makeInteger(get_usesysid(strVal(lfirst(item))));
 			}
 			else if (strcmp(tag, "CREATE USER") == 0)
 			{
-
 				/*
 				 * in this case we already know the uid and it wouldn't be
 				 * in the cache anyway yet
@@ -906,12 +892,12 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 			if (!member(v, newlist))
 				newlist = lcons(v, newlist);
 			else
-
 				/*
 				 * we silently assume here that this error will only come
 				 * up in a ALTER GROUP statement
 				 */
-				elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"", tag, strVal(lfirst(item)), stmt->name);
+				elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"",
+					 tag, strVal(lfirst(item)), stmt->name);
 		}
 
 		newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
@@ -1001,13 +987,7 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 				if (!is_dropuser)
 				{
 					/* Get the uid of the proposed user to drop. */
-					tuple = SearchSysCache(SHADOWNAME,
-								   PointerGetDatum(strVal(lfirst(item))),
-										   0, 0, 0);
-					if (!HeapTupleIsValid(tuple))
-						elog(ERROR, "ALTER GROUP: user \"%s\" does not exist", strVal(lfirst(item)));
-					v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
-					ReleaseSysCache(tuple);
+					v = makeInteger(get_usesysid(strVal(lfirst(item))));
 				}
 				else
 				{
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 9b150d387f9295d06cb9186f3ba5abeeee0a7200..4a21630b06c9b92db2eed19e769a5e9f3e832a54 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.63 2001/06/12 16:34:26 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.64 2001/06/14 01:09:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,14 +25,25 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+
+#define ACL_IDTYPE_GID_KEYWORD	"group"
+#define ACL_IDTYPE_UID_KEYWORD	"user"
+
 static const char *getid(const char *s, char *n);
 static bool aclitemeq(const AclItem *a1, const AclItem *a2);
 static bool aclitemgt(const AclItem *a1, const AclItem *a2);
 
-#define ACL_IDTYPE_GID_KEYWORD	"group"
-#define ACL_IDTYPE_UID_KEYWORD	"user"
+static AclMode convert_priv_string(text *priv_type_text);
+static bool has_table_privilege_cname_cname(char *username, char *relname,
+											text *priv_type_text);
+static bool has_table_privilege_cname_id(char *username, Oid reloid,
+										 text *priv_type_text);
+static bool has_table_privilege_id_cname(int32 usesysid, char *relname,
+										 text *priv_type_text);
+
 
 /*
  * getid
@@ -106,7 +117,6 @@ getid(const char *s, char *n)
 const char *
 aclparse(const char *s, AclItem *aip, unsigned *modechg)
 {
-	HeapTuple	htup;
 	char		name[NAMEDATALEN];
 
 	Assert(s && aip && modechg);
@@ -183,13 +193,7 @@ aclparse(const char *s, AclItem *aip, unsigned *modechg)
 	switch (aip->ai_idtype)
 	{
 		case ACL_IDTYPE_UID:
-			htup = SearchSysCache(SHADOWNAME,
-								  PointerGetDatum(name),
-								  0, 0, 0);
-			if (!HeapTupleIsValid(htup))
-				elog(ERROR, "aclparse: non-existent user \"%s\"", name);
-			aip->ai_id = ((Form_pg_shadow) GETSTRUCT(htup))->usesysid;
-			ReleaseSysCache(htup);
+			aip->ai_id = get_usesysid(name);
 			break;
 		case ACL_IDTYPE_GID:
 			aip->ai_id = get_grosysid(name);
@@ -710,3 +714,367 @@ makeAclString(const char *privileges, const char *grantee, char grant_or_revoke)
 	pfree(str.data);
 	return ret;
 }
+
+
+/*
+ * has_table_privilege_name_name
+ *		Check user privileges on a relation given
+ *		name usename, name relname, and text priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+Datum
+has_table_privilege_name_name(PG_FUNCTION_ARGS)
+{
+	Name		username = PG_GETARG_NAME(0);
+	Name		relname = PG_GETARG_NAME(1);
+	text		*priv_type_text = PG_GETARG_TEXT_P(2);
+	bool		result;
+
+	result = has_table_privilege_cname_cname(NameStr(*username),
+											 NameStr(*relname),
+											 priv_type_text);
+
+	PG_RETURN_BOOL(result);
+}
+
+
+/*
+ * has_table_privilege_name
+ *		Check user privileges on a relation given
+ *		name relname and text priv name.
+ *		current_user is assumed
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+Datum
+has_table_privilege_name(PG_FUNCTION_ARGS)
+{
+	Name		relname = PG_GETARG_NAME(0);
+	text		*priv_type_text = PG_GETARG_TEXT_P(1);
+	int32		usesysid;
+	bool		result;
+
+	usesysid = GetUserId();
+
+	result = has_table_privilege_id_cname(usesysid,
+										  NameStr(*relname),
+										  priv_type_text);
+
+	PG_RETURN_BOOL(result);
+}
+
+
+/*
+ * has_table_privilege_name_id
+ *		Check user privileges on a relation given
+ *		name usename, rel oid, and text priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+Datum
+has_table_privilege_name_id(PG_FUNCTION_ARGS)
+{
+	Name		username = PG_GETARG_NAME(0);
+	Oid			reloid = PG_GETARG_OID(1);
+	text		*priv_type_text = PG_GETARG_TEXT_P(2);
+	bool		result;
+
+	result = has_table_privilege_cname_id(NameStr(*username),
+										  reloid,
+										  priv_type_text);
+
+	PG_RETURN_BOOL(result);
+}
+
+
+/*
+ * has_table_privilege_id
+ *		Check user privileges on a relation given
+ *		rel oid, and text priv name.
+ *		current_user is assumed
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+Datum
+has_table_privilege_id(PG_FUNCTION_ARGS)
+{
+	Oid			reloid = PG_GETARG_OID(0);
+	text		*priv_type_text = PG_GETARG_TEXT_P(1);
+	char	   *relname;
+	int32		usesysid;
+	AclMode		mode;
+	int32		result;
+
+	usesysid = GetUserId();
+
+	/*
+	 * Lookup relname based on rel oid
+	 */
+	relname = get_rel_name(reloid);
+	if (relname == NULL)
+		elog(ERROR, "has_table_privilege: invalid relation oid %u",
+			 reloid);
+
+	/* 
+	 * Convert priv_type_text to an AclMode
+	 */
+	mode = convert_priv_string(priv_type_text);
+
+	/* 
+	 * Finally, check for the privilege
+	 */
+	result = pg_aclcheck(relname, usesysid, mode);
+
+	if (result == ACLCHECK_OK)
+		PG_RETURN_BOOL(true);
+	else
+		PG_RETURN_BOOL(false);
+}
+
+
+/*
+ * has_table_privilege_id_name
+ *		Check user privileges on a relation given
+ *		usesysid, name relname, and priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+Datum
+has_table_privilege_id_name(PG_FUNCTION_ARGS)
+{
+	int32		usesysid = PG_GETARG_INT32(0);
+	Name		relname = PG_GETARG_NAME(1);
+	text		*priv_type_text = PG_GETARG_TEXT_P(2);
+	bool		result;
+
+	result = has_table_privilege_id_cname(usesysid,
+										  NameStr(*relname),
+										  priv_type_text);
+
+	PG_RETURN_BOOL(result);
+}
+
+
+/*
+ * has_table_privilege_id_id
+ *		Check user privileges on a relation given
+ *		usesysid, rel oid, and priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+Datum
+has_table_privilege_id_id(PG_FUNCTION_ARGS)
+{
+	int32		usesysid = PG_GETARG_INT32(0);
+	Oid			reloid = PG_GETARG_OID(1);
+	text		*priv_type_text = PG_GETARG_TEXT_P(2);
+	char		*relname;
+	AclMode		mode;
+	int32		result;
+
+	/*
+	 * Lookup relname based on rel oid
+	 */
+	relname = get_rel_name(reloid);
+	if (relname == NULL)
+		elog(ERROR, "has_table_privilege: invalid relation oid %u",
+			 reloid);
+
+	/* 
+	 * Convert priv_type_text to an AclMode
+	 */
+	mode = convert_priv_string(priv_type_text);
+
+	/* 
+	 * Finally, check for the privilege
+	 */
+	result = pg_aclcheck(relname, usesysid, mode);
+
+	if (result == ACLCHECK_OK)
+		PG_RETURN_BOOL(true);
+	else
+		PG_RETURN_BOOL(false);
+}
+
+/*
+ *		Internal functions.
+ */
+
+/*
+ * convert_priv_string
+ *		Internal function.
+ *		Return mode from priv_type string
+ *
+ * RETURNS
+ *		AclMode
+ */
+
+static AclMode
+convert_priv_string(text *priv_type_text)
+{
+	char	*priv_type = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(priv_type_text)));
+
+	/*
+	 * Return mode from priv_type string
+	 */
+	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;
+
+	elog(ERROR, "has_table_privilege: invalid privilege type %s", priv_type);
+	/*
+	 * We should never get here, but stop the compiler from complaining
+	 */
+	return ACL_NO;
+}
+
+/*
+ * has_table_privilege_cname_cname
+ *		Check user privileges on a relation given
+ *		char *usename, char *relname, and text priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+static bool
+has_table_privilege_cname_cname(char *username, char *relname,
+								text *priv_type_text)
+{
+	int32		usesysid;
+
+	/*
+	 * Lookup userid based on username
+	 */
+	usesysid = get_usesysid(username);
+
+	/*
+	 * Make use of has_table_privilege_id_cname.
+	 * It accepts the arguments we now have.
+	 */
+	return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
+}
+
+
+/*
+ * has_table_privilege_cname_id
+ *		Check user privileges on a relation given
+ *		char *usename, rel oid, and text priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+static bool
+has_table_privilege_cname_id(char *username, Oid reloid,
+							 text *priv_type_text)
+{
+	int32		usesysid;
+	char		*relname;
+
+	/*
+	 * Lookup userid based on username
+	 */
+	usesysid = get_usesysid(username);
+
+	/*
+	 * Lookup relname based on rel oid
+	 */
+	relname = get_rel_name(reloid);
+	if (relname == NULL)
+		elog(ERROR, "has_table_privilege: invalid relation oid %u",
+			 reloid);
+
+	/*
+	 * Make use of has_table_privilege_id_cname.
+	 * It accepts the arguments we now have.
+	 */
+	return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
+}
+
+
+/*
+ * has_table_privilege_id_cname
+ *		Check user privileges on a relation given
+ *		usesysid, char *relname, and text priv name.
+ *
+ * RETURNS
+ *		a boolean value
+ *		't' indicating user has the privilege
+ *		'f' indicating user does not have the privilege
+ */
+static bool
+has_table_privilege_id_cname(int32 usesysid, char *relname,
+							 text *priv_type_text)
+{
+	HeapTuple	tuple;
+	AclMode		mode;
+	int32		result;
+
+	/*
+	 * Check relname is valid.
+	 * This is needed to deal with the case when usename is a superuser
+	 * in which case pg_aclcheck simply returns ACLCHECK_OK
+	 * without validating relname
+	 */
+	tuple = SearchSysCache(RELNAME,
+						   PointerGetDatum(relname),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "has_table_privilege: relation \"%s\" does not exist",
+			 relname);
+	ReleaseSysCache(tuple);
+
+	/* 
+	 * Convert priv_type_text to an AclMode
+	 */
+	mode = convert_priv_string(priv_type_text);
+
+	/* 
+	 * Finally, check for the privilege
+	 */
+	result = pg_aclcheck(relname, usesysid, mode);
+
+	if (result == ACLCHECK_OK)
+		return true;
+	else
+		return false;
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 573c21afd8d24e3882c997b3792a153ab6e2e51d..587213d48f8182ab3fed9d099829c6dd6c47b05b 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.55 2001/05/09 23:13:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.56 2001/06/14 01:09:22 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -18,6 +18,7 @@
 #include "access/tupmacs.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_shadow.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
 #include "utils/array.h"
@@ -25,6 +26,7 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+
 /*				---------- AMOP CACHES ----------						 */
 
 /*
@@ -537,7 +539,7 @@ get_relnatts(Oid relid)
  *
  *		Returns the name of a given relation.
  *
- * Note: returns a palloc'd copy of the string, or NULL if no such operator.
+ * Note: returns a palloc'd copy of the string, or NULL if no such relation.
  */
 char *
 get_rel_name(Oid relid)
@@ -1039,3 +1041,35 @@ free_attstatsslot(Oid atttype,
 	if (numbers)
 		pfree(numbers);
 }
+
+/*				---------- PG_SHADOW CACHE ----------					 */
+
+/*
+ * get_usesysid
+ *
+ *	  Given a user name, look up the user's sysid.
+ *	  Raises an error if no such user (rather than returning zero,
+ *	  which might possibly be a valid usesysid).
+ *
+ * Note: the type of usesysid is currently int4, but may change to Oid
+ * someday.  It'd be reasonable to return zero on failure if we were
+ * using Oid ...
+ */
+int32
+get_usesysid(const char *username)
+{
+	int32		result;
+	HeapTuple	userTup;
+
+	userTup = SearchSysCache(SHADOWNAME,
+							 PointerGetDatum(username),
+							 0, 0, 0);
+	if (!HeapTupleIsValid(userTup))
+		elog(ERROR, "user \"%s\" does not exist", username);
+
+	result = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid;
+
+	ReleaseSysCache(userTup);
+
+	return result;
+}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 58bf546889ec7fa1c947c81fea0a3f2c1d10fc19..df46469f0007eefed292958ba8ef8e8d6d66b86e 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.70 2001/06/13 19:52:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.71 2001/06/14 01:09:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 #include "catalog/pg_shadow.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -442,24 +443,20 @@ InitializeSessionUserId(const char *username)
 }
 
 
-
+/*
+ * Change session auth ID while running
+ */
 void SetSessionAuthorization(const char * username)
 {
-	HeapTuple	userTup;
+	int32		userid;
 
 	if (!AuthenticatedUserIsSuperuser)
 		elog(ERROR, "permission denied");
 
-	userTup = SearchSysCache(SHADOWNAME,
-							 PointerGetDatum(username),
-							 0, 0, 0);
-	if (!HeapTupleIsValid(userTup))
-		elog(ERROR, "user \"%s\" does not exist", username);
+	userid = get_usesysid(username);
 
-	SetSessionUserId(((Form_pg_shadow) GETSTRUCT(userTup))->usesysid);
-	SetUserId(((Form_pg_shadow) GETSTRUCT(userTup))->usesysid);
-
-	ReleaseSysCache(userTup);
+	SetSessionUserId(userid);
+	SetUserId(userid);
 }
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 71b3b545f1693a389a3f5d7e2b1571a6f6c31023..415f3b0372cd2d2c6280ca0221597e017811494c 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.193 2001/06/13 21:12:43 momjian Exp $
+ * $Id: pg_proc.h,v 1.194 2001/06/14 01:09:22 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -2633,6 +2633,20 @@ DESCR("unary plus");
 DATA(insert OID = 1915 (  numeric_uplus	   PGUID 12 f t t t 1 f 1700 "1700" 100 0 0 100  numeric_uplus - ));
 DESCR("unary plus");
 
+DATA(insert OID = 1922 (  has_table_privilege		   PGUID 12 f t f t 3 f 16 "19 19 25" 100 0 0 100	has_table_privilege_name_name - ));
+DESCR("user privilege on relation by username, relname");
+DATA(insert OID = 1923 (  has_table_privilege		   PGUID 12 f t f t 3 f 16 "19 26 25" 100 0 0 100	has_table_privilege_name_id - ));
+DESCR("user privilege on relation by username, rel oid");
+DATA(insert OID = 1924 (  has_table_privilege		   PGUID 12 f t f t 3 f 16 "23 19 25" 100 0 0 100	has_table_privilege_id_name - ));
+DESCR("user privilege on relation by usesysid, relname");
+DATA(insert OID = 1925 (  has_table_privilege		   PGUID 12 f t f t 3 f 16 "23 26 25" 100 0 0 100	has_table_privilege_id_id - ));
+DESCR("user privilege on relation by usesysid, rel oid");
+DATA(insert OID = 1926 (  has_table_privilege		   PGUID 12 f t f t 2 f 16 "19 25" 100 0 0 100	has_table_privilege_name - ));
+DESCR("current user privilege on relation by relname");
+DATA(insert OID = 1927 (  has_table_privilege		   PGUID 12 f t f t 2 f 16 "26 25" 100 0 0 100	has_table_privilege_id - ));
+DESCR("current user privilege on relation by rel oid");
+
+
 /*
  * prototypes for functions pg_proc.c
  */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d739fd83d8c0e76398f7993e4530e52223650850..7ffde98abf700b1297b03f91ece995daf940792e 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.153 2001/06/13 21:08:59 momjian Exp $
+ * $Id: builtins.h,v 1.154 2001/06/14 01:09:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,15 @@
 /*
  *		Defined in adt/
  */
+
+/* acl.c */
+extern Datum has_table_privilege_name_name(PG_FUNCTION_ARGS);
+extern Datum has_table_privilege_name_id(PG_FUNCTION_ARGS);
+extern Datum has_table_privilege_id_name(PG_FUNCTION_ARGS);
+extern Datum has_table_privilege_id_id(PG_FUNCTION_ARGS);
+extern Datum has_table_privilege_name(PG_FUNCTION_ARGS);
+extern Datum has_table_privilege_id(PG_FUNCTION_ARGS);
+
 /* bool.c */
 extern Datum boolin(PG_FUNCTION_ARGS);
 extern Datum boolout(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 719f68a873f3ec1cd8579f5887025992ae47a545..d418e8a10d6f197c8802dd28778baa5e6c695b60 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.33 2001/05/09 23:13:37 tgl Exp $
+ * $Id: lsyscache.h,v 1.34 2001/06/14 01:09:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,6 +51,7 @@ extern bool get_attstatsslot(HeapTuple statstuple,
 extern void free_attstatsslot(Oid atttype,
 							  Datum *values, int nvalues,
 							  float4 *numbers, int nnumbers);
+extern int32 get_usesysid(const char *username);
 
 #define TypeIsToastable(typid)	(get_typstorage(typid) != 'p')
 
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index ea19667a169656ddfa8d7762baa15c1b55dd0ab3..4ada312755ba43363778518e0b209b31443aec02 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -188,6 +188,274 @@ SELECT * FROM atestv3; -- ok
 -----+-----+-------
 (0 rows)
 
+-- has_table_privilege function
+-- bad-input checks
+select has_table_privilege(NULL,'pg_shadow','select');
+ has_table_privilege 
+---------------------
+ 
+(1 row)
+
+select has_table_privilege('pg_shad','select');
+ERROR:  has_table_privilege: relation "pg_shad" does not exist
+select has_table_privilege('nosuchuser','pg_shadow','select');
+ERROR:  user "nosuchuser" does not exist
+select has_table_privilege('pg_shadow','sel');
+ERROR:  has_table_privilege: invalid privilege type sel
+select has_table_privilege(-999999,'pg_shadow','update');
+ERROR:  pg_aclcheck: invalid user id 4293967297
+select has_table_privilege(1,'rule');
+ERROR:  has_table_privilege: invalid relation oid 1
+-- superuser
+\c regression
+select has_table_privilege(current_user,'pg_shadow','select');
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(current_user,'pg_shadow','insert');
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t2.usesysid,'pg_shadow','update')
+from (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t2.usesysid,'pg_shadow','delete')
+from (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(current_user,t1.oid,'rule')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(current_user,t1.oid,'references')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t2.usesysid,t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_shadow') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t2.usesysid,t1.oid,'insert')
+from (select oid from pg_class where relname = 'pg_shadow') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege('pg_shadow','update');
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege('pg_shadow','delete');
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t1.oid,'trigger')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+-- non-superuser
+SET SESSION AUTHORIZATION regressuser3;
+select has_table_privilege(current_user,'pg_class','select');
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(current_user,'pg_class','insert');
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t2.usesysid,'pg_class','update')
+from (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t2.usesysid,'pg_class','delete')
+from (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(current_user,t1.oid,'rule')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(current_user,t1.oid,'references')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t2.usesysid,t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_class') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t2.usesysid,t1.oid,'insert')
+from (select oid from pg_class where relname = 'pg_class') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege('pg_class','update');
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege('pg_class','delete');
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t1.oid,'trigger')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(current_user,'atest1','select');
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(current_user,'atest1','insert');
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t2.usesysid,'atest1','update')
+from (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t2.usesysid,'atest1','delete')
+from (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(current_user,t1.oid,'rule')
+from (select oid from pg_class where relname = 'atest1') as t1;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(current_user,t1.oid,'references')
+from (select oid from pg_class where relname = 'atest1') as t1;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t2.usesysid,t1.oid,'select')
+from (select oid from pg_class where relname = 'atest1') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t2.usesysid,t1.oid,'insert')
+from (select oid from pg_class where relname = 'atest1') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege('atest1','update');
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege('atest1','delete');
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+select has_table_privilege(t1.oid,'select')
+from (select oid from pg_class where relname = 'atest1') as t1;
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+select has_table_privilege(t1.oid,'trigger')
+from (select oid from pg_class where relname = 'atest1') as t1;
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
 -- clean up
 \c regression
 DROP TABLE atest1;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 2a096660834fa9ba064fd3e44826397bf89b841e..95e7b604d3b9e2f4bf62f23ca125f7abd79506bc 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -126,6 +126,106 @@ SELECT * FROM atestv1; -- ok
 SELECT * FROM atestv3; -- ok
 
 
+-- has_table_privilege function
+
+-- bad-input checks
+select has_table_privilege(NULL,'pg_shadow','select');
+select has_table_privilege('pg_shad','select');
+select has_table_privilege('nosuchuser','pg_shadow','select');
+select has_table_privilege('pg_shadow','sel');
+select has_table_privilege(-999999,'pg_shadow','update');
+select has_table_privilege(1,'rule');
+
+-- superuser
+\c regression
+select has_table_privilege(current_user,'pg_shadow','select');
+select has_table_privilege(current_user,'pg_shadow','insert');
+
+select has_table_privilege(t2.usesysid,'pg_shadow','update')
+from (select usesysid from pg_user where usename = current_user) as t2;
+select has_table_privilege(t2.usesysid,'pg_shadow','delete')
+from (select usesysid from pg_user where usename = current_user) as t2;
+
+select has_table_privilege(current_user,t1.oid,'rule')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+select has_table_privilege(current_user,t1.oid,'references')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+
+select has_table_privilege(t2.usesysid,t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_shadow') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+select has_table_privilege(t2.usesysid,t1.oid,'insert')
+from (select oid from pg_class where relname = 'pg_shadow') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+
+select has_table_privilege('pg_shadow','update');
+select has_table_privilege('pg_shadow','delete');
+
+select has_table_privilege(t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+select has_table_privilege(t1.oid,'trigger')
+from (select oid from pg_class where relname = 'pg_shadow') as t1;
+
+-- non-superuser
+SET SESSION AUTHORIZATION regressuser3;
+
+select has_table_privilege(current_user,'pg_class','select');
+select has_table_privilege(current_user,'pg_class','insert');
+
+select has_table_privilege(t2.usesysid,'pg_class','update')
+from (select usesysid from pg_user where usename = current_user) as t2;
+select has_table_privilege(t2.usesysid,'pg_class','delete')
+from (select usesysid from pg_user where usename = current_user) as t2;
+
+select has_table_privilege(current_user,t1.oid,'rule')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+select has_table_privilege(current_user,t1.oid,'references')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+
+select has_table_privilege(t2.usesysid,t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_class') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+select has_table_privilege(t2.usesysid,t1.oid,'insert')
+from (select oid from pg_class where relname = 'pg_class') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+
+select has_table_privilege('pg_class','update');
+select has_table_privilege('pg_class','delete');
+
+select has_table_privilege(t1.oid,'select')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+select has_table_privilege(t1.oid,'trigger')
+from (select oid from pg_class where relname = 'pg_class') as t1;
+
+select has_table_privilege(current_user,'atest1','select');
+select has_table_privilege(current_user,'atest1','insert');
+
+select has_table_privilege(t2.usesysid,'atest1','update')
+from (select usesysid from pg_user where usename = current_user) as t2;
+select has_table_privilege(t2.usesysid,'atest1','delete')
+from (select usesysid from pg_user where usename = current_user) as t2;
+
+select has_table_privilege(current_user,t1.oid,'rule')
+from (select oid from pg_class where relname = 'atest1') as t1;
+select has_table_privilege(current_user,t1.oid,'references')
+from (select oid from pg_class where relname = 'atest1') as t1;
+
+select has_table_privilege(t2.usesysid,t1.oid,'select')
+from (select oid from pg_class where relname = 'atest1') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+select has_table_privilege(t2.usesysid,t1.oid,'insert')
+from (select oid from pg_class where relname = 'atest1') as t1,
+  (select usesysid from pg_user where usename = current_user) as t2;
+
+select has_table_privilege('atest1','update');
+select has_table_privilege('atest1','delete');
+
+select has_table_privilege(t1.oid,'select')
+from (select oid from pg_class where relname = 'atest1') as t1;
+select has_table_privilege(t1.oid,'trigger')
+from (select oid from pg_class where relname = 'atest1') as t1;
+
+
 -- clean up
 
 \c regression