Greetings,

  The attached patch for review implements a few additional role
  attributes (all requested by users or clients in various forums) for
  common operations which currently require superuser privileges.  This
  is not a complete solution for all of the superuser-only privileges we
  have but it's certainly good progress and along the correct path, as
  shown below in a review of the existing superuser checks in the
  backend.

  First though, the new privileges, about which the bikeshedding can
  begin, short-and-sweet format:

  BACKUP:
    pg_start_backup()
    pg_stop_backup()
    pg_switch_xlog()
    pg_create_restore_point()

  LOGROTATE:
    pg_rotate_logfile()

  MONITOR:
    View detailed information regarding other processes.
    pg_stat_get_wal_senders()
    pg_stat_get_activity()

  PROCSIGNAL:
    pg_signal_backend()
    (not allowed to signal superuser-owned backends)

  Before you ask, yes, this patch also does a bit of rework and cleanup.

  Yes, that could be done as an independent patch- just ask, but the
  changes are relatively minor.  One curious item to note is that the
  current if(!superuser()) {} block approach has masked an inconsistency
  in at least the FDW code which required a change to the regression
  tests- namely, we normally force FDW owners to have USAGE rights on
  the FDW, but that was being bypassed when a superuser makes the call.
  I seriously doubt that was intentional.  I won't push back if someone
  really wants it to stay that way though.

  This also includes the previously discussed changes for pgstat and
  friends to use role membership instead of GetUserId() == backend_role.

  Full documentation is not included but will be forthcoming, of course.

  As part of working through what the best solution is regarding the
  existing superuser-only checks, I did a review of more-or-less all of
  the ones which exist in the backend.  From that review, I came to the
  conclusion (certainly one which can be debated, but still) that most
  cases of superuser() checks that should be possible for non-superusers
  to do are yes/no privileges, except for when it comes to server-side
  COPY and CREATE TABLESPACE operations, which need a form of
  directory-level access control also (patch for that will be showing up
  shortly..).

  For posterity's sake, here's my review and comments on the various
  existing superuser checks in the backend (those not addressed above):

  CREATE EXTENSION
    This could be a role attribute as the others above, but I didn't
    want to try and include it in this patch as it has a lot of hairy
    parts, I expect.

  Connect using 'reserved' slots
    This is another one which we might add as another role attribute.

  Only needed during recovery, where it's fine to require superuser:
    pg_xlog_replay_pause()
    pg_xlog_replay_resume()

  Directory / File access (addressed independently):
    libpq/be/fsstubs.c
      lo_import()
      lo_export()

    commands/tablespace.c - create tablespace
    commands/copy.c - server-side COPY TO/FROM

    utils/adt/genfile.c
      pg_read_file() / pg_read_text_file() / pg_read_binary_file()
      pg_stat_file()
      pg_ls_dir()

  Lots of things depend on being able to create C functions.  These, in
  my view, should either be done through extensions or should remain the
  purview of the superuser.  Below are the ones which I identified as
  falling into this category:

  commands/tsearchcmd.c
    Create text search parser
    Create text search template

  tcop/utility.c
    LOAD (load shared library)

  commands/foreigncmds.c
    Create FDW, Alter FDW
    FDW ownership

  commands/functioncmds.c
    create binary-compatible casts
    create untrusted-language functions

  command/proclang.c
    Create procedural languages (unless PL exists in pg_pltemplate)
    Create custom procedural language

  commands/opclasscmds.c
    Create operator class
    Create operator family
    Alter operator family

  commands/aggregatecmds.c
    Create aggregates which use 'internal' types

  commands/functioncmds.c
    create leakproof functions
    alter function to be leakproof

  command/event_trigger.c
    create event trigger

  Other cases which I don't think would make sense to change (and some I
  wonder if we should prevent the superuser from doing, unless they have
  rolcatupdate or similar...):

  commands/alter.c
    rename objects (for objects which don't have an 'owner')
    change object schema (ditto)

  commands/trigger.c
    enable or disable internal triggers

  commands/functioncmds.c
    execute DO blocks with untrusted languages

  commands/dbcommands.c
    allow encoding/locale to be different

  commands/user.c
    set 'superuser' on a role
    alter superuser roles (other options)
    drop superuser roles
    alter role membership for superusers
    force 'granted by' on a role grant
    alter global settings
    rename superuser roles

  utils/misc/guc.c
    set superuser-only GUCs
    use ALTER SYSTEM
    show ALL GUCs
    get superuser-only GUC info
    define custom placeholder GUC

  utils/adt/misc.c
    reload database configuration

  utils/init/postinit.c
    connect in binary upgrade mode
    connect while database is shutting down

  Another discussion could be had regarding the superuser-only GUCs.

    Thanks!

        Stephen
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 133143d..fe7eed9
*** 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,63 ****
  
  	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")));
  
  	startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL);
  
--- 55,65 ----
  
  	backupidstr = text_to_cstring(backupid);
  
! 	if (!has_replication_privilege(GetUserId())
! 		&& !has_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser, replication role or backup role to run a backup")));
  
  	startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL);
  
*************** pg_stop_backup(PG_FUNCTION_ARGS)
*** 82,91 ****
  {
  	XLogRecPtr	stoppoint;
  
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 		 (errmsg("must be superuser or replication role to run a backup"))));
  
  	stoppoint = do_pg_stop_backup(NULL, true, NULL);
  
--- 84,94 ----
  {
  	XLogRecPtr	stoppoint;
  
! 	if (!has_replication_privilege(GetUserId())
! 		&& !has_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser, replication role or backup role to run a backup")));
  
  	stoppoint = do_pg_stop_backup(NULL, true, NULL);
  
*************** pg_switch_xlog(PG_FUNCTION_ARGS)
*** 100,109 ****
  {
  	XLogRecPtr	switchpoint;
  
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 			 (errmsg("must be superuser to switch transaction log files"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
--- 103,112 ----
  {
  	XLogRecPtr	switchpoint;
  
! 	if (!has_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or backup role to switch transaction log files")));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
*************** pg_create_restore_point(PG_FUNCTION_ARGS
*** 129,138 ****
  	char	   *restore_name_str;
  	XLogRecPtr	restorepoint;
  
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to create a restore point"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
--- 132,141 ----
  	char	   *restore_name_str;
  	XLogRecPtr	restorepoint;
  
! 	if (!has_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or backup role to create a restore point"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..64cf9f6
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** static AclMode restrict_and_check_grant(
*** 143,148 ****
--- 143,149 ----
  						 AttrNumber att_number, const char *colname);
  static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum,
  		   Oid roleid, AclMode mask, AclMaskHow how);
+ static bool has_catupdate_privilege(Oid roleid);
  
  
  #ifdef ACLDEBUG
*************** aclcheck_error_type(AclResult aclerr, Oi
*** 3425,3431 ****
  
  /* Check if given user has rolcatupdate privilege according to pg_authid */
  static bool
! has_rolcatupdate(Oid roleid)
  {
  	bool		rolcatupdate;
  	HeapTuple	tuple;
--- 3426,3432 ----
  
  /* Check if given user has rolcatupdate privilege according to pg_authid */
  static bool
! has_catupdate_privilege(Oid roleid)
  {
  	bool		rolcatupdate;
  	HeapTuple	tuple;
*************** pg_class_aclmask(Oid table_oid, Oid role
*** 3630,3636 ****
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!has_rolcatupdate(roleid) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
--- 3631,3637 ----
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!has_catupdate_privilege(roleid) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
*************** has_createrole_privilege(Oid roleid)
*** 5080,5085 ****
--- 5081,5111 ----
  	return result;
  }
  
+ /*
+  * Check whether specified role has REPLICATION privilege (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;
+ }
+ 
+ /*
+  * Check whether specified role has BYPASSRLS privilege (or is a superuser)
+  */
  bool
  has_bypassrls_privilege(Oid roleid)
  {
*************** has_bypassrls_privilege(Oid roleid)
*** 5097,5102 ****
--- 5123,5217 ----
  		ReleaseSysCache(utup);
  	}
  	return result;
+ }
+ 
+ /*
+  * Check whether specified role has BACKUP privilege (or is a superuser)
+  */
+ bool
+ has_backup_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))->rolbackup;
+ 		ReleaseSysCache(utup);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has LOGROTATE privilege (or is a superuser)
+  */
+ bool
+ has_logrotate_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))->rollogrotate;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has MONITOR privilege (or is a superuser)
+  */
+ bool
+ has_monitor_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))->rolmonitor;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has PROCSIGNAL privilege (or is a superuser)
+  */
+ bool
+ has_procsignal_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))->rolprocsignal;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
  }
  
  /*
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 ecdff1e..5fb470f
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATExecChangeOwner(Oid relationOid, Oid n
*** 8625,8651 ****
  		/* 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));
--- 8625,8647 ----
  		/* 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 55a6881..c25e237
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** AlterTypeOwner(List *names, Oid newOwner
*** 3302,3325 ****
  	 */
  	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
--- 3302,3321 ----
  	 */
  	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/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 1a73fd8..6e4cd8b
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** static void DelRoleMems(const char *role
*** 55,69 ****
  			List *memberNames, List *memberIds,
  			bool admin_opt);
  
- 
- /* Check if current user has createrole privileges */
- static bool
- have_createrole_privilege(void)
- {
- 	return has_createrole_privilege(GetUserId());
- }
- 
- 
  /*
   * CREATE ROLE
   */
--- 55,60 ----
*************** CreateRole(CreateRoleStmt *stmt)
*** 88,93 ****
--- 79,88 ----
  	bool		canlogin = false;		/* Can this user login? */
  	bool		isreplication = false;	/* Is this a replication role? */
  	bool		bypassrls = false;		/* Is this a row security enabled role? */
+ 	bool		backup = false;
+ 	bool		logrotate = false;
+ 	bool		monitor = false;
+ 	bool		procsignal = false;
  	int			connlimit = -1; /* maximum connections allowed */
  	List	   *addroleto = NIL;	/* roles to make this a member of */
  	List	   *rolemembers = NIL;		/* roles to be members of this role */
*************** CreateRole(CreateRoleStmt *stmt)
*** 108,113 ****
--- 103,112 ----
  	DefElem    *dadminmembers = NULL;
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
+ 	DefElem    *dbackup = NULL;
+ 	DefElem    *dlogrotate = NULL;
+ 	DefElem    *dmonitor = NULL;
+ 	DefElem    *dprocsignal = NULL;
  
  	/* The defaults can vary depending on the original statement type */
  	switch (stmt->stmt_type)
*************** CreateRole(CreateRoleStmt *stmt)
*** 242,247 ****
--- 241,278 ----
  						 errmsg("conflicting or redundant options")));
  			dbypassRLS = defel;
  		}
+ 		else if (strcmp(defel->defname, "backup") == 0)
+ 		{
+ 			if (dbackup)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dbackup = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "logrotate") == 0)
+ 		{
+ 			if (dlogrotate)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dlogrotate = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "monitor") == 0)
+ 		{
+ 			if (dmonitor)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dmonitor = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "procsignal") == 0)
+ 		{
+ 			if (dprocsignal)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dprocsignal = defel;
+ 		}
  		else
  			elog(ERROR, "option \"%s\" not recognized",
  				 defel->defname);
*************** CreateRole(CreateRoleStmt *stmt)
*** 279,284 ****
--- 310,323 ----
  		validUntil = strVal(dvalidUntil->arg);
  	if (dbypassRLS)
  		bypassrls = intVal(dbypassRLS->arg) != 0;
+ 	if (dbackup)
+ 		backup = intVal(dbackup->arg) != 0;
+ 	if (dlogrotate)
+ 		logrotate = intVal(dlogrotate->arg) != 0;
+ 	if (dmonitor)
+ 		monitor = intVal(dmonitor->arg) != 0;
+ 	if (dprocsignal)
+ 		procsignal = intVal(dprocsignal->arg) != 0;
  
  	/* Check some permissions first */
  	if (issuper)
*************** CreateRole(CreateRoleStmt *stmt)
*** 304,310 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to create role")));
--- 343,349 ----
  	}
  	else
  	{
! 		if (!has_createrole_privilege(GetUserId()))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to create role")));
*************** CreateRole(CreateRoleStmt *stmt)
*** 395,400 ****
--- 434,443 ----
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  
  	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
+ 	new_record[Anum_pg_authid_rolbackup - 1] = BoolGetDatum(backup);
+ 	new_record[Anum_pg_authid_rollogrotate - 1] = BoolGetDatum(logrotate);
+ 	new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor);
+ 	new_record[Anum_pg_authid_rolprocsignal - 1] = BoolGetDatum(procsignal);
  
  	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
  
*************** AlterRole(AlterRoleStmt *stmt)
*** 496,501 ****
--- 539,548 ----
  	Datum		validUntil_datum;		/* same, as timestamptz Datum */
  	bool		validUntil_null;
  	bool		bypassrls = -1;
+ 	bool		backup = -1;
+ 	bool		logrotate = -1;
+ 	bool		monitor = -1;
+ 	bool		procsignal = -1;
  	DefElem    *dpassword = NULL;
  	DefElem    *dissuper = NULL;
  	DefElem    *dinherit = NULL;
*************** AlterRole(AlterRoleStmt *stmt)
*** 507,512 ****
--- 554,563 ----
  	DefElem    *drolemembers = NULL;
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
+ 	DefElem    *dbackup = NULL;
+ 	DefElem    *dlogrotate = NULL;
+ 	DefElem    *dmonitor = NULL;
+ 	DefElem    *dprocsignal = NULL;
  	Oid			roleid;
  
  	/* Extract options from the statement node tree */
*************** AlterRole(AlterRoleStmt *stmt)
*** 609,614 ****
--- 660,697 ----
  						 errmsg("conflicting or redundant options")));
  			dbypassRLS = defel;
  		}
+ 		else if (strcmp(defel->defname, "backup") == 0)
+ 		{
+ 			if (dbackup)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dbackup = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "logrotate") == 0)
+ 		{
+ 			if (dlogrotate)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dlogrotate = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "monitor") == 0)
+ 		{
+ 			if (dmonitor)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dmonitor = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "procsignal") == 0)
+ 		{
+ 			if (dprocsignal)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dprocsignal = defel;
+ 		}
  		else
  			elog(ERROR, "option \"%s\" not recognized",
  				 defel->defname);
*************** AlterRole(AlterRoleStmt *stmt)
*** 642,647 ****
--- 725,738 ----
  		validUntil = strVal(dvalidUntil->arg);
  	if (dbypassRLS)
  		bypassrls = intVal(dbypassRLS->arg);
+ 	if (dbackup)
+ 		backup = intVal(dbackup->arg);
+ 	if (dlogrotate)
+ 		logrotate = intVal(dlogrotate->arg);
+ 	if (dmonitor)
+ 		monitor = intVal(dmonitor->arg);
+ 	if (dprocsignal)
+ 		procsignal = intVal(dprocsignal->arg);
  
  	/*
  	 * Scan the pg_authid relation to be certain the user exists.
*************** AlterRole(AlterRoleStmt *stmt)
*** 682,694 ****
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to change bypassrls attribute")));
  	}
! 	else if (!have_createrole_privilege())
  	{
  		if (!(inherit < 0 &&
  			  createrole < 0 &&
  			  createdb < 0 &&
  			  canlogin < 0 &&
  			  isreplication < 0 &&
  			  !dconnlimit &&
  			  !rolemembers &&
  			  !validUntil &&
--- 773,789 ----
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to change bypassrls attribute")));
  	}
! 	else if (!has_createrole_privilege(GetUserId()))
  	{
  		if (!(inherit < 0 &&
  			  createrole < 0 &&
  			  createdb < 0 &&
  			  canlogin < 0 &&
  			  isreplication < 0 &&
+ 			  backup < 0 &&
+ 			  logrotate < 0 &&
+ 			  monitor < 0 &&
+ 			  procsignal < 0 &&
  			  !dconnlimit &&
  			  !rolemembers &&
  			  !validUntil &&
*************** AlterRole(AlterRoleStmt *stmt)
*** 821,826 ****
--- 916,945 ----
  		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
  	}
  
+ 	if (backup >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolbackup - 1] = BoolGetDatum(backup > 0);
+ 		new_record_repl[Anum_pg_authid_rolbackup - 1] = true;
+ 	}
+ 
+ 	if (logrotate >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rollogrotate - 1] = BoolGetDatum(logrotate > 0);
+ 		new_record_repl[Anum_pg_authid_rollogrotate - 1] = true;
+ 	}
+ 
+ 	if (monitor >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor > 0);
+ 		new_record_repl[Anum_pg_authid_rolmonitor - 1] = true;
+ 	}
+ 
+ 	if (procsignal >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolprocsignal - 1] = BoolGetDatum(procsignal > 0);
+ 		new_record_repl[Anum_pg_authid_rolprocsignal - 1] = true;
+ 	}
+ 
  	new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
  								  new_record_nulls, new_record_repl);
  	simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 898,904 ****
  		}
  		else
  		{
! 			if (!have_createrole_privilege() &&
  				HeapTupleGetOid(roletuple) != GetUserId())
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1017,1023 ----
  		}
  		else
  		{
! 			if (!has_createrole_privilege(GetUserId()) &&
  				HeapTupleGetOid(roletuple) != GetUserId())
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*************** DropRole(DropRoleStmt *stmt)
*** 951,957 ****
  				pg_auth_members_rel;
  	ListCell   *item;
  
! 	if (!have_createrole_privilege())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to drop role")));
--- 1070,1076 ----
  				pg_auth_members_rel;
  	ListCell   *item;
  
! 	if (!has_createrole_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to drop role")));
*************** RenameRole(const char *oldname, const ch
*** 1182,1188 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to rename role")));
--- 1301,1307 ----
  	}
  	else
  	{
! 		if (!has_createrole_privilege(GetUserId()))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to rename role")));
*************** AddRoleMems(const char *rolename, Oid ro
*** 1409,1415 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege() &&
  			!is_admin_of_role(grantorId, roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1528,1534 ----
  	}
  	else
  	{
! 		if (!has_createrole_privilege(GetUserId()) &&
  			!is_admin_of_role(grantorId, roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*************** DelRoleMems(const char *rolename, Oid ro
*** 1555,1561 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege() &&
  			!is_admin_of_role(GetUserId(), roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1674,1680 ----
  	}
  	else
  	{
! 		if (!has_createrole_privilege(GetUserId()) &&
  			!is_admin_of_role(GetUserId(), roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index c98c27a..f23a5f3
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** AlterOptRoleElem:
*** 977,982 ****
--- 977,998 ----
  						 */
  						$$ = makeDefElem("inherit", (Node *)makeInteger(FALSE));
  					}
+ 					else if (strcmp($1, "backup") == 0)
+ 						$$ = makeDefElem("backup", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nobackup") == 0)
+ 						$$ = makeDefElem("backup", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "logrotate") == 0)
+ 						$$ = makeDefElem("logrotate", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nologrotate") == 0)
+ 						$$ = makeDefElem("logrotate", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "monitor") == 0)
+ 						$$ = makeDefElem("monitor", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nomonitor") == 0)
+ 						$$ = makeDefElem("monitor", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "procsignal") == 0)
+ 						$$ = makeDefElem("procsignal", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "noprocsignal") == 0)
+ 						$$ = makeDefElem("procsignal", (Node *)makeInteger(FALSE));
  					else
  						ereport(ERROR,
  								(errcode(ERRCODE_SYNTAX_ERROR),
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/replication/walsender.c b/src/backend/replication/walsender.c
new file mode 100644
index 384c9b6..456bf46
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
***************
*** 72,77 ****
--- 72,78 ----
  #include "storage/proc.h"
  #include "storage/procarray.h"
  #include "tcop/tcopprot.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/guc.h"
  #include "utils/memutils.h"
*************** pg_stat_get_wal_senders(PG_FUNCTION_ARGS
*** 2832,2842 ****
  		memset(nulls, 0, sizeof(nulls));
  		values[0] = Int32GetDatum(walsnd->pid);
  
! 		if (!superuser())
  		{
  			/*
! 			 * Only superusers can see details. Other users only get the pid
! 			 * value to know it's a walsender, but no details.
  			 */
  			MemSet(&nulls[1], true, PG_STAT_GET_WAL_SENDERS_COLS - 1);
  		}
--- 2833,2844 ----
  		memset(nulls, 0, sizeof(nulls));
  		values[0] = Int32GetDatum(walsnd->pid);
  
! 		if (!has_monitor_privilege(GetUserId()))
  		{
  			/*
! 			 * Only users with the MONITOR attribute or superuser privileges can
! 			 * see details. Other users only get the pid value to know it's a
! 			 * walsender, but no details.
  			 */
  			MemSet(&nulls[1], true, PG_STAT_GET_WAL_SENDERS_COLS - 1);
  		}
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
new file mode 100644
index dc6eb2c..4f9ffe5
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
*************** static AclMode convert_role_priv_string(
*** 117,122 ****
--- 117,123 ----
  static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
  
  static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
+ static bool has_inherit_privilege(Oid roleid);
  
  
  /*
*************** RoleMembershipCacheCallback(Datum arg, i
*** 4636,4642 ****
  
  /* Check if specified role has rolinherit set */
  static bool
! has_rolinherit(Oid roleid)
  {
  	bool		result = false;
  	HeapTuple	utup;
--- 4637,4643 ----
  
  /* Check if specified role has rolinherit set */
  static bool
! has_inherit_privilege(Oid roleid)
  {
  	bool		result = false;
  	HeapTuple	utup;
*************** roles_has_privs_of(Oid roleid)
*** 4697,4703 ****
  		int			i;
  
  		/* Ignore non-inheriting roles */
! 		if (!has_rolinherit(memberid))
  			continue;
  
  		/* Find roles that memberid is directly a member of */
--- 4698,4704 ----
  		int			i;
  
  		/* Ignore non-inheriting roles */
! 		if (!has_inherit_privilege(memberid))
  			continue;
  
  		/* Find roles that memberid is directly a member of */
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644
index 67539ec..0515b8f
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 37,42 ****
--- 37,43 ----
  #include "utils/lsyscache.h"
  #include "utils/ruleutils.h"
  #include "tcop/tcopprot.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/timestamp.h"
  
*************** pg_signal_backend(int pid, int sig)
*** 113,119 ****
  		return SIGNAL_BACKEND_ERROR;
  	}
  
! 	if (!(superuser() || proc->roleId == GetUserId()))
  		return SIGNAL_BACKEND_NOPERMISSION;
  
  	/*
--- 114,132 ----
  		return SIGNAL_BACKEND_ERROR;
  	}
  
! 	/*
! 	 * If the current user is not a superuser, then they aren't allowed to
! 	 * signal backends which are owned by a superuser.
! 	 */
! 	if (!superuser() && superuser_arg(proc->roleId))
! 		return SIGNAL_BACKEND_NOPERMISSION;
! 
! 	/*
! 	 * If the current user is not a member of the role owning the process and
! 	 * does not have the PROCSIGNAL permission, then permission is denied.
! 	 */
! 	if (!has_privs_of_role(GetUserId(), proc->roleId)
! 		&& !has_procsignal_privilege(GetUserId()))
  		return SIGNAL_BACKEND_NOPERMISSION;
  
  	/*
*************** pg_reload_conf(PG_FUNCTION_ARGS)
*** 202,211 ****
  Datum
  pg_rotate_logfile(PG_FUNCTION_ARGS)
  {
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to rotate log files"))));
  
  	if (!Logging_collector)
  	{
--- 215,224 ----
  Datum
  pg_rotate_logfile(PG_FUNCTION_ARGS)
  {
! 	if (!has_logrotate_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or have logrotate permission to rotate log files")));
  
  	if (!Logging_collector)
  	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
new file mode 100644
index 44ccd37..c960ebb
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "libpq/ip.h"
  #include "miscadmin.h"
  #include "pgstat.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/inet.h"
  #include "utils/timestamp.h"
*************** pg_stat_get_activity(PG_FUNCTION_ARGS)
*** 625,630 ****
--- 626,632 ----
  		HeapTuple	tuple;
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
+ 		Oid			current_user_id;
  
  		MemSet(values, 0, sizeof(values));
  		MemSet(nulls, 0, sizeof(nulls));
*************** pg_stat_get_activity(PG_FUNCTION_ARGS)
*** 674,681 ****
  		else
  			nulls[15] = true;
  
! 		/* Values only available to same user or superuser */
! 		if (superuser() || beentry->st_userid == GetUserId())
  		{
  			SockAddr	zero_clientaddr;
  
--- 676,689 ----
  		else
  			nulls[15] = true;
  
! 		/*
! 		 * Values only available to roles which are members of this role,
! 		 * or which have the MONITOR privilege.
! 		 */
! 		current_user_id = GetUserId();
! 
! 		if (has_monitor_privilege(current_user_id)
! 			|| has_privs_of_role(current_user_id, beentry->st_userid))
  		{
  			SockAddr	zero_clientaddr;
  
*************** pg_stat_get_backend_activity(PG_FUNCTION
*** 874,883 ****
  	int32		beid = PG_GETARG_INT32(0);
  	PgBackendStatus *beentry;
  	const char *activity;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		activity = "<backend information not available>";
! 	else if (!superuser() && beentry->st_userid != GetUserId())
  		activity = "<insufficient privilege>";
  	else if (*(beentry->st_activity) == '\0')
  		activity = "<command string not enabled>";
--- 882,895 ----
  	int32		beid = PG_GETARG_INT32(0);
  	PgBackendStatus *beentry;
  	const char *activity;
+ 	Oid			current_user_id;
+ 
+ 	current_user_id = GetUserId();
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		activity = "<backend information not available>";
! 	else if (!has_monitor_privilege(current_user_id)
! 			 && !has_privs_of_role(current_user_id, beentry->st_userid))
  		activity = "<insufficient privilege>";
  	else if (*(beentry->st_activity) == '\0')
  		activity = "<command string not enabled>";
*************** pg_stat_get_backend_waiting(PG_FUNCTION_
*** 894,904 ****
  	int32		beid = PG_GETARG_INT32(0);
  	bool		result;
  	PgBackendStatus *beentry;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	if (!superuser() && beentry->st_userid != GetUserId())
  		PG_RETURN_NULL();
  
  	result = beentry->st_waiting;
--- 906,920 ----
  	int32		beid = PG_GETARG_INT32(0);
  	bool		result;
  	PgBackendStatus *beentry;
+ 	Oid			current_user_id;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	current_user_id = GetUserId();
! 
! 	if (!has_monitor_privilege(current_user_id)
! 		&& !has_privs_of_role(current_user_id, beentry->st_userid))
  		PG_RETURN_NULL();
  
  	result = beentry->st_waiting;
*************** pg_stat_get_backend_activity_start(PG_FU
*** 913,923 ****
  	int32		beid = PG_GETARG_INT32(0);
  	TimestampTz result;
  	PgBackendStatus *beentry;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	if (!superuser() && beentry->st_userid != GetUserId())
  		PG_RETURN_NULL();
  
  	result = beentry->st_activity_start_timestamp;
--- 929,943 ----
  	int32		beid = PG_GETARG_INT32(0);
  	TimestampTz result;
  	PgBackendStatus *beentry;
+ 	Oid			current_user_id;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	current_user_id = GetUserId();
! 
! 	if (!has_monitor_privilege(current_user_id)
! 		&& !has_privs_of_role(current_user_id, beentry->st_userid))
  		PG_RETURN_NULL();
  
  	result = beentry->st_activity_start_timestamp;
*************** pg_stat_get_backend_xact_start(PG_FUNCTI
*** 939,949 ****
  	int32		beid = PG_GETARG_INT32(0);
  	TimestampTz result;
  	PgBackendStatus *beentry;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	if (!superuser() && beentry->st_userid != GetUserId())
  		PG_RETURN_NULL();
  
  	result = beentry->st_xact_start_timestamp;
--- 959,973 ----
  	int32		beid = PG_GETARG_INT32(0);
  	TimestampTz result;
  	PgBackendStatus *beentry;
+ 	Oid			current_user_id;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	current_user_id = GetUserId();
! 
! 	if (!has_monitor_privilege(current_user_id)
! 		&& !has_privs_of_role(current_user_id, beentry->st_userid))
  		PG_RETURN_NULL();
  
  	result = beentry->st_xact_start_timestamp;
*************** pg_stat_get_backend_start(PG_FUNCTION_AR
*** 961,971 ****
  	int32		beid = PG_GETARG_INT32(0);
  	TimestampTz result;
  	PgBackendStatus *beentry;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	if (!superuser() && beentry->st_userid != GetUserId())
  		PG_RETURN_NULL();
  
  	result = beentry->st_proc_start_timestamp;
--- 985,999 ----
  	int32		beid = PG_GETARG_INT32(0);
  	TimestampTz result;
  	PgBackendStatus *beentry;
+ 	Oid			current_user_id;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	current_user_id = GetUserId();
! 
! 	if (!has_monitor_privilege(current_user_id)
! 		&& !has_privs_of_role(current_user_id, beentry->st_userid))
  		PG_RETURN_NULL();
  
  	result = beentry->st_proc_start_timestamp;
*************** pg_stat_get_backend_client_addr(PG_FUNCT
*** 985,995 ****
  	SockAddr	zero_clientaddr;
  	char		remote_host[NI_MAXHOST];
  	int			ret;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	if (!superuser() && beentry->st_userid != GetUserId())
  		PG_RETURN_NULL();
  
  	/* A zeroed client addr means we don't know */
--- 1013,1027 ----
  	SockAddr	zero_clientaddr;
  	char		remote_host[NI_MAXHOST];
  	int			ret;
+ 	Oid			current_user_id;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	current_user_id = GetUserId();
! 
! 	if (!has_monitor_privilege(current_user_id)
! 		&& !has_privs_of_role(current_user_id, beentry->st_userid))
  		PG_RETURN_NULL();
  
  	/* A zeroed client addr means we don't know */
*************** pg_stat_get_backend_client_port(PG_FUNCT
*** 1032,1042 ****
  	SockAddr	zero_clientaddr;
  	char		remote_port[NI_MAXSERV];
  	int			ret;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	if (!superuser() && beentry->st_userid != GetUserId())
  		PG_RETURN_NULL();
  
  	/* A zeroed client addr means we don't know */
--- 1064,1082 ----
  	SockAddr	zero_clientaddr;
  	char		remote_port[NI_MAXSERV];
  	int			ret;
+ 	Oid			current_user_id;
  
  	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
  		PG_RETURN_NULL();
  
! 	/*
! 	 * User must have MONITOR attribute, be superuser or be the same
! 	 * backend user.
! 	 */
! 	current_user_id = GetUserId();
! 
! 	if (!has_monitor_privilege(current_user_id)
! 		&& !has_privs_of_role(current_user_id, beentry->st_userid))
  		PG_RETURN_NULL();
  
  	/* A zeroed client addr means we don't know */
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/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
new file mode 100644
index 3b63d2b..b636925
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
*************** CATALOG(pg_authid,1260) BKI_SHARED_RELAT
*** 53,58 ****
--- 53,62 ----
  	bool		rolcanlogin;	/* allowed to log in as session user? */
  	bool		rolreplication; /* role used for streaming replication */
  	bool		rolbypassrls;	/* allowed to bypass row level security? */
+ 	bool		rolbackup;		/* allowed to peform backup operations? */
+ 	bool		rollogrotate;	/* allowed to rotate log files? */
+ 	bool		rolmonitor;		/* allowed to view pg_stat_* details? */
+ 	bool		rolprocsignal;	/* allowed to signal backed processes? */
  	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
  
  	/* remaining fields may be null; use heap_getattr to read them! */
*************** typedef FormData_pg_authid *Form_pg_auth
*** 74,80 ****
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					12
  #define Anum_pg_authid_rolname			1
  #define Anum_pg_authid_rolsuper			2
  #define Anum_pg_authid_rolinherit		3
--- 78,84 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					16
  #define Anum_pg_authid_rolname			1
  #define Anum_pg_authid_rolsuper			2
  #define Anum_pg_authid_rolinherit		3
*************** typedef FormData_pg_authid *Form_pg_auth
*** 84,92 ****
  #define Anum_pg_authid_rolcanlogin		7
  #define Anum_pg_authid_rolreplication	8
  #define Anum_pg_authid_rolbypassrls		9
! #define Anum_pg_authid_rolconnlimit		10
! #define Anum_pg_authid_rolpassword		11
! #define Anum_pg_authid_rolvaliduntil	12
  
  /* ----------------
   *		initial contents of pg_authid
--- 88,100 ----
  #define Anum_pg_authid_rolcanlogin		7
  #define Anum_pg_authid_rolreplication	8
  #define Anum_pg_authid_rolbypassrls		9
! #define Anum_pg_authid_rolbackup		10
! #define Anum_pg_authid_rollogrotate		11
! #define Anum_pg_authid_rolmonitor		12
! #define Anum_pg_authid_rolprocsignal	13
! #define Anum_pg_authid_rolconnlimit		14
! #define Anum_pg_authid_rolpassword		15
! #define Anum_pg_authid_rolvaliduntil	16
  
  /* ----------------
   *		initial contents of pg_authid
*************** typedef FormData_pg_authid *Form_pg_auth
*** 95,101 ****
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
--- 103,109 ----
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t t t t t -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
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..ac242b2
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** extern bool pg_event_trigger_ownercheck(
*** 328,332 ****
--- 328,337 ----
  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);
+ extern bool has_backup_privilege(Oid roleid);
+ extern bool has_logrotate_privilege(Oid roleid);
+ extern bool has_monitor_privilege(Oid roleid);
+ extern bool has_procsignal_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+
  

Attachment: signature.asc
Description: Digital signature

Reply via email to