Stephen,

  We, as a community, have gotten flak from time-to-time about the
>   superuser role.  We also tend to wish to avoid unnecessary
>   optimization as it complicates the code base and makes folks reviewing
>   the code wonder at the exceptions.
>

I have attached a patch for consideration/discussion that addresses these
items.  I have based it off of current master (0c013e0).  I have done some
cursory testing including check-world with success.


>       My 2c about this function is that it should be completely removed
>       and the place where it's checked replaced  with just the
>       'has_rolreplication' call and error.  It's only called in one
>       place and it'd be a simple one-liner anyway.  As for
>       has_rolreplication, I don't understand why it's in miscinit.c when
>       the rest of the has_* set is in acl.c.
>

With all the other pg_has_* functions being found in acl.c I agree that it
would seem odd that this (or any other related function) would be found
elsewhere.  Since aclchk.c also has a couple of functions that follow the
pattern of "has_<priv>_privilege", I moved this function there, for now, as
well as modified it to include superuser checks as they were not previously
there.  The only related function I found in acl.c was "has_rolinherit"
which is a static function. :-/  There is also a static function
"has_rolcatupdate" in aclchk.c.  I would agree that acl.c (or aclchk.c?)
would be a more appropriate location for these functions, though in some of
the above cases, it might require exposing them (I'm not sure that I see
any harm in doing so).

Perhaps refactoring to consolidate all of these in either acl.c or aclchk.c
with the following pattern might be a step in the right direction?

* has_createrole_privilege
* has_bypassrls_privilege
* has_inherit_privilege
* has_catupdate_privilege
* has_replication_privilege
* has_???_privilege

In either case, I think following a "convention" on the naming of these
functions (unless there is semantic reason not to) would also help to
reduce any confusion or head scratching.

  Removing these design patterns may also help to avoid ending up with
>   more of them in the future as folks copy and/or crib off of what we've
>   already done to implement their features...
>

I agree.

-Adam

-- 
Adam Brightwell - adam.brightw...@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 133143d..695a13c
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
***************
*** 27,32 ****
--- 27,33 ----
  #include "miscadmin.h"
  #include "replication/walreceiver.h"
  #include "storage/smgr.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/numeric.h"
  #include "utils/guc.h"
*************** pg_start_backup(PG_FUNCTION_ARGS)
*** 54,60 ****
  
  	backupidstr = text_to_cstring(backupid);
  
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		   errmsg("must be superuser or replication role to run a backup")));
--- 55,61 ----
  
  	backupidstr = text_to_cstring(backupid);
  
! 	if (!has_replication_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		   errmsg("must be superuser or replication role to run a backup")));
*************** pg_stop_backup(PG_FUNCTION_ARGS)
*** 82,88 ****
  {
  	XLogRecPtr	stoppoint;
  
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		 (errmsg("must be superuser or replication role to run a backup"))));
--- 83,89 ----
  {
  	XLogRecPtr	stoppoint;
  
! 	if (!has_replication_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		 (errmsg("must be superuser or replication role to run a backup"))));
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..a386fe2
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** has_createrole_privilege(Oid roleid)
*** 5080,5085 ****
--- 5080,5107 ----
  	return result;
  }
  
+ /*
+  * Check whether specified role has REPLICATION priviledge (or is a superuser)
+  */
+ bool
+ has_replication_privilege(Oid roleid)
+ {
+ 	bool		result = false;
+ 	HeapTuple	utup;
+ 
+ 	/* Superusers bypass all permission checking. */
+ 	if (superuser_arg(roleid))
+ 		return true;
+ 
+ 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
+ 	if (HeapTupleIsValid(utup))
+ 	{
+ 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
+ }
+ 
  bool
  has_bypassrls_privilege(Oid roleid)
  {
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
new file mode 100644
index c9a9baf..ed89b23
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
*************** AlterObjectOwner_internal(Relation rel,
*** 807,852 ****
  		bool	   *nulls;
  		bool	   *replaces;
  
! 		/* Superusers can bypass permission checks */
! 		if (!superuser())
  		{
! 			AclObjectKind aclkind = get_object_aclkind(classId);
  
! 			/* must be owner */
! 			if (!has_privs_of_role(GetUserId(), old_ownerId))
  			{
! 				char	   *objname;
! 				char		namebuf[NAMEDATALEN];
! 
! 				if (Anum_name != InvalidAttrNumber)
! 				{
! 					datum = heap_getattr(oldtup, Anum_name,
! 										 RelationGetDescr(rel), &isnull);
! 					Assert(!isnull);
! 					objname = NameStr(*DatumGetName(datum));
! 				}
! 				else
! 				{
! 					snprintf(namebuf, sizeof(namebuf), "%u",
! 							 HeapTupleGetOid(oldtup));
! 					objname = namebuf;
! 				}
! 				aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname);
  			}
! 			/* Must be able to become new owner */
! 			check_is_member_of_role(GetUserId(), new_ownerId);
! 
! 			/* New owner must have CREATE privilege on namespace */
! 			if (OidIsValid(namespaceId))
  			{
! 				AclResult	aclresult;
! 
! 				aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
! 												  ACL_CREATE);
! 				if (aclresult != ACLCHECK_OK)
! 					aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
! 								   get_namespace_name(namespaceId));
  			}
  		}
  
  		/* Build a modified tuple */
--- 807,848 ----
  		bool	   *nulls;
  		bool	   *replaces;
  
! 		AclObjectKind aclkind = get_object_aclkind(classId);
! 
! 		/* must be owner */
! 		if (!has_privs_of_role(GetUserId(), old_ownerId))
  		{
! 			char	   *objname;
! 			char		namebuf[NAMEDATALEN];
  
! 			if (Anum_name != InvalidAttrNumber)
  			{
! 				datum = heap_getattr(oldtup, Anum_name,
! 									 RelationGetDescr(rel), &isnull);
! 				Assert(!isnull);
! 				objname = NameStr(*DatumGetName(datum));
  			}
! 			else
  			{
! 				snprintf(namebuf, sizeof(namebuf), "%u",
! 						 HeapTupleGetOid(oldtup));
! 				objname = namebuf;
  			}
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname);
+ 		}
+ 		/* Must be able to become new owner */
+ 		check_is_member_of_role(GetUserId(), new_ownerId);
+ 
+ 		/* New owner must have CREATE privilege on namespace */
+ 		if (OidIsValid(namespaceId))
+ 		{
+ 			AclResult	aclresult;
+ 
+ 			aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
+ 											  ACL_CREATE);
+ 			if (aclresult != ACLCHECK_OK)
+ 				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 							   get_namespace_name(namespaceId));
  		}
  
  		/* Build a modified tuple */
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
new file mode 100644
index ab4ed6c..aad6ae4
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
*************** AlterForeignServerOwner_internal(Relatio
*** 332,361 ****
  
  	if (form->srvowner != newOwnerId)
  	{
! 		/* Superusers can always do it */
! 		if (!superuser())
! 		{
! 			Oid			srvId;
! 			AclResult	aclresult;
  
! 			srvId = HeapTupleGetOid(tup);
  
! 			/* Must be owner */
! 			if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
! 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
! 							   NameStr(form->srvname));
  
! 			/* Must be able to become new owner */
! 			check_is_member_of_role(GetUserId(), newOwnerId);
  
! 			/* New owner must have USAGE privilege on foreign-data wrapper */
! 			aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
! 			if (aclresult != ACLCHECK_OK)
! 			{
! 				ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
  
! 				aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
! 			}
  		}
  
  		form->srvowner = newOwnerId;
--- 332,359 ----
  
  	if (form->srvowner != newOwnerId)
  	{
! 		Oid			srvId;
! 		AclResult	aclresult;
  
! 		srvId = HeapTupleGetOid(tup);
  
! 		/* Must be owner */
! 		if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
! 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
! 						   NameStr(form->srvname));
  
! 		/* Must be able to become new owner */
! 		check_is_member_of_role(GetUserId(), newOwnerId);
  
! 		/* New owner must have USAGE privilege on foreign-data wrapper */
! 		aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId,
! 													 ACL_USAGE);
  
! 		if (aclresult != ACLCHECK_OK)
! 		{
! 			ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
! 
! 			aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
  		}
  
  		form->srvowner = newOwnerId;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index cb16c53..b3d864c
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATExecChangeOwner(Oid relationOid, Oid n
*** 8624,8650 ****
  		/* skip permission checks when recursing to index or toast table */
  		if (!recursing)
  		{
! 			/* Superusers can always do it */
! 			if (!superuser())
! 			{
! 				Oid			namespaceOid = tuple_class->relnamespace;
! 				AclResult	aclresult;
  
! 				/* Otherwise, must be owner of the existing object */
! 				if (!pg_class_ownercheck(relationOid, GetUserId()))
! 					aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
! 								   RelationGetRelationName(target_rel));
  
! 				/* Must be able to become new owner */
! 				check_is_member_of_role(GetUserId(), newOwnerId);
  
! 				/* New owner must have CREATE privilege on namespace */
! 				aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
! 												  ACL_CREATE);
! 				if (aclresult != ACLCHECK_OK)
! 					aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
! 								   get_namespace_name(namespaceOid));
! 			}
  		}
  
  		memset(repl_null, false, sizeof(repl_null));
--- 8624,8646 ----
  		/* skip permission checks when recursing to index or toast table */
  		if (!recursing)
  		{
! 			Oid			namespaceOid = tuple_class->relnamespace;
! 			AclResult	aclresult;
  
! 			/* Must be owner of the existing object */
! 			if (!pg_class_ownercheck(relationOid, GetUserId()))
! 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
! 							   RelationGetRelationName(target_rel));
  
! 			/* Must be able to become new owner */
! 			check_is_member_of_role(GetUserId(), newOwnerId);
  
! 			/* New owner must have CREATE privilege on namespace */
! 			aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
! 											  ACL_CREATE);
! 			if (aclresult != ACLCHECK_OK)
! 				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
! 							   get_namespace_name(namespaceOid));
  		}
  
  		memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
new file mode 100644
index ad364ef..c154d21
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** AlterTypeOwner(List *names, Oid newOwner
*** 3301,3324 ****
  	 */
  	if (typTup->typowner != newOwnerId)
  	{
! 		/* Superusers can always do it */
! 		if (!superuser())
! 		{
! 			/* Otherwise, must be owner of the existing object */
! 			if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
! 				aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup));
  
! 			/* Must be able to become new owner */
! 			check_is_member_of_role(GetUserId(), newOwnerId);
  
! 			/* New owner must have CREATE privilege on namespace */
! 			aclresult = pg_namespace_aclcheck(typTup->typnamespace,
! 											  newOwnerId,
! 											  ACL_CREATE);
! 			if (aclresult != ACLCHECK_OK)
! 				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
! 							   get_namespace_name(typTup->typnamespace));
! 		}
  
  		/*
  		 * If it's a composite type, invoke ATExecChangeOwner so that we fix
--- 3301,3320 ----
  	 */
  	if (typTup->typowner != newOwnerId)
  	{
! 		/* Must be owner of the existing object */
! 		if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
! 			aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup));
  
! 		/* Must be able to become new owner */
! 		check_is_member_of_role(GetUserId(), newOwnerId);
  
! 		/* New owner must have CREATE privilege on namespace */
! 		aclresult = pg_namespace_aclcheck(typTup->typnamespace,
! 										  newOwnerId,
! 										  ACL_CREATE);
! 		if (aclresult != ACLCHECK_OK)
! 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
! 						   get_namespace_name(typTup->typnamespace));
  
  		/*
  		 * If it's a composite type, invoke ATExecChangeOwner so that we fix
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
new file mode 100644
index 3a5ec2f..a7417c9
*** a/src/backend/replication/logical/logicalfuncs.c
--- b/src/backend/replication/logical/logicalfuncs.c
***************
*** 27,32 ****
--- 27,33 ----
  
  #include "mb/pg_wchar.h"
  
+ #include "utils/acl.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/inval.h"
*************** XLogRead(char *buf, TimeLineID tli, XLog
*** 200,214 ****
  	}
  }
  
- static void
- check_permissions(void)
- {
- 	if (!superuser() && !has_rolreplication(GetUserId()))
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 				 (errmsg("must be superuser or replication role to use replication slots"))));
- }
- 
  /*
   * read_page callback for logical decoding contexts.
   *
--- 201,206 ----
*************** pg_logical_slot_get_changes_guts(Functio
*** 322,328 ****
  	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	check_permissions();
  
  	CheckLogicalDecodingRequirements();
  
--- 314,323 ----
  	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	if (!has_replication_privilege(GetUserId()))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or replication role to use replication slots")));
  
  	CheckLogicalDecodingRequirements();
  
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
new file mode 100644
index bd4701f..ebae1a2
*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 20,37 ****
  #include "replication/slot.h"
  #include "replication/logical.h"
  #include "replication/logicalfuncs.h"
  #include "utils/builtins.h"
  #include "utils/pg_lsn.h"
  
- static void
- check_permissions(void)
- {
- 	if (!superuser() && !has_rolreplication(GetUserId()))
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 				 (errmsg("must be superuser or replication role to use replication slots"))));
- }
- 
  /*
   * SQL function for creating a new physical (streaming replication)
   * replication slot.
--- 20,29 ----
  #include "replication/slot.h"
  #include "replication/logical.h"
  #include "replication/logicalfuncs.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/pg_lsn.h"
  
  /*
   * SQL function for creating a new physical (streaming replication)
   * replication slot.
*************** pg_create_physical_replication_slot(PG_F
*** 51,57 ****
  	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	check_permissions();
  
  	CheckSlotRequirements();
  
--- 43,52 ----
  	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	if (!has_replication_privilege(GetUserId()))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or replication role to use replication slots")));
  
  	CheckSlotRequirements();
  
*************** pg_create_logical_replication_slot(PG_FU
*** 94,100 ****
  	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	check_permissions();
  
  	CheckLogicalDecodingRequirements();
  
--- 89,98 ----
  	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	if (!has_replication_privilege(GetUserId()))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or replication role to use replication slots")));
  
  	CheckLogicalDecodingRequirements();
  
*************** pg_drop_replication_slot(PG_FUNCTION_ARG
*** 143,149 ****
  {
  	Name		name = PG_GETARG_NAME(0);
  
! 	check_permissions();
  
  	CheckSlotRequirements();
  
--- 141,150 ----
  {
  	Name		name = PG_GETARG_NAME(0);
  
! 	if (!has_replication_privilege(GetUserId()))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or replication role to use replication slots")));
  
  	CheckSlotRequirements();
  
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
new file mode 100644
index a703c67..2e41dc5
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
*************** SetUserIdAndContext(Oid userid, bool sec
*** 317,341 ****
  		SecurityRestrictionContext &= ~SECURITY_LOCAL_USERID_CHANGE;
  }
  
- 
- /*
-  * Check whether specified role has explicit REPLICATION privilege
-  */
- bool
- has_rolreplication(Oid roleid)
- {
- 	bool		result = false;
- 	HeapTuple	utup;
- 
- 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- 	if (HeapTupleIsValid(utup))
- 	{
- 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
- 		ReleaseSysCache(utup);
- 	}
- 	return result;
- }
- 
  /*
   * Initialize user identity during normal backend startup
   */
--- 317,322 ----
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
new file mode 100644
index 6a6a445..28c25f4
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** InitPostgres(const char *in_dbname, Oid
*** 761,767 ****
  	{
  		Assert(!bootstrap);
  
! 		if (!superuser() && !has_rolreplication(GetUserId()))
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser or replication role to start walsender")));
--- 761,767 ----
  	{
  		Assert(!bootstrap);
  
! 		if (!has_replication_privilege(GetUserId()))
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser or replication role to start walsender")));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
new file mode 100644
index 2ba9885..011bd62
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
*************** extern void ValidatePgVersion(const char
*** 434,440 ****
  extern void process_shared_preload_libraries(void);
  extern void process_session_preload_libraries(void);
  extern void pg_bindtextdomain(const char *domain);
- extern bool has_rolreplication(Oid roleid);
  
  /* in access/transam/xlog.c */
  extern bool BackupInProgress(void);
--- 434,439 ----
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
new file mode 100644
index a8e3164..1c52fdc
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** extern bool pg_event_trigger_ownercheck(
*** 328,332 ****
--- 328,333 ----
  extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
  extern bool has_createrole_privilege(Oid roleid);
  extern bool has_bypassrls_privilege(Oid roleid);
+ extern bool has_replication_privilege(Oid roleid);
  
  #endif   /* ACL_H */
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
new file mode 100644
index e4dedb0..b3b5cd0
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
*************** ERROR:  must be owner of foreign server
*** 394,399 ****
--- 394,400 ----
  ALTER SERVER s1 OWNER TO regress_test_role;                 -- ERROR
  ERROR:  must be owner of foreign server s1
  RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role;
  ALTER SERVER s1 OWNER TO regress_test_role;
  GRANT regress_test_role2 TO regress_test_role;
  SET ROLE regress_test_role;
*************** GRANT USAGE ON FOREIGN DATA WRAPPER foo
*** 417,422 ****
--- 418,424 ----
  SET ROLE regress_test_role;
  ALTER SERVER s1 OWNER TO regress_test_indirect;
  RESET ROLE;
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
  DROP ROLE regress_test_indirect;                            -- ERROR
  ERROR:  role "regress_test_indirect" cannot be dropped because some objects depend on it
  DETAIL:  owner of server s1
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
new file mode 100644
index de9dbc8..91d51c9
*** a/src/test/regress/sql/foreign_data.sql
--- b/src/test/regress/sql/foreign_data.sql
*************** SET ROLE regress_test_role;
*** 164,169 ****
--- 164,170 ----
  ALTER SERVER s1 VERSION '1.1';                              -- ERROR
  ALTER SERVER s1 OWNER TO regress_test_role;                 -- ERROR
  RESET ROLE;
+ GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role;
  ALTER SERVER s1 OWNER TO regress_test_role;
  GRANT regress_test_role2 TO regress_test_role;
  SET ROLE regress_test_role;
*************** GRANT USAGE ON FOREIGN DATA WRAPPER foo
*** 183,188 ****
--- 184,190 ----
  SET ROLE regress_test_role;
  ALTER SERVER s1 OWNER TO regress_test_indirect;
  RESET ROLE;
+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
  DROP ROLE regress_test_indirect;                            -- ERROR
  \des+
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to