From 31c775adeb2251a9c66328cbc9016877e5e4f085 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 27 Apr 2002 03:45:03 +0000
Subject: [PATCH] Restructure aclcheck error reporting to make
 permission-failure messages more uniform and internationalizable: the global
 array aclcheck_error_strings[] is gone in favor of a subroutine
 aclcheck_error().  Partial implementation of namespace-related permission
 checks --- not all done yet.

---
 src/backend/catalog/aclchk.c             | 79 +++++++++++++-----------
 src/backend/catalog/namespace.c          | 13 +++-
 src/backend/catalog/pg_operator.c        | 10 ++-
 src/backend/commands/aggregatecmds.c     | 30 ++++-----
 src/backend/commands/comment.c           | 34 +++-------
 src/backend/commands/copy.c              | 10 ++-
 src/backend/commands/functioncmds.c      | 40 +++++++++---
 src/backend/commands/indexcmds.c         | 21 ++++++-
 src/backend/commands/lockcmds.c          |  7 ++-
 src/backend/commands/operatorcmds.c      | 18 ++++--
 src/backend/commands/schemacmds.c        | 15 ++++-
 src/backend/commands/tablecmds.c         | 51 ++++++++-------
 src/backend/commands/trigger.c           | 14 ++---
 src/backend/commands/typecmds.c          | 32 +++++++---
 src/backend/executor/execMain.c          | 27 +++++---
 src/backend/executor/execQual.c          |  5 +-
 src/backend/rewrite/rewriteDefine.c      | 12 ++--
 src/backend/rewrite/rewriteRemove.c      | 13 ++--
 src/backend/tcop/utility.c               | 12 ++--
 src/backend/utils/adt/acl.c              | 14 ++---
 src/backend/utils/cache/fcache.c         | 11 +++-
 src/backend/utils/cache/lsyscache.c      | 29 ++++++++-
 src/include/utils/acl.h                  | 30 ++++-----
 src/include/utils/fcache.h               |  4 +-
 src/include/utils/lsyscache.h            |  3 +-
 src/test/regress/expected/privileges.out | 46 +++++++-------
 26 files changed, 353 insertions(+), 227 deletions(-)

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 48cca5fcb8c..194292bb422 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.66 2002/04/21 00:26:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.67 2002/04/27 03:45:00 tgl Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -46,16 +46,7 @@ static void ExecuteGrantStmt_Namespace(GrantStmt *stmt);
 
 static const char *privilege_to_string(AclMode privilege);
 
-static int32 aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode);
-
-/* warning messages, now more explicit. */
-/* MUST correspond to the order of the ACLCHECK_* result codes in acl.h. */
-const char * const aclcheck_error_strings[] = {
-	"No error.",
-	"Permission denied.",
-	"Table does not exist.",
-	"Must be table owner."
-};
+static AclResult aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode);
 
 
 #ifdef ACLDEBUG
@@ -208,8 +199,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
 		pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
 
 		if (!pg_class_ownercheck(relOid, GetUserId()))
-			elog(ERROR, "%s: permission denied",
-				 relvar->relname);
+			aclcheck_error(ACLCHECK_NOT_OWNER, relvar->relname);
 
 		if (pg_class_tuple->relkind == RELKIND_INDEX)
 			elog(ERROR, "\"%s\" is an index",
@@ -409,7 +399,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
 		pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
 
 		if (!pg_proc_ownercheck(oid, GetUserId()))
-			elog(ERROR, "permission denied");
+			aclcheck_error(ACLCHECK_NOT_OWNER,
+						   NameStr(pg_proc_tuple->proname));
 
 		/*
 		 * If there's no ACL, create a default using the pg_proc.proowner
@@ -601,7 +592,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 		pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
 
 		if (!pg_namespace_ownercheck(tuple->t_data->t_oid, GetUserId()))
-			elog(ERROR, "permission denied");
+			aclcheck_error(ACLCHECK_NOT_OWNER, nspname);
 
 		/*
 		 * If there's no ACL, create a default using the pg_namespace.nspowner
@@ -776,6 +767,7 @@ in_group(AclId uid, AclId gid)
 	return result;
 }
 
+
 /*
  * aclcheck
  *
@@ -785,7 +777,7 @@ in_group(AclId uid, AclId gid)
  *
  * The ACL list is expected to be sorted in standard order.
  */
-static int32
+static AclResult
 aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode)
 {
 	AclItem    *aip,
@@ -902,15 +894,38 @@ aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode)
 }
 
 
+/*
+ * Standardized reporting of aclcheck permissions failures.
+ */
+void
+aclcheck_error(AclResult errcode, const char *objectname)
+{
+	switch (errcode)
+	{
+		case ACLCHECK_OK:
+			/* no error, so return to caller */
+			break;
+		case ACLCHECK_NO_PRIV:
+			elog(ERROR, "%s: permission denied", objectname);
+			break;
+		case ACLCHECK_NOT_OWNER:
+			elog(ERROR, "%s: must be owner", objectname);
+			break;
+		default:
+			elog(ERROR, "%s: unexpected AclResult %d",
+				 objectname, (int) errcode);
+			break;
+	}
+}
+
+
 /*
  * Exported routine for checking a user's access privileges to a table
- *
- * Returns an ACLCHECK_* result code.
  */
-int32
+AclResult
 pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode)
 {
-	int32		result;
+	AclResult	result;
 	bool		usesuper,
 				usecatupd;
 	HeapTuple	tuple;
@@ -1004,13 +1019,11 @@ pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode)
 
 /*
  * Exported routine for checking a user's access privileges to a database
- *
- * Returns an ACLCHECK_* result code.
  */
-int32
+AclResult
 pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode)
 {
-	int32		result;
+	AclResult	result;
 	Relation	pg_database;
 	ScanKeyData entry[1];
 	HeapScanDesc scan;
@@ -1069,13 +1082,11 @@ pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode)
 
 /*
  * Exported routine for checking a user's access privileges to a function
- *
- * Returns an ACLCHECK_* result code.
  */
-int32
+AclResult
 pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode)
 {
-	int32		result;
+	AclResult	result;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1124,13 +1135,11 @@ pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode)
 
 /*
  * Exported routine for checking a user's access privileges to a language
- *
- * Returns an ACLCHECK_* result code.
  */
-int32
+AclResult
 pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode)
 {
-	int32		result;
+	AclResult	result;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
@@ -1176,13 +1185,11 @@ pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode)
 
 /*
  * Exported routine for checking a user's access privileges to a namespace
- *
- * Returns an ACLCHECK_* result code.
  */
-int32
+AclResult
 pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode)
 {
-	int32		result;
+	AclResult	result;
 	HeapTuple	tuple;
 	Datum		aclDatum;
 	bool		isNull;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index be9dc9f3733..902af689693 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.13 2002/04/26 01:24:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.14 2002/04/27 03:45:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "storage/backendid.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
@@ -974,6 +975,16 @@ GetTempTableNamespace(void)
 	char		namespaceName[NAMEDATALEN];
 	Oid			namespaceId;
 
+	/*
+	 * First, do permission check to see if we are authorized to make
+	 * temp tables.  We use a nonstandard error message here since
+	 * "databasename: permission denied" might be a tad cryptic.
+	 */
+	if (pg_database_aclcheck(MyDatabaseId, GetUserId(),
+							 ACL_CREATE_TEMP) != ACLCHECK_OK)
+		elog(ERROR, "%s: not authorized to create temp tables",
+			 DatabaseName);
+
 	snprintf(namespaceName, NAMEDATALEN, "pg_temp_%d", MyBackendId);
 
 	namespaceId = GetSysCacheOid(NAMESPACENAME,
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 52cb26ee6a5..53a5d61cd90 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.67 2002/04/25 02:56:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.68 2002/04/27 03:45:00 tgl Exp $
  *
  * NOTES
  *	  these routines moved here from commands/define.c and somewhat cleaned up.
@@ -26,6 +26,7 @@
 #include "miscadmin.h"
 #include "parser/parse_func.h"
 #include "parser/parse_oper.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -697,6 +698,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
 	bool		otherDefined;
 	char	   *otherName;
 	Oid			otherNamespace;
+	AclResult	aclresult;
 
 	other_oid = OperatorLookup(otherOp,
 							   otherLeftTypeId,
@@ -727,6 +729,12 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
 	}
 
 	/* not in catalogs, different from operator, so make shell */
+
+	aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(),
+									  ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(otherNamespace));
+
 	other_oid = OperatorShellMake(otherName,
 								  otherNamespace,
 								  otherLeftTypeId,
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 7ee0c8f0784..c3c1ed16dfc 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -26,6 +26,7 @@
 #include "catalog/catname.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
@@ -45,6 +46,7 @@ DefineAggregate(List *names, List *parameters)
 {
 	char	   *aggName;
 	Oid			aggNamespace;
+	AclResult	aclresult;
 	List	   *transfuncName = NIL;
 	List	   *finalfuncName = NIL;
 	TypeName   *baseType = NULL;
@@ -57,6 +59,11 @@ DefineAggregate(List *names, List *parameters)
 	/* Convert list of names to a name and namespace */
 	aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
 
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(aggNamespace));
+
 	foreach(pl, parameters)
 	{
 		DefElem    *defel = (DefElem *) lfirst(pl);
@@ -157,20 +164,6 @@ RemoveAggregate(List *aggName, TypeName *aggType)
 
 	procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
 
-	/* Permission check */
-
-	if (!pg_proc_ownercheck(procOid, GetUserId()))
-	{
-		if (basetypeID == InvalidOid)
-			elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied",
-				 NameListToString(aggName));
-		else
-			elog(ERROR, "RemoveAggregate: aggregate %s for type %s: permission denied",
-				 NameListToString(aggName), format_type_be(basetypeID));
-	}
-
-	/* Remove the pg_proc tuple */
-
 	relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
 
 	tup = SearchSysCache(PROCOID,
@@ -180,9 +173,16 @@ RemoveAggregate(List *aggName, TypeName *aggType)
 		elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s",
 			 NameListToString(aggName));
 
+	/* Permission check: must own agg or its namespace */
+	if (!pg_proc_ownercheck(procOid, GetUserId()) &&
+		!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
+								 GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName));
+
 	/* Delete any comments associated with this function */
 	DeleteComments(procOid, RelationGetRelid(relation));
 
+	/* Remove the pg_proc tuple */
 	simple_heap_delete(relation, &tup->t_self);
 
 	ReleaseSysCache(tup);
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 3968ef14ef9..0eb59b912f3 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
  * Copyright (c) 1999-2001, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.44 2002/04/24 02:50:30 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.45 2002/04/27 03:45:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -324,8 +324,7 @@ CommentRelation(int objtype, List *relname, char *comment)
 
 	/* Check object security */
 	if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
-		elog(ERROR, "you are not permitted to comment on class '%s'",
-			 RelationGetRelationName(relation));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
 
 	/* Next, verify that the relation type matches the intent */
 
@@ -395,8 +394,7 @@ CommentAttribute(List *qualname, char *comment)
 	/* Check object security */
 
 	if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
-		elog(ERROR, "you are not permitted to comment on class '%s'",
-			 RelationGetRelationName(relation));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
 
 	/* Now, fetch the attribute number from the system cache */
 
@@ -498,7 +496,7 @@ CommentRule(List *qualname, char *comment)
 	Oid			reloid;
 	Oid			ruleoid;
 	Oid			classoid;
-	int32		aclcheck;
+	AclResult	aclcheck;
 
 	/* Separate relname and trig name */
 	nnames = length(qualname);
@@ -573,8 +571,7 @@ CommentRule(List *qualname, char *comment)
 
 	aclcheck = pg_class_aclcheck(reloid, GetUserId(), ACL_RULE);
 	if (aclcheck != ACLCHECK_OK)
-		elog(ERROR, "you are not permitted to comment on rule '%s'",
-			 rulename);
+		aclcheck_error(aclcheck, rulename);
 
 	/* pg_rewrite doesn't have a hard-coded OID, so must look it up */
 
@@ -613,8 +610,7 @@ CommentType(List *typename, char *comment)
 	/* Check object security */
 
 	if (!pg_type_ownercheck(oid, GetUserId()))
-		elog(ERROR, "you are not permitted to comment on type %s",
-			 TypeNameToString(tname));
+		aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(tname));
 
 	/* Call CreateComments() to create/drop the comments */
 
@@ -649,14 +645,7 @@ CommentAggregate(List *aggregate, List *arguments, char *comment)
 	/* Next, validate the user's attempt to comment */
 
 	if (!pg_proc_ownercheck(oid, GetUserId()))
-	{
-		if (baseoid == InvalidOid)
-			elog(ERROR, "you are not permitted to comment on aggregate %s for all types",
-				 NameListToString(aggregate));
-		else
-			elog(ERROR, "you are not permitted to comment on aggregate %s for type %s",
-				 NameListToString(aggregate), format_type_be(baseoid));
-	}
+		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggregate));
 
 	/* Call CreateComments() to create/drop the comments */
 
@@ -685,8 +674,7 @@ CommentProc(List *function, List *arguments, char *comment)
 	/* Now, validate the user's ability to comment on this function */
 
 	if (!pg_proc_ownercheck(oid, GetUserId()))
-		elog(ERROR, "you are not permitted to comment on function %s",
-			 NameListToString(function));
+		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(function));
 
 	/* Call CreateComments() to create/drop the comments */
 
@@ -723,8 +711,7 @@ CommentOperator(List *opername, List *arguments, char *comment)
 	/* Valid user's ability to comment on this operator */
 
 	if (!pg_oper_ownercheck(oid, GetUserId()))
-		elog(ERROR, "you are not permitted to comment on operator '%s'",
-			 NameListToString(opername));
+		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(opername));
 
 	/* Get the procedure associated with the operator */
 
@@ -775,8 +762,7 @@ CommentTrigger(List *qualname, char *comment)
 	/* Check object security */
 
 	if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
-		elog(ERROR, "you are not permitted to comment on trigger '%s' for relation '%s'",
-			 trigname, RelationGetRelationName(relation));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
 
 	/*
 	 * Fetch the trigger tuple from pg_trigger.  There can be only one
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 45e108027fd..1ab3ae14fcf 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.152 2002/03/29 19:06:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.153 2002/04/27 03:45:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -266,8 +266,8 @@ DoCopy(const RangeVar *relation, bool binary, bool oids, bool from, bool pipe,
 {
 	FILE	   *fp;
 	Relation	rel;
-	const AclMode required_access = (from ? ACL_INSERT : ACL_SELECT);
-	int32		aclresult;
+	AclMode		required_access = (from ? ACL_INSERT : ACL_SELECT);
+	AclResult	aclresult;
 
 	/*
 	 * Open and lock the relation, using the appropriate lock type.
@@ -278,9 +278,7 @@ DoCopy(const RangeVar *relation, bool binary, bool oids, bool from, bool pipe,
 	aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
 								  required_access);
 	if (aclresult != ACLCHECK_OK)
-		elog(ERROR, "%s: %s",
-			 RelationGetRelationName(rel),
-			 aclcheck_error_strings[aclresult]);
+		aclcheck_error(aclresult, RelationGetRelationName(rel));
 	if (!pipe && !superuser())
 		elog(ERROR, "You must have Postgres superuser privilege to do a COPY "
 			 "directly to or from a file.  Anyone can COPY to stdout or "
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 86b7cd50ec7..ce4b43b5936 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.2 2002/04/21 00:26:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.3 2002/04/27 03:45:01 tgl Exp $
  *
  * DESCRIPTION
  *	  These routines take the parse tree and pick out the
@@ -86,6 +86,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
 		else
 		{
 			Oid			namespaceId;
+			AclResult	aclresult;
 			char	   *typname;
 
 			if (languageOid == SQLlanguageId)
@@ -94,6 +95,10 @@ compute_return_type(TypeName *returnType, Oid languageOid,
 				 typnam);
 			namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
 															&typname);
+			aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
+											  ACL_CREATE);
+			if (aclresult != ACLCHECK_OK)
+				aclcheck_error(aclresult, get_namespace_name(namespaceId));
 			rettype = TypeShellMake(typname, namespaceId);
 			if (!OidIsValid(rettype))
 				elog(ERROR, "could not create type %s", typnam);
@@ -295,6 +300,7 @@ CreateFunction(ProcedureStmt *stmt)
 	Oid			languageOid;
 	char	   *funcname;
 	Oid			namespaceId;
+	AclResult	aclresult;
 	int			parameterCount;
 	Oid			parameterTypes[FUNC_MAX_ARGS];
 	int32		byte_pct,
@@ -311,6 +317,11 @@ CreateFunction(ProcedureStmt *stmt)
 	namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
 													&funcname);
 
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(namespaceId));
+
 	/* Convert language name to canonical case */
 	case_translate_language_name(stmt->language, languageName);
 
@@ -324,10 +335,21 @@ CreateFunction(ProcedureStmt *stmt)
 	languageOid = languageTuple->t_data->t_oid;
 	languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
 
-	if (!((languageStruct->lanpltrusted
-		   && pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE) == ACLCHECK_OK)
-		  || superuser()))
-		elog(ERROR, "permission denied");
+	if (languageStruct->lanpltrusted)
+	{
+		/* if trusted language, need USAGE privilege */
+		AclResult	aclresult;
+
+		aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE);
+		if (aclresult != ACLCHECK_OK)
+			aclcheck_error(aclresult, NameStr(languageStruct->lanname));
+	}
+	else
+	{
+		/* if untrusted language, must be superuser */
+		if (!superuser())
+			aclcheck_error(ACLCHECK_NO_PRIV, NameStr(languageStruct->lanname));
+	}
 
 	ReleaseSysCache(languageTuple);
 
@@ -404,9 +426,11 @@ RemoveFunction(List *functionName,		/* function name to be removed */
 		elog(ERROR, "RemoveFunction: couldn't find tuple for function %s",
 			 NameListToString(functionName));
 
-	if (!pg_proc_ownercheck(funcOid, GetUserId()))
-		elog(ERROR, "RemoveFunction: function '%s': permission denied",
-			 NameListToString(functionName));
+	/* Permission check: must own func or its namespace */
+	if (!pg_proc_ownercheck(funcOid, GetUserId()) &&
+		!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
+								 GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(functionName));
 
 	if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
 		elog(ERROR, "RemoveFunction: function '%s' is an aggregate"
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 14889a4f0c3..df6f81cd1db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.71 2002/04/17 20:57:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.72 2002/04/27 03:45:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 #include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_func.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -73,6 +74,7 @@ DefineIndex(RangeVar *heapRelation,
 	Oid		   *classObjectId;
 	Oid			accessMethodId;
 	Oid			relationId;
+	Oid			namespaceId;
 	Relation	rel;
 	HeapTuple	tuple;
 	Form_pg_am	accessMethodForm;
@@ -102,6 +104,7 @@ DefineIndex(RangeVar *heapRelation,
 			 heapRelation->relname);
 
 	relationId = RelationGetRelid(rel);
+	namespaceId = RelationGetNamespace(rel);
 
 	if (!IsBootstrapProcessingMode() &&
 		IsSystemRelation(rel) &&
@@ -110,6 +113,22 @@ DefineIndex(RangeVar *heapRelation,
 
 	heap_close(rel, NoLock);
 
+	/*
+	 * Verify we (still) have CREATE rights in the rel's namespace.
+	 * (Presumably we did when the rel was created, but maybe not anymore.)
+	 * Skip check if bootstrapping, since permissions machinery may not
+	 * be working yet; also, always allow if it's a temp table.
+	 */
+	if (!IsBootstrapProcessingMode() && !isTempNamespace(namespaceId))
+	{
+		AclResult	aclresult;
+
+		aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
+										  ACL_CREATE);
+		if (aclresult != ACLCHECK_OK)
+			aclcheck_error(aclresult, get_namespace_name(namespaceId));
+	}
+
 	/*
 	 * look up the access method, verify it can handle the requested
 	 * features
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index c91669572ef..80e42e3aa13 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/lockcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/lockcmds.c,v 1.2 2002/04/27 03:45:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 #include "commands/lockcmds.h"
 #include "miscadmin.h"
 #include "utils/acl.h"
+#include "utils/lsyscache.h"
 
 
 /*
@@ -38,7 +39,7 @@ LockTableCommand(LockStmt *lockstmt)
 	{
 		RangeVar   *relation = lfirst(p);
 		Oid			reloid;
-		int32		aclresult;
+		AclResult	aclresult;
 		Relation	rel;
 
 		/*
@@ -55,7 +56,7 @@ LockTableCommand(LockStmt *lockstmt)
 										  ACL_UPDATE | ACL_DELETE);
 
 		if (aclresult != ACLCHECK_OK)
-			elog(ERROR, "LOCK TABLE: permission denied");
+			aclcheck_error(aclresult, get_rel_name(reloid));
 
 		rel = relation_open(reloid, lockstmt->mode);
 
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index ffc35ea1ae9..de8ec06acbf 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.2 2002/04/16 23:08:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.3 2002/04/27 03:45:01 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -44,6 +44,7 @@
 #include "parser/parse_oper.h"
 #include "parser/parse_type.h"
 #include "utils/acl.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -60,6 +61,7 @@ DefineOperator(List *names, List *parameters)
 {
 	char	   *oprName;
 	Oid			oprNamespace;
+	AclResult	aclresult;
 	uint16		precedence = 0;			/* operator precedence */
 	bool		canHash = false;		/* operator hashes */
 	bool		canMerge = false;		/* operator merges */
@@ -85,6 +87,11 @@ DefineOperator(List *names, List *parameters)
 	/* Convert list of names to a name and namespace */
 	oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
 
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(oprNamespace));
+
 	/*
 	 * loop over the definition list and extract the information we need.
 	 */
@@ -226,14 +233,15 @@ RemoveOperator(List *operatorName,		/* operator name */
 	tup = SearchSysCacheCopy(OPEROID,
 							 ObjectIdGetDatum(operOid),
 							 0, 0, 0);
-
 	if (!HeapTupleIsValid(tup))	/* should not happen */
 		elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
 			 NameListToString(operatorName));
 
-	if (!pg_oper_ownercheck(operOid, GetUserId()))
-		elog(ERROR, "RemoveOperator: operator '%s': permission denied",
-			 NameListToString(operatorName));
+	/* Permission check: must own operator or its namespace */
+	if (!pg_oper_ownercheck(operOid, GetUserId()) &&
+		!pg_namespace_ownercheck(((Form_pg_operator) GETSTRUCT(tup))->oprnamespace,
+								 GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
 
 	/* Delete any comments associated with this operator */
 	DeleteComments(operOid, RelationGetRelid(relation));
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 191d5e329b8..30bc4077d38 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.2 2002/04/27 03:45:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "parser/analyze.h"
 #include "tcop/utility.h"
+#include "utils/acl.h"
 #include "utils/lsyscache.h"
 
 
@@ -36,9 +37,14 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
 	const char *owner_name;
 	Oid			owner_userid;
 	Oid			saved_userid;
+	AclResult	aclresult;
 
 	saved_userid = GetUserId();
 
+	/*
+	 * Figure out user identities.
+	 */
+
 	if (!authId)
 	{
 		owner_userid = saved_userid;
@@ -67,6 +73,13 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
 				 owner_name, authId);
 	}
 
+	/*
+	 * Permissions checks.
+	 */
+	aclresult = pg_database_aclcheck(MyDatabaseId, saved_userid, ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, DatabaseName);
+
 	if (!allowSystemTableMods && IsReservedName(schemaName))
 		elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas",
 			 schemaName);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7bdc238e77c..32930710c6e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.10 2002/04/26 19:29:47 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.11 2002/04/27 03:45:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,10 +106,22 @@ DefineRelation(CreateStmt *stmt, char relkind)
 
 	/*
 	 * Look up the namespace in which we are supposed to create the
-	 * relation.
+	 * relation.  Check we have permission to create there.
+	 * Skip check if bootstrapping, since permissions machinery may not
+	 * be working yet; also, always allow if it's a temp table.
 	 */
 	namespaceId = RangeVarGetCreationNamespace(stmt->relation);
 
+	if (!IsBootstrapProcessingMode() && !isTempNamespace(namespaceId))
+	{
+		AclResult	aclresult;
+
+		aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
+										  ACL_CREATE);
+		if (aclresult != ACLCHECK_OK)
+			aclcheck_error(aclresult, get_namespace_name(namespaceId));
+	}
+
 	/*
 	 * Merge domain attributes into the known columns before processing table
 	 * inheritance.  Otherwise we risk adding double constraints to a
@@ -307,8 +319,7 @@ TruncateRelation(const RangeVar *relation)
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(relid, GetUserId()))
-		elog(ERROR, "you do not own relation \"%s\"",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/* Keep the lock until transaction commit */
 	heap_close(rel, NoLock);
@@ -483,8 +494,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 		 * demand that creator of a child table own the parent.
 		 */
 		if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
-			elog(ERROR, "you do not own table \"%s\"",
-				 parent->relname);
+			aclcheck_error(ACLCHECK_NOT_OWNER,
+						   RelationGetRelationName(relation));
 
 		/*
 		 * Reject duplications in the list of parents.
@@ -1003,8 +1014,8 @@ renameatt(Oid relid,
 		elog(ERROR, "renameatt: class \"%s\" is a system catalog",
 			 RelationGetRelationName(targetrelation));
 	if (!pg_class_ownercheck(relid, GetUserId()))
-		elog(ERROR, "renameatt: you do not own class \"%s\"",
-			 RelationGetRelationName(targetrelation));
+		aclcheck_error(ACLCHECK_NOT_OWNER,
+					   RelationGetRelationName(targetrelation));
 
 	/*
 	 * if the 'recurse' flag is set then we are supposed to rename this
@@ -1558,8 +1569,7 @@ AlterTableAddColumn(Oid myrelid,
 		elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
 			 RelationGetRelationName(rel));
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Recurse to add the column to child classes, if requested.
@@ -1761,8 +1771,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid,
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Propagate to children if desired
@@ -1912,8 +1921,7 @@ AlterTableAlterColumnSetNotNull(Oid myrelid,
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Propagate to children if desired
@@ -2048,8 +2056,7 @@ AlterTableAlterColumnDefault(Oid myrelid,
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Propagate to children if desired
@@ -2208,8 +2215,7 @@ AlterTableAlterColumnFlags(Oid myrelid,
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Check the supplied parameters before anything else
@@ -2370,8 +2376,7 @@ AlterTableAddConstraint(Oid myrelid,
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	if (inh)
 	{
@@ -2695,8 +2700,7 @@ AlterTableDropConstraint(Oid myrelid,
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Since all we have is the name of the constraint, we have to look
@@ -2857,8 +2861,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(relOid, GetUserId()))
-		elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-			 RelationGetRelationName(rel));
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * lock the pg_class tuple for update (is that really needed?)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b826297a718..52e9761d759 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.115 2002/04/26 19:29:47 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.116 2002/04/27 03:45:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,6 +58,7 @@ CreateTrigger(CreateTrigStmt *stmt)
 	Datum		values[Natts_pg_trigger];
 	char		nulls[Natts_pg_trigger];
 	Relation	rel;
+	AclResult	aclresult;
 	Relation	tgrel;
 	SysScanDesc	tgscan;
 	ScanKeyData key;
@@ -84,10 +85,10 @@ CreateTrigger(CreateTrigStmt *stmt)
 		elog(ERROR, "CreateTrigger: can't create trigger for system relation %s",
 			stmt->relation->relname);
 
-	if (pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
-						  stmt->isconstraint ? ACL_REFERENCES : ACL_TRIGGER)
-		!= ACLCHECK_OK)
-		elog(ERROR, "permission denied");
+	aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
+						  stmt->isconstraint ? ACL_REFERENCES : ACL_TRIGGER);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, RelationGetRelationName(rel));
 
 	/*
 	 * If trigger is an RI constraint, use trigger name as constraint name
@@ -337,8 +338,7 @@ DropTrigger(Oid relid, const char *trigname)
 			 RelationGetRelationName(rel));
 
 	if (!pg_class_ownercheck(relid, GetUserId()))
-		elog(ERROR, "%s: %s", RelationGetRelationName(rel),
-			 aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
+		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
 	/*
 	 * Search pg_trigger, delete target trigger, count remaining triggers
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 13dbf04aaac..74e5a15cc8d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.2 2002/04/27 03:45:02 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -45,6 +45,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -60,6 +61,7 @@ DefineType(List *names, List *parameters)
 {
 	char	   *typeName;
 	Oid			typeNamespace;
+	AclResult	aclresult;
 	int16		internalLength = -1;	/* int2 */
 	int16		externalLength = -1;	/* int2 */
 	Oid			elemType = InvalidOid;
@@ -83,6 +85,11 @@ DefineType(List *names, List *parameters)
 	/* Convert list of names to a name and namespace */
 	typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
 
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(typeNamespace));
+
 	/*
 	 * Type names must be one character shorter than other names, allowing
 	 * room to create the corresponding array type name with prepended
@@ -288,9 +295,11 @@ RemoveType(List *names)
 		elog(ERROR, "Type \"%s\" does not exist",
 			 TypeNameToString(typename));
 
-	if (!pg_type_ownercheck(typeoid, GetUserId()))
-		elog(ERROR, "RemoveType: type '%s': permission denied",
-			 TypeNameToString(typename));
+	/* Permission check: must own type or its namespace */
+	if (!pg_type_ownercheck(typeoid, GetUserId()) &&
+		!pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
+								 GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
 
 	/* Delete any comments associated with this type */
 	DeleteComments(typeoid, RelationGetRelid(relation));
@@ -334,6 +343,7 @@ DefineDomain(CreateDomainStmt *stmt)
 {
 	char	   *domainName;
 	Oid			domainNamespace;
+	AclResult	aclresult;
 	int16		internalLength;
 	int16		externalLength;
 	Oid			inputProcedure;
@@ -360,6 +370,12 @@ DefineDomain(CreateDomainStmt *stmt)
 	domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
 														&domainName);
 
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
+									  ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_namespace_name(domainNamespace));
+
 	/*
 	 * Domainnames, unlike typenames don't need to account for the '_'
 	 * prefix.  So they can be one character longer.
@@ -586,9 +602,11 @@ RemoveDomain(List *names, int behavior)
 		elog(ERROR, "RemoveDomain: type '%s' does not exist",
 			 TypeNameToString(typename));
 
-	if (!pg_type_ownercheck(typeoid, GetUserId()))
-		elog(ERROR, "RemoveDomain: type '%s': permission denied",
-			 TypeNameToString(typename));
+	/* Permission check: must own type or its namespace */
+	if (!pg_type_ownercheck(typeoid, GetUserId()) &&
+		!pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
+								 GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
 
 	/* Check that this is actually a domain */
 	typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index a2045a9eb80..da12fa9a1db 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.158 2002/04/15 05:22:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.159 2002/04/27 03:45:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -360,7 +360,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 {
 	Oid			relOid;
 	Oid			userid;
-	int32		aclcheck_result;
+	AclResult	aclcheck_result;
 
 	/*
 	 * If it's a subquery RTE, ignore it --- it will be checked when
@@ -388,9 +388,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 	{
 		aclcheck_result = CHECK(ACL_SELECT);
 		if (aclcheck_result != ACLCHECK_OK)
-			elog(ERROR, "%s: %s",
-				 get_rel_name(relOid),
-				 aclcheck_error_strings[aclcheck_result]);
+			aclcheck_error(aclcheck_result, get_rel_name(relOid));
 	}
 
 	if (rte->checkForWrite)
@@ -419,9 +417,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 				break;
 		}
 		if (aclcheck_result != ACLCHECK_OK)
-			elog(ERROR, "%s: %s",
-				 get_rel_name(relOid),
-				 aclcheck_error_strings[aclcheck_result]);
+			aclcheck_error(aclcheck_result, get_rel_name(relOid));
 	}
 }
 
@@ -701,7 +697,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 		if (!parseTree->isPortal)
 		{
 			/*
-			 * a select into table
+			 * a select into table --- need to create the "into" table
 			 */
 			if (parseTree->into != NULL)
 			{
@@ -711,11 +707,22 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 				TupleDesc	tupdesc;
 
 				/*
-				 * create the "into" relation
+				 * find namespace to create in, check permissions
 				 */
 				intoName = parseTree->into->relname;
 				namespaceId = RangeVarGetCreationNamespace(parseTree->into);
 
+				if (!isTempNamespace(namespaceId))
+				{
+					AclResult	aclresult;
+
+					aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
+													  ACL_CREATE);
+					if (aclresult != ACLCHECK_OK)
+						aclcheck_error(aclresult,
+									   get_namespace_name(namespaceId));
+				}
+
 				/*
 				 * have to copy tupType to get rid of constraints
 				 */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index e77eeed8030..5cc1bbaa718 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.90 2002/02/18 23:11:13 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.91 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -658,9 +658,6 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
 	bool		hasSetArg;
 	int			i;
 
-	if (!fcache->permission_ok)
-		elog(ERROR, "permission denied");
-
 	/*
 	 * arguments is a list of expressions to evaluate before passing to
 	 * the function manager.  We skip the evaluation if it was already
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 922030c4bea..6d4c81c70a2 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.68 2002/04/19 23:13:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.69 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -127,7 +127,7 @@ DefineQueryRewrite(RuleStmt *stmt)
 			   *event_qualP;
 	List	   *l;
 	Query	   *query;
-	int32		aclcheck_result;
+	AclResult	aclresult;
 	bool		RelisBecomingView = false;
 
 	/*
@@ -144,11 +144,9 @@ DefineQueryRewrite(RuleStmt *stmt)
 	/*
 	 * Check user has permission to apply rules to this relation.
 	 */
-	aclcheck_result = pg_class_aclcheck(ev_relid, GetUserId(), ACL_RULE);
-	if (aclcheck_result != ACLCHECK_OK)
-		elog(ERROR, "%s: %s",
-			 RelationGetRelationName(event_relation),
-			 aclcheck_error_strings[aclcheck_result]);
+	aclresult = pg_class_aclcheck(ev_relid, GetUserId(), ACL_RULE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, RelationGetRelationName(event_relation));
 
 	/*
 	 * No rule actions that modify OLD or NEW
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
index df85fa504a0..97bf6589c90 100644
--- a/src/backend/rewrite/rewriteRemove.c
+++ b/src/backend/rewrite/rewriteRemove.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.48 2002/04/18 20:01:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.49 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,7 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
 	Oid			ruleId;
 	Oid			eventRelationOid;
 	bool		hasMoreRules;
-	int32		aclcheck_result;
+	AclResult	aclresult;
 
 	/*
 	 * Open the pg_rewrite relation.
@@ -82,12 +82,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
 	/*
 	 * Verify user has appropriate permissions.
 	 */
-	aclcheck_result = pg_class_aclcheck(eventRelationOid, GetUserId(),
-										ACL_RULE);
-	if (aclcheck_result != ACLCHECK_OK)
-		elog(ERROR, "%s: %s",
-			 RelationGetRelationName(event_relation),
-			 aclcheck_error_strings[aclcheck_result]);
+	aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, RelationGetRelationName(event_relation));
 
 	/* do not allow the removal of a view's SELECT rule */
 	if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index d1d670228ff..771f3d30cc8 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.151 2002/04/24 02:48:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.152 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -124,9 +124,10 @@ CheckDropPermissions(RangeVar *rel, char rightkind)
 	if (classform->relkind != rightkind)
 		DropErrorMsg(rel->relname, classform->relkind, rightkind);
 
-	if (!pg_class_ownercheck(relOid, GetUserId()))
-		elog(ERROR, "you do not own %s \"%s\"",
-			 rentry->name, rel->relname);
+	/* Allow DROP to either table owner or schema owner */
+	if (!pg_class_ownercheck(relOid, GetUserId()) &&
+		!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, rel->relname);
 
 	if (!allowSystemTableMods && IsSystemClass(classform))
 		elog(ERROR, "%s \"%s\" is a system %s",
@@ -149,8 +150,7 @@ CheckOwnership(RangeVar *rel, bool noCatalogs)
 		elog(ERROR, "Relation \"%s\" does not exist", rel->relname);
 
 	if (!pg_class_ownercheck(relOid, GetUserId()))
-		elog(ERROR, "%s: %s", rel->relname,
-			 aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
+		aclcheck_error(ACLCHECK_NOT_OWNER, rel->relname);
 
 	if (noCatalogs)
 	{
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 14a06504c5c..1f1de3e498f 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.72 2002/04/26 01:24:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.73 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -664,7 +664,7 @@ has_table_privilege_name_name(PG_FUNCTION_ARGS)
 	int32		usesysid;
 	Oid			reloid;
 	AclMode		mode;
-	int32		aclresult;
+	AclResult	aclresult;
 
 	/*
 	 * Lookup userid based on username
@@ -709,7 +709,7 @@ has_table_privilege_name(PG_FUNCTION_ARGS)
 	int32		usesysid;
 	Oid			reloid;
 	AclMode		mode;
-	int32		aclresult;
+	AclResult	aclresult;
 
 	usesysid = GetUserId();
 
@@ -750,7 +750,7 @@ has_table_privilege_name_id(PG_FUNCTION_ARGS)
 	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
 	int32		usesysid;
 	AclMode		mode;
-	int32		aclresult;
+	AclResult	aclresult;
 
 	/*
 	 * Lookup userid based on username
@@ -789,7 +789,7 @@ has_table_privilege_id(PG_FUNCTION_ARGS)
 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
 	int32		usesysid;
 	AclMode		mode;
-	int32		aclresult;
+	AclResult	aclresult;
 
 	usesysid = GetUserId();
 
@@ -825,7 +825,7 @@ has_table_privilege_id_name(PG_FUNCTION_ARGS)
 	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
 	Oid			reloid;
 	AclMode		mode;
-	int32		aclresult;
+	AclResult	aclresult;
 
 	/*
 	 * Lookup rel OID based on relname
@@ -863,7 +863,7 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS)
 	Oid			reloid = PG_GETARG_OID(1);
 	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
 	AclMode		mode;
-	int32		aclresult;
+	AclResult	aclresult;
 
 	/*
 	 * Convert priv_type_text to an AclMode
diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c
index cb11cbe4e26..6d6efdbaa30 100644
--- a/src/backend/utils/cache/fcache.c
+++ b/src/backend/utils/cache/fcache.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.43 2002/04/21 00:26:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.44 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "miscadmin.h"
 #include "utils/acl.h"
 #include "utils/fcache.h"
+#include "utils/lsyscache.h"
 
 
 /*
@@ -26,6 +27,12 @@ FunctionCachePtr
 init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt)
 {
 	FunctionCachePtr retval;
+	AclResult	aclresult;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_func_name(foid));
 
 	/* Safety check (should never fail, as parser should check sooner) */
 	if (nargs > FUNC_MAX_ARGS)
@@ -42,7 +49,5 @@ init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt)
 	/* Initialize additional info */
 	retval->setArgsValid = false;
 
-	retval->permission_ok = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE) == ACLCHECK_OK;
-
 	return retval;
 }
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 6699a179d37..f6b98c5bee6 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.70 2002/04/16 23:08:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.71 2002/04/27 03:45:03 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -564,6 +564,33 @@ get_oprjoin(Oid opno)
 
 /*				---------- FUNCTION CACHE ----------					 */
 
+/*
+ * get_func_name
+ *	  returns the name of the function with the given funcid
+ *
+ * Note: returns a palloc'd copy of the string, or NULL if no such function.
+ */
+char *
+get_func_name(Oid funcid)
+{
+	HeapTuple	tp;
+
+	tp = SearchSysCache(PROCOID,
+						ObjectIdGetDatum(funcid),
+						0, 0, 0);
+	if (HeapTupleIsValid(tp))
+	{
+		Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
+		char	   *result;
+
+		result = pstrdup(NameStr(functup->proname));
+		ReleaseSysCache(tp);
+		return result;
+	}
+	else
+		return NULL;
+}
+
 /*
  * get_func_rettype
  *		Given procedure id, return the function's result type.
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 146f04a27af..cf06ee74717 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.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: acl.h,v 1.43 2002/04/21 00:26:44 tgl Exp $
+ * $Id: acl.h,v 1.44 2002/04/27 03:45:03 tgl Exp $
  *
  * NOTES
  *	  For backward-compatibility purposes we have to allow there
@@ -165,13 +165,12 @@ typedef ArrayType IdList;
 
 
 /* result codes for pg_*_aclcheck */
-#define ACLCHECK_OK				  0
-#define ACLCHECK_NO_PRIV		  1
-#define ACLCHECK_NO_CLASS		  2
-#define ACLCHECK_NOT_OWNER		  3
-
-/* error messages (index by ACLCHECK_* result code).  set in aclchk.c. */
-extern const char * const aclcheck_error_strings[];
+typedef enum
+{
+	ACLCHECK_OK = 0,
+	ACLCHECK_NO_PRIV,
+	ACLCHECK_NOT_OWNER
+} AclResult;
 
 /*
  * routines used internally
@@ -181,7 +180,7 @@ extern Acl *aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
 					   unsigned modechg);
 
 /*
- * exported routines (from acl.c)
+ * SQL functions (from acl.c)
  */
 extern Datum aclitemin(PG_FUNCTION_ARGS);
 extern Datum aclitemout(PG_FUNCTION_ARGS);
@@ -196,12 +195,13 @@ extern void ExecuteGrantStmt(GrantStmt *stmt);
 extern AclId get_grosysid(char *groname);
 extern char *get_groname(AclId grosysid);
 
-/* these return ACLCHECK_* result codes */
-extern int32 pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode);
-extern int32 pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode);
-extern int32 pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode);
-extern int32 pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode);
-extern int32 pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode);
+extern AclResult pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode);
+extern AclResult pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode);
+extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode);
+extern AclResult pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode);
+extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode);
+
+extern void aclcheck_error(AclResult errcode, const char *objectname);
 
 /* ownercheck routines just return true (owner) or false (not) */
 extern bool pg_class_ownercheck(Oid class_oid, Oid userid);
diff --git a/src/include/utils/fcache.h b/src/include/utils/fcache.h
index fdc84c2122f..6bb75f52df5 100644
--- a/src/include/utils/fcache.h
+++ b/src/include/utils/fcache.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fcache.h,v 1.21 2002/02/18 23:11:46 petere Exp $
+ * $Id: fcache.h,v 1.22 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,8 +41,6 @@ typedef struct FunctionCache
 	 */
 	FmgrInfo	func;
 
-	bool permission_ok;
-
 	/*
 	 * setArgsValid is true when we are evaluating a set-valued function
 	 * and we are in the middle of a call series; we want to pass the same
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 2ad672b1e44..f57219df119 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.49 2002/04/05 00:31:35 tgl Exp $
+ * $Id: lsyscache.h,v 1.50 2002/04/27 03:45:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@ extern Oid	get_commutator(Oid opno);
 extern Oid	get_negator(Oid opno);
 extern RegProcedure get_oprrest(Oid opno);
 extern RegProcedure get_oprjoin(Oid opno);
+extern char *get_func_name(Oid funcid);
 extern Oid	get_func_rettype(Oid funcid);
 extern char func_volatile(Oid funcid);
 extern Oid	get_relname_relid(const char *relname, Oid relnamespace);
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 8f77a00ad12..c9c013e758f 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -69,11 +69,11 @@ SELECT * FROM atest2; -- ok
 
 INSERT INTO atest1 VALUES (2, 'two'); -- ok
 INSERT INTO atest2 VALUES ('foo', true); -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 INSERT INTO atest1 SELECT 1, b FROM atest1; -- ok
 UPDATE atest1 SET a = 1 WHERE a = 2; -- ok
 UPDATE atest2 SET col2 = NOT col2; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 SELECT * FROM atest1 FOR UPDATE; -- ok
  a |  b  
 ---+-----
@@ -82,15 +82,15 @@ SELECT * FROM atest1 FOR UPDATE; -- ok
 (2 rows)
 
 SELECT * FROM atest2 FOR UPDATE; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 DELETE FROM atest2; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- fail
-ERROR:  LOCK TABLE: permission denied
+ERROR:  atest2: permission denied
 COPY atest2 FROM stdin; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 GRANT ALL ON atest1 TO PUBLIC; -- fail
-ERROR:  atest1: permission denied
+ERROR:  atest1: must be owner
 -- checks in subquery, both ok
 SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) );
  a | b 
@@ -117,33 +117,33 @@ SELECT * FROM atest1; -- ok
 (2 rows)
 
 SELECT * FROM atest2; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 INSERT INTO atest1 VALUES (2, 'two'); -- fail
-ERROR:  atest1: Permission denied.
+ERROR:  atest1: permission denied
 INSERT INTO atest2 VALUES ('foo', true); -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 INSERT INTO atest1 SELECT 1, b FROM atest1; -- fail
-ERROR:  atest1: Permission denied.
+ERROR:  atest1: permission denied
 UPDATE atest1 SET a = 1 WHERE a = 2; -- fail
-ERROR:  atest1: Permission denied.
+ERROR:  atest1: permission denied
 UPDATE atest2 SET col2 = NULL; -- ok
 UPDATE atest2 SET col2 = NOT col2; -- fails; requires SELECT on atest2
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 UPDATE atest2 SET col2 = true WHERE atest1.a = 5; -- ok
 SELECT * FROM atest1 FOR UPDATE; -- fail
-ERROR:  atest1: Permission denied.
+ERROR:  atest1: permission denied
 SELECT * FROM atest2 FOR UPDATE; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 DELETE FROM atest2; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- ok
 COPY atest2 FROM stdin; -- fail
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 -- checks in subquery, both fail
 SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) );
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) );
-ERROR:  atest2: Permission denied.
+ERROR:  atest2: permission denied
 SET SESSION AUTHORIZATION regressuser4;
 COPY atest2 FROM stdin; -- ok
 SELECT * FROM atest1; -- ok
@@ -159,7 +159,7 @@ CREATE TABLE atest3 (one int, two int, three int);
 GRANT DELETE ON atest3 TO GROUP regressgroup2;
 SET SESSION AUTHORIZATION regressuser1;
 SELECT * FROM atest3; -- fail
-ERROR:  atest3: Permission denied.
+ERROR:  atest3: permission denied
 DELETE FROM atest3; -- ok
 -- views
 SET SESSION AUTHORIZATION regressuser3;
@@ -214,10 +214,10 @@ SELECT testfunc1(5), testfunc2(5); -- ok
 (1 row)
 
 CREATE FUNCTION testfunc3(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; -- fail
-ERROR:  permission denied
+ERROR:  sql: permission denied
 SET SESSION AUTHORIZATION regressuser3;
 SELECT testfunc1(5); -- fail
-ERROR:  permission denied
+ERROR:  testfunc1: permission denied
 SET SESSION AUTHORIZATION regressuser4;
 SELECT testfunc1(5); -- ok
  testfunc1 
@@ -226,7 +226,7 @@ SELECT testfunc1(5); -- ok
 (1 row)
 
 DROP FUNCTION testfunc1(int); -- fail
-ERROR:  RemoveFunction: function 'testfunc1': permission denied
+ERROR:  testfunc1: must be owner
 \c -
 DROP FUNCTION testfunc1(int); -- ok
 -- restore to sanity
-- 
GitLab